This patch adds support for the NXP PCAL9555A GPIO expander's extra
features, called Agile I/O:
Input latching, interrupt masks and open-drain output stages can be
configured via 3 optional device tree properties.

Cc: Linus Walleij <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Signed-off-by: Clemens Gruber <[email protected]>
---
 .../devicetree/bindings/gpio/gpio-pca953x.txt      | 24 +++++++-
 drivers/gpio/gpio-pca953x.c                        | 68 ++++++++++++++++++++++
 2 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt 
b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
index b9a42f2..5c58687 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
@@ -11,6 +11,7 @@ Required properties:
        nxp,pca9539
        nxp,pca9554
        nxp,pca9555
+       nxp,pcal9555a
        nxp,pca9556
        nxp,pca9557
        nxp,pca9574
@@ -26,8 +27,15 @@ Required properties:
        ti,tca6424
        exar,xra1202
 
-Example:
+Supported chips with Agile I/O features:
+- nxp,pcal9555a
 
+Optional properties for chips with Agile I/O:
+- nxp,input-latch: Latch input states by setting the pin's corresponding bits
+- nxp,intr-mask: Enable interrupts by clearing the pin's corresponding mask 
bits
+- nxp,open-drain: Enable the open-drain output stage, one bit per port (bank)
+
+Examples:
 
        gpio@20 {
                compatible = "nxp,pca9505";
@@ -37,3 +45,17 @@ Example:
                interrupt-parent = <&gpio3>;
                interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
        };
+
+       gpio@22 {
+               compatible = "nxp,pcal9555a";
+               reg = <0x22>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               interrupt-controller;
+               #interrupt-cells = <2>;
+               interrupt-parent = <&gpio2>;
+               interrupts = <20 IRQ_TYPE_EDGE_FALLING>;
+               nxp,input-latch = <0x8000>; /* Latch the input of pin I0.0 */
+               nxp,intr-mask = <0xf000>; /* Mask interrupts I0.0-I0.3 */
+               nxp,open-drain = <0x2>; /* Output port 1 (O1.0-O1.7) as 
open-drain */
+       };
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index d233eb3..95698c9 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2005 Ben Gardner <[email protected]>
  *  Copyright (C) 2007 Marvell International Ltd.
+ *  Copyright (C) 2015 Clemens Gruber <[email protected]>
  *
  *  Derived from drivers/i2c/chips/pca9539.c
  *
@@ -26,6 +27,9 @@
 #define PCA953X_OUTPUT         1
 #define PCA953X_INVERT         2
 #define PCA953X_DIRECTION      3
+#define PCAL95XXA_INPUT_LATCH  0x44
+#define PCAL95XXA_INTR_MASK    0x4A
+#define PCAL95XXA_OUTPUT_CONFIG        0x4F
 
 #define REG_ADDR_AI            0x80
 
@@ -40,6 +44,7 @@
 
 #define PCA_GPIO_MASK          0x00FF
 #define PCA_INT                        0x0100
+#define PCA_AGILEIO            0x0200
 #define PCA953X_TYPE           0x1000
 #define PCA957X_TYPE           0x2000
 
@@ -53,6 +58,7 @@ static const struct i2c_device_id pca953x_id[] = {
        { "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
        { "pca9554", 8  | PCA953X_TYPE | PCA_INT, },
        { "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
+       { "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_AGILEIO, },
        { "pca9556", 8  | PCA953X_TYPE, },
        { "pca9557", 8  | PCA953X_TYPE, },
        { "pca9574", 8  | PCA957X_TYPE | PCA_INT, },
@@ -614,6 +620,53 @@ out:
        return ret;
 }
 
+static int device_pcal95xxa_agileio_setup(struct pca953x_chip *chip,
+                                         struct device_node *node)
+{
+       int ret;
+       u32 input_latch = 0;
+       u32 intr_mask = 0;
+       u32 open_drain = 0;
+
+       /* Input latch */
+       if (of_property_read_u32(node, "nxp,input-latch", &input_latch) == 0) {
+               if (input_latch > 0xFFFF)
+                       return -EINVAL;
+
+               ret = i2c_smbus_write_word_data(chip->client,
+                                               PCAL95XXA_INPUT_LATCH,
+                                               rol16(input_latch, 8));
+               if (ret)
+                       return -EIO;
+       }
+
+       /* Interrupt mask */
+       if (of_property_read_u32(node, "nxp,intr-mask", &intr_mask) == 0) {
+               if (intr_mask > 0xFFFF)
+                       return -EINVAL;
+
+               ret = i2c_smbus_write_word_data(chip->client,
+                                               PCAL95XXA_INTR_MASK,
+                                               rol16(intr_mask, 8));
+               if (ret)
+                       return -EIO;
+       }
+
+       /* Open-drain output stage per port (bank) */
+       if (of_property_read_u32(node, "nxp,open-drain", &open_drain) == 0) {
+               if (open_drain > 0x3)
+                       return -EINVAL;
+
+               ret = i2c_smbus_write_byte_data(chip->client,
+                                               PCAL95XXA_OUTPUT_CONFIG,
+                                               (u8)open_drain);
+               if (ret)
+                       return -EIO;
+       }
+
+       return 0;
+}
+
 static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
 {
        int ret;
@@ -645,6 +698,7 @@ out:
 static int pca953x_probe(struct i2c_client *client,
                                   const struct i2c_device_id *id)
 {
+       struct device_node *node = client->dev.of_node;
        struct pca953x_platform_data *pdata;
        struct pca953x_chip *chip;
        int irq_base = 0;
@@ -700,6 +754,19 @@ static int pca953x_probe(struct i2c_client *client,
                        dev_warn(&client->dev, "setup failed, %d\n", ret);
        }
 
+       /* Configure Agile I/O features, if supported by the chip */
+       if ((id->driver_data & PCA_AGILEIO) && IS_ENABLED(CONFIG_OF) && node) {
+               /* Only expanders with 16-bit supported */
+               if (NBANK(chip) == 2) {
+                       ret = device_pcal95xxa_agileio_setup(chip, node);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                        "Agile I/O setup failed, %d\n", ret);
+               } else
+                       dev_warn(&client->dev,
+                                "Agile I/O not supported on this chip\n");
+       }
+
        i2c_set_clientdata(client, chip);
        return 0;
 }
@@ -735,6 +802,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
        { .compatible = "nxp,pca9539", },
        { .compatible = "nxp,pca9554", },
        { .compatible = "nxp,pca9555", },
+       { .compatible = "nxp,pcal9555a", },
        { .compatible = "nxp,pca9556", },
        { .compatible = "nxp,pca9557", },
        { .compatible = "nxp,pca9574", },
-- 
2.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to