[PATCH v2] gpio-pca953x: Support NXP PCAL9555A with Agile I/O

From: Clemens Gruber
Date: Tue Jul 07 2015 - 15:50:54 EST


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 <linus.walleij@xxxxxxxxxx>
Cc: Alexandre Courbot <gnurou@xxxxxxxxx>
Signed-off-by: Clemens Gruber <clemens.gruber@xxxxxxxxxxxx>
---

Changes from v1:
- The mapping between bits and pins was counterintuitive. Corrected!
- Improved documentation for new properties and example comments

---
.../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..6609906 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: Enable input latch, one bit per pin
+- nxp,intr-mask: Unmask interrupts by clearing the mask bits per pin
+- nxp,open-drain: Configure outputs as open-drain, one bit per 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 = <0x1>; /* Latch the input state of I0.0 (bit [0]) */
+ nxp,intr-mask = <0x000f>; /* Mask interrupts of I0.3-0 (bits [3..0]) */
+ nxp,open-drain = <0x2>; /* Enable open-drain stage for output port 1 */
+ };
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index d233eb3..b3191f7 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2005 Ben Gardner <bgardner@xxxxxxxxxx>
* Copyright (C) 2007 Marvell International Ltd.
+ * Copyright (C) 2015 Clemens Gruber <clemens.gruber@xxxxxxxxxxxx>
*
* 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,
+ (u16)input_latch);
+ 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,
+ (u16)intr_mask);
+ 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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/