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