1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * gpiolib support for Wolfson WM831x PMICs |
4 | * |
5 | * Copyright 2009 Wolfson Microelectronics PLC. |
6 | * |
7 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> |
8 | * |
9 | */ |
10 | |
11 | #include <linux/cleanup.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/module.h> |
15 | #include <linux/gpio/driver.h> |
16 | #include <linux/mfd/core.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/seq_file.h> |
19 | |
20 | #include <linux/mfd/wm831x/core.h> |
21 | #include <linux/mfd/wm831x/pdata.h> |
22 | #include <linux/mfd/wm831x/gpio.h> |
23 | #include <linux/mfd/wm831x/irq.h> |
24 | |
25 | struct wm831x_gpio { |
26 | struct wm831x *wm831x; |
27 | struct gpio_chip gpio_chip; |
28 | }; |
29 | |
30 | static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) |
31 | { |
32 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
33 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
34 | int val = WM831X_GPN_DIR; |
35 | |
36 | if (wm831x->has_gpio_ena) |
37 | val |= WM831X_GPN_TRI; |
38 | |
39 | return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, |
40 | WM831X_GPN_DIR | WM831X_GPN_TRI | |
41 | WM831X_GPN_FN_MASK, val); |
42 | } |
43 | |
44 | static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) |
45 | { |
46 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
47 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
48 | int ret; |
49 | |
50 | ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); |
51 | if (ret < 0) |
52 | return ret; |
53 | |
54 | if (ret & 1 << offset) |
55 | return 1; |
56 | else |
57 | return 0; |
58 | } |
59 | |
60 | static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) |
61 | { |
62 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
63 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
64 | |
65 | wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, mask: 1 << offset, |
66 | val: value << offset); |
67 | } |
68 | |
69 | static int wm831x_gpio_direction_out(struct gpio_chip *chip, |
70 | unsigned offset, int value) |
71 | { |
72 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
73 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
74 | int val = 0; |
75 | int ret; |
76 | |
77 | if (wm831x->has_gpio_ena) |
78 | val |= WM831X_GPN_TRI; |
79 | |
80 | ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, |
81 | WM831X_GPN_DIR | WM831X_GPN_TRI | |
82 | WM831X_GPN_FN_MASK, val); |
83 | if (ret < 0) |
84 | return ret; |
85 | |
86 | /* Can only set GPIO state once it's in output mode */ |
87 | wm831x_gpio_set(chip, offset, value); |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
93 | { |
94 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
95 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
96 | |
97 | return irq_create_mapping(host: wm831x->irq_domain, |
98 | WM831X_IRQ_GPIO_1 + offset); |
99 | } |
100 | |
101 | static int wm831x_gpio_set_debounce(struct wm831x *wm831x, unsigned offset, |
102 | unsigned debounce) |
103 | { |
104 | int reg = WM831X_GPIO1_CONTROL + offset; |
105 | int ret, fn; |
106 | |
107 | ret = wm831x_reg_read(wm831x, reg); |
108 | if (ret < 0) |
109 | return ret; |
110 | |
111 | switch (ret & WM831X_GPN_FN_MASK) { |
112 | case 0: |
113 | case 1: |
114 | break; |
115 | default: |
116 | /* Not in GPIO mode */ |
117 | return -EBUSY; |
118 | } |
119 | |
120 | if (debounce >= 32 && debounce <= 64) |
121 | fn = 0; |
122 | else if (debounce >= 4000 && debounce <= 8000) |
123 | fn = 1; |
124 | else |
125 | return -EINVAL; |
126 | |
127 | return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, val: fn); |
128 | } |
129 | |
130 | static int wm831x_set_config(struct gpio_chip *chip, unsigned int offset, |
131 | unsigned long config) |
132 | { |
133 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
134 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
135 | int reg = WM831X_GPIO1_CONTROL + offset; |
136 | |
137 | switch (pinconf_to_config_param(config)) { |
138 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
139 | return wm831x_set_bits(wm831x, reg, |
140 | WM831X_GPN_OD_MASK, WM831X_GPN_OD); |
141 | case PIN_CONFIG_DRIVE_PUSH_PULL: |
142 | return wm831x_set_bits(wm831x, reg, |
143 | WM831X_GPN_OD_MASK, val: 0); |
144 | case PIN_CONFIG_INPUT_DEBOUNCE: |
145 | return wm831x_gpio_set_debounce(wm831x, offset, |
146 | debounce: pinconf_to_config_argument(config)); |
147 | default: |
148 | break; |
149 | } |
150 | |
151 | return -ENOTSUPP; |
152 | } |
153 | |
154 | #ifdef CONFIG_DEBUG_FS |
155 | static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) |
156 | { |
157 | struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(gc: chip); |
158 | struct wm831x *wm831x = wm831x_gpio->wm831x; |
159 | int i, tristated; |
160 | |
161 | for (i = 0; i < chip->ngpio; i++) { |
162 | int gpio = i + chip->base; |
163 | int reg; |
164 | const char *pull, *powerdomain; |
165 | |
166 | /* We report the GPIO even if it's not requested since |
167 | * we're also reporting things like alternate |
168 | * functions which apply even when the GPIO is not in |
169 | * use as a GPIO. |
170 | */ |
171 | char *label __free(kfree) = gpiochip_dup_line_label(gc: chip, offset: i); |
172 | if (IS_ERR(ptr: label)) { |
173 | dev_err(wm831x->dev, "Failed to duplicate label\n" ); |
174 | continue; |
175 | } |
176 | |
177 | seq_printf(m: s, fmt: " gpio-%-3d (%-20.20s) " , |
178 | gpio, label ?: "Unrequested" ); |
179 | |
180 | reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); |
181 | if (reg < 0) { |
182 | dev_err(wm831x->dev, |
183 | "GPIO control %d read failed: %d\n" , |
184 | gpio, reg); |
185 | seq_putc(m: s, c: '\n'); |
186 | continue; |
187 | } |
188 | |
189 | switch (reg & WM831X_GPN_PULL_MASK) { |
190 | case WM831X_GPIO_PULL_NONE: |
191 | pull = "nopull" ; |
192 | break; |
193 | case WM831X_GPIO_PULL_DOWN: |
194 | pull = "pulldown" ; |
195 | break; |
196 | case WM831X_GPIO_PULL_UP: |
197 | pull = "pullup" ; |
198 | break; |
199 | default: |
200 | pull = "INVALID PULL" ; |
201 | break; |
202 | } |
203 | |
204 | switch (i + 1) { |
205 | case 1 ... 3: |
206 | case 7 ... 9: |
207 | if (reg & WM831X_GPN_PWR_DOM) |
208 | powerdomain = "VPMIC" ; |
209 | else |
210 | powerdomain = "DBVDD" ; |
211 | break; |
212 | |
213 | case 4 ... 6: |
214 | case 10 ... 12: |
215 | if (reg & WM831X_GPN_PWR_DOM) |
216 | powerdomain = "SYSVDD" ; |
217 | else |
218 | powerdomain = "DBVDD" ; |
219 | break; |
220 | |
221 | case 13 ... 16: |
222 | powerdomain = "TPVDD" ; |
223 | break; |
224 | |
225 | default: |
226 | BUG(); |
227 | break; |
228 | } |
229 | |
230 | tristated = reg & WM831X_GPN_TRI; |
231 | if (wm831x->has_gpio_ena) |
232 | tristated = !tristated; |
233 | |
234 | seq_printf(m: s, fmt: " %s %s %s %s%s\n" |
235 | " %s%s (0x%4x)\n" , |
236 | reg & WM831X_GPN_DIR ? "in" : "out" , |
237 | wm831x_gpio_get(chip, offset: i) ? "high" : "low" , |
238 | pull, |
239 | powerdomain, |
240 | reg & WM831X_GPN_POL ? "" : " inverted" , |
241 | reg & WM831X_GPN_OD ? "open-drain" : "push-pull" , |
242 | tristated ? " tristated" : "" , |
243 | reg); |
244 | } |
245 | } |
246 | #else |
247 | #define wm831x_gpio_dbg_show NULL |
248 | #endif |
249 | |
250 | static const struct gpio_chip template_chip = { |
251 | .label = "wm831x" , |
252 | .owner = THIS_MODULE, |
253 | .direction_input = wm831x_gpio_direction_in, |
254 | .get = wm831x_gpio_get, |
255 | .direction_output = wm831x_gpio_direction_out, |
256 | .set = wm831x_gpio_set, |
257 | .to_irq = wm831x_gpio_to_irq, |
258 | .set_config = wm831x_set_config, |
259 | .dbg_show = wm831x_gpio_dbg_show, |
260 | .can_sleep = true, |
261 | }; |
262 | |
263 | static int wm831x_gpio_probe(struct platform_device *pdev) |
264 | { |
265 | struct wm831x *wm831x = dev_get_drvdata(dev: pdev->dev.parent); |
266 | struct wm831x_pdata *pdata = &wm831x->pdata; |
267 | struct wm831x_gpio *wm831x_gpio; |
268 | |
269 | device_set_node(dev: &pdev->dev, dev_fwnode(pdev->dev.parent)); |
270 | |
271 | wm831x_gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*wm831x_gpio), |
272 | GFP_KERNEL); |
273 | if (wm831x_gpio == NULL) |
274 | return -ENOMEM; |
275 | |
276 | wm831x_gpio->wm831x = wm831x; |
277 | wm831x_gpio->gpio_chip = template_chip; |
278 | wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; |
279 | wm831x_gpio->gpio_chip.parent = &pdev->dev; |
280 | if (pdata && pdata->gpio_base) |
281 | wm831x_gpio->gpio_chip.base = pdata->gpio_base; |
282 | else |
283 | wm831x_gpio->gpio_chip.base = -1; |
284 | |
285 | return devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, wm831x_gpio); |
286 | } |
287 | |
288 | static struct platform_driver wm831x_gpio_driver = { |
289 | .driver.name = "wm831x-gpio" , |
290 | .probe = wm831x_gpio_probe, |
291 | }; |
292 | |
293 | static int __init wm831x_gpio_init(void) |
294 | { |
295 | return platform_driver_register(&wm831x_gpio_driver); |
296 | } |
297 | subsys_initcall(wm831x_gpio_init); |
298 | |
299 | static void __exit wm831x_gpio_exit(void) |
300 | { |
301 | platform_driver_unregister(&wm831x_gpio_driver); |
302 | } |
303 | module_exit(wm831x_gpio_exit); |
304 | |
305 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>" ); |
306 | MODULE_DESCRIPTION("GPIO interface for WM831x PMICs" ); |
307 | MODULE_LICENSE("GPL" ); |
308 | MODULE_ALIAS("platform:wm831x-gpio" ); |
309 | |