linux/drivers/gpio/gpio-wm8994.c
<<
>>
Prefs
   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
  25struct wm8994_gpio {
  26        struct wm8994 *wm8994;
  27        struct gpio_chip gpio_chip;
  28};
  29
  30static 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
  53static 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
  62static 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
  78static 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
  91static 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
 102static 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
 123static 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
 133static 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
 187static 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
 247static 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
 261static 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
 267        wm8994_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8994_gpio),
 268                                   GFP_KERNEL);
 269        if (wm8994_gpio == NULL)
 270                return -ENOMEM;
 271
 272        wm8994_gpio->wm8994 = wm8994;
 273        wm8994_gpio->gpio_chip = template_chip;
 274        wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX;
 275        wm8994_gpio->gpio_chip.parent = &pdev->dev;
 276        if (pdata && pdata->gpio_base)
 277                wm8994_gpio->gpio_chip.base = pdata->gpio_base;
 278        else
 279                wm8994_gpio->gpio_chip.base = -1;
 280
 281        return devm_gpiochip_add_data(&pdev->dev, &wm8994_gpio->gpio_chip, wm8994_gpio);
 282}
 283
 284static struct platform_driver wm8994_gpio_driver = {
 285        .driver.name    = "wm8994-gpio",
 286        .probe          = wm8994_gpio_probe,
 287};
 288
 289static int __init wm8994_gpio_init(void)
 290{
 291        return platform_driver_register(&wm8994_gpio_driver);
 292}
 293subsys_initcall(wm8994_gpio_init);
 294
 295static void __exit wm8994_gpio_exit(void)
 296{
 297        platform_driver_unregister(&wm8994_gpio_driver);
 298}
 299module_exit(wm8994_gpio_exit);
 300
 301MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 302MODULE_DESCRIPTION("GPIO interface for WM8994");
 303MODULE_LICENSE("GPL");
 304MODULE_ALIAS("platform:wm8994-gpio");
 305