162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * GPIO latch driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2022 Sascha Hauer <s.hauer@pengutronix.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This driver implements a GPIO (or better GPO as there is no input)
862306a36Sopenharmony_ci * multiplexer based on latches like this:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * CLK0 ----------------------.        ,--------.
1162306a36Sopenharmony_ci * CLK1 -------------------.  `--------|>    #0 |
1262306a36Sopenharmony_ci *                         |           |        |
1362306a36Sopenharmony_ci * OUT0 ----------------+--|-----------|D0    Q0|-----|<
1462306a36Sopenharmony_ci * OUT1 --------------+-|--|-----------|D1    Q1|-----|<
1562306a36Sopenharmony_ci * OUT2 ------------+-|-|--|-----------|D2    Q2|-----|<
1662306a36Sopenharmony_ci * OUT3 ----------+-|-|-|--|-----------|D3    Q3|-----|<
1762306a36Sopenharmony_ci * OUT4 --------+-|-|-|-|--|-----------|D4    Q4|-----|<
1862306a36Sopenharmony_ci * OUT5 ------+-|-|-|-|-|--|-----------|D5    Q5|-----|<
1962306a36Sopenharmony_ci * OUT6 ----+-|-|-|-|-|-|--|-----------|D6    Q6|-----|<
2062306a36Sopenharmony_ci * OUT7 --+-|-|-|-|-|-|-|--|-----------|D7    Q7|-----|<
2162306a36Sopenharmony_ci *        | | | | | | | |  |           `--------'
2262306a36Sopenharmony_ci *        | | | | | | | |  |
2362306a36Sopenharmony_ci *        | | | | | | | |  |           ,--------.
2462306a36Sopenharmony_ci *        | | | | | | | |  `-----------|>    #1 |
2562306a36Sopenharmony_ci *        | | | | | | | |              |        |
2662306a36Sopenharmony_ci *        | | | | | | | `--------------|D0    Q0|-----|<
2762306a36Sopenharmony_ci *        | | | | | | `----------------|D1    Q1|-----|<
2862306a36Sopenharmony_ci *        | | | | | `------------------|D2    Q2|-----|<
2962306a36Sopenharmony_ci *        | | | | `--------------------|D3    Q3|-----|<
3062306a36Sopenharmony_ci *        | | | `----------------------|D4    Q4|-----|<
3162306a36Sopenharmony_ci *        | | `------------------------|D5    Q5|-----|<
3262306a36Sopenharmony_ci *        | `--------------------------|D6    Q6|-----|<
3362306a36Sopenharmony_ci *        `----------------------------|D7    Q7|-----|<
3462306a36Sopenharmony_ci *                                     `--------'
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * The above is just an example. The actual number of number of latches and
3762306a36Sopenharmony_ci * the number of inputs per latch is derived from the number of GPIOs given
3862306a36Sopenharmony_ci * in the corresponding device tree properties.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/err.h>
4262306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
4362306a36Sopenharmony_ci#include <linux/gpio/driver.h>
4462306a36Sopenharmony_ci#include <linux/module.h>
4562306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
4662306a36Sopenharmony_ci#include <linux/platform_device.h>
4762306a36Sopenharmony_ci#include <linux/delay.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "gpiolib.h"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct gpio_latch_priv {
5262306a36Sopenharmony_ci	struct gpio_chip gc;
5362306a36Sopenharmony_ci	struct gpio_descs *clk_gpios;
5462306a36Sopenharmony_ci	struct gpio_descs *latched_gpios;
5562306a36Sopenharmony_ci	int n_latched_gpios;
5662306a36Sopenharmony_ci	unsigned int setup_duration_ns;
5762306a36Sopenharmony_ci	unsigned int clock_duration_ns;
5862306a36Sopenharmony_ci	unsigned long *shadow;
5962306a36Sopenharmony_ci	/*
6062306a36Sopenharmony_ci	 * Depending on whether any of the underlying GPIOs may sleep we either
6162306a36Sopenharmony_ci	 * use a mutex or a spinlock to protect our shadow map.
6262306a36Sopenharmony_ci	 */
6362306a36Sopenharmony_ci	union {
6462306a36Sopenharmony_ci		struct mutex mutex; /* protects @shadow */
6562306a36Sopenharmony_ci		spinlock_t spinlock; /* protects @shadow */
6662306a36Sopenharmony_ci	};
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_OUT;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void gpio_latch_set_unlocked(struct gpio_latch_priv *priv,
7562306a36Sopenharmony_ci				    void (*set)(struct gpio_desc *desc, int value),
7662306a36Sopenharmony_ci				    unsigned int offset, bool val)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int latch = offset / priv->n_latched_gpios;
7962306a36Sopenharmony_ci	int i;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	assign_bit(offset, priv->shadow, val);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	for (i = 0; i < priv->n_latched_gpios; i++)
8462306a36Sopenharmony_ci		set(priv->latched_gpios->desc[i],
8562306a36Sopenharmony_ci		    test_bit(latch * priv->n_latched_gpios + i, priv->shadow));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ndelay(priv->setup_duration_ns);
8862306a36Sopenharmony_ci	set(priv->clk_gpios->desc[latch], 1);
8962306a36Sopenharmony_ci	ndelay(priv->clock_duration_ns);
9062306a36Sopenharmony_ci	set(priv->clk_gpios->desc[latch], 0);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
9662306a36Sopenharmony_ci	unsigned long flags;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	spin_lock_irqsave(&priv->spinlock, flags);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	gpio_latch_set_unlocked(priv, gpiod_set_value, offset, val);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->spinlock, flags);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void gpio_latch_set_can_sleep(struct gpio_chip *gc, unsigned int offset, int val)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct gpio_latch_priv *priv = gpiochip_get_data(gc);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	mutex_lock(&priv->mutex);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	gpio_latch_set_unlocked(priv, gpiod_set_value_cansleep, offset, val);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	mutex_unlock(&priv->mutex);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic bool gpio_latch_can_sleep(struct gpio_latch_priv *priv, unsigned int n_latches)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int i;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	for (i = 0; i < n_latches; i++)
12162306a36Sopenharmony_ci		if (gpiod_cansleep(priv->clk_gpios->desc[i]))
12262306a36Sopenharmony_ci			return true;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (i = 0; i < priv->n_latched_gpios; i++)
12562306a36Sopenharmony_ci		if (gpiod_cansleep(priv->latched_gpios->desc[i]))
12662306a36Sopenharmony_ci			return true;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return false;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Some value which is still acceptable to delay in atomic context.
13362306a36Sopenharmony_ci * If we need to go higher we might have to switch to usleep_range(),
13462306a36Sopenharmony_ci * but that cannot ne used in atomic context and the driver would have
13562306a36Sopenharmony_ci * to be adjusted to support that.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_ci#define DURATION_NS_MAX 5000
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int gpio_latch_probe(struct platform_device *pdev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct gpio_latch_priv *priv;
14262306a36Sopenharmony_ci	unsigned int n_latches;
14362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!priv)
14762306a36Sopenharmony_ci		return -ENOMEM;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	priv->clk_gpios = devm_gpiod_get_array(&pdev->dev, "clk", GPIOD_OUT_LOW);
15062306a36Sopenharmony_ci	if (IS_ERR(priv->clk_gpios))
15162306a36Sopenharmony_ci		return PTR_ERR(priv->clk_gpios);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	priv->latched_gpios = devm_gpiod_get_array(&pdev->dev, "latched", GPIOD_OUT_LOW);
15462306a36Sopenharmony_ci	if (IS_ERR(priv->latched_gpios))
15562306a36Sopenharmony_ci		return PTR_ERR(priv->latched_gpios);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	n_latches = priv->clk_gpios->ndescs;
15862306a36Sopenharmony_ci	priv->n_latched_gpios = priv->latched_gpios->ndescs;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	priv->shadow = devm_bitmap_zalloc(&pdev->dev, n_latches * priv->n_latched_gpios,
16162306a36Sopenharmony_ci					  GFP_KERNEL);
16262306a36Sopenharmony_ci	if (!priv->shadow)
16362306a36Sopenharmony_ci		return -ENOMEM;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (gpio_latch_can_sleep(priv, n_latches)) {
16662306a36Sopenharmony_ci		priv->gc.can_sleep = true;
16762306a36Sopenharmony_ci		priv->gc.set = gpio_latch_set_can_sleep;
16862306a36Sopenharmony_ci		mutex_init(&priv->mutex);
16962306a36Sopenharmony_ci	} else {
17062306a36Sopenharmony_ci		priv->gc.can_sleep = false;
17162306a36Sopenharmony_ci		priv->gc.set = gpio_latch_set;
17262306a36Sopenharmony_ci		spin_lock_init(&priv->spinlock);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns);
17662306a36Sopenharmony_ci	if (priv->setup_duration_ns > DURATION_NS_MAX) {
17762306a36Sopenharmony_ci		dev_warn(&pdev->dev, "setup-duration-ns too high, limit to %d\n",
17862306a36Sopenharmony_ci			 DURATION_NS_MAX);
17962306a36Sopenharmony_ci		priv->setup_duration_ns = DURATION_NS_MAX;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns);
18362306a36Sopenharmony_ci	if (priv->clock_duration_ns > DURATION_NS_MAX) {
18462306a36Sopenharmony_ci		dev_warn(&pdev->dev, "clock-duration-ns too high, limit to %d\n",
18562306a36Sopenharmony_ci			 DURATION_NS_MAX);
18662306a36Sopenharmony_ci		priv->clock_duration_ns = DURATION_NS_MAX;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	priv->gc.get_direction = gpio_latch_get_direction;
19062306a36Sopenharmony_ci	priv->gc.ngpio = n_latches * priv->n_latched_gpios;
19162306a36Sopenharmony_ci	priv->gc.owner = THIS_MODULE;
19262306a36Sopenharmony_ci	priv->gc.base = -1;
19362306a36Sopenharmony_ci	priv->gc.parent = &pdev->dev;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic const struct of_device_id gpio_latch_ids[] = {
20162306a36Sopenharmony_ci	{
20262306a36Sopenharmony_ci		.compatible = "gpio-latch",
20362306a36Sopenharmony_ci	},
20462306a36Sopenharmony_ci	{ /* sentinel */ }
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_latch_ids);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic struct platform_driver gpio_latch_driver = {
20962306a36Sopenharmony_ci	.driver	= {
21062306a36Sopenharmony_ci		.name		= "gpio-latch",
21162306a36Sopenharmony_ci		.of_match_table	= gpio_latch_ids,
21262306a36Sopenharmony_ci	},
21362306a36Sopenharmony_ci	.probe	= gpio_latch_probe,
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_cimodule_platform_driver(gpio_latch_driver);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
21862306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
21962306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO latch driver");
220