OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /* |
2 | * NXP 74HC153 - Dual 4-input multiplexer GPIO driver |
||
3 | * |
||
4 | * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> |
||
5 | * |
||
6 | * This program is free software; you can redistribute it and/or modify |
||
7 | * it under the terms of the GNU General Public License version 2 as |
||
8 | * published by the Free Software Foundation. |
||
9 | */ |
||
10 | |||
11 | #include <linux/version.h> |
||
12 | #include <linux/module.h> |
||
13 | #include <linux/init.h> |
||
14 | #include <linux/gpio.h> |
||
15 | #include <linux/slab.h> |
||
16 | #include <linux/platform_device.h> |
||
17 | #include <linux/nxp_74hc153.h> |
||
18 | |||
19 | #define NXP_74HC153_NUM_GPIOS 8 |
||
20 | #define NXP_74HC153_S0_MASK 0x1 |
||
21 | #define NXP_74HC153_S1_MASK 0x2 |
||
22 | #define NXP_74HC153_BANK_MASK 0x4 |
||
23 | |||
24 | struct nxp_74hc153_chip { |
||
25 | struct device *parent; |
||
26 | struct gpio_chip gpio_chip; |
||
27 | struct mutex lock; |
||
28 | }; |
||
29 | |||
30 | static struct nxp_74hc153_chip *gpio_to_nxp(struct gpio_chip *gc) |
||
31 | { |
||
32 | return container_of(gc, struct nxp_74hc153_chip, gpio_chip); |
||
33 | } |
||
34 | |||
35 | static int nxp_74hc153_direction_input(struct gpio_chip *gc, unsigned offset) |
||
36 | { |
||
37 | return 0; |
||
38 | } |
||
39 | |||
40 | static int nxp_74hc153_direction_output(struct gpio_chip *gc, |
||
41 | unsigned offset, int val) |
||
42 | { |
||
43 | return -EINVAL; |
||
44 | } |
||
45 | |||
46 | static int nxp_74hc153_get_value(struct gpio_chip *gc, unsigned offset) |
||
47 | { |
||
48 | struct nxp_74hc153_chip *nxp; |
||
49 | struct nxp_74hc153_platform_data *pdata; |
||
50 | unsigned s0; |
||
51 | unsigned s1; |
||
52 | unsigned pin; |
||
53 | int ret; |
||
54 | |||
55 | nxp = gpio_to_nxp(gc); |
||
56 | pdata = nxp->parent->platform_data; |
||
57 | |||
58 | s0 = !!(offset & NXP_74HC153_S0_MASK); |
||
59 | s1 = !!(offset & NXP_74HC153_S1_MASK); |
||
60 | pin = (offset & NXP_74HC153_BANK_MASK) ? pdata->gpio_pin_2y |
||
61 | : pdata->gpio_pin_1y; |
||
62 | |||
63 | mutex_lock(&nxp->lock); |
||
64 | gpio_set_value(pdata->gpio_pin_s0, s0); |
||
65 | gpio_set_value(pdata->gpio_pin_s1, s1); |
||
66 | ret = gpio_get_value(pin); |
||
67 | mutex_unlock(&nxp->lock); |
||
68 | |||
69 | return ret; |
||
70 | } |
||
71 | |||
72 | static void nxp_74hc153_set_value(struct gpio_chip *gc, |
||
73 | unsigned offset, int val) |
||
74 | { |
||
75 | /* not supported */ |
||
76 | } |
||
77 | |||
78 | static int nxp_74hc153_probe(struct platform_device *pdev) |
||
79 | { |
||
80 | struct nxp_74hc153_platform_data *pdata; |
||
81 | struct nxp_74hc153_chip *nxp; |
||
82 | struct gpio_chip *gc; |
||
83 | int err; |
||
84 | |||
85 | pdata = pdev->dev.platform_data; |
||
86 | if (pdata == NULL) { |
||
87 | dev_dbg(&pdev->dev, "no platform data specified\n"); |
||
88 | return -EINVAL; |
||
89 | } |
||
90 | |||
91 | nxp = kzalloc(sizeof(struct nxp_74hc153_chip), GFP_KERNEL); |
||
92 | if (nxp == NULL) { |
||
93 | dev_err(&pdev->dev, "no memory for private data\n"); |
||
94 | return -ENOMEM; |
||
95 | } |
||
96 | |||
97 | err = gpio_request(pdata->gpio_pin_s0, dev_name(&pdev->dev)); |
||
98 | if (err) { |
||
99 | dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n", |
||
100 | pdata->gpio_pin_s0, err); |
||
101 | goto err_free_nxp; |
||
102 | } |
||
103 | |||
104 | err = gpio_request(pdata->gpio_pin_s1, dev_name(&pdev->dev)); |
||
105 | if (err) { |
||
106 | dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n", |
||
107 | pdata->gpio_pin_s1, err); |
||
108 | goto err_free_s0; |
||
109 | } |
||
110 | |||
111 | err = gpio_request(pdata->gpio_pin_1y, dev_name(&pdev->dev)); |
||
112 | if (err) { |
||
113 | dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n", |
||
114 | pdata->gpio_pin_1y, err); |
||
115 | goto err_free_s1; |
||
116 | } |
||
117 | |||
118 | err = gpio_request(pdata->gpio_pin_2y, dev_name(&pdev->dev)); |
||
119 | if (err) { |
||
120 | dev_err(&pdev->dev, "unable to claim gpio %u, err=%d\n", |
||
121 | pdata->gpio_pin_2y, err); |
||
122 | goto err_free_1y; |
||
123 | } |
||
124 | |||
125 | err = gpio_direction_output(pdata->gpio_pin_s0, 0); |
||
126 | if (err) { |
||
127 | dev_err(&pdev->dev, |
||
128 | "unable to set direction of gpio %u, err=%d\n", |
||
129 | pdata->gpio_pin_s0, err); |
||
130 | goto err_free_2y; |
||
131 | } |
||
132 | |||
133 | err = gpio_direction_output(pdata->gpio_pin_s1, 0); |
||
134 | if (err) { |
||
135 | dev_err(&pdev->dev, |
||
136 | "unable to set direction of gpio %u, err=%d\n", |
||
137 | pdata->gpio_pin_s1, err); |
||
138 | goto err_free_2y; |
||
139 | } |
||
140 | |||
141 | err = gpio_direction_input(pdata->gpio_pin_1y); |
||
142 | if (err) { |
||
143 | dev_err(&pdev->dev, |
||
144 | "unable to set direction of gpio %u, err=%d\n", |
||
145 | pdata->gpio_pin_1y, err); |
||
146 | goto err_free_2y; |
||
147 | } |
||
148 | |||
149 | err = gpio_direction_input(pdata->gpio_pin_2y); |
||
150 | if (err) { |
||
151 | dev_err(&pdev->dev, |
||
152 | "unable to set direction of gpio %u, err=%d\n", |
||
153 | pdata->gpio_pin_2y, err); |
||
154 | goto err_free_2y; |
||
155 | } |
||
156 | |||
157 | nxp->parent = &pdev->dev; |
||
158 | mutex_init(&nxp->lock); |
||
159 | |||
160 | gc = &nxp->gpio_chip; |
||
161 | |||
162 | gc->direction_input = nxp_74hc153_direction_input; |
||
163 | gc->direction_output = nxp_74hc153_direction_output; |
||
164 | gc->get = nxp_74hc153_get_value; |
||
165 | gc->set = nxp_74hc153_set_value; |
||
166 | gc->can_sleep = 1; |
||
167 | |||
168 | gc->base = pdata->gpio_base; |
||
169 | gc->ngpio = NXP_74HC153_NUM_GPIOS; |
||
170 | gc->label = dev_name(nxp->parent); |
||
171 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) |
||
172 | gc->dev = nxp->parent; |
||
173 | #else |
||
174 | gc->parent = nxp->parent; |
||
175 | #endif |
||
176 | gc->owner = THIS_MODULE; |
||
177 | |||
178 | err = gpiochip_add(&nxp->gpio_chip); |
||
179 | if (err) { |
||
180 | dev_err(&pdev->dev, "unable to add gpio chip, err=%d\n", err); |
||
181 | goto err_free_2y; |
||
182 | } |
||
183 | |||
184 | platform_set_drvdata(pdev, nxp); |
||
185 | return 0; |
||
186 | |||
187 | err_free_2y: |
||
188 | gpio_free(pdata->gpio_pin_2y); |
||
189 | err_free_1y: |
||
190 | gpio_free(pdata->gpio_pin_1y); |
||
191 | err_free_s1: |
||
192 | gpio_free(pdata->gpio_pin_s1); |
||
193 | err_free_s0: |
||
194 | gpio_free(pdata->gpio_pin_s0); |
||
195 | err_free_nxp: |
||
196 | kfree(nxp); |
||
197 | return err; |
||
198 | } |
||
199 | |||
200 | static int nxp_74hc153_remove(struct platform_device *pdev) |
||
201 | { |
||
202 | struct nxp_74hc153_chip *nxp = platform_get_drvdata(pdev); |
||
203 | struct nxp_74hc153_platform_data *pdata = pdev->dev.platform_data; |
||
204 | |||
205 | if (nxp) { |
||
206 | gpiochip_remove(&nxp->gpio_chip); |
||
207 | gpio_free(pdata->gpio_pin_2y); |
||
208 | gpio_free(pdata->gpio_pin_1y); |
||
209 | gpio_free(pdata->gpio_pin_s1); |
||
210 | gpio_free(pdata->gpio_pin_s0); |
||
211 | |||
212 | kfree(nxp); |
||
213 | platform_set_drvdata(pdev, NULL); |
||
214 | } |
||
215 | |||
216 | return 0; |
||
217 | } |
||
218 | |||
219 | static struct platform_driver nxp_74hc153_driver = { |
||
220 | .probe = nxp_74hc153_probe, |
||
221 | .remove = nxp_74hc153_remove, |
||
222 | .driver = { |
||
223 | .name = NXP_74HC153_DRIVER_NAME, |
||
224 | .owner = THIS_MODULE, |
||
225 | }, |
||
226 | }; |
||
227 | |||
228 | static int __init nxp_74hc153_init(void) |
||
229 | { |
||
230 | return platform_driver_register(&nxp_74hc153_driver); |
||
231 | } |
||
232 | subsys_initcall(nxp_74hc153_init); |
||
233 | |||
234 | static void __exit nxp_74hc153_exit(void) |
||
235 | { |
||
236 | platform_driver_unregister(&nxp_74hc153_driver); |
||
237 | } |
||
238 | module_exit(nxp_74hc153_exit); |
||
239 | |||
240 | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
||
241 | MODULE_DESCRIPTION("GPIO expander driver for NXP 74HC153"); |
||
242 | MODULE_LICENSE("GPL v2"); |
||
243 | MODULE_ALIAS("platform:" NXP_74HC153_DRIVER_NAME); |