layerscape: switch to 5.10 Kernel
[openwrt/openwrt.git] / target / linux / ath79 / files / drivers / gpio / gpio-latch.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * GPIO latch driver
4 *
5 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
6 */
7
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/types.h>
12 #include <linux/gpio/consumer.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/platform_device.h>
15 #include <linux/of_platform.h>
16 #include <linux/of_gpio.h>
17
18 #define GPIO_LATCH_DRIVER_NAME "gpio-latch"
19 #define GPIO_LATCH_LINES 9
20
21 struct gpio_latch_chip {
22 struct gpio_chip gc;
23 struct mutex mutex;
24 struct mutex latch_mutex;
25 bool latch_enabled;
26 int le_gpio;
27 bool le_active_low;
28 struct gpio_desc *gpios[GPIO_LATCH_LINES];
29 };
30
31 static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc)
32 {
33 return container_of(gc, struct gpio_latch_chip, gc);
34 }
35
36 static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable)
37 {
38 mutex_lock(&glc->mutex);
39
40 if (enable)
41 glc->latch_enabled = true;
42
43 if (glc->latch_enabled)
44 mutex_lock(&glc->latch_mutex);
45 }
46
47 static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable)
48 {
49 if (glc->latch_enabled)
50 mutex_unlock(&glc->latch_mutex);
51
52 if (disable)
53 glc->latch_enabled = true;
54
55 mutex_unlock(&glc->mutex);
56 }
57
58 static int
59 gpio_latch_get(struct gpio_chip *gc, unsigned offset)
60 {
61 struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
62 int ret;
63
64 gpio_latch_lock(glc, false);
65 ret = gpiod_get_value(glc->gpios[offset]);
66 gpio_latch_unlock(glc, false);
67
68 return ret;
69 }
70
71 static void
72 gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value)
73 {
74 struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
75 bool enable_latch = false;
76 bool disable_latch = false;
77
78 if (offset == glc->le_gpio) {
79 enable_latch = value ^ glc->le_active_low;
80 disable_latch = !enable_latch;
81 }
82
83 gpio_latch_lock(glc, enable_latch);
84 gpiod_set_raw_value(glc->gpios[offset], value);
85 gpio_latch_unlock(glc, disable_latch);
86 }
87
88 static int
89 gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value)
90 {
91 struct gpio_latch_chip *glc = to_gpio_latch_chip(gc);
92 bool enable_latch = false;
93 bool disable_latch = false;
94 int ret;
95
96 if (offset == glc->le_gpio) {
97 enable_latch = value ^ glc->le_active_low;
98 disable_latch = !enable_latch;
99 }
100
101 gpio_latch_lock(glc, enable_latch);
102 ret = gpiod_direction_output_raw(glc->gpios[offset], value);
103 gpio_latch_unlock(glc, disable_latch);
104
105 return ret;
106 }
107
108 static int gpio_latch_probe(struct platform_device *pdev)
109 {
110 struct gpio_latch_chip *glc;
111 struct gpio_chip *gc;
112 struct device *dev = &pdev->dev;
113 struct device_node *of_node = dev->of_node;
114 int i, n;
115
116 glc = devm_kzalloc(dev, sizeof(*glc), GFP_KERNEL);
117 if (!glc)
118 return -ENOMEM;
119
120 mutex_init(&glc->mutex);
121 mutex_init(&glc->latch_mutex);
122
123 n = gpiod_count(dev, NULL);
124 if (n <= 0) {
125 dev_err(dev, "failed to get gpios: %d\n", n);
126 return n;
127 } else if (n != GPIO_LATCH_LINES) {
128 dev_err(dev, "expected %d gpios\n", GPIO_LATCH_LINES);
129 return -EINVAL;
130 }
131
132 for (i = 0; i < n; i++) {
133 glc->gpios[i] = devm_gpiod_get_index_optional(dev, NULL, i,
134 GPIOD_OUT_LOW);
135 if (IS_ERR(glc->gpios[i])) {
136 dev_err(dev, "failed to get gpio %d: %d\n", i,
137 PTR_ERR(glc->gpios[i]));
138 return PTR_ERR(glc->gpios[i]);
139 }
140 }
141
142 glc->le_gpio = 8;
143 glc->le_active_low = gpiod_is_active_low(glc->gpios[glc->le_gpio]);
144
145 if (!glc->gpios[glc->le_gpio]) {
146 dev_err(dev, "missing required latch-enable gpio %d\n",
147 glc->le_gpio);
148 return -EINVAL;
149 }
150
151 gc = &glc->gc;
152 gc->label = GPIO_LATCH_DRIVER_NAME;
153 gc->can_sleep = true;
154 gc->base = -1;
155 gc->ngpio = GPIO_LATCH_LINES;
156 gc->get = gpio_latch_get;
157 gc->set = gpio_latch_set;
158 gc->direction_output = gpio_latch_direction_output;
159 gc->of_node = of_node;
160
161 platform_set_drvdata(pdev, glc);
162
163 i = gpiochip_add(&glc->gc);
164 if (i) {
165 dev_err(dev, "gpiochip_add() failed: %d\n", i);
166 return i;
167 }
168
169 return 0;
170 }
171
172 static int gpio_latch_remove(struct platform_device *pdev)
173 {
174 struct gpio_latch_chip *glc = platform_get_drvdata(pdev);
175
176 gpiochip_remove(&glc->gc);
177 return 0;
178 }
179
180 static const struct of_device_id gpio_latch_match[] = {
181 { .compatible = GPIO_LATCH_DRIVER_NAME },
182 {},
183 };
184
185 MODULE_DEVICE_TABLE(of, gpio_latch_match);
186
187 static struct platform_driver gpio_latch_driver = {
188 .probe = gpio_latch_probe,
189 .remove = gpio_latch_remove,
190 .driver = {
191 .name = GPIO_LATCH_DRIVER_NAME,
192 .owner = THIS_MODULE,
193 .of_match_table = gpio_latch_match,
194 },
195 };
196
197 module_platform_driver(gpio_latch_driver);
198
199 MODULE_DESCRIPTION("GPIO latch driver");
200 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
201 MODULE_AUTHOR("Denis Kalashnikov <denis281089@gmail.com>");
202 MODULE_LICENSE("GPL v2");
203 MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME);