diff options
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/wm831x-gpio.c | 252 | ||||
-rw-r--r-- | include/linux/mfd/wm831x/gpio.h | 55 |
4 files changed, 315 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 96dda81c9228..6b4c484a699a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -155,6 +155,13 @@ config GPIO_TWL4030 | |||
155 | Say yes here to access the GPIO signals of various multi-function | 155 | Say yes here to access the GPIO signals of various multi-function |
156 | power management chips from Texas Instruments. | 156 | power management chips from Texas Instruments. |
157 | 157 | ||
158 | config GPIO_WM831X | ||
159 | tristate "WM831x GPIOs" | ||
160 | depends on MFD_WM831X | ||
161 | help | ||
162 | Say yes here to access the GPIO signals of WM831x power management | ||
163 | chips from Wolfson Microelectronics. | ||
164 | |||
158 | comment "PCI GPIO expanders:" | 165 | comment "PCI GPIO expanders:" |
159 | 166 | ||
160 | config GPIO_BT8XX | 167 | config GPIO_BT8XX |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9244c6fcd8be..ea7c745f26a8 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -14,3 +14,4 @@ obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o | |||
14 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o | 14 | obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o |
15 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o | 15 | obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o |
16 | obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o | 16 | obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o |
17 | obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o | ||
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c new file mode 100644 index 000000000000..f9c09a54ec7f --- /dev/null +++ b/drivers/gpio/wm831x-gpio.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs | ||
3 | * | ||
4 | * Copyright 2009 Wolfson Microelectronics PLC. | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/mfd/core.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/seq_file.h> | ||
21 | |||
22 | #include <linux/mfd/wm831x/core.h> | ||
23 | #include <linux/mfd/wm831x/pdata.h> | ||
24 | #include <linux/mfd/wm831x/gpio.h> | ||
25 | |||
26 | #define WM831X_GPIO_MAX 16 | ||
27 | |||
28 | struct wm831x_gpio { | ||
29 | struct wm831x *wm831x; | ||
30 | struct gpio_chip gpio_chip; | ||
31 | }; | ||
32 | |||
33 | static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip) | ||
34 | { | ||
35 | return container_of(chip, struct wm831x_gpio, gpio_chip); | ||
36 | } | ||
37 | |||
38 | static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) | ||
39 | { | ||
40 | struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); | ||
41 | struct wm831x *wm831x = wm831x_gpio->wm831x; | ||
42 | |||
43 | return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, | ||
44 | WM831X_GPN_DIR | WM831X_GPN_TRI, | ||
45 | WM831X_GPN_DIR); | ||
46 | } | ||
47 | |||
48 | static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
49 | { | ||
50 | struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); | ||
51 | struct wm831x *wm831x = wm831x_gpio->wm831x; | ||
52 | int ret; | ||
53 | |||
54 | ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); | ||
55 | if (ret < 0) | ||
56 | return ret; | ||
57 | |||
58 | if (ret & 1 << offset) | ||
59 | return 1; | ||
60 | else | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int wm831x_gpio_direction_out(struct gpio_chip *chip, | ||
65 | unsigned offset, int value) | ||
66 | { | ||
67 | struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); | ||
68 | struct wm831x *wm831x = wm831x_gpio->wm831x; | ||
69 | |||
70 | return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, | ||
71 | WM831X_GPN_DIR | WM831X_GPN_TRI, 0); | ||
72 | } | ||
73 | |||
74 | static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
75 | { | ||
76 | struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); | ||
77 | struct wm831x *wm831x = wm831x_gpio->wm831x; | ||
78 | |||
79 | wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, | ||
80 | value << offset); | ||
81 | } | ||
82 | |||
83 | #ifdef CONFIG_DEBUG_FS | ||
84 | static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) | ||
85 | { | ||
86 | struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); | ||
87 | struct wm831x *wm831x = wm831x_gpio->wm831x; | ||
88 | int i; | ||
89 | |||
90 | for (i = 0; i < chip->ngpio; i++) { | ||
91 | int gpio = i + chip->base; | ||
92 | int reg; | ||
93 | const char *label, *pull, *powerdomain; | ||
94 | |||
95 | /* We report the GPIO even if it's not requested since | ||
96 | * we're also reporting things like alternate | ||
97 | * functions which apply even when the GPIO is not in | ||
98 | * use as a GPIO. | ||
99 | */ | ||
100 | label = gpiochip_is_requested(chip, i); | ||
101 | if (!label) | ||
102 | label = "Unrequested"; | ||
103 | |||
104 | seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); | ||
105 | |||
106 | reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); | ||
107 | if (reg < 0) { | ||
108 | dev_err(wm831x->dev, | ||
109 | "GPIO control %d read failed: %d\n", | ||
110 | gpio, reg); | ||
111 | seq_printf(s, "\n"); | ||
112 | continue; | ||
113 | } | ||
114 | |||
115 | switch (reg & WM831X_GPN_PULL_MASK) { | ||
116 | case WM831X_GPIO_PULL_NONE: | ||
117 | pull = "nopull"; | ||
118 | break; | ||
119 | case WM831X_GPIO_PULL_DOWN: | ||
120 | pull = "pulldown"; | ||
121 | break; | ||
122 | case WM831X_GPIO_PULL_UP: | ||
123 | pull = "pullup"; | ||
124 | default: | ||
125 | pull = "INVALID PULL"; | ||
126 | break; | ||
127 | } | ||
128 | |||
129 | switch (i + 1) { | ||
130 | case 1 ... 3: | ||
131 | case 7 ... 9: | ||
132 | if (reg & WM831X_GPN_PWR_DOM) | ||
133 | powerdomain = "VPMIC"; | ||
134 | else | ||
135 | powerdomain = "DBVDD"; | ||
136 | break; | ||
137 | |||
138 | case 4 ... 6: | ||
139 | case 10 ... 12: | ||
140 | if (reg & WM831X_GPN_PWR_DOM) | ||
141 | powerdomain = "SYSVDD"; | ||
142 | else | ||
143 | powerdomain = "DBVDD"; | ||
144 | break; | ||
145 | |||
146 | case 13 ... 16: | ||
147 | powerdomain = "TPVDD"; | ||
148 | break; | ||
149 | |||
150 | default: | ||
151 | BUG(); | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | seq_printf(s, " %s %s %s %s%s\n" | ||
156 | " %s%s (0x%4x)\n", | ||
157 | reg & WM831X_GPN_DIR ? "in" : "out", | ||
158 | wm831x_gpio_get(chip, i) ? "high" : "low", | ||
159 | pull, | ||
160 | powerdomain, | ||
161 | reg & WM831X_GPN_POL ? " inverted" : "", | ||
162 | reg & WM831X_GPN_OD ? "open-drain" : "CMOS", | ||
163 | reg & WM831X_GPN_TRI ? " tristated" : "", | ||
164 | reg); | ||
165 | } | ||
166 | } | ||
167 | #else | ||
168 | #define wm831x_gpio_dbg_show NULL | ||
169 | #endif | ||
170 | |||
171 | static struct gpio_chip template_chip = { | ||
172 | .label = "wm831x", | ||
173 | .owner = THIS_MODULE, | ||
174 | .direction_input = wm831x_gpio_direction_in, | ||
175 | .get = wm831x_gpio_get, | ||
176 | .direction_output = wm831x_gpio_direction_out, | ||
177 | .set = wm831x_gpio_set, | ||
178 | .dbg_show = wm831x_gpio_dbg_show, | ||
179 | .can_sleep = 1, | ||
180 | }; | ||
181 | |||
182 | static int __devinit wm831x_gpio_probe(struct platform_device *pdev) | ||
183 | { | ||
184 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | ||
185 | struct wm831x_pdata *pdata = wm831x->dev->platform_data; | ||
186 | struct wm831x_gpio *wm831x_gpio; | ||
187 | int ret; | ||
188 | |||
189 | wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL); | ||
190 | if (wm831x_gpio == NULL) | ||
191 | return -ENOMEM; | ||
192 | |||
193 | wm831x_gpio->wm831x = wm831x; | ||
194 | wm831x_gpio->gpio_chip = template_chip; | ||
195 | wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX; | ||
196 | wm831x_gpio->gpio_chip.dev = &pdev->dev; | ||
197 | if (pdata && pdata->gpio_base) | ||
198 | wm831x_gpio->gpio_chip.base = pdata->gpio_base; | ||
199 | else | ||
200 | wm831x_gpio->gpio_chip.base = -1; | ||
201 | |||
202 | ret = gpiochip_add(&wm831x_gpio->gpio_chip); | ||
203 | if (ret < 0) { | ||
204 | dev_err(&pdev->dev, "Could not register gpiochip, %d\n", | ||
205 | ret); | ||
206 | goto err; | ||
207 | } | ||
208 | |||
209 | platform_set_drvdata(pdev, wm831x_gpio); | ||
210 | |||
211 | return ret; | ||
212 | |||
213 | err: | ||
214 | kfree(wm831x_gpio); | ||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | static int __devexit wm831x_gpio_remove(struct platform_device *pdev) | ||
219 | { | ||
220 | struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev); | ||
221 | int ret; | ||
222 | |||
223 | ret = gpiochip_remove(&wm831x_gpio->gpio_chip); | ||
224 | if (ret == 0) | ||
225 | kfree(wm831x_gpio); | ||
226 | |||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | static struct platform_driver wm831x_gpio_driver = { | ||
231 | .driver.name = "wm831x-gpio", | ||
232 | .driver.owner = THIS_MODULE, | ||
233 | .probe = wm831x_gpio_probe, | ||
234 | .remove = __devexit_p(wm831x_gpio_remove), | ||
235 | }; | ||
236 | |||
237 | static int __init wm831x_gpio_init(void) | ||
238 | { | ||
239 | return platform_driver_register(&wm831x_gpio_driver); | ||
240 | } | ||
241 | subsys_initcall(wm831x_gpio_init); | ||
242 | |||
243 | static void __exit wm831x_gpio_exit(void) | ||
244 | { | ||
245 | platform_driver_unregister(&wm831x_gpio_driver); | ||
246 | } | ||
247 | module_exit(wm831x_gpio_exit); | ||
248 | |||
249 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
250 | MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); | ||
251 | MODULE_LICENSE("GPL"); | ||
252 | MODULE_ALIAS("platform:wm831x-gpio"); | ||
diff --git a/include/linux/mfd/wm831x/gpio.h b/include/linux/mfd/wm831x/gpio.h new file mode 100644 index 000000000000..2835614af0e3 --- /dev/null +++ b/include/linux/mfd/wm831x/gpio.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * include/linux/mfd/wm831x/gpio.h -- GPIO for WM831x | ||
3 | * | ||
4 | * Copyright 2009 Wolfson Microelectronics PLC. | ||
5 | * | ||
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef __MFD_WM831X_GPIO_H__ | ||
16 | #define __MFD_WM831X_GPIO_H__ | ||
17 | |||
18 | /* | ||
19 | * R16440-16455 (0x4038-0x4047) - GPIOx Control | ||
20 | */ | ||
21 | #define WM831X_GPN_DIR 0x8000 /* GPN_DIR */ | ||
22 | #define WM831X_GPN_DIR_MASK 0x8000 /* GPN_DIR */ | ||
23 | #define WM831X_GPN_DIR_SHIFT 15 /* GPN_DIR */ | ||
24 | #define WM831X_GPN_DIR_WIDTH 1 /* GPN_DIR */ | ||
25 | #define WM831X_GPN_PULL_MASK 0x6000 /* GPN_PULL - [14:13] */ | ||
26 | #define WM831X_GPN_PULL_SHIFT 13 /* GPN_PULL - [14:13] */ | ||
27 | #define WM831X_GPN_PULL_WIDTH 2 /* GPN_PULL - [14:13] */ | ||
28 | #define WM831X_GPN_INT_MODE 0x1000 /* GPN_INT_MODE */ | ||
29 | #define WM831X_GPN_INT_MODE_MASK 0x1000 /* GPN_INT_MODE */ | ||
30 | #define WM831X_GPN_INT_MODE_SHIFT 12 /* GPN_INT_MODE */ | ||
31 | #define WM831X_GPN_INT_MODE_WIDTH 1 /* GPN_INT_MODE */ | ||
32 | #define WM831X_GPN_PWR_DOM 0x0800 /* GPN_PWR_DOM */ | ||
33 | #define WM831X_GPN_PWR_DOM_MASK 0x0800 /* GPN_PWR_DOM */ | ||
34 | #define WM831X_GPN_PWR_DOM_SHIFT 11 /* GPN_PWR_DOM */ | ||
35 | #define WM831X_GPN_PWR_DOM_WIDTH 1 /* GPN_PWR_DOM */ | ||
36 | #define WM831X_GPN_POL 0x0400 /* GPN_POL */ | ||
37 | #define WM831X_GPN_POL_MASK 0x0400 /* GPN_POL */ | ||
38 | #define WM831X_GPN_POL_SHIFT 10 /* GPN_POL */ | ||
39 | #define WM831X_GPN_POL_WIDTH 1 /* GPN_POL */ | ||
40 | #define WM831X_GPN_OD 0x0200 /* GPN_OD */ | ||
41 | #define WM831X_GPN_OD_MASK 0x0200 /* GPN_OD */ | ||
42 | #define WM831X_GPN_OD_SHIFT 9 /* GPN_OD */ | ||
43 | #define WM831X_GPN_OD_WIDTH 1 /* GPN_OD */ | ||
44 | #define WM831X_GPN_TRI 0x0080 /* GPN_TRI */ | ||
45 | #define WM831X_GPN_TRI_MASK 0x0080 /* GPN_TRI */ | ||
46 | #define WM831X_GPN_TRI_SHIFT 7 /* GPN_TRI */ | ||
47 | #define WM831X_GPN_TRI_WIDTH 1 /* GPN_TRI */ | ||
48 | #define WM831X_GPN_FN_MASK 0x000F /* GPN_FN - [3:0] */ | ||
49 | #define WM831X_GPN_FN_SHIFT 0 /* GPN_FN - [3:0] */ | ||
50 | #define WM831X_GPN_FN_WIDTH 4 /* GPN_FN - [3:0] */ | ||
51 | |||
52 | #define WM831X_GPIO_PULL_NONE (0 << WM831X_GPN_PULL_SHIFT) | ||
53 | #define WM831X_GPIO_PULL_DOWN (1 << WM831X_GPN_PULL_SHIFT) | ||
54 | #define WM831X_GPIO_PULL_UP (2 << WM831X_GPN_PULL_SHIFT) | ||
55 | #endif | ||