linux/drivers/gpio/gpio-arizona.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * gpiolib support for Wolfson Arizona class devices
   4 *
   5 * Copyright 2012 Wolfson Microelectronics PLC.
   6 *
   7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/slab.h>
  12#include <linux/module.h>
  13#include <linux/gpio/driver.h>
  14#include <linux/platform_device.h>
  15#include <linux/pm_runtime.h>
  16#include <linux/seq_file.h>
  17
  18#include <linux/mfd/arizona/core.h>
  19#include <linux/mfd/arizona/pdata.h>
  20#include <linux/mfd/arizona/registers.h>
  21
  22struct arizona_gpio {
  23        struct arizona *arizona;
  24        struct gpio_chip gpio_chip;
  25};
  26
  27static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
  28{
  29        struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
  30        struct arizona *arizona = arizona_gpio->arizona;
  31        bool persistent = gpiochip_line_is_persistent(chip, offset);
  32        bool change;
  33        int ret;
  34
  35        ret = regmap_update_bits_check(arizona->regmap,
  36                                       ARIZONA_GPIO1_CTRL + offset,
  37                                       ARIZONA_GPN_DIR, ARIZONA_GPN_DIR,
  38                                       &change);
  39        if (ret < 0)
  40                return ret;
  41
  42        if (change && persistent) {
  43                pm_runtime_mark_last_busy(chip->parent);
  44                pm_runtime_put_autosuspend(chip->parent);
  45        }
  46
  47        return 0;
  48}
  49
  50static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
  51{
  52        struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
  53        struct arizona *arizona = arizona_gpio->arizona;
  54        unsigned int reg, val;
  55        int ret;
  56
  57        reg = ARIZONA_GPIO1_CTRL + offset;
  58        ret = regmap_read(arizona->regmap, reg, &val);
  59        if (ret < 0)
  60                return ret;
  61
  62        /* Resume to read actual registers for input pins */
  63        if (val & ARIZONA_GPN_DIR) {
  64                ret = pm_runtime_get_sync(chip->parent);
  65                if (ret < 0) {
  66                        dev_err(chip->parent, "Failed to resume: %d\n", ret);
  67                        pm_runtime_put_autosuspend(chip->parent);
  68                        return ret;
  69                }
  70
  71                /* Register is cached, drop it to ensure a physical read */
  72                ret = regcache_drop_region(arizona->regmap, reg, reg);
  73                if (ret < 0) {
  74                        dev_err(chip->parent, "Failed to drop cache: %d\n",
  75                                ret);
  76                        pm_runtime_put_autosuspend(chip->parent);
  77                        return ret;
  78                }
  79
  80                ret = regmap_read(arizona->regmap, reg, &val);
  81                if (ret < 0) {
  82                        pm_runtime_put_autosuspend(chip->parent);
  83                        return ret;
  84                }
  85
  86                pm_runtime_mark_last_busy(chip->parent);
  87                pm_runtime_put_autosuspend(chip->parent);
  88        }
  89
  90        if (val & ARIZONA_GPN_LVL)
  91                return 1;
  92        else
  93                return 0;
  94}
  95
  96static int arizona_gpio_direction_out(struct gpio_chip *chip,
  97                                     unsigned offset, int value)
  98{
  99        struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
 100        struct arizona *arizona = arizona_gpio->arizona;
 101        bool persistent = gpiochip_line_is_persistent(chip, offset);
 102        unsigned int val;
 103        int ret;
 104
 105        ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val);
 106        if (ret < 0)
 107                return ret;
 108
 109        if ((val & ARIZONA_GPN_DIR) && persistent) {
 110                ret = pm_runtime_get_sync(chip->parent);
 111                if (ret < 0) {
 112                        dev_err(chip->parent, "Failed to resume: %d\n", ret);
 113                        pm_runtime_put(chip->parent);
 114                        return ret;
 115                }
 116        }
 117
 118        if (value)
 119                value = ARIZONA_GPN_LVL;
 120
 121        return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
 122                                  ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
 123}
 124
 125static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 126{
 127        struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
 128        struct arizona *arizona = arizona_gpio->arizona;
 129
 130        if (value)
 131                value = ARIZONA_GPN_LVL;
 132
 133        regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
 134                           ARIZONA_GPN_LVL, value);
 135}
 136
 137static const struct gpio_chip template_chip = {
 138        .label                  = "arizona",
 139        .owner                  = THIS_MODULE,
 140        .direction_input        = arizona_gpio_direction_in,
 141        .get                    = arizona_gpio_get,
 142        .direction_output       = arizona_gpio_direction_out,
 143        .set                    = arizona_gpio_set,
 144        .can_sleep              = true,
 145};
 146
 147static int arizona_gpio_probe(struct platform_device *pdev)
 148{
 149        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
 150        struct arizona_pdata *pdata = &arizona->pdata;
 151        struct arizona_gpio *arizona_gpio;
 152        int ret;
 153
 154        arizona_gpio = devm_kzalloc(&pdev->dev, sizeof(*arizona_gpio),
 155                                    GFP_KERNEL);
 156        if (!arizona_gpio)
 157                return -ENOMEM;
 158
 159        arizona_gpio->arizona = arizona;
 160        arizona_gpio->gpio_chip = template_chip;
 161        arizona_gpio->gpio_chip.parent = &pdev->dev;
 162#ifdef CONFIG_OF_GPIO
 163        arizona_gpio->gpio_chip.of_node = arizona->dev->of_node;
 164#endif
 165
 166        switch (arizona->type) {
 167        case WM5102:
 168        case WM5110:
 169        case WM8280:
 170        case WM8997:
 171        case WM8998:
 172        case WM1814:
 173                arizona_gpio->gpio_chip.ngpio = 5;
 174                break;
 175        case WM1831:
 176        case CS47L24:
 177                arizona_gpio->gpio_chip.ngpio = 2;
 178                break;
 179        default:
 180                dev_err(&pdev->dev, "Unknown chip variant %d\n",
 181                        arizona->type);
 182                return -EINVAL;
 183        }
 184
 185        if (pdata->gpio_base)
 186                arizona_gpio->gpio_chip.base = pdata->gpio_base;
 187        else
 188                arizona_gpio->gpio_chip.base = -1;
 189
 190        pm_runtime_enable(&pdev->dev);
 191
 192        ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
 193                                     arizona_gpio);
 194        if (ret < 0) {
 195                pm_runtime_disable(&pdev->dev);
 196                dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
 197                        ret);
 198                return ret;
 199        }
 200
 201        return 0;
 202}
 203
 204static struct platform_driver arizona_gpio_driver = {
 205        .driver.name    = "arizona-gpio",
 206        .probe          = arizona_gpio_probe,
 207};
 208
 209module_platform_driver(arizona_gpio_driver);
 210
 211MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 212MODULE_DESCRIPTION("GPIO interface for Arizona devices");
 213MODULE_LICENSE("GPL");
 214MODULE_ALIAS("platform:arizona-gpio");
 215