linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Mack <daniel@zonque.org>
To: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
	linux-i2c@vger.kernel.org, alsa-devel@alsa-project.org,
	devicetree@vger.kernel.org, linux-clk@vger.kernel.org
Cc: mturquette@baylibre.com, sboyd@kernel.org, robh+dt@kernel.org,
	broonie@kernel.org, lee.jones@linaro.org, lars@metafoo.de,
	pascal.huerst@gmail.com, Daniel Mack <daniel@zonque.org>
Subject: [PATCH 08/10] gpio: Add driver for AD242x GPIO controllers
Date: Mon,  9 Dec 2019 19:35:09 +0100	[thread overview]
Message-ID: <20191209183511.3576038-10-daniel@zonque.org> (raw)
In-Reply-To: <20191209183511.3576038-1-daniel@zonque.org>

This driver makes the 8 GPIOs on AD242x nodes available to consumers.

Apart from that, it also allows putting the GPIO lines in a 'gpio over
distance' mode. This mirrors the state of several GPIOs in the topology
without further interaction by any driver. For instance, when a GPIO pin
on the master node is put in input mode, and another one on a slave node
is in output mode, they can be linked together through virtual ports.
Then, the pin on the slave node will reflect the logical level on
whatever is applied to the respective pin on the master node.

Signed-off-by: Daniel Mack <daniel@zonque.org>
---
 drivers/gpio/Kconfig       |   6 +
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-ad242x.c | 229 +++++++++++++++++++++++++++++++++++++
 3 files changed, 236 insertions(+)
 create mode 100644 drivers/gpio/gpio-ad242x.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8adffd42f8cb..c8af1159a585 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -845,6 +845,12 @@ endmenu
 menu "I2C GPIO expanders"
 	depends on I2C
 
+config GPIO_AD242X
+	tristate "AD242x A2B GPIO controller"
+	depends on MFD_AD242X
+	help
+	  This option enables support for GPIOs on AD242x A2B nodes.
+
 config GPIO_ADP5588
 	tristate "ADP5588 I2C GPIO expander"
 	help
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 34eb8b2b12dd..2490ce6e6905 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_GPIO_104_IDI_48)		+= gpio-104-idi-48.o
 obj-$(CONFIG_GPIO_104_IDIO_16)		+= gpio-104-idio-16.o
 obj-$(CONFIG_GPIO_74X164)		+= gpio-74x164.o
 obj-$(CONFIG_GPIO_74XX_MMIO)		+= gpio-74xx-mmio.o
+obj-$(CONFIG_GPIO_AD242X)		+= gpio-ad242x.o
 obj-$(CONFIG_GPIO_ADNP)			+= gpio-adnp.o
 obj-$(CONFIG_GPIO_ADP5520)		+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)		+= gpio-adp5588.o
diff --git a/drivers/gpio/gpio-ad242x.c b/drivers/gpio/gpio-ad242x.c
new file mode 100644
index 000000000000..8970e434b56a
--- /dev/null
+++ b/drivers/gpio/gpio-ad242x.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/mfd/ad242x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct ad242x_gpio {
+	struct gpio_chip chip;
+	struct ad242x_node *node;
+	u32 gpio_od_mask;
+};
+
+static int ad242x_gpio_request(struct gpio_chip *chip, unsigned int gpio)
+{
+	struct ad242x_gpio *ad242x_gpio = gpiochip_get_data(chip);
+
+	if (gpio == 0 && ad242x_node_is_master(ad242x_gpio->node))
+		return -EBUSY;
+
+	if (ad242x_gpio->gpio_od_mask & BIT(gpio))
+		return -EBUSY;
+
+	return 0;
+}
+
+static int ad242x_gpio_get_value(struct gpio_chip *chip, unsigned int gpio)
+{
+	struct ad242x_gpio *ad242x_gpio = gpiochip_get_data(chip);
+	struct regmap *regmap = ad242x_gpio->node->regmap;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(regmap, AD242X_GPIODAT_IN, &val);
+	if (ret < 0)
+		return ret;
+
+	return !!(val & BIT(gpio));
+}
+
+static void ad242x_gpio_set_value(struct gpio_chip *chip,
+				  unsigned int gpio, int value)
+{
+	struct ad242x_gpio *ad242x_gpio = gpiochip_get_data(chip);
+	struct regmap *regmap = ad242x_gpio->node->regmap;
+	uint8_t bit = BIT(gpio);
+	int ret;
+
+	if (value)
+		ret = regmap_write(regmap, AD242X_GPIODAT_SET, bit);
+	else
+		ret = regmap_write(regmap, AD242X_GPIODAT_CLR, bit);
+
+	if (ret < 0)
+		dev_err(ad242x_gpio->node->dev,
+			"Unable to set GPIO #%d: %d\n", gpio, ret);
+}
+
+static int ad242x_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned int gpio)
+{
+	struct ad242x_gpio *ad242x_gpio = gpiochip_get_data(chip);
+	struct regmap *regmap = ad242x_gpio->node->regmap;
+	uint8_t bit = BIT(gpio);
+	int ret;
+
+	ret = regmap_update_bits(regmap, AD242X_GPIOOEN, bit, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(regmap, AD242X_GPIOIEN, bit, bit);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(regmap, AD242X_INTMSK1, bit, bit);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ad242x_gpio_direction_output(struct gpio_chip *chip,
+					unsigned int gpio, int value)
+{
+	struct ad242x_gpio *ad242x_gpio = gpiochip_get_data(chip);
+	struct regmap *regmap = ad242x_gpio->node->regmap;
+	uint8_t bit = BIT(gpio);
+	int ret;
+
+	ret = regmap_update_bits(regmap, AD242X_GPIOIEN, bit, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(regmap, AD242X_GPIOOEN, bit, bit);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(regmap, AD242X_INTMSK1, bit, 0);
+	if (ret < 0)
+		return ret;
+
+	ad242x_gpio_set_value(chip, gpio, value);
+
+	return 0;
+}
+
+static int ad242x_gpio_over_distance_init(struct device *dev,
+					  struct ad242x_gpio *ad242x_gpio)
+{
+	struct regmap *regmap = ad242x_gpio->node->regmap;
+	struct device_node *np, *child_np;
+	int ret = 0;
+
+	np = of_get_child_by_name(dev->of_node, "gpio-over-distance");
+	if (!np)
+		return 0;
+
+	for_each_available_child_of_node(np, child_np) {
+		u32 reg, port_mask, bit;
+		bool output, inv;
+
+		ret = of_property_read_u32(child_np, "reg", &reg);
+		if (ret < 0)
+			continue;
+
+		ret = of_property_read_u32(child_np, "adi,virtual-port-mask",
+					   &port_mask);
+		if (ret < 0)
+			continue;
+
+		if (reg > 7) {
+			ret = -EINVAL;
+			break;
+		}
+
+		bit = BIT(reg);
+
+		ret = regmap_update_bits(regmap, AD242X_GPIODEN, bit, bit);
+		if (ret < 0)
+			break;
+
+		ret = regmap_write(regmap, AD242X_GPIOD_MSK(reg), port_mask);
+		if (ret < 0)
+			break;
+
+		output = of_property_read_bool(child_np, "adi,gpio-output");
+		ret = regmap_update_bits(regmap, AD242X_GPIOOEN,
+					 bit, output ? bit : 0);
+		if (ret < 0)
+			break;
+
+		inv = of_property_read_bool(child_np, "adi,gpio-inverted");
+		ret = regmap_update_bits(regmap, AD242X_GPIODINV,
+					 bit, inv ? bit : 0);
+		if (ret < 0)
+			break;
+
+		ad242x_gpio->gpio_od_mask |= bit;
+		dev_info(dev,
+			 "pin %d set up as gpio-over-distance, port mask 0x%02x\n",
+			 reg, port_mask);
+	}
+
+	of_node_put(np);
+
+	return ret;
+}
+
+static int ad242x_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ad242x_gpio *ad242x_gpio;
+	int ret;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	ad242x_gpio = devm_kzalloc(dev, sizeof(*ad242x_gpio), GFP_KERNEL);
+	if (!ad242x_gpio)
+		return -ENOMEM;
+
+	ad242x_gpio->node = dev_get_drvdata(dev->parent);
+
+	ad242x_gpio->chip.request = ad242x_gpio_request;
+	ad242x_gpio->chip.direction_input = ad242x_gpio_direction_input;
+	ad242x_gpio->chip.direction_output = ad242x_gpio_direction_output;
+	ad242x_gpio->chip.get = ad242x_gpio_get_value;
+	ad242x_gpio->chip.set = ad242x_gpio_set_value;
+	ad242x_gpio->chip.can_sleep = 1;
+	ad242x_gpio->chip.base = -1;
+	ad242x_gpio->chip.ngpio = 8;
+	ad242x_gpio->chip.label = "ad242x-gpio";
+	ad242x_gpio->chip.owner = THIS_MODULE;
+	ad242x_gpio->chip.parent = dev;
+
+	dev_info(dev, "A2B node ID %d\n", ad242x_gpio->node->id);
+
+	ret = ad242x_gpio_over_distance_init(dev, ad242x_gpio);
+	if (ret < 0) {
+		dev_err(dev, "GPIO over distance init failed: %d\n", ret);
+		return ret;
+	}
+
+	return devm_gpiochip_add_data(dev, &ad242x_gpio->chip, ad242x_gpio);
+}
+
+static const struct of_device_id ad242x_gpio_of_match[] = {
+	{ .compatible = "adi,ad2428w-gpio", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ad242x_gpio_of_match);
+
+static struct platform_driver ad242x_gpio_driver = {
+	.driver = {
+		.name = "ad242x-gpio",
+		.of_match_table = ad242x_gpio_of_match,
+	},
+	.probe = ad242x_gpio_probe,
+};
+module_platform_driver(ad242x_gpio_driver);
+
+MODULE_DESCRIPTION("AD242x GPIO driver");
+MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.23.0


  parent reply	other threads:[~2019-12-09 18:43 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-09 18:35 [PATCH 00/10] mfd: Add support for Analog Devices A2B transceiver Daniel Mack
2019-12-09 18:35 ` [PATCH 01/10] dt-bindings: mfd: Add documentation for ad242x Daniel Mack
2019-12-19 19:29   ` Rob Herring
2019-12-09 18:35 ` [PATCH 02/10] dt-bindings: i2c: Add documentation for ad242x i2c controllers Daniel Mack
2020-01-08  3:45   ` Rob Herring
2019-12-09 18:35 ` [PATCH 03/10] dt-bindings: gpio: Add documentation for ad242x GPIO controllers Daniel Mack
2019-12-09 18:35 ` [PATCH 03/10] dt-bindings: gpio: Add documentation for AD242x " Daniel Mack
2019-12-09 18:35 ` [PATCH 04/10] dt-bindings: clock: Add documentation for AD242x clock providers Daniel Mack
2019-12-24  7:32   ` Stephen Boyd
2019-12-09 18:35 ` [PATCH 05/10] dt-bindings: sound: Add documentation for AD242x codecs Daniel Mack
2019-12-09 18:35 ` [PATCH 06/10] mfd: Add core driver for AD242x A2B transceivers Daniel Mack
2019-12-17 13:39   ` Lee Jones
2019-12-17 13:46     ` Lee Jones
2019-12-17 19:36       ` Daniel Mack
2019-12-17 19:24     ` Daniel Mack
2019-12-18 11:20       ` Luca Ceresoli
2019-12-17 19:16   ` [alsa-devel] " Pierre-Louis Bossart
2019-12-18  9:40     ` Daniel Mack
2019-12-09 18:35 ` [PATCH 07/10] i2c: Add driver for AD242x bus controller Daniel Mack
2019-12-12 16:11   ` Luca Ceresoli
2019-12-12 16:33     ` Wolfram Sang
2019-12-15 20:27       ` Daniel Mack
2019-12-17  8:35         ` Luca Ceresoli
2019-12-17 18:17           ` Daniel Mack
2019-12-09 18:35 ` Daniel Mack [this message]
2019-12-09 18:35 ` [PATCH 09/10] clk: Add support for AD242x clock output providers Daniel Mack
2019-12-24  7:46   ` Stephen Boyd
2019-12-09 18:35 ` [PATCH 10/10] ASoC: Add codec component for AD242x nodes Daniel Mack
2019-12-16 14:23   ` Mark Brown
2019-12-17 19:28   ` [alsa-devel] " Pierre-Louis Bossart
2019-12-18  9:49     ` Daniel Mack
2019-12-18 15:32       ` Pierre-Louis Bossart
2019-12-17 19:29 ` [alsa-devel] [PATCH 00/10] mfd: Add support for Analog Devices A2B transceiver Pierre-Louis Bossart
2019-12-18  9:53   ` Daniel Mack

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191209183511.3576038-10-daniel@zonque.org \
    --to=daniel@zonque.org \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=lars@metafoo.de \
    --cc=lee.jones@linaro.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=pascal.huerst@gmail.com \
    --cc=robh+dt@kernel.org \
    --cc=sboyd@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).