|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * GPIO latch driver | 
|  | * | 
|  | *  Copyright (C) 2022 Sascha Hauer <s.hauer@pengutronix.de> | 
|  | * | 
|  | * This driver implements a GPIO (or better GPO as there is no input) | 
|  | * multiplexer based on latches like this: | 
|  | * | 
|  | * CLK0 ----------------------.        ,--------. | 
|  | * CLK1 -------------------.  `--------|>    #0 | | 
|  | *                         |           |        | | 
|  | * OUT0 ----------------+--|-----------|D0    Q0|-----|< | 
|  | * OUT1 --------------+-|--|-----------|D1    Q1|-----|< | 
|  | * OUT2 ------------+-|-|--|-----------|D2    Q2|-----|< | 
|  | * OUT3 ----------+-|-|-|--|-----------|D3    Q3|-----|< | 
|  | * OUT4 --------+-|-|-|-|--|-----------|D4    Q4|-----|< | 
|  | * OUT5 ------+-|-|-|-|-|--|-----------|D5    Q5|-----|< | 
|  | * OUT6 ----+-|-|-|-|-|-|--|-----------|D6    Q6|-----|< | 
|  | * OUT7 --+-|-|-|-|-|-|-|--|-----------|D7    Q7|-----|< | 
|  | *        | | | | | | | |  |           `--------' | 
|  | *        | | | | | | | |  | | 
|  | *        | | | | | | | |  |           ,--------. | 
|  | *        | | | | | | | |  `-----------|>    #1 | | 
|  | *        | | | | | | | |              |        | | 
|  | *        | | | | | | | `--------------|D0    Q0|-----|< | 
|  | *        | | | | | | `----------------|D1    Q1|-----|< | 
|  | *        | | | | | `------------------|D2    Q2|-----|< | 
|  | *        | | | | `--------------------|D3    Q3|-----|< | 
|  | *        | | | `----------------------|D4    Q4|-----|< | 
|  | *        | | `------------------------|D5    Q5|-----|< | 
|  | *        | `--------------------------|D6    Q6|-----|< | 
|  | *        `----------------------------|D7    Q7|-----|< | 
|  | *                                     `--------' | 
|  | * | 
|  | * The above is just an example. The actual number of number of latches and | 
|  | * the number of inputs per latch is derived from the number of GPIOs given | 
|  | * in the corresponding device tree properties. | 
|  | */ | 
|  |  | 
|  | #include <linux/cleanup.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/gpio/consumer.h> | 
|  | #include <linux/gpio/driver.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/mod_devicetable.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/property.h> | 
|  | #include <linux/delay.h> | 
|  |  | 
|  | #include "gpiolib.h" | 
|  |  | 
|  | struct gpio_latch_priv { | 
|  | struct gpio_chip gc; | 
|  | struct gpio_descs *clk_gpios; | 
|  | struct gpio_descs *latched_gpios; | 
|  | int n_latched_gpios; | 
|  | unsigned int setup_duration_ns; | 
|  | unsigned int clock_duration_ns; | 
|  | unsigned long *shadow; | 
|  | /* | 
|  | * Depending on whether any of the underlying GPIOs may sleep we either | 
|  | * use a mutex or a spinlock to protect our shadow map. | 
|  | */ | 
|  | union { | 
|  | struct mutex mutex; /* protects @shadow */ | 
|  | spinlock_t spinlock; /* protects @shadow */ | 
|  | }; | 
|  | }; | 
|  |  | 
|  | static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset) | 
|  | { | 
|  | return GPIO_LINE_DIRECTION_OUT; | 
|  | } | 
|  |  | 
|  | static int gpio_latch_set_unlocked(struct gpio_latch_priv *priv, | 
|  | int (*set)(struct gpio_desc *desc, int value), | 
|  | unsigned int offset, bool val) | 
|  | { | 
|  | int latch = offset / priv->n_latched_gpios, i, ret; | 
|  |  | 
|  | assign_bit(offset, priv->shadow, val); | 
|  |  | 
|  | for (i = 0; i < priv->n_latched_gpios; i++) { | 
|  | ret = set(priv->latched_gpios->desc[i], | 
|  | test_bit(latch * priv->n_latched_gpios + i, | 
|  | priv->shadow)); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ndelay(priv->setup_duration_ns); | 
|  | set(priv->clk_gpios->desc[latch], 1); | 
|  | ndelay(priv->clock_duration_ns); | 
|  | set(priv->clk_gpios->desc[latch], 0); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val) | 
|  | { | 
|  | struct gpio_latch_priv *priv = gpiochip_get_data(gc); | 
|  |  | 
|  | guard(spinlock_irqsave)(&priv->spinlock); | 
|  |  | 
|  | return gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val); | 
|  | } | 
|  |  | 
|  | static int gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val) | 
|  | { | 
|  | struct gpio_latch_priv *priv = gpiochip_get_data(gc); | 
|  |  | 
|  | guard(mutex)(&priv->mutex); | 
|  |  | 
|  | return gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val); | 
|  | } | 
|  |  | 
|  | static bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < n_latches; i++) | 
|  | if (gpiod_cansleep(priv->clk_gpios->desc[i])) | 
|  | return true; | 
|  |  | 
|  | for (i = 0; i < priv->n_latched_gpios; i++) | 
|  | if (gpiod_cansleep(priv->latched_gpios->desc[i])) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Some value which is still acceptable to delay in atomic context. | 
|  | * If we need to go higher we might have to switch to usleep_range(), | 
|  | * but that cannot ne used in atomic context and the driver would have | 
|  | * to be adjusted to support that. | 
|  | */ | 
|  | #define DURATION_NS_MAX 5000 | 
|  |  | 
|  | static int gpio_latch_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct gpio_latch_priv *priv; | 
|  | unsigned int n_latches; | 
|  |  | 
|  | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | 
|  | if (!priv) | 
|  | return -ENOMEM; | 
|  |  | 
|  | priv->clk_gpios = devm_gpiod_get_array(dev, "clk", GPIOD_OUT_LOW); | 
|  | if (IS_ERR(priv->clk_gpios)) | 
|  | return PTR_ERR(priv->clk_gpios); | 
|  |  | 
|  | priv->latched_gpios = devm_gpiod_get_array(dev, "latched", GPIOD_OUT_LOW); | 
|  | if (IS_ERR(priv->latched_gpios)) | 
|  | return PTR_ERR(priv->latched_gpios); | 
|  |  | 
|  | n_latches = priv->clk_gpios->ndescs; | 
|  | priv->n_latched_gpios = priv->latched_gpios->ndescs; | 
|  |  | 
|  | priv->shadow = devm_bitmap_zalloc(dev, n_latches * priv->n_latched_gpios, | 
|  | GFP_KERNEL); | 
|  | if (!priv->shadow) | 
|  | return -ENOMEM; | 
|  |  | 
|  | if (gpio_latch_can_sleep(priv, n_latches)) { | 
|  | priv->gc.can_sleep = true; | 
|  | priv->gc.set = gpio_latch_set_can_sleep; | 
|  | mutex_init(&priv->mutex); | 
|  | } else { | 
|  | priv->gc.can_sleep = false; | 
|  | priv->gc.set = gpio_latch_set; | 
|  | spin_lock_init(&priv->spinlock); | 
|  | } | 
|  |  | 
|  | device_property_read_u32(dev, "setup-duration-ns", | 
|  | &priv->setup_duration_ns); | 
|  | if (priv->setup_duration_ns > DURATION_NS_MAX) { | 
|  | dev_warn(dev, "setup-duration-ns too high, limit to %d\n", | 
|  | DURATION_NS_MAX); | 
|  | priv->setup_duration_ns = DURATION_NS_MAX; | 
|  | } | 
|  |  | 
|  | device_property_read_u32(dev, "clock-duration-ns", | 
|  | &priv->clock_duration_ns); | 
|  | if (priv->clock_duration_ns > DURATION_NS_MAX) { | 
|  | dev_warn(dev, "clock-duration-ns too high, limit to %d\n", | 
|  | DURATION_NS_MAX); | 
|  | priv->clock_duration_ns = DURATION_NS_MAX; | 
|  | } | 
|  |  | 
|  | priv->gc.get_direction = gpio_latch_get_direction; | 
|  | priv->gc.ngpio = n_latches * priv->n_latched_gpios; | 
|  | priv->gc.owner = THIS_MODULE; | 
|  | priv->gc.base = -1; | 
|  | priv->gc.parent = dev; | 
|  |  | 
|  | platform_set_drvdata(pdev, priv); | 
|  |  | 
|  | return devm_gpiochip_add_data(dev, &priv->gc, priv); | 
|  | } | 
|  |  | 
|  | static const struct of_device_id gpio_latch_ids[] = { | 
|  | { | 
|  | .compatible = "gpio-latch", | 
|  | }, | 
|  | { /* sentinel */ } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, gpio_latch_ids); | 
|  |  | 
|  | static struct platform_driver gpio_latch_driver = { | 
|  | .driver	= { | 
|  | .name		= "gpio-latch", | 
|  | .of_match_table	= gpio_latch_ids, | 
|  | }, | 
|  | .probe	= gpio_latch_probe, | 
|  | }; | 
|  | module_platform_driver(gpio_latch_driver); | 
|  |  | 
|  | MODULE_LICENSE("GPL v2"); | 
|  | MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); | 
|  | MODULE_DESCRIPTION("GPIO latch driver"); |