Blame view

drivers/gpio/gpio-wm831x.c 7.46 KB
e4b736f18   Mark Brown   gpio: Add WM831X ...
1
  /*
c103de240   Grant Likely   gpio: reorganize ...
2
   * gpiolib support for Wolfson WM831x PMICs
e4b736f18   Mark Brown   gpio: Add WM831X ...
3
4
5
6
7
8
9
10
11
12
13
14
15
   *
   * Copyright 2009 Wolfson Microelectronics PLC.
   *
   * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   *
   */
  
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
e4b736f18   Mark Brown   gpio: Add WM831X ...
17
18
19
20
21
22
23
24
25
  #include <linux/module.h>
  #include <linux/gpio.h>
  #include <linux/mfd/core.h>
  #include <linux/platform_device.h>
  #include <linux/seq_file.h>
  
  #include <linux/mfd/wm831x/core.h>
  #include <linux/mfd/wm831x/pdata.h>
  #include <linux/mfd/wm831x/gpio.h>
dc0fb25c1   Mark Brown   gpiolib: Implemen...
26
  #include <linux/mfd/wm831x/irq.h>
e4b736f18   Mark Brown   gpio: Add WM831X ...
27

e4b736f18   Mark Brown   gpio: Add WM831X ...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  struct wm831x_gpio {
  	struct wm831x *wm831x;
  	struct gpio_chip gpio_chip;
  };
  
  static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip)
  {
  	return container_of(chip, struct wm831x_gpio, gpio_chip);
  }
  
  static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
f92e8f814   Mark Brown   mfd: Add WM831x r...
42
43
44
45
  	int val = WM831X_GPN_DIR;
  
  	if (wm831x->has_gpio_ena)
  		val |= WM831X_GPN_TRI;
e4b736f18   Mark Brown   gpio: Add WM831X ...
46
47
  
  	return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
1bca748cc   Mark Brown   gpiolib: Force wm...
48
49
  			       WM831X_GPN_DIR | WM831X_GPN_TRI |
  			       WM831X_GPN_FN_MASK, val);
e4b736f18   Mark Brown   gpio: Add WM831X ...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  }
  
  static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
  	int ret;
  
  	ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
  	if (ret < 0)
  		return ret;
  
  	if (ret & 1 << offset)
  		return 1;
  	else
  		return 0;
  }
3383d23d8   Mark Brown   gpiolib: Actually...
67
  static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
e4b736f18   Mark Brown   gpio: Add WM831X ...
68
69
70
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
3383d23d8   Mark Brown   gpiolib: Actually...
71
72
  	wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset,
  			value << offset);
e4b736f18   Mark Brown   gpio: Add WM831X ...
73
  }
3383d23d8   Mark Brown   gpiolib: Actually...
74
75
  static int wm831x_gpio_direction_out(struct gpio_chip *chip,
  				     unsigned offset, int value)
e4b736f18   Mark Brown   gpio: Add WM831X ...
76
77
78
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
f92e8f814   Mark Brown   mfd: Add WM831x r...
79
  	int val = 0;
3383d23d8   Mark Brown   gpiolib: Actually...
80
  	int ret;
e4b736f18   Mark Brown   gpio: Add WM831X ...
81

f92e8f814   Mark Brown   mfd: Add WM831x r...
82
83
  	if (wm831x->has_gpio_ena)
  		val |= WM831X_GPN_TRI;
3383d23d8   Mark Brown   gpiolib: Actually...
84
  	ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
1bca748cc   Mark Brown   gpiolib: Force wm...
85
86
  			      WM831X_GPN_DIR | WM831X_GPN_TRI |
  			      WM831X_GPN_FN_MASK, val);
3383d23d8   Mark Brown   gpiolib: Actually...
87
88
89
90
91
92
93
  	if (ret < 0)
  		return ret;
  
  	/* Can only set GPIO state once it's in output mode */
  	wm831x_gpio_set(chip, offset, value);
  
  	return 0;
e4b736f18   Mark Brown   gpio: Add WM831X ...
94
  }
dc0fb25c1   Mark Brown   gpiolib: Implemen...
95
96
97
98
99
100
101
102
103
104
  static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
  
  	if (!wm831x->irq_base)
  		return -EINVAL;
  
  	return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
  }
b12c35e22   Mark Brown   gpiolib: Implemen...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
  				    unsigned debounce)
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
  	int reg = WM831X_GPIO1_CONTROL + offset;
  	int ret, fn;
  
  	ret = wm831x_reg_read(wm831x, reg);
  	if (ret < 0)
  		return ret;
  
  	switch (ret & WM831X_GPN_FN_MASK) {
  	case 0:
  	case 1:
  		break;
  	default:
  		/* Not in GPIO mode */
  		return -EBUSY;
  	}
  
  	if (debounce >= 32 && debounce <= 64)
  		fn = 0;
  	else if (debounce >= 4000 && debounce <= 8000)
  		fn = 1;
  	else
  		return -EINVAL;
  
  	return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
  }
e4b736f18   Mark Brown   gpio: Add WM831X ...
135
136
137
138
139
  #ifdef CONFIG_DEBUG_FS
  static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
  {
  	struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
  	struct wm831x *wm831x = wm831x_gpio->wm831x;
f92e8f814   Mark Brown   mfd: Add WM831x r...
140
  	int i, tristated;
e4b736f18   Mark Brown   gpio: Add WM831X ...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  
  	for (i = 0; i < chip->ngpio; i++) {
  		int gpio = i + chip->base;
  		int reg;
  		const char *label, *pull, *powerdomain;
  
  		/* We report the GPIO even if it's not requested since
  		 * we're also reporting things like alternate
  		 * functions which apply even when the GPIO is not in
  		 * use as a GPIO.
  		 */
  		label = gpiochip_is_requested(chip, i);
  		if (!label)
  			label = "Unrequested";
  
  		seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
  
  		reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i);
  		if (reg < 0) {
  			dev_err(wm831x->dev,
  				"GPIO control %d read failed: %d
  ",
  				gpio, reg);
  			seq_printf(s, "
  ");
  			continue;
  		}
  
  		switch (reg & WM831X_GPN_PULL_MASK) {
  		case WM831X_GPIO_PULL_NONE:
  			pull = "nopull";
  			break;
  		case WM831X_GPIO_PULL_DOWN:
  			pull = "pulldown";
  			break;
  		case WM831X_GPIO_PULL_UP:
  			pull = "pullup";
164d5c398   Axel Lin   gpio: wm831x: add...
178
  			break;
e4b736f18   Mark Brown   gpio: Add WM831X ...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  		default:
  			pull = "INVALID PULL";
  			break;
  		}
  
  		switch (i + 1) {
  		case 1 ... 3:
  		case 7 ... 9:
  			if (reg & WM831X_GPN_PWR_DOM)
  				powerdomain = "VPMIC";
  			else
  				powerdomain = "DBVDD";
  			break;
  
  		case 4 ... 6:
  		case 10 ... 12:
  			if (reg & WM831X_GPN_PWR_DOM)
  				powerdomain = "SYSVDD";
  			else
  				powerdomain = "DBVDD";
  			break;
  
  		case 13 ... 16:
  			powerdomain = "TPVDD";
  			break;
  
  		default:
  			BUG();
  			break;
  		}
f92e8f814   Mark Brown   mfd: Add WM831x r...
209
210
211
  		tristated = reg & WM831X_GPN_TRI;
  		if (wm831x->has_gpio_ena)
  			tristated = !tristated;
e4b736f18   Mark Brown   gpio: Add WM831X ...
212
213
214
215
216
217
218
219
  		seq_printf(s, " %s %s %s %s%s
  "
  			   "                                  %s%s (0x%4x)
  ",
  			   reg & WM831X_GPN_DIR ? "in" : "out",
  			   wm831x_gpio_get(chip, i) ? "high" : "low",
  			   pull,
  			   powerdomain,
6b8274faf   Mark Brown   gpiolib: Correct ...
220
  			   reg & WM831X_GPN_POL ? "" : " inverted",
e4b736f18   Mark Brown   gpio: Add WM831X ...
221
  			   reg & WM831X_GPN_OD ? "open-drain" : "CMOS",
f92e8f814   Mark Brown   mfd: Add WM831x r...
222
  			   tristated ? " tristated" : "",
e4b736f18   Mark Brown   gpio: Add WM831X ...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  			   reg);
  	}
  }
  #else
  #define wm831x_gpio_dbg_show NULL
  #endif
  
  static struct gpio_chip template_chip = {
  	.label			= "wm831x",
  	.owner			= THIS_MODULE,
  	.direction_input	= wm831x_gpio_direction_in,
  	.get			= wm831x_gpio_get,
  	.direction_output	= wm831x_gpio_direction_out,
  	.set			= wm831x_gpio_set,
dc0fb25c1   Mark Brown   gpiolib: Implemen...
237
  	.to_irq			= wm831x_gpio_to_irq,
b12c35e22   Mark Brown   gpiolib: Implemen...
238
  	.set_debounce		= wm831x_gpio_set_debounce,
e4b736f18   Mark Brown   gpio: Add WM831X ...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  	.dbg_show		= wm831x_gpio_dbg_show,
  	.can_sleep		= 1,
  };
  
  static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
  {
  	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
  	struct wm831x_pdata *pdata = wm831x->dev->platform_data;
  	struct wm831x_gpio *wm831x_gpio;
  	int ret;
  
  	wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL);
  	if (wm831x_gpio == NULL)
  		return -ENOMEM;
  
  	wm831x_gpio->wm831x = wm831x;
  	wm831x_gpio->gpio_chip = template_chip;
6f2ecaae7   Mark Brown   gpiolib: Make WM8...
256
  	wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio;
e4b736f18   Mark Brown   gpio: Add WM831X ...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  	wm831x_gpio->gpio_chip.dev = &pdev->dev;
  	if (pdata && pdata->gpio_base)
  		wm831x_gpio->gpio_chip.base = pdata->gpio_base;
  	else
  		wm831x_gpio->gpio_chip.base = -1;
  
  	ret = gpiochip_add(&wm831x_gpio->gpio_chip);
  	if (ret < 0) {
  		dev_err(&pdev->dev, "Could not register gpiochip, %d
  ",
  			ret);
  		goto err;
  	}
  
  	platform_set_drvdata(pdev, wm831x_gpio);
  
  	return ret;
  
  err:
  	kfree(wm831x_gpio);
  	return ret;
  }
  
  static int __devexit wm831x_gpio_remove(struct platform_device *pdev)
  {
  	struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev);
  	int ret;
  
  	ret = gpiochip_remove(&wm831x_gpio->gpio_chip);
  	if (ret == 0)
  		kfree(wm831x_gpio);
  
  	return ret;
  }
  
  static struct platform_driver wm831x_gpio_driver = {
  	.driver.name	= "wm831x-gpio",
  	.driver.owner	= THIS_MODULE,
  	.probe		= wm831x_gpio_probe,
  	.remove		= __devexit_p(wm831x_gpio_remove),
  };
  
  static int __init wm831x_gpio_init(void)
  {
  	return platform_driver_register(&wm831x_gpio_driver);
  }
  subsys_initcall(wm831x_gpio_init);
  
  static void __exit wm831x_gpio_exit(void)
  {
  	platform_driver_unregister(&wm831x_gpio_driver);
  }
  module_exit(wm831x_gpio_exit);
  
  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  MODULE_DESCRIPTION("GPIO interface for WM831x PMICs");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:wm831x-gpio");