166dbe750SLinus Walleij // SPDX-License-Identifier: GPL-2.0+ 2c103de24SGrant Likely /* 3c103de24SGrant Likely * gpiolib support for Wolfson WM8994 4c103de24SGrant Likely * 5c103de24SGrant Likely * Copyright 2009 Wolfson Microelectronics PLC. 6c103de24SGrant Likely * 7c103de24SGrant Likely * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8c103de24SGrant Likely * 9c103de24SGrant Likely */ 10c103de24SGrant Likely 11*abeec1adSBartosz Golaszewski #include <linux/cleanup.h> 12c103de24SGrant Likely #include <linux/kernel.h> 13c103de24SGrant Likely #include <linux/slab.h> 14c103de24SGrant Likely #include <linux/module.h> 1510833c4bSLinus Walleij #include <linux/gpio/driver.h> 16c103de24SGrant Likely #include <linux/mfd/core.h> 17c103de24SGrant Likely #include <linux/platform_device.h> 18c103de24SGrant Likely #include <linux/seq_file.h> 19224a1f90SMark Brown #include <linux/regmap.h> 20c103de24SGrant Likely 21c103de24SGrant Likely #include <linux/mfd/wm8994/core.h> 22c103de24SGrant Likely #include <linux/mfd/wm8994/pdata.h> 23c103de24SGrant Likely #include <linux/mfd/wm8994/gpio.h> 24c103de24SGrant Likely #include <linux/mfd/wm8994/registers.h> 25c103de24SGrant Likely 26c103de24SGrant Likely struct wm8994_gpio { 27c103de24SGrant Likely struct wm8994 *wm8994; 28c103de24SGrant Likely struct gpio_chip gpio_chip; 29c103de24SGrant Likely }; 30c103de24SGrant Likely 31c103de24SGrant Likely static int wm8994_gpio_request(struct gpio_chip *chip, unsigned offset) 32c103de24SGrant Likely { 33765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 34c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 35c103de24SGrant Likely 36c103de24SGrant Likely switch (wm8994->type) { 37c103de24SGrant Likely case WM8958: 38c103de24SGrant Likely switch (offset) { 39c103de24SGrant Likely case 1: 40c103de24SGrant Likely case 2: 41c103de24SGrant Likely case 3: 42c103de24SGrant Likely case 4: 43c103de24SGrant Likely case 6: 44c103de24SGrant Likely return -EINVAL; 45c103de24SGrant Likely } 46c103de24SGrant Likely break; 47c103de24SGrant Likely default: 48c103de24SGrant Likely break; 49c103de24SGrant Likely } 50c103de24SGrant Likely 51c103de24SGrant Likely return 0; 52c103de24SGrant Likely } 53c103de24SGrant Likely 54c103de24SGrant Likely static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset) 55c103de24SGrant Likely { 56765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 57c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 58c103de24SGrant Likely 59c103de24SGrant Likely return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, 60c103de24SGrant Likely WM8994_GPN_DIR, WM8994_GPN_DIR); 61c103de24SGrant Likely } 62c103de24SGrant Likely 63c103de24SGrant Likely static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset) 64c103de24SGrant Likely { 65765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 66c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 67c103de24SGrant Likely int ret; 68c103de24SGrant Likely 69c103de24SGrant Likely ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset); 70c103de24SGrant Likely if (ret < 0) 71c103de24SGrant Likely return ret; 72c103de24SGrant Likely 73c103de24SGrant Likely if (ret & WM8994_GPN_LVL) 74c103de24SGrant Likely return 1; 75c103de24SGrant Likely else 76c103de24SGrant Likely return 0; 77c103de24SGrant Likely } 78c103de24SGrant Likely 79c103de24SGrant Likely static int wm8994_gpio_direction_out(struct gpio_chip *chip, 80c103de24SGrant Likely unsigned offset, int value) 81c103de24SGrant Likely { 82765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 83c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 84c103de24SGrant Likely 858cd578b6SMark Brown if (value) 868cd578b6SMark Brown value = WM8994_GPN_LVL; 878cd578b6SMark Brown 88c103de24SGrant Likely return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, 898cd578b6SMark Brown WM8994_GPN_DIR | WM8994_GPN_LVL, value); 90c103de24SGrant Likely } 91c103de24SGrant Likely 92c103de24SGrant Likely static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 93c103de24SGrant Likely { 94765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 95c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 96c103de24SGrant Likely 97c103de24SGrant Likely if (value) 98c103de24SGrant Likely value = WM8994_GPN_LVL; 99c103de24SGrant Likely 100c103de24SGrant Likely wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); 101c103de24SGrant Likely } 102c103de24SGrant Likely 1032956b5d9SMika Westerberg static int wm8994_gpio_set_config(struct gpio_chip *chip, unsigned int offset, 1042956b5d9SMika Westerberg unsigned long config) 105190ea434SLinus Walleij { 106190ea434SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 107190ea434SLinus Walleij struct wm8994 *wm8994 = wm8994_gpio->wm8994; 108190ea434SLinus Walleij 1092956b5d9SMika Westerberg switch (pinconf_to_config_param(config)) { 1102956b5d9SMika Westerberg case PIN_CONFIG_DRIVE_OPEN_DRAIN: 111190ea434SLinus Walleij return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, 112190ea434SLinus Walleij WM8994_GPN_OP_CFG_MASK, 113190ea434SLinus Walleij WM8994_GPN_OP_CFG); 1142956b5d9SMika Westerberg case PIN_CONFIG_DRIVE_PUSH_PULL: 115190ea434SLinus Walleij return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, 116190ea434SLinus Walleij WM8994_GPN_OP_CFG_MASK, 0); 117190ea434SLinus Walleij default: 118190ea434SLinus Walleij break; 119190ea434SLinus Walleij } 120190ea434SLinus Walleij 121190ea434SLinus Walleij return -ENOTSUPP; 122190ea434SLinus Walleij } 123190ea434SLinus Walleij 124c103de24SGrant Likely static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset) 125c103de24SGrant Likely { 126765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 127c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 128c103de24SGrant Likely 129224a1f90SMark Brown return regmap_irq_get_virq(wm8994->irq_data, offset); 130c103de24SGrant Likely } 131c103de24SGrant Likely 132c103de24SGrant Likely 133c103de24SGrant Likely #ifdef CONFIG_DEBUG_FS 134d0ad5e89SMark Brown static const char *wm8994_gpio_fn(u16 fn) 135d0ad5e89SMark Brown { 136d0ad5e89SMark Brown switch (fn) { 137d0ad5e89SMark Brown case WM8994_GP_FN_PIN_SPECIFIC: 138d0ad5e89SMark Brown return "pin-specific"; 139d0ad5e89SMark Brown case WM8994_GP_FN_GPIO: 140d0ad5e89SMark Brown return "GPIO"; 141d0ad5e89SMark Brown case WM8994_GP_FN_SDOUT: 142d0ad5e89SMark Brown return "SDOUT"; 143d0ad5e89SMark Brown case WM8994_GP_FN_IRQ: 144d0ad5e89SMark Brown return "IRQ"; 145d0ad5e89SMark Brown case WM8994_GP_FN_TEMPERATURE: 146d0ad5e89SMark Brown return "Temperature"; 147d0ad5e89SMark Brown case WM8994_GP_FN_MICBIAS1_DET: 148d0ad5e89SMark Brown return "MICBIAS1 detect"; 149d0ad5e89SMark Brown case WM8994_GP_FN_MICBIAS1_SHORT: 150d0ad5e89SMark Brown return "MICBIAS1 short"; 151d0ad5e89SMark Brown case WM8994_GP_FN_MICBIAS2_DET: 152d0ad5e89SMark Brown return "MICBIAS2 detect"; 153d0ad5e89SMark Brown case WM8994_GP_FN_MICBIAS2_SHORT: 154d0ad5e89SMark Brown return "MICBIAS2 short"; 155d0ad5e89SMark Brown case WM8994_GP_FN_FLL1_LOCK: 156d0ad5e89SMark Brown return "FLL1 lock"; 157d0ad5e89SMark Brown case WM8994_GP_FN_FLL2_LOCK: 158d0ad5e89SMark Brown return "FLL2 lock"; 159d0ad5e89SMark Brown case WM8994_GP_FN_SRC1_LOCK: 160d0ad5e89SMark Brown return "SRC1 lock"; 161d0ad5e89SMark Brown case WM8994_GP_FN_SRC2_LOCK: 162d0ad5e89SMark Brown return "SRC2 lock"; 163d0ad5e89SMark Brown case WM8994_GP_FN_DRC1_ACT: 164d0ad5e89SMark Brown return "DRC1 activity"; 165d0ad5e89SMark Brown case WM8994_GP_FN_DRC2_ACT: 166d0ad5e89SMark Brown return "DRC2 activity"; 167d0ad5e89SMark Brown case WM8994_GP_FN_DRC3_ACT: 168d0ad5e89SMark Brown return "DRC3 activity"; 169d0ad5e89SMark Brown case WM8994_GP_FN_WSEQ_STATUS: 170d0ad5e89SMark Brown return "Write sequencer"; 171d0ad5e89SMark Brown case WM8994_GP_FN_FIFO_ERROR: 172d0ad5e89SMark Brown return "FIFO error"; 173d0ad5e89SMark Brown case WM8994_GP_FN_OPCLK: 174d0ad5e89SMark Brown return "OPCLK"; 175d0ad5e89SMark Brown case WM8994_GP_FN_THW: 176d0ad5e89SMark Brown return "Thermal warning"; 177d0ad5e89SMark Brown case WM8994_GP_FN_DCS_DONE: 178d0ad5e89SMark Brown return "DC servo"; 179d0ad5e89SMark Brown case WM8994_GP_FN_FLL1_OUT: 180d0ad5e89SMark Brown return "FLL1 output"; 181d0ad5e89SMark Brown case WM8994_GP_FN_FLL2_OUT: 182d0ad5e89SMark Brown return "FLL1 output"; 183d0ad5e89SMark Brown default: 184d0ad5e89SMark Brown return "Unknown"; 185d0ad5e89SMark Brown } 186d0ad5e89SMark Brown } 187d0ad5e89SMark Brown 188c103de24SGrant Likely static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 189c103de24SGrant Likely { 190765aa587SLinus Walleij struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); 191c103de24SGrant Likely struct wm8994 *wm8994 = wm8994_gpio->wm8994; 192c103de24SGrant Likely int i; 193c103de24SGrant Likely 194c103de24SGrant Likely for (i = 0; i < chip->ngpio; i++) { 195c103de24SGrant Likely int gpio = i + chip->base; 196c103de24SGrant Likely int reg; 197c103de24SGrant Likely 198c103de24SGrant Likely /* We report the GPIO even if it's not requested since 199c103de24SGrant Likely * we're also reporting things like alternate 200c103de24SGrant Likely * functions which apply even when the GPIO is not in 201c103de24SGrant Likely * use as a GPIO. 202c103de24SGrant Likely */ 203*abeec1adSBartosz Golaszewski char *label __free(kfree) = gpiochip_dup_line_label(chip, i); 204*abeec1adSBartosz Golaszewski if (IS_ERR(label)) { 205*abeec1adSBartosz Golaszewski dev_err(wm8994->dev, "Failed to duplicate label\n"); 206*abeec1adSBartosz Golaszewski continue; 207*abeec1adSBartosz Golaszewski } 208c103de24SGrant Likely 209*abeec1adSBartosz Golaszewski seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, 210*abeec1adSBartosz Golaszewski label ?: "Unrequested"); 211c103de24SGrant Likely 212c103de24SGrant Likely reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i); 213c103de24SGrant Likely if (reg < 0) { 214c103de24SGrant Likely dev_err(wm8994->dev, 215c103de24SGrant Likely "GPIO control %d read failed: %d\n", 216c103de24SGrant Likely gpio, reg); 217c103de24SGrant Likely seq_printf(s, "\n"); 218c103de24SGrant Likely continue; 219c103de24SGrant Likely } 220c103de24SGrant Likely 221d0ad5e89SMark Brown if (reg & WM8994_GPN_DIR) 222d0ad5e89SMark Brown seq_printf(s, "in "); 223d0ad5e89SMark Brown else 224d0ad5e89SMark Brown seq_printf(s, "out "); 225d0ad5e89SMark Brown 226d0ad5e89SMark Brown if (reg & WM8994_GPN_PU) 227d0ad5e89SMark Brown seq_printf(s, "pull up "); 228d0ad5e89SMark Brown 229d0ad5e89SMark Brown if (reg & WM8994_GPN_PD) 230d0ad5e89SMark Brown seq_printf(s, "pull down "); 231d0ad5e89SMark Brown 232d0ad5e89SMark Brown if (reg & WM8994_GPN_POL) 233d0ad5e89SMark Brown seq_printf(s, "inverted "); 234d0ad5e89SMark Brown else 235d0ad5e89SMark Brown seq_printf(s, "noninverted "); 236d0ad5e89SMark Brown 237d0ad5e89SMark Brown if (reg & WM8994_GPN_OP_CFG) 238d0ad5e89SMark Brown seq_printf(s, "open drain "); 239d0ad5e89SMark Brown else 240190ea434SLinus Walleij seq_printf(s, "push-pull "); 241d0ad5e89SMark Brown 242d0ad5e89SMark Brown seq_printf(s, "%s (%x)\n", 243d0ad5e89SMark Brown wm8994_gpio_fn(reg & WM8994_GPN_FN_MASK), reg); 244c103de24SGrant Likely } 245c103de24SGrant Likely } 246c103de24SGrant Likely #else 247c103de24SGrant Likely #define wm8994_gpio_dbg_show NULL 248c103de24SGrant Likely #endif 249c103de24SGrant Likely 250e35b5ab0SJulia Lawall static const struct gpio_chip template_chip = { 251c103de24SGrant Likely .label = "wm8994", 252c103de24SGrant Likely .owner = THIS_MODULE, 253c103de24SGrant Likely .request = wm8994_gpio_request, 254c103de24SGrant Likely .direction_input = wm8994_gpio_direction_in, 255c103de24SGrant Likely .get = wm8994_gpio_get, 256c103de24SGrant Likely .direction_output = wm8994_gpio_direction_out, 257c103de24SGrant Likely .set = wm8994_gpio_set, 2582956b5d9SMika Westerberg .set_config = wm8994_gpio_set_config, 259c103de24SGrant Likely .to_irq = wm8994_gpio_to_irq, 260c103de24SGrant Likely .dbg_show = wm8994_gpio_dbg_show, 2619fb1f39eSLinus Walleij .can_sleep = true, 262c103de24SGrant Likely }; 263c103de24SGrant Likely 2643836309dSBill Pemberton static int wm8994_gpio_probe(struct platform_device *pdev) 265c103de24SGrant Likely { 266c103de24SGrant Likely struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); 267e56aee18SJingoo Han struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev); 268c103de24SGrant Likely struct wm8994_gpio *wm8994_gpio; 269c103de24SGrant Likely 270f8d203c0SMark Brown wm8994_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8994_gpio), 271f8d203c0SMark Brown GFP_KERNEL); 272c103de24SGrant Likely if (wm8994_gpio == NULL) 273c103de24SGrant Likely return -ENOMEM; 274c103de24SGrant Likely 275c103de24SGrant Likely wm8994_gpio->wm8994 = wm8994; 276c103de24SGrant Likely wm8994_gpio->gpio_chip = template_chip; 277c103de24SGrant Likely wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX; 27858383c78SLinus Walleij wm8994_gpio->gpio_chip.parent = &pdev->dev; 279c103de24SGrant Likely if (pdata && pdata->gpio_base) 280c103de24SGrant Likely wm8994_gpio->gpio_chip.base = pdata->gpio_base; 281c103de24SGrant Likely else 282c103de24SGrant Likely wm8994_gpio->gpio_chip.base = -1; 283c103de24SGrant Likely 284e076aedaSAlexandru Ardelean return devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip, wm8994_gpio); 285c103de24SGrant Likely } 286c103de24SGrant Likely 287c103de24SGrant Likely static struct platform_driver wm8994_gpio_driver = { 288c103de24SGrant Likely .driver.name = "wm8994-gpio", 289c103de24SGrant Likely .probe = wm8994_gpio_probe, 290c103de24SGrant Likely }; 291c103de24SGrant Likely 292c103de24SGrant Likely static int __init wm8994_gpio_init(void) 293c103de24SGrant Likely { 294c103de24SGrant Likely return platform_driver_register(&wm8994_gpio_driver); 295c103de24SGrant Likely } 296c103de24SGrant Likely subsys_initcall(wm8994_gpio_init); 297c103de24SGrant Likely 298c103de24SGrant Likely static void __exit wm8994_gpio_exit(void) 299c103de24SGrant Likely { 300c103de24SGrant Likely platform_driver_unregister(&wm8994_gpio_driver); 301c103de24SGrant Likely } 302c103de24SGrant Likely module_exit(wm8994_gpio_exit); 303c103de24SGrant Likely 304c103de24SGrant Likely MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 305c103de24SGrant Likely MODULE_DESCRIPTION("GPIO interface for WM8994"); 306c103de24SGrant Likely MODULE_LICENSE("GPL"); 307c103de24SGrant Likely MODULE_ALIAS("platform:wm8994-gpio"); 308