blob: f683547cfd6bf5822ee735cdee180cc99a2635c3 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * NXP 74HC153 - Dual 4-input multiplexer GPIO driver
3 *
4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
5 * Copyright (C) 2020 Mauri Sandberg <sandberg@mailfence.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Example device tree definition:
12 *
13 * gpio-extender {
14 * compatible = "nxp,74hc153-gpio";
15 * gpio-controller;
16 * #gpio-cells = <2>;
17 *
18 * // GPIOs used by this node
19 * gpio-s0 = <&gpio 9 GPIO_ACTIVE_HIGH>;
20 * gpio-s1 = <&gpio 11 GPIO_ACTIVE_HIGH>;
21 * gpio-1y = <&gpio 12 GPIO_ACTIVE_HIGH>;
22 * gpio-2y = <&gpio 14 GPIO_ACTIVE_HIGH>;
23 * };
24 *
25 */
26
27#include <linux/version.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/gpio.h>
31#include <linux/slab.h>
32#include <linux/platform_device.h>
33#include <linux/of_gpio.h>
34
35#define NXP_74HC153_NUM_GPIOS 8
36#define NXP_74HC153_S0_MASK 0x1
37#define NXP_74HC153_S1_MASK 0x2
38#define NXP_74HC153_BANK_MASK 0x4
39
40#define NXP_74HC153_DRIVER_NAME "nxp-74hc153"
41
42struct nxp_74hc153_config {
43 unsigned gpio_pin_s0;
44 unsigned gpio_pin_s1;
45 unsigned gpio_pin_1y;
46 unsigned gpio_pin_2y;
47};
48
49struct nxp_74hc153_chip {
50 struct device *parent;
51 struct gpio_chip gpio_chip;
52 struct mutex lock;
53 struct nxp_74hc153_config config;
54};
55
56static struct nxp_74hc153_chip *gpio_to_nxp(struct gpio_chip *gc)
57{
58 return container_of(gc, struct nxp_74hc153_chip, gpio_chip);
59}
60
61static int nxp_74hc153_direction_input(struct gpio_chip *gc, unsigned offset)
62{
63 return 0;
64}
65
66static int nxp_74hc153_direction_output(struct gpio_chip *gc,
67 unsigned offset, int val)
68{
69 return -EINVAL;
70}
71
72static int nxp_74hc153_get_value(struct gpio_chip *gc, unsigned offset)
73{
74 struct nxp_74hc153_chip *nxp;
75 struct nxp_74hc153_platform_data *pdata;
76 unsigned s0;
77 unsigned s1;
78 unsigned pin;
79 int ret;
80
81 nxp = gpio_to_nxp(gc);
82 pdata = nxp->parent->platform_data;
83
84 s0 = !!(offset & NXP_74HC153_S0_MASK);
85 s1 = !!(offset & NXP_74HC153_S1_MASK);
86 pin = (offset & NXP_74HC153_BANK_MASK) ? nxp->config.gpio_pin_2y
87 : nxp->config.gpio_pin_1y;
88
89 mutex_lock(&nxp->lock);
90 gpio_set_value(nxp->config.gpio_pin_s0, s0);
91 gpio_set_value(nxp->config.gpio_pin_s1, s1);
92 ret = gpio_get_value(pin);
93 mutex_unlock(&nxp->lock);
94
95 return ret;
96}
97
98static void nxp_74hc153_set_value(struct gpio_chip *gc,
99 unsigned offset, int val)
100{
101 /* not supported */
102}
103
104static int nxp_74hc153_probe(struct platform_device *pdev)
105{
106 struct device_node *np = pdev->dev.of_node;
107 struct nxp_74hc153_chip *nxp;
108 struct gpio_chip *gc;
109 int err;
110 unsigned gpio_s0;
111 unsigned gpio_s1;
112 unsigned gpio_1y;
113 unsigned gpio_2y;
114
115 nxp = kzalloc(sizeof(struct nxp_74hc153_chip), GFP_KERNEL);
116 if (nxp == NULL) {
117 dev_err(&pdev->dev, "no memory for private data\n");
118 return -ENOMEM;
119 }
120
121 gpio_s0 = of_get_named_gpio(np, "gpio-s0", 0);
122 gpio_s1 = of_get_named_gpio(np, "gpio-s1", 0);
123 gpio_1y = of_get_named_gpio(np, "gpio-1y", 0);
124 gpio_2y = of_get_named_gpio(np, "gpio-2y", 0);
125
126 if (!gpio_is_valid(gpio_s0) || !gpio_is_valid(gpio_s1) ||
127 !gpio_is_valid(gpio_1y) || !gpio_is_valid(gpio_2y)) {
128
129 dev_err(&pdev->dev, "control GPIO(s) are missing\n");
130 err = -EINVAL;
131 goto err_free_nxp;
132 } else {
133 nxp->config.gpio_pin_s0 = gpio_s0;
134 nxp->config.gpio_pin_s1 = gpio_s1;
135 nxp->config.gpio_pin_1y = gpio_1y;
136 nxp->config.gpio_pin_2y = gpio_2y;
137 }
138
139 // apply pin configuration
140 err = gpio_request(nxp->config.gpio_pin_s0, dev_name(&pdev->dev));
141 if (err) {
142 dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n",
143 nxp->config.gpio_pin_s0, err);
144 goto err_free_nxp;
145 }
146
147 err = gpio_request(nxp->config.gpio_pin_s1, dev_name(&pdev->dev));
148 if (err) {
149 dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n",
150 nxp->config.gpio_pin_s1, err);
151 goto err_free_s0;
152 }
153
154 err = gpio_request(nxp->config.gpio_pin_1y, dev_name(&pdev->dev));
155 if (err) {
156 dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n",
157 nxp->config.gpio_pin_1y, err);
158 goto err_free_s1;
159 }
160
161 err = gpio_request(nxp->config.gpio_pin_2y, dev_name(&pdev->dev));
162 if (err) {
163 dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n",
164 nxp->config.gpio_pin_2y, err);
165 goto err_free_1y;
166 }
167
168 err = gpio_direction_output(nxp->config.gpio_pin_s0, 0);
169 if (err) {
170 dev_err(&pdev->dev,
171 "unable to set direction of gpio %u, err=%d\n",
172 nxp->config.gpio_pin_s0, err);
173 goto err_free_2y;
174 }
175
176 err = gpio_direction_output(nxp->config.gpio_pin_s1, 0);
177 if (err) {
178 dev_err(&pdev->dev,
179 "unable to set direction of gpio %u, err=%d\n",
180 nxp->config.gpio_pin_s1, err);
181 goto err_free_2y;
182 }
183
184 err = gpio_direction_input(nxp->config.gpio_pin_1y);
185 if (err) {
186 dev_err(&pdev->dev,
187 "unable to set direction of gpio %u, err=%d\n",
188 nxp->config.gpio_pin_1y, err);
189 goto err_free_2y;
190 }
191
192 err = gpio_direction_input(nxp->config.gpio_pin_2y);
193 if (err) {
194 dev_err(&pdev->dev,
195 "unable to set direction of gpio %u, err=%d\n",
196 nxp->config.gpio_pin_2y, err);
197 goto err_free_2y;
198 }
199
200 nxp->parent = &pdev->dev;
201 mutex_init(&nxp->lock);
202
203 gc = &nxp->gpio_chip;
204
205 gc->direction_input = nxp_74hc153_direction_input;
206 gc->direction_output = nxp_74hc153_direction_output;
207 gc->get = nxp_74hc153_get_value;
208 gc->set = nxp_74hc153_set_value;
209 gc->can_sleep = 1;
210
211 gc->base = -1;
212 gc->ngpio = NXP_74HC153_NUM_GPIOS;
213 gc->label = dev_name(nxp->parent);
214 gc->parent = nxp->parent;
215 gc->owner = THIS_MODULE;
216 gc->of_node = np;
217
218 err = gpiochip_add(&nxp->gpio_chip);
219 if (err) {
220 dev_err(&pdev->dev, "unable to add gpio chip, err=%d\n", err);
221 goto err_free_2y;
222 }
223
224 platform_set_drvdata(pdev, nxp);
225 return 0;
226
227err_free_2y:
228 gpio_free(nxp->config.gpio_pin_2y);
229err_free_1y:
230 gpio_free(nxp->config.gpio_pin_1y);
231err_free_s1:
232 gpio_free(nxp->config.gpio_pin_s1);
233err_free_s0:
234 gpio_free(nxp->config.gpio_pin_s0);
235err_free_nxp:
236 kfree(nxp);
237 return err;
238}
239
240static int nxp_74hc153_remove(struct platform_device *pdev)
241{
242 struct nxp_74hc153_chip *nxp = platform_get_drvdata(pdev);
243
244 if (nxp) {
245 gpiochip_remove(&nxp->gpio_chip);
246 gpio_free(nxp->config.gpio_pin_2y);
247 gpio_free(nxp->config.gpio_pin_1y);
248 gpio_free(nxp->config.gpio_pin_s1);
249 gpio_free(nxp->config.gpio_pin_s0);
250
251 kfree(nxp);
252 platform_set_drvdata(pdev, NULL);
253 }
254
255 return 0;
256}
257
258static struct of_device_id nxp_74hc153_id[] = {
259 {
260 .compatible = "nxp,74hc153-gpio",
261 .data = NULL,
262 }, { /* sentinel */ }
263};
264MODULE_DEVICE_TABLE(of, nxp_74hc153_id);
265
266static struct platform_driver nxp_74hc153_driver = {
267 .probe = nxp_74hc153_probe,
268 .remove = nxp_74hc153_remove,
269 .driver = {
270 .name = NXP_74HC153_DRIVER_NAME,
271 .owner = THIS_MODULE,
272 .of_match_table = nxp_74hc153_id,
273 },
274};
275
276static int __init nxp_74hc153_init(void)
277{
278 return platform_driver_register(&nxp_74hc153_driver);
279}
280subsys_initcall(nxp_74hc153_init);
281
282static void __exit nxp_74hc153_exit(void)
283{
284 platform_driver_unregister(&nxp_74hc153_driver);
285}
286module_exit(nxp_74hc153_exit);
287
288MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
289MODULE_DESCRIPTION("GPIO expander driver for NXP 74HC153");
290MODULE_LICENSE("GPL v2");
291MODULE_ALIAS("platform:" NXP_74HC153_DRIVER_NAME);