Input: mpr121 - add polling mode
authorMichal Vokáč <michal.vokac@ysoft.com>
Wed, 16 Oct 2019 00:17:29 +0000 (17:17 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 16 Oct 2019 00:43:54 +0000 (17:43 -0700)
In case the interrupt line is not available, polling can be used
to read out the state of the keys. Period of the polling needs to
be configured by the poll-interval DT property.

Signed-off-by: Michal Vokáč <michal.vokac@ysoft.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Documentation/devicetree/bindings/input/fsl,mpr121-touchkey.yaml
Documentation/devicetree/bindings/input/input.yaml
drivers/input/keyboard/mpr121_touchkey.c

index c6fbcdf7855612a9c1bd0e562865d1435e39bc30..5b37be0be4e927d297682085f7b00b99ec055b5b 100644 (file)
@@ -17,6 +17,10 @@ description: |
 allOf:
   - $ref: input.yaml#
 
+anyOf:
+  - required: [ interrupts ]
+  - required: [ poll-interval ]
+
 properties:
   compatible:
     const: fsl,mpr121-touchkey
@@ -41,12 +45,12 @@ properties:
 required:
   - compatible
   - reg
-  - interrupts
   - vdd-supply
   - linux,keycodes
 
 examples:
   - |
+    // Example with interrupts
     #include "dt-bindings/input/input.h"
     i2c {
         #address-cells = <1>;
@@ -64,3 +68,22 @@ examples:
                              <KEY_8>, <KEY_9>, <KEY_A>, <KEY_B>;
         };
     };
+
+  - |
+    // Example with polling
+    #include "dt-bindings/input/input.h"
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        mpr121@5a {
+            compatible = "fsl,mpr121-touchkey";
+            reg = <0x5a>;
+            poll-interval = <20>;
+            autorepeat;
+            vdd-supply = <&ldo4_reg>;
+            linux,keycodes = <KEY_0>, <KEY_1>, <KEY_2>, <KEY_3>,
+                             <KEY_4>, <KEY_5>, <KEY_6>, <KEY_7>,
+                             <KEY_8>, <KEY_9>, <KEY_A>, <KEY_B>;
+        };
+    };
index ca8fe84a2e623f74a758161c8f54e33976de7eca..6d519046b3afc9de9822f3f85f865e7a52538890 100644 (file)
@@ -24,6 +24,10 @@ properties:
           minimum: 0
           maximum: 0xff
 
+  poll-interval:
+    description: Poll interval time in milliseconds.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
   power-off-time-sec:
     description:
       Duration in seconds which the key should be kept pressed for device to
index ee80de44ce3f6a49460655989027405c38b30a13..40d6e5087cdef060acce22fdd4aa2a5d082cf230 100644 (file)
@@ -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       200
+
 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,14 @@ 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,14 +238,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))
                return PTR_ERR(vdd_supply);
@@ -274,6 +279,7 @@ 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]);
@@ -288,13 +294,40 @@ 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, "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) {
+               if (poll_interval < MPR121_MIN_POLL_INTERVAL)
+                       return -EINVAL;
+
+               if (poll_interval > MPR121_MAX_POLL_INTERVAL)
+                       return -EINVAL;
+
+               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);