diff mbox series

[v2,2/2] gpio: gpio-adg1414: New driver

Message ID 20250213-for_upstream-v2-2-ec4eff3b3cd5@analog.com
State New
Headers show
Series Add support for ADG1414 Serially-Controlled Octal SPST Switches | expand

Commit Message

Kim Seer Paller Feb. 13, 2025, 1:15 p.m. UTC
The ADG1414 is a 9.5 Ω RON ±15 V/+12 V/±5 V iCMOS Serially-Controlled
Octal SPST Switches

Signed-off-by: Kim Seer Paller <kimseer.paller@analog.com>
---
 MAINTAINERS                 |   1 +
 drivers/gpio/Kconfig        |  10 +++
 drivers/gpio/Makefile       |   1 +
 drivers/gpio/gpio-adg1414.c | 162 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 174 insertions(+)

Comments

Linus Walleij Feb. 14, 2025, 11:22 p.m. UTC | #1
Let's check with Jonathan Cameron (IIO maintainer) on this as well.
He might have ideas.

For reference, the datasheet:
https://www.analog.com/media/en/technical-documentation/data-sheets/adg1414.pdf

(By the way: add the datasheet to a special Datasheet: tag in the
commit please!)

On Fri, Feb 14, 2025 at 2:17 PM Nuno Sá <noname.nuno@gmail.com> wrote:
> On Fri, 2025-02-14 at 00:25 +0100, Linus Walleij wrote:

> > Now, the kernel does not have switch subsystem I think,
> > so this is something like a special case, so we might be
> > compelled to make an exception, if the users will all be in
>
> Exactly, since we could not find anything, the best fit seemed like the gpio
> subsystem. I was the one suggesting it since a new subsystem for a simple device
> like this looked excessive. If we had more devices that would fit such a class
> of devices, maybe it would make more sense to start thinking on such a
> subsystem?
>
> > say userspace and make use of this switch for factory lines
> > or similar.
>
> Kim should know better again (about usecases) but I would also assume this is
> for userspace use.

Actually the GPIO documentation Documentation/driver-api/gpio/using-gpio.rst
even talks about this for userspace use cases:

"The userspace ABI is intended for one-off deployments. Examples are prototypes,
factory lines, maker community projects, workshop specimen, production tools,
industrial automation, PLC-type use cases, door controllers, in short a piece
of specialized equipment that is not produced by the numbers, requiring
operators to have a deep knowledge of the equipment and knows about the
software-hardware interface to be set up. They should not have a natural fit
to any existing kernel subsystem and not be a good fit for an operating system,
because of not being reusable or abstract enough, or involving a lot of non
computer hardware related policy."

If this is the usecase, like controlling an external switch for such things,
using the GPIO subsystem might actually be reasonable in my opinion,
(even if the DT bindings end up in their own category).

If the switches control stuff related to computer machinery (i.e. integrated
into a laptop to switch on/off the fans...) then no. So it depends on how
and where it will be used.

Yours,
Linus Walleij
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 66d92be0f57daa9eabb48d7e53b6b2bea0c40863..65877ca70b7e929353798c5639bf38593241d83e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -503,6 +503,7 @@  M:	Kim Seer Paller <kimseer.paller@analog.com>
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/gpio/gpio-adg1414.yaml
+F:	drivers/gpio/gpio-adg1414.c
 
 ADM1025 HARDWARE MONITOR DRIVER
 M:	Jean Delvare <jdelvare@suse.com>
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 98b4d1633b258b93d05ba66d53f647e7ec6ac364..4b797ddebd21bc879524f8504b343add2730c793 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1797,6 +1797,16 @@  config GPIO_74X164
 	  shift registers. This driver can be used to provide access
 	  to more GPIO outputs.
 
+config GPIO_ADG1414
+	tristate "ADG1414 Serially-Controlled Octal SPST Switches"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADG1414
+	  Serially-Controlled Octal SPST Switches
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gpio-adg1414.
+
 config GPIO_MAX3191X
 	tristate "Maxim MAX3191x industrial serializer"
 	select CRC8
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index af3ba4d81b583842893ea69e677fbe2abf31bc7b..58d723cb32feebd35db13ae9cae5d232feba3f5b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -24,6 +24,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_ADG1414)		+= gpio-adg1414.o
 obj-$(CONFIG_GPIO_ADNP)			+= gpio-adnp.o
 obj-$(CONFIG_GPIO_ADP5520)		+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5585)		+= gpio-adp5585.o
diff --git a/drivers/gpio/gpio-adg1414.c b/drivers/gpio/gpio-adg1414.c
new file mode 100644
index 0000000000000000000000000000000000000000..91e07c38fb1c86162afd9cfcdf1d7cfcccb03234
--- /dev/null
+++ b/drivers/gpio/gpio-adg1414.c
@@ -0,0 +1,162 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADG1414 Serially-Controlled Octal SPST Switches
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#define ADG1414_MAX_DEVICES		4
+
+struct adg1414_state {
+	struct spi_device *spi;
+	struct gpio_chip chip;
+	struct regmap *regmap;
+	struct mutex lock; /* protect sensor state */
+	u32 buf;
+
+	__be32 tx __aligned(ARCH_DMA_MINALIGN);
+};
+
+static int adg1414_spi_write(void *context, const void *data, size_t count)
+{
+	struct adg1414_state *st = context;
+
+	struct spi_transfer xfer = {
+		.tx_buf = &st->tx,
+		.len = count,
+	};
+
+	return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int adg1414_spi_read(void *context, const void *reg, size_t reg_size,
+			    void *val, size_t val_size)
+{
+	return 0;
+}
+
+static int adg1414_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct adg1414_state *st = gpiochip_get_data(chip);
+
+	guard(mutex)(&st->lock);
+
+	return st->buf & BIT(offset);
+}
+
+static void adg1414_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+	struct adg1414_state *st = gpiochip_get_data(chip);
+
+	guard(mutex)(&st->lock);
+
+	if (value)
+		st->buf |= BIT(offset);
+	else
+		st->buf &= ~BIT(offset);
+
+	st->tx = cpu_to_be32(st->buf << (32 - st->chip.ngpio));
+
+	adg1414_spi_write(st, 0, st->chip.ngpio / 8);
+}
+
+static const struct regmap_bus adg1414_regmap_bus = {
+	.write = adg1414_spi_write,
+	.read =	adg1414_spi_read,
+	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config adg1414_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int adg1414_get_direction(struct gpio_chip *chip,
+				 unsigned int offset)
+{
+	return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int adg1414_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct adg1414_state *st;
+	struct gpio_desc *reset;
+	u32 num_devices;
+	int ret;
+
+	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	st->spi = spi;
+
+	st->regmap = devm_regmap_init(dev, &adg1414_regmap_bus, st,
+				      &adg1414_regmap_config);
+	if (IS_ERR(st->regmap))
+		return dev_err_probe(dev, PTR_ERR(st->regmap),
+				     "Failed to initialize regmap");
+
+	/* Use reset pin to reset the device */
+	reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(reset))
+		return dev_err_probe(dev, PTR_ERR(reset),
+				     "Failed to get reset gpio");
+
+	if (reset) {
+		fsleep(1);
+		gpiod_set_value_cansleep(reset, 0);
+	}
+
+	num_devices = 1;
+	ret = device_property_read_u32(dev, "#daisy-chained-devices",
+				       &num_devices);
+	if (!ret) {
+		if (!num_devices || num_devices > ADG1414_MAX_DEVICES)
+			return dev_err_probe(dev, ret,
+			       "Failed to get daisy-chained-devices property\n");
+	}
+
+	st->chip.label = "adg1414";
+	st->chip.parent = dev;
+	st->chip.get_direction = adg1414_get_direction;
+	st->chip.set = adg1414_set;
+	st->chip.get = adg1414_get;
+	st->chip.base = -1;
+	st->chip.ngpio =  num_devices * 8;
+	st->chip.can_sleep = true;
+
+	ret = devm_mutex_init(dev, &st->lock);
+	if (ret)
+		return ret;
+
+	return devm_gpiochip_add_data(dev, &st->chip, st);
+}
+
+static const struct of_device_id adg1414_of_match[] = {
+	{ .compatible = "adi,adg1414-gpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adg1414_of_match);
+
+static struct spi_driver adg1414_driver = {
+	.driver = {
+		.name = "adg1414-gpio",
+		.of_match_table = adg1414_of_match,
+	},
+	.probe = adg1414_probe,
+};
+module_spi_driver(adg1414_driver);
+
+MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>");
+MODULE_DESCRIPTION("ADG1414 Serially-Controlled Octal SPST Switches");
+MODULE_LICENSE("GPL");