'probe' function is never called for i2c 'tca8418 keypad' driver

For the past few days I’ve been trying to get an i2c keypad to work with the dragonboard-410c but the ‘probe’ function from the tca8418_keypad driver is never called. What do I need to do to make sure tca8418_keypad_probe() gets properly called?

The physical setup seems fine (validated manually through the command line and using an i2c analyzer, see extra information at the end of the post) so the problem seems to be a kernel configuration issue.

I have enabled ‘TCA8418 Keypad Support’ in the kernel using ‘linux-linaro-qcomlt_4.4.bbappend’ located in a custom layer, along with a simple patch to add debug traces in the tca8418 driver. I can confirm that the driver is properly installed since I get a trace when I boot the board:

...
[    0.295441] usbcore: registered new device driver usb
[    0.297314] ===> tca8418_keypad_init
[    0.298178] media: Linux media interface: v0.10
...

I know that i2c devices are not automatically detected and need to be declared in a ‘board.c’ file matching the target architecture. I tried adding the following under arch/arm/mach-qcom but it didn’t change anything:

static struct i2c_board_info myproject_i2c_board_info[] __initdata =
{
   {
        I2C_BOARD_INFO("tca8418_keypad", 0x34),
   },
};

static void __init myproject_init(void)
{
   i2c_register_board_info(0, myproject_i2c_board_info, ARRAY_SIZE(myproject_i2c_board_info));
}

If it’s not arch/arm/mach-qcom, what architecture is it? What do I need to modify in the kernel so that the tca8418_keypad_probe() function gets called?

I saw that some hardware configurations are done through *.dtsi files, do I need to register i2c drivers through that? If so, how?

Any help on this would be much appreciated, thanks.

Extra information:

The keypad is located on i2c-0 @ 0x34 as shown by i2cdetect:

dragonboard-410c:/home/linaro# i2cdetect -y -r 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: – – – – – – – – – – – – –
10: – – – – – – – – – – – – – 1d – –
20: – – – – – – – – – – – – – – – –
30: – – – – 34 – – – – – – – – – – –
40: – – – – – – – – – – – – – – – –
50: – – – – – – – – – – – – – – – –
60: – – – – – – – – – – – – – – – –
70: – – – – – – – –

From the command line I can manually register the keypad driver (and probe() gets called), but I get an error:

echo tca8418_keypad 0x34 > /sys/bus/i2c/devices/i2c-0/new_device
[  135.231098] ===> tca8418_keypad_probe
[  135.231199] tca8418_keypad 0-0034: missing DT data

From the command line I can manually configure the keypad as the tca8418 driver would:
i2cset -y 0 0x34 0x1D 0xFF
i2cset -y 0 0x34 0x1E 0xFF
i2cset -y 0 0x34 0x1F 0x3
i2cset -y 0 0x34 0x29 0xFF
i2cset -y 0 0x34 0x2a 0xFF
i2cset -y 0 0x34 0x2b 0x3

Once configured, I can press a physical key on the keypad and then do ‘i2cget -y 0 0x34 0x4’ to read the FIFO. The key values that are read make sense (proper key values, including press/release flags).

It sounds like you have the hardware connected correctly, that is the first part of the problem which you have solved.

The second part of the problem is setting up the software. You need to rebuild the kernel to add the tca8418 driver if it isn’t there already, reconfigure the kernel and ensure the driver is there.

make menuconfig

You also need to make changes to the device tree to get the kernel to probe for the HW. I previously added a Dallas DS1307 which is a I2C peripheral, here is the change I made to arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi

i2c@78b6000 {
/* On Low speed Expansion */
		Label = “LS-I2C0”
                status = "okay";
+		rtc@68 {
+		compatible = "maxim,ds1307";
+		reg = <0x68>;
+		};
};

You need to add something similar for your I2C device.

Thanks a lot ljking, you put me back on the right track.

I’ve added the following to arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi and the probe function is finally called!

keypad@34 {
                compatible = "ti,tca8418";
                reg = <0x34>;
                interrupts = <110 0>;
                keypad,num-rows = <8>;
                keypad,num-columns = <10>;
                linux,keymap = <KEY_0
                                KEY_1
                                KEY_2
                                KEY_3
                                KEY_4
                                KEY_5
                                KEY_6
                                KEY_7
                                KEY_8
                                KEY_9
                                KEY_A
                                KEY_B
                                KEY_C
                                KEY_D
                                KEY_E
                                KEY_F
                                KEY_G
                                KEY_H
                                KEY_I >;
            };

Now that the probe function gets called, I’m having a weird error:


[    2.330920] tca8418_keypad 0-0034: Unable to claim irq 197; error -22
[    2.337828] input: tca8418 as /devices/platform/soc/78b6000.i2c/i2c-0/0-0034/input/input1

The IRQ for my keypad is GPIO 110, but then I get an ‘irq 197’ error. I do not understand what is the relation between gpio110 and irq197. I tried other values (or just skipping the parameter completely) but then I get an ‘irq 0’ error instead.

I think I’m specifying the interrupts parameter incorrectly, but the https://www.kernel.org/doc/Documentation/devicetree/bindings/input/tca8418_keypad.txt documentation is rather vague, simply stating that “interrupts: IRQ line number, should trigger on falling edge” without any kind of expected values.

Official documentation for other input modules) are just as vague, sometimes using 1,2 or 3 parameters, but without much details.

Does anyone have a link to documentation explaining what the syntax should be like for the different DT fields? Haven’t been able to find anything relevant yet.

I’m trying to figure out a similar issue (no driver probe call with a NXP PN7150 in Android, but I think I have a hardware problem to fix first).

Try putting interrupt-parent = <&msm_gpio>; before your interrupts = <110 0>;.
I noticed that in apq8016-sbc.dtsi (at least the one I got with the Android code) there are a few extra I2C devices defined, and they all have that line. I used those examples to get a touch panel working.

For reference, here’s the working touch panel config. I hope it helps you sort out your interrupt config.

focaltech@38 { compatible = "focaltech,5x06"; reg = <0x38>; interrupt-parent = <&msm_gpio>; interrupts = <69 0x2>; vdd-supply = <&pm8916_l17>; vcc_i2c-supply = <&pm8916_l6>; /* pins used by touchscreen */ pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; pinctrl-0 = <&ts_int_active &ts_reset_active>; pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; pinctrl-2 = <&ts_release>; focaltech,name = "ft5336"; focaltech,family-id = <0x14>; focaltech,reset-gpio = <&msm_gpio 13 0x0>; focaltech,irq-gpio = <&msm_gpio 69 0x0>; focaltech,display-coords = <0 0 480 800>; focaltech,panel-coords = <0 0 480 800>; //focaltech,button-map= <139 102 158>; focaltech,no-force-update; focaltech,i2c-pull-up; focaltech,group-id = <1>; focaltech,hard-reset-delay-ms = <20>; focaltech,soft-reset-delay-ms = <200>; focaltech,num-max-touches = <5>; focaltech,fw-delay-aa-ms = <30>; focaltech,fw-delay-55-ms = <30>; focaltech,fw-upgrade-id1 = <0x79>; focaltech,fw-upgrade-id2 = <0x18>; focaltech,fw-delay-readid-ms = <10>; focaltech,fw-delay-era-flsh-ms = <2000>; focaltech,fw-auto-cal; focaltech,ignore-id-check; };

I didn’t find “msm_gpio” in the kernel but there was multiple intances of “msmgpio” so I tried that. The driver now registers properly and is visible when using ‘evtest’. I also changed the second ‘interrupts’ parameter to specify the IRQ type (falling edge in this case).

The final configuration in apq8016-sbc.dtsi looks like this:


keypad@34 {
    compatible = &quot;ti,tca8418&quot;;
    reg = &lt;0x34&gt;;
    interrupts = &lt;110 IRQ_TYPE_EDGE_FALLING&gt;;
    interrupt-parent = &lt;&amp;msmgpio&gt;;
    keypad,num-rows = &lt;8&gt;;
    keypad,num-columns = &lt;10&gt;;
    linux,keymap = &lt;KEY_NUMERIC_0
                    KEY_NUMERIC_1
                    ...
                    KEY_NUMERIC_8
                    KEY_NUMERIC_9&gt;;
    };
};


Unfortunately I do not get any events when using evtest because there is no interrupt generated on gpio110 (validated using a scope). The problem is probably related to the gpio pin configuration but the default settings seems to be ok, but I’m probably missing a configuration line to make sure the pin can act as an interrupt:


ext-pri-ws-line {
  ext_pri_ws_act: ext_pa_on {
    pinmux {
      function = &quot;pri_mi2s_ws&quot;;
      pins = &quot;gpio110&quot;;
    };
    pinconf {
        pins = &quot;gpio110&quot;;
	drive-strength = &lt;8&gt;;
	bias-pull-none;
    };
  };

  ext_pri_ws_sus: ext_pa_off {
    pinmux {
      function = &quot;pri_mi2s_ws&quot;;
      pins = &quot;gpio110&quot;;
    };
    pinconf {
      pins = &quot;gpio110&quot;;
      drive-strength = &lt;2&gt;;
      bias-disable;
    };
  };
};