xref: /linux/drivers/gpio/gpio-wm8994.c (revision 224a1f90458b5005fa7aec5e8a6d64fd8eccb208)
1c103de24SGrant Likely /*
2c103de24SGrant Likely  * gpiolib support for Wolfson WM8994
3c103de24SGrant Likely  *
4c103de24SGrant Likely  * Copyright 2009 Wolfson Microelectronics PLC.
5c103de24SGrant Likely  *
6c103de24SGrant Likely  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7c103de24SGrant Likely  *
8c103de24SGrant Likely  *  This program is free software; you can redistribute  it and/or modify it
9c103de24SGrant Likely  *  under  the terms of  the GNU General  Public License as published by the
10c103de24SGrant Likely  *  Free Software Foundation;  either version 2 of the  License, or (at your
11c103de24SGrant Likely  *  option) any later version.
12c103de24SGrant Likely  *
13c103de24SGrant Likely  */
14c103de24SGrant Likely 
15c103de24SGrant Likely #include <linux/kernel.h>
16c103de24SGrant Likely #include <linux/slab.h>
17c103de24SGrant Likely #include <linux/module.h>
18c103de24SGrant Likely #include <linux/gpio.h>
19c103de24SGrant Likely #include <linux/mfd/core.h>
20c103de24SGrant Likely #include <linux/platform_device.h>
21c103de24SGrant Likely #include <linux/seq_file.h>
22*224a1f90SMark Brown #include <linux/regmap.h>
23c103de24SGrant Likely 
24c103de24SGrant Likely #include <linux/mfd/wm8994/core.h>
25c103de24SGrant Likely #include <linux/mfd/wm8994/pdata.h>
26c103de24SGrant Likely #include <linux/mfd/wm8994/gpio.h>
27c103de24SGrant Likely #include <linux/mfd/wm8994/registers.h>
28c103de24SGrant Likely 
29c103de24SGrant Likely struct wm8994_gpio {
30c103de24SGrant Likely 	struct wm8994 *wm8994;
31c103de24SGrant Likely 	struct gpio_chip gpio_chip;
32c103de24SGrant Likely };
33c103de24SGrant Likely 
34c103de24SGrant Likely static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip)
35c103de24SGrant Likely {
36c103de24SGrant Likely 	return container_of(chip, struct wm8994_gpio, gpio_chip);
37c103de24SGrant Likely }
38c103de24SGrant Likely 
39c103de24SGrant Likely static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset)
40c103de24SGrant Likely {
41c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
42c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
43c103de24SGrant Likely 
44c103de24SGrant Likely 	switch (wm8994->type) {
45c103de24SGrant Likely 	case WM8958:
46c103de24SGrant Likely 		switch (offset) {
47c103de24SGrant Likely 		case 1:
48c103de24SGrant Likely 		case 2:
49c103de24SGrant Likely 		case 3:
50c103de24SGrant Likely 		case 4:
51c103de24SGrant Likely 		case 6:
52c103de24SGrant Likely 			return -EINVAL;
53c103de24SGrant Likely 		}
54c103de24SGrant Likely 		break;
55c103de24SGrant Likely 	default:
56c103de24SGrant Likely 		break;
57c103de24SGrant Likely 	}
58c103de24SGrant Likely 
59c103de24SGrant Likely 	return 0;
60c103de24SGrant Likely }
61c103de24SGrant Likely 
62c103de24SGrant Likely static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
63c103de24SGrant Likely {
64c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
65c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
66c103de24SGrant Likely 
67c103de24SGrant Likely 	return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
68c103de24SGrant Likely 			       WM8994_GPN_DIR, WM8994_GPN_DIR);
69c103de24SGrant Likely }
70c103de24SGrant Likely 
71c103de24SGrant Likely static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset)
72c103de24SGrant Likely {
73c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
74c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
75c103de24SGrant Likely 	int ret;
76c103de24SGrant Likely 
77c103de24SGrant Likely 	ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset);
78c103de24SGrant Likely 	if (ret < 0)
79c103de24SGrant Likely 		return ret;
80c103de24SGrant Likely 
81c103de24SGrant Likely 	if (ret & WM8994_GPN_LVL)
82c103de24SGrant Likely 		return 1;
83c103de24SGrant Likely 	else
84c103de24SGrant Likely 		return 0;
85c103de24SGrant Likely }
86c103de24SGrant Likely 
87c103de24SGrant Likely static int wm8994_gpio_direction_out(struct gpio_chip *chip,
88c103de24SGrant Likely 				     unsigned offset, int value)
89c103de24SGrant Likely {
90c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
91c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
92c103de24SGrant Likely 
93c103de24SGrant Likely 	return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
94c103de24SGrant Likely 			       WM8994_GPN_DIR, 0);
95c103de24SGrant Likely }
96c103de24SGrant Likely 
97c103de24SGrant Likely static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
98c103de24SGrant Likely {
99c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
100c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
101c103de24SGrant Likely 
102c103de24SGrant Likely 	if (value)
103c103de24SGrant Likely 		value = WM8994_GPN_LVL;
104c103de24SGrant Likely 
105c103de24SGrant Likely 	wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value);
106c103de24SGrant Likely }
107c103de24SGrant Likely 
108c103de24SGrant Likely static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
109c103de24SGrant Likely {
110c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
111c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
112c103de24SGrant Likely 
113*224a1f90SMark Brown 	return regmap_irq_get_virq(wm8994->irq_data, offset);
114c103de24SGrant Likely }
115c103de24SGrant Likely 
116c103de24SGrant Likely 
117c103de24SGrant Likely #ifdef CONFIG_DEBUG_FS
118d0ad5e89SMark Brown static const char *wm8994_gpio_fn(u16 fn)
119d0ad5e89SMark Brown {
120d0ad5e89SMark Brown 	switch (fn) {
121d0ad5e89SMark Brown 	case WM8994_GP_FN_PIN_SPECIFIC:
122d0ad5e89SMark Brown 		return "pin-specific";
123d0ad5e89SMark Brown 	case WM8994_GP_FN_GPIO:
124d0ad5e89SMark Brown 		return "GPIO";
125d0ad5e89SMark Brown 	case WM8994_GP_FN_SDOUT:
126d0ad5e89SMark Brown 		return "SDOUT";
127d0ad5e89SMark Brown 	case WM8994_GP_FN_IRQ:
128d0ad5e89SMark Brown 		return "IRQ";
129d0ad5e89SMark Brown 	case WM8994_GP_FN_TEMPERATURE:
130d0ad5e89SMark Brown 		return "Temperature";
131d0ad5e89SMark Brown 	case WM8994_GP_FN_MICBIAS1_DET:
132d0ad5e89SMark Brown 		return "MICBIAS1 detect";
133d0ad5e89SMark Brown 	case WM8994_GP_FN_MICBIAS1_SHORT:
134d0ad5e89SMark Brown 		return "MICBIAS1 short";
135d0ad5e89SMark Brown 	case WM8994_GP_FN_MICBIAS2_DET:
136d0ad5e89SMark Brown 		return "MICBIAS2 detect";
137d0ad5e89SMark Brown 	case WM8994_GP_FN_MICBIAS2_SHORT:
138d0ad5e89SMark Brown 		return "MICBIAS2 short";
139d0ad5e89SMark Brown 	case WM8994_GP_FN_FLL1_LOCK:
140d0ad5e89SMark Brown 		return "FLL1 lock";
141d0ad5e89SMark Brown 	case WM8994_GP_FN_FLL2_LOCK:
142d0ad5e89SMark Brown 		return "FLL2 lock";
143d0ad5e89SMark Brown 	case WM8994_GP_FN_SRC1_LOCK:
144d0ad5e89SMark Brown 		return "SRC1 lock";
145d0ad5e89SMark Brown 	case WM8994_GP_FN_SRC2_LOCK:
146d0ad5e89SMark Brown 		return "SRC2 lock";
147d0ad5e89SMark Brown 	case WM8994_GP_FN_DRC1_ACT:
148d0ad5e89SMark Brown 		return "DRC1 activity";
149d0ad5e89SMark Brown 	case WM8994_GP_FN_DRC2_ACT:
150d0ad5e89SMark Brown 		return "DRC2 activity";
151d0ad5e89SMark Brown 	case WM8994_GP_FN_DRC3_ACT:
152d0ad5e89SMark Brown 		return "DRC3 activity";
153d0ad5e89SMark Brown 	case WM8994_GP_FN_WSEQ_STATUS:
154d0ad5e89SMark Brown 		return "Write sequencer";
155d0ad5e89SMark Brown 	case WM8994_GP_FN_FIFO_ERROR:
156d0ad5e89SMark Brown 		return "FIFO error";
157d0ad5e89SMark Brown 	case WM8994_GP_FN_OPCLK:
158d0ad5e89SMark Brown 		return "OPCLK";
159d0ad5e89SMark Brown 	case WM8994_GP_FN_THW:
160d0ad5e89SMark Brown 		return "Thermal warning";
161d0ad5e89SMark Brown 	case WM8994_GP_FN_DCS_DONE:
162d0ad5e89SMark Brown 		return "DC servo";
163d0ad5e89SMark Brown 	case WM8994_GP_FN_FLL1_OUT:
164d0ad5e89SMark Brown 		return "FLL1 output";
165d0ad5e89SMark Brown 	case WM8994_GP_FN_FLL2_OUT:
166d0ad5e89SMark Brown 		return "FLL1 output";
167d0ad5e89SMark Brown 	default:
168d0ad5e89SMark Brown 		return "Unknown";
169d0ad5e89SMark Brown 	}
170d0ad5e89SMark Brown }
171d0ad5e89SMark Brown 
172c103de24SGrant Likely static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
173c103de24SGrant Likely {
174c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
175c103de24SGrant Likely 	struct wm8994 *wm8994 = wm8994_gpio->wm8994;
176c103de24SGrant Likely 	int i;
177c103de24SGrant Likely 
178c103de24SGrant Likely 	for (i = 0; i < chip->ngpio; i++) {
179c103de24SGrant Likely 		int gpio = i + chip->base;
180c103de24SGrant Likely 		int reg;
181c103de24SGrant Likely 		const char *label;
182c103de24SGrant Likely 
183c103de24SGrant Likely 		/* We report the GPIO even if it's not requested since
184c103de24SGrant Likely 		 * we're also reporting things like alternate
185c103de24SGrant Likely 		 * functions which apply even when the GPIO is not in
186c103de24SGrant Likely 		 * use as a GPIO.
187c103de24SGrant Likely 		 */
188c103de24SGrant Likely 		label = gpiochip_is_requested(chip, i);
189c103de24SGrant Likely 		if (!label)
190c103de24SGrant Likely 			label = "Unrequested";
191c103de24SGrant Likely 
192c103de24SGrant Likely 		seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
193c103de24SGrant Likely 
194c103de24SGrant Likely 		reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);
195c103de24SGrant Likely 		if (reg < 0) {
196c103de24SGrant Likely 			dev_err(wm8994->dev,
197c103de24SGrant Likely 				"GPIO control %d read failed: %d\n",
198c103de24SGrant Likely 				gpio, reg);
199c103de24SGrant Likely 			seq_printf(s, "\n");
200c103de24SGrant Likely 			continue;
201c103de24SGrant Likely 		}
202c103de24SGrant Likely 
203d0ad5e89SMark Brown 		if (reg & WM8994_GPN_DIR)
204d0ad5e89SMark Brown 			seq_printf(s, "in ");
205d0ad5e89SMark Brown 		else
206d0ad5e89SMark Brown 			seq_printf(s, "out ");
207d0ad5e89SMark Brown 
208d0ad5e89SMark Brown 		if (reg & WM8994_GPN_PU)
209d0ad5e89SMark Brown 			seq_printf(s, "pull up ");
210d0ad5e89SMark Brown 
211d0ad5e89SMark Brown 		if (reg & WM8994_GPN_PD)
212d0ad5e89SMark Brown 			seq_printf(s, "pull down ");
213d0ad5e89SMark Brown 
214d0ad5e89SMark Brown 		if (reg & WM8994_GPN_POL)
215d0ad5e89SMark Brown 			seq_printf(s, "inverted ");
216d0ad5e89SMark Brown 		else
217d0ad5e89SMark Brown 			seq_printf(s, "noninverted ");
218d0ad5e89SMark Brown 
219d0ad5e89SMark Brown 		if (reg & WM8994_GPN_OP_CFG)
220d0ad5e89SMark Brown 			seq_printf(s, "open drain ");
221d0ad5e89SMark Brown 		else
222d0ad5e89SMark Brown 			seq_printf(s, "CMOS ");
223d0ad5e89SMark Brown 
224d0ad5e89SMark Brown 		seq_printf(s, "%s (%x)\n",
225d0ad5e89SMark Brown 			   wm8994_gpio_fn(reg & WM8994_GPN_FN_MASK), reg);
226c103de24SGrant Likely 	}
227c103de24SGrant Likely }
228c103de24SGrant Likely #else
229c103de24SGrant Likely #define wm8994_gpio_dbg_show NULL
230c103de24SGrant Likely #endif
231c103de24SGrant Likely 
232c103de24SGrant Likely static struct gpio_chip template_chip = {
233c103de24SGrant Likely 	.label			= "wm8994",
234c103de24SGrant Likely 	.owner			= THIS_MODULE,
235c103de24SGrant Likely 	.request		= wm8994_gpio_request,
236c103de24SGrant Likely 	.direction_input	= wm8994_gpio_direction_in,
237c103de24SGrant Likely 	.get			= wm8994_gpio_get,
238c103de24SGrant Likely 	.direction_output	= wm8994_gpio_direction_out,
239c103de24SGrant Likely 	.set			= wm8994_gpio_set,
240c103de24SGrant Likely 	.to_irq			= wm8994_gpio_to_irq,
241c103de24SGrant Likely 	.dbg_show		= wm8994_gpio_dbg_show,
242c103de24SGrant Likely 	.can_sleep		= 1,
243c103de24SGrant Likely };
244c103de24SGrant Likely 
245c103de24SGrant Likely static int __devinit wm8994_gpio_probe(struct platform_device *pdev)
246c103de24SGrant Likely {
247c103de24SGrant Likely 	struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
248c103de24SGrant Likely 	struct wm8994_pdata *pdata = wm8994->dev->platform_data;
249c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio;
250c103de24SGrant Likely 	int ret;
251c103de24SGrant Likely 
252c103de24SGrant Likely 	wm8994_gpio = kzalloc(sizeof(*wm8994_gpio), GFP_KERNEL);
253c103de24SGrant Likely 	if (wm8994_gpio == NULL)
254c103de24SGrant Likely 		return -ENOMEM;
255c103de24SGrant Likely 
256c103de24SGrant Likely 	wm8994_gpio->wm8994 = wm8994;
257c103de24SGrant Likely 	wm8994_gpio->gpio_chip = template_chip;
258c103de24SGrant Likely 	wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX;
259c103de24SGrant Likely 	wm8994_gpio->gpio_chip.dev = &pdev->dev;
260c103de24SGrant Likely 	if (pdata && pdata->gpio_base)
261c103de24SGrant Likely 		wm8994_gpio->gpio_chip.base = pdata->gpio_base;
262c103de24SGrant Likely 	else
263c103de24SGrant Likely 		wm8994_gpio->gpio_chip.base = -1;
264c103de24SGrant Likely 
265c103de24SGrant Likely 	ret = gpiochip_add(&wm8994_gpio->gpio_chip);
266c103de24SGrant Likely 	if (ret < 0) {
267c103de24SGrant Likely 		dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
268c103de24SGrant Likely 			ret);
269c103de24SGrant Likely 		goto err;
270c103de24SGrant Likely 	}
271c103de24SGrant Likely 
272c103de24SGrant Likely 	platform_set_drvdata(pdev, wm8994_gpio);
273c103de24SGrant Likely 
274c103de24SGrant Likely 	return ret;
275c103de24SGrant Likely 
276c103de24SGrant Likely err:
277c103de24SGrant Likely 	kfree(wm8994_gpio);
278c103de24SGrant Likely 	return ret;
279c103de24SGrant Likely }
280c103de24SGrant Likely 
281c103de24SGrant Likely static int __devexit wm8994_gpio_remove(struct platform_device *pdev)
282c103de24SGrant Likely {
283c103de24SGrant Likely 	struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev);
284c103de24SGrant Likely 	int ret;
285c103de24SGrant Likely 
286c103de24SGrant Likely 	ret = gpiochip_remove(&wm8994_gpio->gpio_chip);
287c103de24SGrant Likely 	if (ret == 0)
288c103de24SGrant Likely 		kfree(wm8994_gpio);
289c103de24SGrant Likely 
290c103de24SGrant Likely 	return ret;
291c103de24SGrant Likely }
292c103de24SGrant Likely 
293c103de24SGrant Likely static struct platform_driver wm8994_gpio_driver = {
294c103de24SGrant Likely 	.driver.name	= "wm8994-gpio",
295c103de24SGrant Likely 	.driver.owner	= THIS_MODULE,
296c103de24SGrant Likely 	.probe		= wm8994_gpio_probe,
297c103de24SGrant Likely 	.remove		= __devexit_p(wm8994_gpio_remove),
298c103de24SGrant Likely };
299c103de24SGrant Likely 
300c103de24SGrant Likely static int __init wm8994_gpio_init(void)
301c103de24SGrant Likely {
302c103de24SGrant Likely 	return platform_driver_register(&wm8994_gpio_driver);
303c103de24SGrant Likely }
304c103de24SGrant Likely subsys_initcall(wm8994_gpio_init);
305c103de24SGrant Likely 
306c103de24SGrant Likely static void __exit wm8994_gpio_exit(void)
307c103de24SGrant Likely {
308c103de24SGrant Likely 	platform_driver_unregister(&wm8994_gpio_driver);
309c103de24SGrant Likely }
310c103de24SGrant Likely module_exit(wm8994_gpio_exit);
311c103de24SGrant Likely 
312c103de24SGrant Likely MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
313c103de24SGrant Likely MODULE_DESCRIPTION("GPIO interface for WM8994");
314c103de24SGrant Likely MODULE_LICENSE("GPL");
315c103de24SGrant Likely MODULE_ALIAS("platform:wm8994-gpio");
316