On 27. 07. 19 9:31, Dmitry Torokhov wrote:
On Fri, Jul 26, 2019 at 01:31:31PM +0200, Michal Vokáč wrote:
On 25. 07. 19 16:40, Dmitry Torokhov wrote:
On Thu, Jul 25, 2019 at 02:58:02PM +0200, Michal Vokáč wrote:
On 25. 07. 19 10:57, Dmitry Torokhov wrote:
Hi Michal,
On Tue, May 21, 2019 at 08:51:17AM +0200, Michal Vokáč wrote:
On 21. 05. 19 7:37, Dmitry Torokhov wrote:
Hi Michal,
On Fri, May 17, 2019 at 03:12:49PM +0200, Michal Vokáč wrote:
Hi,
I have to deal with a situation where we have a custom i.MX6 based
platform in production that uses the MPR121 touchkey controller.
Unfortunately the chip is connected using only the I2C interface.
The interrupt line is not used. Back in 2015 (Linux v3.14), my
colleague modded the existing mpr121_touchkey.c driver to use polling
instead of interrupt.
For quite some time yet I am in a process of updating the product from
the ancient Freescale v3.14 kernel to the latest mainline and pushing
any needed changes upstream. The DT files for our imx6dl-yapp4 platform
already made it into v5.1-rc.
I rebased and updated our mpr121 patch to the latest mainline.
It is created as a separate driver, similarly to gpio_keys_polled.
The I2C device is quite susceptible to ESD. An ESD test quite often
causes reset of the chip or some register randomly changes its value.
The [PATCH 3/4] adds a write-through register cache. With the cache
this state can be detected and the device can be re-initialied.
The main question is: Is there any chance that such a polled driver
could be accepted? Is it correct to implement it as a separate driver
or should it be done as an option in the existing driver? I can not
really imagine how I would do that though..
There are also certain worries that the MPR121 chip may no longer be
available in nonspecifically distant future. In case of EOL I will need
to add a polled driver for an other touchkey chip. May it be already
in mainline or a completely new one.
I think that my addition of input_polled_dev was ultimately a wrong
thing to do. I am looking into enabling polling mode for regular input
devices as we then can enable polling mode in existing drivers.
OK, that sounds good. Especially when one needs to switch from one chip
to another that is already in tree, the need for a whole new polling
driver is eliminated.
Could you please try the patch below and see if it works for your use
case? Note that I have not tried running it, but it compiles so it must
be good ;)
Hi Dmitry,
Thank you very much for the patch!
I gave it a shot and it seems you forgot to add the input-poller.h file
to the patch.. it does not compile on my side :(
Oops ;) Please see the updated patch below.
Thank you, now it is (almost) good as you said :D
Input: add support for polling to input devices
From: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
Separating "normal" and "polled" input devices was a mistake, as often we want
to allow the very same device work on both interrupt-driven and polled mode,
depending on the board on which the device is used.
This introduces new APIs:
- input_setup_polling
- input_set_poll_interval
- input_set_min_poll_interval
- input_set_max_poll_interval
These new APIs allow switching an input device into polled mode with sysfs
attributes matching drivers using input_polled_dev APIs that will be eventually
removed.
After reading this I am not really sure what else needs to be done
to test/use the poller. I suspect I need to modify the input device
driver (mpr121_touchkey.c in my case) like this:
If the interrupt gpio is not provided in DT, the device driver probe
function should:
- not request the threaded interrupt
- call input_setup_polling and provide it with poll_fn
Can the mpr_touchkey_interrupt function be used as is for this
purpose? The only problem I see is it returns IRQ_HANDLED.
I'd factor out code suitable for polling from mpr_touchkey_interrupt()
and then do
static irqreturn_t mpr_touchkey_interrupt(...)
{
mpr_touchkey_report(...);
return IRQ_HANDLED;
}
Probably a trivial problem for experienced kernel hacker but I can not
wrap my head around this - the interrupt handler takes the mpr121
device id as an argument while the poller poll_fn takes struct input_dev.
I fail to figure out how to get the device id from the input device.
Thanks for the hints Dmitry. I am trying my best but still have some
issues with the input_set/get_drvdata.
The kernel Oopses on NULL pointer dereference in mpr_touchkey_report.
Here is the backtrace:
[ 2.916960] 8<--- cut here ---
[ 2.920022] Unable to handle kernel NULL pointer dereference at virtual address 000001d0
[ 2.928138] pgd = (ptrval)
[ 2.930851] [000001d0] *pgd=00000000
[ 2.934439] Internal error: Oops: 5 [#1] SMP ARM
[ 2.939065] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.3.0-rc1-00001-g7278b7c3986c-dirty #2
[ 2.947503] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[ 2.954044] PC is at mpr_touchkey_report+0x18/0x1bc
[ 2.958932] LR is at input_dev_poller_start+0x30/0x3c
[ 2.963987] pc : [<80728c50>] lr : [<8071f444>] psr: 20000013
[ 2.970255] sp : e8131c10 ip : e8131c68 fp : e8131c64
[ 2.975480] r10: 000000c9 r9 : 8108339c r8 : 81083340
[ 2.980707] r7 : 00000000 r6 : e86cf574 r5 : e86b8480 r4 : e86b8040
[ 2.987236] r3 : 80728c38 r2 : e8128000 r1 : 00000000 r0 : 00000000
[ 2.993767] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
[ 3.000906] Control: 10c5387d Table: 1000404a DAC: 00000051
[ 3.006656] Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
[ 3.012667] Stack: (0xe8131c10 to 0xe8132000)
[ 3.017033] 1c00: 60000013 8017f290 e8131c64 e8131c28
[ 3.025219] 1c20: 8017f290 8017f098 80e7eb04 e8131cd4 0000013d 00000000 80bc6ba4 e86b8040
[ 3.033403] 1c40: e86b8480 e86cf574 00000000 81083340 8108339c 000000c9 e8131c7c e8131c68
[ 3.041587] 1c60: 8071f444 80728c44 e86cf400 e86b8480 e8131c9c e8131c80 8071d230 8071f420
[ 3.049769] 1c80: e86b8480 00000000 e86cf400 8106baa4 e8131cbc e8131ca0 80591b00 8071d1a0
[ 3.057951] 1ca0: 80c567c4 e86cf400 8106baa4 8106baa4 e8131cdc e8131cc0 8071d75c 80591a98
[ 3.066136] 1cc0: e86cf400 00000000 e86ba1c0 8106baa4 e8131d04 e8131ce0 8071df50 8071d6cc
[ 3.074320] 1ce0: 00000000 e834a400 e86cf400 e86ba0c0 e834a420 51eb851f e8131d54 e8131d08
[ 3.082502] 1d00: 807291c4 8071dbc0 00000000 00000000 00000000 0d3abafe 00325aa0 81006548
[ 3.090686] 1d20: 000003e8 0d3abafe 8063907c e834a420 80728e34 81083950 e834a400 00000000
[ 3.098867] 1d40: 00000000 00000000 e8131d7c e8131d58 80747410 80728e40 81123a00 e834a420
[ 3.107051] 1d60: 81123b0c 00000000 81083950 00000000 e8131dac e8131d80 8061f684 807471c4
[ 3.115233] 1d80: 00000000 e834a420 81083950 81083950 81006548 00000000 80f8c83c 80f008ac
[ 3.123415] 1da0: e8131de4 e8131db0 8061fca8 8061f590 e8131dcc e8131dc0 8080e234 8061ed70
[ 3.131599] 1dc0: e834a420 00000000 81083950 81006548 00000000 80f8c83c e8131e04 e8131de8
[ 3.139784] 1de0: 8061ffcc 8061fc44 81083950 e834a420 8061ffd4 81006548 e8131e24 e8131e08
[ 3.147969] 1e00: 80620040 8061ff70 00000000 81083950 8061ffd4 81006548 e8131e54 e8131e28
[ 3.156151] 1e20: 8061d974 8061ffe0 e8131e60 e821b758 e83413b4 0d3abafe 81083950 e8697f00
[ 3.164333] 1e40: 81084b4c 00000000 e8131e64 e8131e58 806200e0 8061d8fc e8131e8c e8131e68
[ 3.172517] 1e60: 8061e314 806200c0 80e7f9a0 e8131e78 81083950 81006548 80f4a898 ffffe000
[ 3.180699] 1e80: e8131ea4 e8131e90 80620ce4 8061e1b8 81083934 81006548 e8131ebc e8131ea8
[ 3.188882] 1ea0: 80748e18 80620c64 810c0660 81006548 e8131ecc e8131ec0 80f4a8bc 80748dd8
[ 3.197065] 1ec0: e8131f44 e8131ed0 80f01330 80f4a8a4 00000000 80e12924 80e12904 80e12900
[ 3.205250] 1ee0: 80e24610 81006548 00000000 80e128dc 00000006 00000006 00000000 80f008ac
[ 3.213433] 1f00: 80e7eac0 80ee9234 8016f4d4 ebfffb37 ebfffb3f 0d3abafe e8131f44 0d3abafe
[ 3.220284] g_ether gadget: high-speed config #1: CDC Ethernet (ECM)
[ 3.221616] 1f20: 810c0660 00000007 810c0660 80fc21f0 810c5980 810c5980 e8131f94 e8131f48
[ 3.236147] 1f40: 80f0174c 80f01274 00000006 00000006 00000000 80f008ac 801346cc 80133d54
[ 3.244330] 1f60: 80ee9234 00000154 e8131f8c 00000000 80bdb670 00000000 00000000 00000000
[ 3.252512] 1f80: 00000000 00000000 e8131fac e8131f98 80bdb688 80f0146c 00000000 80bdb670
[ 3.260695] 1fa0: 00000000 e8131fb0 801010e8 80bdb67c 00000000 00000000 00000000 00000000
[ 3.268878] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 3.277061] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
[ 3.285237] Backtrace:
[ 3.287699] [<80728c38>] (mpr_touchkey_report) from [<8071f444>] (input_dev_poller_start+0x30/0x3c)
[ 3.296753] r10:000000c9 r9:8108339c r8:81083340 r7:00000000 r6:e86cf574 r5:e86b8480
[ 3.304584] r4:e86b8040
[ 3.307128] [<8071f414>] (input_dev_poller_start) from [<8071d230>] (input_open_device+0x9c/0xc4)
[ 3.316002] r5:e86b8480 r4:e86cf400
[ 3.319590] [<8071d194>] (input_open_device) from [<80591b00>] (kbd_connect+0x74/0x90)
[ 3.327510] r7:8106baa4 r6:e86cf400 r5:00000000 r4:e86b8480
[ 3.333180] [<80591a8c>] (kbd_connect) from [<8071d75c>] (input_attach_handler+0x9c/0xd0)
[ 3.341361] r7:8106baa4 r6:8106baa4 r5:e86cf400 r4:80c567c4
[ 3.347029] [<8071d6c0>] (input_attach_handler) from [<8071df50>] (input_register_device+0x39c/0x40c)
[ 3.356251] r7:8106baa4 r6:e86ba1c0 r5:00000000 r4:e86cf400
[ 3.361923] [<8071dbb4>] (input_register_device) from [<807291c4>] (mpr_touchkey_probe+0x390/0x4d4)
[ 3.370974] r9:51eb851f r8:e834a420 r7:e86ba0c0 r6:e86cf400 r5:e834a400 r4:00000000
[ 3.378727] [<80728e34>] (mpr_touchkey_probe) from [<80747410>] (i2c_device_probe+0x258/0x27c)
[ 3.387344] r10:00000000 r9:00000000 r8:00000000 r7:e834a400 r6:81083950 r5:80728e34
[ 3.395174] r4:e834a420
[ 3.397715] [<807471b8>] (i2c_device_probe) from [<8061f684>] (really_probe+0x100/0x2d8)
[ 3.405810] r9:00000000 r8:81083950 r7:00000000 r6:81123b0c r5:e834a420 r4:81123a00
[ 3.413562] [<8061f584>] (really_probe) from [<8061fca8>] (driver_probe_device+0x70/0x180)
[ 3.421832] r10:80f008ac r9:80f8c83c r8:00000000 r7:81006548 r6:81083950 r5:81083950
[ 3.429662] r4:e834a420 r3:00000000
[ 3.433245] [<8061fc38>] (driver_probe_device) from [<8061ffcc>] (device_driver_attach+0x68/0x70)
[ 3.442121] r9:80f8c83c r8:00000000 r7:81006548 r6:81083950 r5:00000000 r4:e834a420
[ 3.449872] [<8061ff64>] (device_driver_attach) from [<80620040>] (__driver_attach+0x6c/0xe0)
[ 3.458399] r7:81006548 r6:8061ffd4 r5:e834a420 r4:81083950
[ 3.464071] [<8061ffd4>] (__driver_attach) from [<8061d974>] (bus_for_each_dev+0x84/0xc4)
[ 3.472251] r7:81006548 r6:8061ffd4 r5:81083950 r4:00000000
[ 3.477920] [<8061d8f0>] (bus_for_each_dev) from [<806200e0>] (driver_attach+0x2c/0x30)
[ 3.485926] r7:00000000 r6:81084b4c r5:e8697f00 r4:81083950
[ 3.491594] [<806200b4>] (driver_attach) from [<8061e314>] (bus_add_driver+0x168/0x1ec)
[ 3.499604] [<8061e1ac>] (bus_add_driver) from [<80620ce4>] (driver_register+0x8c/0x124)
[ 3.507697] r7:ffffe000 r6:80f4a898 r5:81006548 r4:81083950
[ 3.513366] [<80620c58>] (driver_register) from [<80748e18>] (i2c_register_driver+0x4c/0x8c)
[ 3.521804] r5:81006548 r4:81083934
[ 3.525394] [<80748dcc>] (i2c_register_driver) from [<80f4a8bc>] (mpr_touchkey_driver_init+0x24/0x28)
[ 3.534616] r5:81006548 r4:810c0660
[ 3.538203] [<80f4a898>] (mpr_touchkey_driver_init) from [<80f01330>] (do_one_initcall+0xc8/0x1f8)
[ 3.547169] [<80f01268>] (do_one_initcall) from [<80f0174c>] (kernel_init_freeable+0x2ec/0x380)
[ 3.555872] r8:810c5980 r7:810c5980 r6:80fc21f0 r5:810c0660 r4:00000007
[ 3.562587] [<80f01460>] (kernel_init_freeable) from [<80bdb688>] (kernel_init+0x18/0x120)
[ 3.570856] r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:80bdb670
[ 3.578685] r4:00000000
[ 3.581230] [<80bdb670>] (kernel_init) from [<801010e8>] (ret_from_fork+0x14/0x2c)
[ 3.588802] Exception stack(0xe8131fb0 to 0xe8131ff8)
[ 3.593859] 1fa0: 00000000 00000000 00000000 00000000
[ 3.602042] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 3.610222] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 3.616839] r5:80bdb670 r4:00000000
[ 3.620422] Code: e24cb004 e24dd02c e52de004 e8bd4000 (e59051d0)
[ 3.626572] ---[ end trace eb840c8cb957e159 ]---
I can confirm the poller mechanism works fine if I leave the
mpr_touchkey_report function empty and just return.
I can also confirm the interrupt mechanism works as fine if I bodge
a wire from some available GPIO (commented lines in dtsi).
Here is my code:
diff --git a/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi b/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi
index e8d800fec637..7516da441915 100644
--- a/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi
+++ b/arch/arm/boot/dts/imx6dl-yapp4-common.dtsi
@@ -4,6 +4,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/input/input.h>
#include <dt-bindings/pwm/pwm.h>
/ {
@@ -330,6 +331,21 @@
vcc-supply = <&sw2_reg>;
status = "disabled";
};
+
+ touchkeys: keys@5a {
+ compatible = "fsl,mpr121-touchkey";
+ //pinctrl-0 = <&pinctrl_key_irq>;
+ reg = <0x5a>;
+ vdd-supply = <&sw2_reg>;
+ autorepeat;
+ linux,keycodes = <KEY_1>, <KEY_2>, <KEY_3>, <KEY_4>, <KEY_5>,
+ <KEY_6>, <KEY_7>, <KEY_8>, <KEY_9>,
+ <KEY_BACKSPACE>, <KEY_0>, <KEY_ENTER>;
+ linux,poll-interval = <50>;
+ //interrupt-parent = <&gpio1>;
+ //interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
+ status = "disabled";
+ };
};
&iomuxc {
@@ -433,6 +449,12 @@
>;
};
+ pinctrl_key_irq: keyirq {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_CMD__GPIO1_IO18 0x1b098
+ >;
+ };
+
pinctrl_touch: touchgrp {
fsl,pins = <
MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x1b098
diff --git a/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts b/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts
index f97927064750..84c275bfdd38 100644
--- a/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts
+++ b/arch/arm/boot/dts/imx6dl-yapp4-hydra.dts
@@ -45,6 +45,10 @@
status = "okay";
};
+&touchkeys {
+ status = "okay";
+};
+
&usdhc3 {
status = "okay";
};
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index e9ceaa16b46a..d6b9f6acddca 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -7,7 +7,7 @@
*
* Based on mcs_touchkey.c
*/
-
+#define DEBUG
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -54,6 +54,9 @@
/* MPR121 has 12 keys */
#define MPR121_MAX_KEY_COUNT 12
+#define MPR121_MIN_POLL_INTERVAL 10
+#define MPR121_MAX_POLL_INTERVAL 2000
+
struct mpr121_touchkey {
struct i2c_client *client;
struct input_dev *input_dev;
@@ -115,11 +118,11 @@ static struct regulator *mpr121_vdd_supply_init(struct device *dev)
return vdd_supply;
}
-static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
+static void mpr_touchkey_report(struct input_dev *dev)
{
- struct mpr121_touchkey *mpr121 = dev_id;
- struct i2c_client *client = mpr121->client;
+ struct mpr121_touchkey *mpr121 = input_get_drvdata(dev);
struct input_dev *input = mpr121->input_dev;
+ struct i2c_client *client = mpr121->client;
unsigned long bit_changed;
unsigned int key_num;
int reg;
@@ -127,14 +130,14 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
- goto out;
+ return;
}
reg <<= 8;
reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
if (reg < 0) {
dev_err(&client->dev, "i2c read error [%d]\n", reg);
- goto out;
+ return;
}
reg &= TOUCH_STATUS_MASK;
@@ -155,8 +158,13 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
}
input_sync(input);
+}
+
+static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
+{
+ struct mpr121_touchkey *mpr121 = dev_id;
+ mpr_touchkey_report(mpr121->input_dev);
-out:
return IRQ_HANDLED;
}
@@ -229,13 +237,10 @@ static int mpr_touchkey_probe(struct i2c_client *client,
int vdd_uv;
struct mpr121_touchkey *mpr121;
struct input_dev *input_dev;
+ u32 poll_interval = 0;
int error;
int i;
- if (!client->irq) {
- dev_err(dev, "irq number should not be zero\n");
- return -EINVAL;
- }
vdd_supply = mpr121_vdd_supply_init(dev);
if (IS_ERR(vdd_supply))
@@ -275,11 +280,13 @@ static int mpr_touchkey_probe(struct i2c_client *client,
if (device_property_read_bool(dev, "autorepeat"))
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(input_dev, mpr121);
input_dev->keycode = mpr121->keycodes;
input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
input_dev->keycodemax = mpr121->keycount;
+
for (i = 0; i < mpr121->keycount; i++)
input_set_capability(input_dev, EV_KEY, mpr121->keycodes[i]);
@@ -289,13 +296,34 @@ static int mpr_touchkey_probe(struct i2c_client *client,
return error;
}
- error = devm_request_threaded_irq(dev, client->irq, NULL,
- mpr_touchkey_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev->driver->name, mpr121);
- if (error) {
- dev_err(dev, "Failed to register interrupt\n");
- return error;
+ device_property_read_u32(dev, "linux,poll-interval", &poll_interval);
+
+ if (client->irq) {
+ error = devm_request_threaded_irq(dev, client->irq, NULL,
+ mpr_touchkey_interrupt,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev->driver->name, mpr121);
+ if (error) {
+ dev_err(dev, "Failed to register interrupt\n");
+ return error;
+ }
+ } else if (poll_interval > 0) {
+ error = input_setup_polling(input_dev, mpr_touchkey_report);
+ if (error) {
+ dev_err(dev, "Failed to setup polling\n");
+ return error;
+ }
+
+ input_set_poll_interval(input_dev, poll_interval);
+ input_set_min_poll_interval(input_dev,
+ MPR121_MIN_POLL_INTERVAL);
+ input_set_max_poll_interval(input_dev,
+ MPR121_MAX_POLL_INTERVAL);
+ } else {
+ dev_err(dev,
+ "invalid IRQ number and polling not configured\n");
+ return -EINVAL;
}
error = input_register_device(input_dev);
--