1e4b736f1SMark Brown /* 2c103de24SGrant Likely * gpiolib support for Wolfson WM831x PMICs 3e4b736f1SMark Brown * 4e4b736f1SMark Brown * Copyright 2009 Wolfson Microelectronics PLC. 5e4b736f1SMark Brown * 6e4b736f1SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7e4b736f1SMark Brown * 8e4b736f1SMark Brown * This program is free software; you can redistribute it and/or modify it 9e4b736f1SMark Brown * under the terms of the GNU General Public License as published by the 10e4b736f1SMark Brown * Free Software Foundation; either version 2 of the License, or (at your 11e4b736f1SMark Brown * option) any later version. 12e4b736f1SMark Brown * 13e4b736f1SMark Brown */ 14e4b736f1SMark Brown 15e4b736f1SMark Brown #include <linux/kernel.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17e4b736f1SMark Brown #include <linux/module.h> 18e4b736f1SMark Brown #include <linux/gpio.h> 19e4b736f1SMark Brown #include <linux/mfd/core.h> 20e4b736f1SMark Brown #include <linux/platform_device.h> 21e4b736f1SMark Brown #include <linux/seq_file.h> 22e4b736f1SMark Brown 23e4b736f1SMark Brown #include <linux/mfd/wm831x/core.h> 24e4b736f1SMark Brown #include <linux/mfd/wm831x/pdata.h> 25e4b736f1SMark Brown #include <linux/mfd/wm831x/gpio.h> 26dc0fb25cSMark Brown #include <linux/mfd/wm831x/irq.h> 27e4b736f1SMark Brown 28e4b736f1SMark Brown struct wm831x_gpio { 29e4b736f1SMark Brown struct wm831x *wm831x; 30e4b736f1SMark Brown struct gpio_chip gpio_chip; 31e4b736f1SMark Brown }; 32e4b736f1SMark Brown 33e4b736f1SMark Brown static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) 34e4b736f1SMark Brown { 359b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 36e4b736f1SMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 37f92e8f81SMark Brown int val = WM831X_GPN_DIR; 38f92e8f81SMark Brown 39f92e8f81SMark Brown if (wm831x->has_gpio_ena) 40f92e8f81SMark Brown val |= WM831X_GPN_TRI; 41e4b736f1SMark Brown 42e4b736f1SMark Brown return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, 431bca748cSMark Brown WM831X_GPN_DIR | WM831X_GPN_TRI | 441bca748cSMark Brown WM831X_GPN_FN_MASK, val); 45e4b736f1SMark Brown } 46e4b736f1SMark Brown 47e4b736f1SMark Brown static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) 48e4b736f1SMark Brown { 499b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 50e4b736f1SMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 51e4b736f1SMark Brown int ret; 52e4b736f1SMark Brown 53e4b736f1SMark Brown ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); 54e4b736f1SMark Brown if (ret < 0) 55e4b736f1SMark Brown return ret; 56e4b736f1SMark Brown 57e4b736f1SMark Brown if (ret & 1 << offset) 58e4b736f1SMark Brown return 1; 59e4b736f1SMark Brown else 60e4b736f1SMark Brown return 0; 61e4b736f1SMark Brown } 62e4b736f1SMark Brown 63e4b736f1SMark Brown static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 64e4b736f1SMark Brown { 659b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 66e4b736f1SMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 67e4b736f1SMark Brown 68e4b736f1SMark Brown wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, 69e4b736f1SMark Brown value << offset); 70e4b736f1SMark Brown } 71e4b736f1SMark Brown 723383d23dSMark Brown static int wm831x_gpio_direction_out(struct gpio_chip *chip, 733383d23dSMark Brown unsigned offset, int value) 743383d23dSMark Brown { 759b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 763383d23dSMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 77f92e8f81SMark Brown int val = 0; 783383d23dSMark Brown int ret; 793383d23dSMark Brown 80f92e8f81SMark Brown if (wm831x->has_gpio_ena) 81f92e8f81SMark Brown val |= WM831X_GPN_TRI; 82f92e8f81SMark Brown 833383d23dSMark Brown ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, 841bca748cSMark Brown WM831X_GPN_DIR | WM831X_GPN_TRI | 851bca748cSMark Brown WM831X_GPN_FN_MASK, val); 863383d23dSMark Brown if (ret < 0) 873383d23dSMark Brown return ret; 883383d23dSMark Brown 893383d23dSMark Brown /* Can only set GPIO state once it's in output mode */ 903383d23dSMark Brown wm831x_gpio_set(chip, offset, value); 913383d23dSMark Brown 923383d23dSMark Brown return 0; 933383d23dSMark Brown } 943383d23dSMark Brown 95dc0fb25cSMark Brown static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) 96dc0fb25cSMark Brown { 979b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 98dc0fb25cSMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 99dc0fb25cSMark Brown 100cd99758bSMark Brown return irq_create_mapping(wm831x->irq_domain, 101cd99758bSMark Brown WM831X_IRQ_GPIO_1 + offset); 102dc0fb25cSMark Brown } 103dc0fb25cSMark Brown 104*2956b5d9SMika Westerberg static int wm831x_gpio_set_debounce(struct wm831x *wm831x, unsigned offset, 105b12c35e2SMark Brown unsigned debounce) 106b12c35e2SMark Brown { 107b12c35e2SMark Brown int reg = WM831X_GPIO1_CONTROL + offset; 108b12c35e2SMark Brown int ret, fn; 109b12c35e2SMark Brown 110b12c35e2SMark Brown ret = wm831x_reg_read(wm831x, reg); 111b12c35e2SMark Brown if (ret < 0) 112b12c35e2SMark Brown return ret; 113b12c35e2SMark Brown 114b12c35e2SMark Brown switch (ret & WM831X_GPN_FN_MASK) { 115b12c35e2SMark Brown case 0: 116b12c35e2SMark Brown case 1: 117b12c35e2SMark Brown break; 118b12c35e2SMark Brown default: 119b12c35e2SMark Brown /* Not in GPIO mode */ 120b12c35e2SMark Brown return -EBUSY; 121b12c35e2SMark Brown } 122b12c35e2SMark Brown 123b12c35e2SMark Brown if (debounce >= 32 && debounce <= 64) 124b12c35e2SMark Brown fn = 0; 125b12c35e2SMark Brown else if (debounce >= 4000 && debounce <= 8000) 126b12c35e2SMark Brown fn = 1; 127b12c35e2SMark Brown else 128b12c35e2SMark Brown return -EINVAL; 129b12c35e2SMark Brown 130b12c35e2SMark Brown return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); 131b12c35e2SMark Brown } 132b12c35e2SMark Brown 133*2956b5d9SMika Westerberg static int wm831x_set_config(struct gpio_chip *chip, unsigned int offset, 134*2956b5d9SMika Westerberg unsigned long config) 13551c27da1SLinus Walleij { 13651c27da1SLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 13751c27da1SLinus Walleij struct wm831x *wm831x = wm831x_gpio->wm831x; 13851c27da1SLinus Walleij int reg = WM831X_GPIO1_CONTROL + offset; 13951c27da1SLinus Walleij 140*2956b5d9SMika Westerberg switch (pinconf_to_config_param(config)) { 141*2956b5d9SMika Westerberg case PIN_CONFIG_DRIVE_OPEN_DRAIN: 14251c27da1SLinus Walleij return wm831x_set_bits(wm831x, reg, 14351c27da1SLinus Walleij WM831X_GPN_OD_MASK, WM831X_GPN_OD); 144*2956b5d9SMika Westerberg case PIN_CONFIG_DRIVE_PUSH_PULL: 14551c27da1SLinus Walleij return wm831x_set_bits(wm831x, reg, 14651c27da1SLinus Walleij WM831X_GPN_OD_MASK, 0); 147*2956b5d9SMika Westerberg case PIN_CONFIG_INPUT_DEBOUNCE: 148*2956b5d9SMika Westerberg return wm831x_gpio_set_debounce(wm831x, offset, 149*2956b5d9SMika Westerberg pinconf_to_config_argument(config)); 15051c27da1SLinus Walleij default: 15151c27da1SLinus Walleij break; 15251c27da1SLinus Walleij } 15351c27da1SLinus Walleij 15451c27da1SLinus Walleij return -ENOTSUPP; 15551c27da1SLinus Walleij } 15651c27da1SLinus Walleij 157e4b736f1SMark Brown #ifdef CONFIG_DEBUG_FS 158e4b736f1SMark Brown static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 159e4b736f1SMark Brown { 1609b3c817bSLinus Walleij struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); 161e4b736f1SMark Brown struct wm831x *wm831x = wm831x_gpio->wm831x; 162f92e8f81SMark Brown int i, tristated; 163e4b736f1SMark Brown 164e4b736f1SMark Brown for (i = 0; i < chip->ngpio; i++) { 165e4b736f1SMark Brown int gpio = i + chip->base; 166e4b736f1SMark Brown int reg; 167e4b736f1SMark Brown const char *label, *pull, *powerdomain; 168e4b736f1SMark Brown 169e4b736f1SMark Brown /* We report the GPIO even if it's not requested since 170e4b736f1SMark Brown * we're also reporting things like alternate 171e4b736f1SMark Brown * functions which apply even when the GPIO is not in 172e4b736f1SMark Brown * use as a GPIO. 173e4b736f1SMark Brown */ 174e4b736f1SMark Brown label = gpiochip_is_requested(chip, i); 175e4b736f1SMark Brown if (!label) 176e4b736f1SMark Brown label = "Unrequested"; 177e4b736f1SMark Brown 178e4b736f1SMark Brown seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); 179e4b736f1SMark Brown 180e4b736f1SMark Brown reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); 181e4b736f1SMark Brown if (reg < 0) { 182e4b736f1SMark Brown dev_err(wm831x->dev, 183e4b736f1SMark Brown "GPIO control %d read failed: %d\n", 184e4b736f1SMark Brown gpio, reg); 185e4b736f1SMark Brown seq_printf(s, "\n"); 186e4b736f1SMark Brown continue; 187e4b736f1SMark Brown } 188e4b736f1SMark Brown 189e4b736f1SMark Brown switch (reg & WM831X_GPN_PULL_MASK) { 190e4b736f1SMark Brown case WM831X_GPIO_PULL_NONE: 191e4b736f1SMark Brown pull = "nopull"; 192e4b736f1SMark Brown break; 193e4b736f1SMark Brown case WM831X_GPIO_PULL_DOWN: 194e4b736f1SMark Brown pull = "pulldown"; 195e4b736f1SMark Brown break; 196e4b736f1SMark Brown case WM831X_GPIO_PULL_UP: 197e4b736f1SMark Brown pull = "pullup"; 198164d5c39SAxel Lin break; 199e4b736f1SMark Brown default: 200e4b736f1SMark Brown pull = "INVALID PULL"; 201e4b736f1SMark Brown break; 202e4b736f1SMark Brown } 203e4b736f1SMark Brown 204e4b736f1SMark Brown switch (i + 1) { 205e4b736f1SMark Brown case 1 ... 3: 206e4b736f1SMark Brown case 7 ... 9: 207e4b736f1SMark Brown if (reg & WM831X_GPN_PWR_DOM) 208e4b736f1SMark Brown powerdomain = "VPMIC"; 209e4b736f1SMark Brown else 210e4b736f1SMark Brown powerdomain = "DBVDD"; 211e4b736f1SMark Brown break; 212e4b736f1SMark Brown 213e4b736f1SMark Brown case 4 ... 6: 214e4b736f1SMark Brown case 10 ... 12: 215e4b736f1SMark Brown if (reg & WM831X_GPN_PWR_DOM) 216e4b736f1SMark Brown powerdomain = "SYSVDD"; 217e4b736f1SMark Brown else 218e4b736f1SMark Brown powerdomain = "DBVDD"; 219e4b736f1SMark Brown break; 220e4b736f1SMark Brown 221e4b736f1SMark Brown case 13 ... 16: 222e4b736f1SMark Brown powerdomain = "TPVDD"; 223e4b736f1SMark Brown break; 224e4b736f1SMark Brown 225e4b736f1SMark Brown default: 226e4b736f1SMark Brown BUG(); 227e4b736f1SMark Brown break; 228e4b736f1SMark Brown } 229e4b736f1SMark Brown 230f92e8f81SMark Brown tristated = reg & WM831X_GPN_TRI; 231f92e8f81SMark Brown if (wm831x->has_gpio_ena) 232f92e8f81SMark Brown tristated = !tristated; 233f92e8f81SMark Brown 234e4b736f1SMark Brown seq_printf(s, " %s %s %s %s%s\n" 235e4b736f1SMark Brown " %s%s (0x%4x)\n", 236e4b736f1SMark Brown reg & WM831X_GPN_DIR ? "in" : "out", 237e4b736f1SMark Brown wm831x_gpio_get(chip, i) ? "high" : "low", 238e4b736f1SMark Brown pull, 239e4b736f1SMark Brown powerdomain, 2406b8274faSMark Brown reg & WM831X_GPN_POL ? "" : " inverted", 24151c27da1SLinus Walleij reg & WM831X_GPN_OD ? "open-drain" : "push-pull", 242f92e8f81SMark Brown tristated ? " tristated" : "", 243e4b736f1SMark Brown reg); 244e4b736f1SMark Brown } 245e4b736f1SMark Brown } 246e4b736f1SMark Brown #else 247e4b736f1SMark Brown #define wm831x_gpio_dbg_show NULL 248e4b736f1SMark Brown #endif 249e4b736f1SMark Brown 250e35b5ab0SJulia Lawall static const struct gpio_chip template_chip = { 251e4b736f1SMark Brown .label = "wm831x", 252e4b736f1SMark Brown .owner = THIS_MODULE, 253e4b736f1SMark Brown .direction_input = wm831x_gpio_direction_in, 254e4b736f1SMark Brown .get = wm831x_gpio_get, 255e4b736f1SMark Brown .direction_output = wm831x_gpio_direction_out, 256e4b736f1SMark Brown .set = wm831x_gpio_set, 257dc0fb25cSMark Brown .to_irq = wm831x_gpio_to_irq, 258*2956b5d9SMika Westerberg .set_config = wm831x_set_config, 259e4b736f1SMark Brown .dbg_show = wm831x_gpio_dbg_show, 2609fb1f39eSLinus Walleij .can_sleep = true, 261e4b736f1SMark Brown }; 262e4b736f1SMark Brown 2633836309dSBill Pemberton static int wm831x_gpio_probe(struct platform_device *pdev) 264e4b736f1SMark Brown { 265e4b736f1SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 266e56aee18SJingoo Han struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev); 267e4b736f1SMark Brown struct wm831x_gpio *wm831x_gpio; 268e4b736f1SMark Brown int ret; 269e4b736f1SMark Brown 270718bc6e3SAxel Lin wm831x_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm831x_gpio), 271718bc6e3SAxel Lin GFP_KERNEL); 272e4b736f1SMark Brown if (wm831x_gpio == NULL) 273e4b736f1SMark Brown return -ENOMEM; 274e4b736f1SMark Brown 275e4b736f1SMark Brown wm831x_gpio->wm831x = wm831x; 276e4b736f1SMark Brown wm831x_gpio->gpio_chip = template_chip; 2776f2ecaaeSMark Brown wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; 27858383c78SLinus Walleij wm831x_gpio->gpio_chip.parent = &pdev->dev; 279e4b736f1SMark Brown if (pdata && pdata->gpio_base) 280e4b736f1SMark Brown wm831x_gpio->gpio_chip.base = pdata->gpio_base; 281e4b736f1SMark Brown else 282e4b736f1SMark Brown wm831x_gpio->gpio_chip.base = -1; 283e4b736f1SMark Brown 28461da4848SLaxman Dewangan ret = devm_gpiochip_add_data(&pdev->dev, &wm831x_gpio->gpio_chip, 28561da4848SLaxman Dewangan wm831x_gpio); 286e4b736f1SMark Brown if (ret < 0) { 287718bc6e3SAxel Lin dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); 288718bc6e3SAxel Lin return ret; 289e4b736f1SMark Brown } 290e4b736f1SMark Brown 291e4b736f1SMark Brown platform_set_drvdata(pdev, wm831x_gpio); 292e4b736f1SMark Brown 293e4b736f1SMark Brown return ret; 294e4b736f1SMark Brown } 295e4b736f1SMark Brown 296e4b736f1SMark Brown static struct platform_driver wm831x_gpio_driver = { 297e4b736f1SMark Brown .driver.name = "wm831x-gpio", 298e4b736f1SMark Brown .probe = wm831x_gpio_probe, 299e4b736f1SMark Brown }; 300e4b736f1SMark Brown 301e4b736f1SMark Brown static int __init wm831x_gpio_init(void) 302e4b736f1SMark Brown { 303e4b736f1SMark Brown return platform_driver_register(&wm831x_gpio_driver); 304e4b736f1SMark Brown } 305e4b736f1SMark Brown subsys_initcall(wm831x_gpio_init); 306e4b736f1SMark Brown 307e4b736f1SMark Brown static void __exit wm831x_gpio_exit(void) 308e4b736f1SMark Brown { 309e4b736f1SMark Brown platform_driver_unregister(&wm831x_gpio_driver); 310e4b736f1SMark Brown } 311e4b736f1SMark Brown module_exit(wm831x_gpio_exit); 312e4b736f1SMark Brown 313e4b736f1SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 314e4b736f1SMark Brown MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); 315e4b736f1SMark Brown MODULE_LICENSE("GPL"); 316e4b736f1SMark Brown MODULE_ALIAS("platform:wm831x-gpio"); 317