Connect MAX7359 to AML-S905X-CC

I have a MAX7359 keypad which is already supported by linux as a keyboard. See: [https://github.com/torvalds/linux/blob/master/drivers/input/keyboard/max7359_keypad.c](max7359 driver source)

I have attached it to my AML-S905X-CC and enabled pin 3 and 5 of the header and followed instructions on this page: How to Enable I2C on AML-S905X-CC Le Potato

sudo ldto enable i2c-ao

I can confirm that the i2c communication is working via i2cget command. For that test I used prebuilt raspbian flashed to a micro sdcard.

I believe the next steps to get the MAX7359 working as a keyboard are:

  • Rebuild the kernel with CONFIG_KEYBOARD_MAX7359 enabled and flash it
  • Write a device tree overlay in the libretech-wiring-tool project for the MAX7359, rebuild LWT, copy it to the device and run a command to enable the overlay

Hopefully this is an approach that will work, if there’s any easier way let me know.

I followed this post SPI and u-boot tweaks on Le Potato Buildroot and using buildroot I can produce an sdcard.img which I can flash to an sdcard and reach login via UART console. I added CONFIG_KEYBOARD_MAX7359=y to board/librecomputer/amlogic/linux.config and rebuilt and flashed and looking at /proc/cofig.gz it appears to have worked.

I’m only vaguely familar with the linux device tree, I assume I need to make an entry which is a child of the i2c bus it is attached to, specifiy keypads i2c address, and the interrupt line it will use (I have chosen pin 11 which is apparently GPIOAO_8), along with the rows/cols of the keypad matrix. I’m not 100% what to cput in the “compatible” field, in other driver source code I see a line like compatible = "abc,xyz", but I don’t see any such line in max7359_keypad.c I only see the string "max7359" in a couple places.

Here’s the dtsi file I created which compiles (after adding input.h and linux-event-codes.h)

/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/meson-gic.h>

/{
	compatible = "libretech,cc", "amlogic,s905x", "amlogic,meson-gxl";

	fragment@1 {
		target = <&i2c_AO>;
		
		__overlay__ {
			max7359@20 {
				compatible = "max7359";
				reg = <0x20>;
				interrupt-parent = <&gpio_intc>;
				interrupts = <MESON_GIC_GXL_GPIOAO_8 0x2008>;
				linux,keymap = <
				MATRIX_KEY(0, 0, KEY_NUMERIC_0)
				MATRIX_KEY(0, 1, KEY_NUMERIC_1)
				MATRIX_KEY(0, 2, KEY_NUMERIC_2)
				MATRIX_KEY(1, 0, KEY_NUMERIC_3)
				MATRIX_KEY(1, 1, KEY_NUMERIC_4)
				MATRIX_KEY(1, 2, KEY_NUMERIC_5)
				MATRIX_KEY(2, 0, KEY_NUMERIC_6)
				MATRIX_KEY(2, 1, KEY_NUMERIC_7)
				MATRIX_KEY(2, 2, KEY_NUMERIC_8)
				MATRIX_KEY(3, 0, KEY_NUMERIC_9)
				MATRIX_KEY(3, 1, KEY_ENTER)
				MATRIX_KEY(3, 2, KEY_ESC)
				>;
				status = "okay";
			};
		};
	};
};

Let me know if anything in the device tree file looks obviously wrong.

This appears to produce a file named libretech-dtoverlay_2023.08.17_all.deb but my Buildroot produces barebones embedded system which doesn’t have dpkg. I tried unpacking the deb file and copying the contents to the filesystem of the device but I cannot run the ldto program for some reason:

# pwd
/opt/librecomputer/libretech-wiring-tool
# ls -l
total 8
-rwxr-xr-x    1 root     root          5586 Jan  1 00:24 ldto
drwxr-xr-x    8 root     root           160 Jan  1 00:24 libre-computer
# ./ldto
-sh: ./ldto: not found

Can I use the kernel produced by buildroot with a prebuilt distro such as Raspbian which will have dpkg and allow me to easily install my custom version of the libretech wiring tool?

Alternatively maybe I can just modify the device tree that is used by buildroot so support for the keypad is there at boot and skip modifying and loading the libretech wiring tool, but looking at libretech-buildroot it looks like the device tree files are downloaded from somewhere, I only see them in the output directory, for example ./output/build/linux-6.1.30/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts and modifying that doesn’t seem to work.

I figured out a way to tell buildroot to use a local kernel from source, I created the file libretech-buildroot/local.mk with the following:

LINUX_OVERRIDE_SRCDIR = ../custom-buildroot-linux

I copied the entire linux source that had be previously downloaded from output/build/linux-6.1.30 to ../custom-buildroot-linux so I could make modifications to the device tree there.

I also decided to make CONFIG_KEYBOARD_MAX7359 a kernel module so I can load and unload it with modprobe and rmmod.

Then I modified the meson-gxl-s905x-libretech-cc.dts file like so

*** 20,25 ****
--- 20,26 ----
  	aliases {
  		serial0 = &uart_AO;
  		ethernet0 = &ethmac;
+ 		i2c0 = &i2c_AO;
  	};
  
  	dio2133: analog-amplifier {
***************
*** 354,356 ****
--- 355,385 ----
  	 */
  	phy-supply = <&hdmi_5v>;
  };
+ 
+ &i2c_AO {
+ 	status = "okay";
+ 	pinctrl-0 = <&i2c_ao_pins>;
+ 	pinctrl-names = "default";
+ 
+ 	max7359@20 {
+ 		compatible = "max7359";
+ 		reg = <0x20>;
+ 		interrupt-parent = <&gpio_intc>;
+ 		interrupts = <8 0x2008>;
+ 		linux,keymap = <
+ 		MATRIX_KEY(0, 0, KEY_NUMERIC_0)
+ 		MATRIX_KEY(0, 1, KEY_NUMERIC_1)
+ 		MATRIX_KEY(0, 2, KEY_NUMERIC_2)
+ 		MATRIX_KEY(1, 0, KEY_NUMERIC_3)
+ 		MATRIX_KEY(1, 1, KEY_NUMERIC_4)
+ 		MATRIX_KEY(1, 2, KEY_NUMERIC_5)
+ 		MATRIX_KEY(2, 0, KEY_NUMERIC_6)
+ 		MATRIX_KEY(2, 1, KEY_NUMERIC_7)
+ 		MATRIX_KEY(2, 2, KEY_NUMERIC_8)
+ 		MATRIX_KEY(3, 0, KEY_NUMERIC_9)
+ 		MATRIX_KEY(3, 1, KEY_ENTER)
+ 		MATRIX_KEY(3, 2, KEY_ESC)
+ 		>;
+ 		status = "okay";
+ 	};
+ };

I based the above on the i2c configuration I saw in the kernel file meson8m2-mxiii-plus.dts and in the libretech wiring tool file i2c-ao.dts.

I tried using i2cdetect -y 0 but I don’t see any electrical traffic on the i2c bus via header pins 3 and 5 at all.

When I attempt to load the module with modprobe max7359_keypad the communication clearly fails but that’s not surprising given that i2cdetect isn’t working.

Here’s what that failure looks like though it’s probably not useful info:

[ 5887.565730] ------------[ cut here ]------------
[ 5887.565860] WARNING: CPU: 2 PID: 1233 at kernel/irq/irqdomain.c:839 irq_create_fwspec_mapping+0x2b0/0x330
[ 5887.574350] Modules linked in: max7359_keypad(+) snd_soc_hdmi_codec dw_hdmi_i2s_audio meson_gxl dwmac_generic meson_drm crct10dif_ce dwmac_meson8b lima stmmac_platform meson_canvas drm_shmem_helper drm_dma_helper snd_soc_meson_gx_sound_card snd_soc_meson_card_utils meson_dw_hdmi dw_hdmi cec gpu_sched stmmac pcs_xpcs drm_display_helper meson_ir meson_rng amlogic_gxl_crypto rc_core rng_core crypto_engine meson_gxbb_wdt snd_soc_meson_t9015 snd_soc_meson_aiu snd_soc_meson_codec_glue display_connector drm_kms_helper nvmem_meson_efuse snd_soc_simple_amplifier drm
[ 5887.623873] CPU: 2 PID: 1233 Comm: modprobe Not tainted 6.1.30 #8
[ 5887.629329] Hardware name: libre-computer aml-s905x-cc/aml-s905x-cc, BIOS 2023.07+ 07/01/2023
[ 5887.637814] pstate: 00000005 (nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 5887.644691] pc : irq_create_fwspec_mapping+0x2b0/0x330
[ 5887.649756] lr : irq_create_fwspec_mapping+0x64/0x330
[ 5887.654758] sp : ffff80000af5b790
[ 5887.658012] x29: ffff80000af5b790 x28: 0000000000000000 x27: ffff80000af5bce0
[ 5887.665135] x26: ffff80000af5bc90 x25: ffff80000af5bce0 x24: 000000000000002d
[ 5887.672207] x23: ffffdddd260eeb10 x22: 0000000000000000 x21: 0000000000000000
[ 5887.679280] x20: ffff80000af5b7f8 x19: ffff5bcac18d9800 x18: ffffffffffffffff
[ 5887.686353] x17: 0000000000000000 x16: ffffdddd247992f0 x15: ffff5bcb311aaf8a
[ 5887.693426] x14: ffffffffffffffff x13: ffff5bcb311aaf88 x12: 622e756e672e6574
[ 5887.700497] x11: 0000000000000048 x10: 000000000003d480 x9 : 0000000000000005
[ 5887.707570] x8 : ffff80000af5b918 x7 : 0000000000000000 x6 : 070f1900f2f2f5f0
[ 5887.714642] x5 : ffffdddd2433e650 x4 : 0000000000000008 x3 : ffff80000af5b7c8
[ 5887.721715] x2 : ffff80000af5b7d0 x1 : 0000000000002008 x0 : 0000000000002008
[ 5887.728788] Call trace:
[ 5887.731142]  irq_create_fwspec_mapping+0x2b0/0x330
[ 5887.735917]  irq_create_of_mapping+0x68/0x90
[ 5887.740134]  of_irq_get+0x8c/0xd0
[ 5887.743399]  i2c_device_probe+0x270/0x300
[ 5887.747376]  really_probe+0xbc/0x2e0
[ 5887.750907]  __driver_probe_device+0x78/0x120
[ 5887.755229]  driver_probe_device+0xd8/0x160
[ 5887.759367]  __driver_attach+0x94/0x1a0
[ 5887.763157]  bus_for_each_dev+0x70/0xd0
[ 5887.766953]  driver_attach+0x24/0x30
[ 5887.770485]  bus_add_driver+0x154/0x210
[ 5887.774284]  driver_register+0x78/0x130
[ 5887.778079]  i2c_register_driver+0x48/0xd0
[ 5887.787304]  max7359_i2c_driver_init+0x20/0x1000 [max7359_keypad]
[ 5887.793361]  do_one_initcall+0x50/0x1d0
[ 5887.798589]  do_init_module+0x48/0x1d0
[ 5887.803780]  load_module+0x17b4/0x1ca0
[ 5887.808935]  __do_sys_finit_module+0xa8/0x100
[ 5887.814075]  __arm64_sys_finit_module+0x20/0x30
[ 5887.819185]  invoke_syscall+0x48/0x120
[ 5887.824250]  el0_svc_common.constprop.0+0x44/0x100
[ 5887.829322]  do_el0_svc+0x30/0xd0
[ 5887.834341]  el0_svc+0x2c/0x90
[ 5887.839237]  el0t_64_sync_handler+0xbc/0x140
[ 5887.842742]  el0t_64_sync+0x18c/0x190
[ 5887.846242] ---[ end trace 0000000000000000 ]---
[ 5888.381539] max7359 0-0020: max7359_read_reg: reg 0x0, err -110
[ 5888.387041] max7359 0-0020: failed to detect device

I made an important discovery, I noticed that when I disconnected the connection to the MAX7359 from pin 3 and 5 so the i2c bus is floating I got strange output from the i2cdetect command:

# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 
10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 
20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 
30: -- -- -- -- -- -- -- -- 38 39 3a 3b 3c 3d 3e 3f 
40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 
70: 70 71 72 73 74 75 76 77 

I guessed this was because the pins are now floating and i2c spec requires there to be a pull up on both SDA and SCL lines and I was missing that. After adding pull up resistors I successfully see my chip and basic i2c communication is working!

What doesn’t make sense is how raspbian was working, I guess somewhere in there an internal pull up was configured on those i2c pins?

Now that I have i2c communication working I think things are looking good, my interrupt isn’t working right now but I feel that’s likely solvable soon.

  1. For buildroot, we recommend using the follow: GitHub - libre-computer-project/libretech-buildroot: libretech buildroot
  2. Use the aml-a311d-cc-v01 as an example to create the defconfig and overlay for any other board.
  3. You may need to create new genimage cfg and scripts to your filesystem design.
  4. Our linux tree already enable that device driver that as a module.
  5. We enable the internal pull-ups for those pins by default using our bootloaders. The buildroot tree genimage scripts makes use of this already.
  6. For overlays, we recommend pre-creating the dtb and putting it in the default search path of the bootloader.

Thanks for your tips.

I am currently using GitHub - libre-computer-project/libretech-buildroot: libretech buildroot on the 2023.02.x/amlogic branch. I then utilized configs/aml-s905x-cc_defconfig.

I didn’t see MAX7359 enabled as a module, see https://github.com/libre-computer-project/libretech-buildroot/blob/2023.02.x/amlogic/board/librecomputer/amlogic/linux.config

Should I be on master branch instead? I don’t see a aml-s905x-cc_defconfig on the master branch so I’m not clear how to use it right now. This project is my first time using buildroot so maybe I’m missing some understanding. Anyway since I’m making progress with my current setup I am continuing to focus on getting the MAX7359 working and perhaps later I can figure out how to implement it as an overlay.

My current setup is working OK except for the interrupt at the moment. I notice that the call trace from the kernel in my previous post appears to point to a problem happening at kernel/irq/irqdomain.c:839. Kernel code there is:

	/*
	 * WARN if the irqchip returns a type with bits
	 * outside the sense mask set and clear these bits.
	 */
	if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
		type &= IRQ_TYPE_SENSE_MASK;

Does something look wrong with my device tree interrupt configuration?

+ 		interrupt-parent = <&gpio_intc>;
+ 		interrupts = <8 0x2008>;

I see actually I should just do:

+		interrupts = <8 IRQ_TYPE_LEVEL_LOW>;

Linux doesn’t like it when I try to include IRQF_ONESHOT which is 0x2000 in flags. But it’s just a warning anyway. Even after that fix I’m not seeing the Linux interrupt handler trigger though my logic analyzer shows header pin 11 is going from high to low. I noticed in the following comment about GPIOAO_8 which I was trying to use.

  • Requires 2J1 jumper to be positioned to pass GPIOAO_8 to 40 pin header. Default is set to HDMI CEC. Move the jumper to the two pins on the edge of the board for controlling GPIO on the 40 pin header.

I am using GPIOAO 9 instead now since that appears to be freely available and interrupts are working which is great.

Please visit GitHub - libre-computer-project/libretech-buildroot: libretech buildroot

We have updated some elements of how we plan to support buildroot architecturally across our boards.