[RFC/PATCH 1/2] gpio: Add PCA9621 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The PCA9621 is an I2C 8-bit output open-drain expander.

Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
---
 .../devicetree/bindings/gpio/nxp,pca9621.txt       |  19 +++
 drivers/gpio/Kconfig                               |   6 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-pca9621.c                        | 163 +++++++++++++++++++++
 4 files changed, 189 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/nxp,pca9621.txt
 create mode 100644 drivers/gpio/gpio-pca9621.c

diff --git a/Documentation/devicetree/bindings/gpio/nxp,pca9621.txt b/Documentation/devicetree/bindings/gpio/nxp,pca9621.txt
new file mode 100644
index 000000000000..f863aa23150b
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/nxp,pca9621.txt
@@ -0,0 +1,19 @@
+* NXP PCA9621 I2C GPIO multiplexer
+
+Required properties:
+
+ - compatible: Should be "nxp,pca9621".
+ - gpio-controller: Marks the device as a gpio controller.
+ - #gpio-cells: Should be 2. The first cell is the GPIO number and the second
+   cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the
+   GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported.
+
+
+Example:
+
+	gpio@20 {
+		compatible = "nxp,pca9621";
+		reg = <0x20>;
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8f1fe739c985..f0e19fc0add8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -644,6 +644,12 @@ config GPIO_PCA953X_IRQ
 	  Say yes here to enable the pca953x to be used as an interrupt
 	  controller. It requires the driver to be built in the kernel.
 
+config GPIO_PCA9621
+	tristate "PCA9621 I2C output port"
+	depends on I2C
+	help
+	  Say yes here to provide access to the PCA9621 I2C output expander.
+
 config GPIO_PCF857X
 	tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
 	depends on I2C
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f82cd678ce08..36f5cb5eade6 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
 obj-$(CONFIG_GPIO_OCTEON)	+= gpio-octeon.o
 obj-$(CONFIG_GPIO_OMAP)		+= gpio-omap.o
 obj-$(CONFIG_GPIO_PCA953X)	+= gpio-pca953x.o
+obj-$(CONFIG_GPIO_PCA9621)	+= gpio-pca9621.o
 obj-$(CONFIG_GPIO_PCF857X)	+= gpio-pcf857x.o
 obj-$(CONFIG_GPIO_PCH)		+= gpio-pch.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
diff --git a/drivers/gpio/gpio-pca9621.c b/drivers/gpio/gpio-pca9621.c
new file mode 100644
index 000000000000..c26bb5126dea
--- /dev/null
+++ b/drivers/gpio/gpio-pca9621.c
@@ -0,0 +1,163 @@
+/*
+ * Driver for pca9621 I2C GPIO expanders
+ *
+ * Copyright (C) 2015 Laurent Pinchart
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+struct pca9621 {
+	struct gpio_chip	chip;
+	struct i2c_client	*client;
+	struct mutex		lock;		/* protect 'out' */
+	unsigned int		out;
+};
+
+static inline struct pca9621 *to_pca9621(struct gpio_chip *chip)
+{
+	return container_of(chip, struct pca9621, chip);
+}
+
+static int pca9621_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct pca9621 *gpio = to_pca9621(chip);
+	int ret;
+
+	mutex_lock(&gpio->lock);
+
+	gpio->out &= ~(1 << offset);
+	ret = i2c_smbus_write_byte(gpio->client, gpio->out);
+
+	mutex_unlock(&gpio->lock);
+
+	return ret;
+}
+
+static int pca9621_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pca9621 *gpio = to_pca9621(chip);
+	int ret;
+
+	/* The output is open-drain and can't be driven high. */
+	if (value)
+		return -EINVAL;
+
+	mutex_lock(&gpio->lock);
+
+	gpio->out |= 1 << offset;
+	ret = i2c_smbus_write_byte(gpio->client, gpio->out);
+
+	mutex_unlock(&gpio->lock);
+
+	return ret;
+}
+
+static int pca9621_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct pca9621 *gpio = to_pca9621(chip);
+
+	return !(gpio->out & (1 << offset));
+}
+
+static void pca9621_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	pca9621_output(chip, offset, value);
+}
+
+static int pca9621_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct pca9621 *gpio;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE))
+		return -EIO;
+
+	gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
+	if (!gpio)
+		return -ENOMEM;
+
+	mutex_init(&gpio->lock);
+
+	gpio->chip.base = -1;
+	gpio->chip.can_sleep = true;
+	gpio->chip.dev = &client->dev;
+	gpio->chip.owner = THIS_MODULE;
+	gpio->chip.get = pca9621_get;
+	gpio->chip.set = pca9621_set;
+	gpio->chip.direction_input = pca9621_input;
+	gpio->chip.direction_output = pca9621_output;
+	gpio->chip.ngpio = 8;
+	gpio->chip.label = client->name;
+
+	gpio->client = client;
+	i2c_set_clientdata(client, gpio);
+
+	/* Read the current output state. */
+	ret = i2c_smbus_read_byte(gpio->client);
+	if (ret < 0)
+		return ret;
+
+	gpio->out = ret;
+
+	ret = gpiochip_add(&gpio->chip);
+	if (ret < 0)
+		return ret;
+
+	dev_info(&client->dev, "probed\n");
+
+	return 0;
+}
+
+static int pca9621_remove(struct i2c_client *client)
+{
+	struct pca9621 *gpio = i2c_get_clientdata(client);
+
+	gpiochip_remove(&gpio->chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id pca9621_id[] = {
+	{ "pca9621" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pca9621_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pca9621_of_table[] = {
+	{ .compatible = "nxp,pca9621" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pca9621_of_table);
+#endif
+
+static struct i2c_driver pca9621_driver = {
+	.driver = {
+		.name	= "pca9621",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(pca9621_of_table),
+	},
+	.probe	= pca9621_probe,
+	.remove	= pca9621_remove,
+	.id_table = pca9621_id,
+};
+module_i2c_driver(pca9621_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Laurent Pinchart");
-- 
2.4.9

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



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux