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