linux/drivers/gpio/gpio-madera.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * GPIO support for Cirrus Logic Madera codecs
   4 *
   5 * Copyright (C) 2015-2018 Cirrus Logic
   6 */
   7
   8#include <linux/gpio/driver.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12
  13#include <linux/mfd/madera/core.h>
  14#include <linux/mfd/madera/pdata.h>
  15#include <linux/mfd/madera/registers.h>
  16
  17struct madera_gpio {
  18        struct madera *madera;
  19        /* storage space for the gpio_chip we're using */
  20        struct gpio_chip gpio_chip;
  21};
  22
  23static int madera_gpio_get_direction(struct gpio_chip *chip,
  24                                     unsigned int offset)
  25{
  26        struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
  27        struct madera *madera = madera_gpio->madera;
  28        unsigned int reg_offset = 2 * offset;
  29        unsigned int val;
  30        int ret;
  31
  32        ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_2 + reg_offset,
  33                          &val);
  34        if (ret < 0)
  35                return ret;
  36
  37        if (val & MADERA_GP1_DIR_MASK)
  38                return GPIO_LINE_DIRECTION_IN;
  39
  40        return GPIO_LINE_DIRECTION_OUT;
  41}
  42
  43static int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
  44{
  45        struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
  46        struct madera *madera = madera_gpio->madera;
  47        unsigned int reg_offset = 2 * offset;
  48
  49        return regmap_update_bits(madera->regmap,
  50                                  MADERA_GPIO1_CTRL_2 + reg_offset,
  51                                  MADERA_GP1_DIR_MASK, MADERA_GP1_DIR);
  52}
  53
  54static int madera_gpio_get(struct gpio_chip *chip, unsigned int offset)
  55{
  56        struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
  57        struct madera *madera = madera_gpio->madera;
  58        unsigned int reg_offset = 2 * offset;
  59        unsigned int val;
  60        int ret;
  61
  62        ret = regmap_read(madera->regmap, MADERA_GPIO1_CTRL_1 + reg_offset,
  63                          &val);
  64        if (ret < 0)
  65                return ret;
  66
  67        return !!(val & MADERA_GP1_LVL_MASK);
  68}
  69
  70static int madera_gpio_direction_out(struct gpio_chip *chip,
  71                                     unsigned int offset, int value)
  72{
  73        struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
  74        struct madera *madera = madera_gpio->madera;
  75        unsigned int reg_offset = 2 * offset;
  76        unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
  77        int ret;
  78
  79        ret = regmap_update_bits(madera->regmap,
  80                                 MADERA_GPIO1_CTRL_2 + reg_offset,
  81                                 MADERA_GP1_DIR_MASK, 0);
  82        if (ret < 0)
  83                return ret;
  84
  85        return regmap_update_bits(madera->regmap,
  86                                  MADERA_GPIO1_CTRL_1 + reg_offset,
  87                                  MADERA_GP1_LVL_MASK, reg_val);
  88}
  89
  90static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset,
  91                            int value)
  92{
  93        struct madera_gpio *madera_gpio = gpiochip_get_data(chip);
  94        struct madera *madera = madera_gpio->madera;
  95        unsigned int reg_offset = 2 * offset;
  96        unsigned int reg_val = value ? MADERA_GP1_LVL : 0;
  97        int ret;
  98
  99        ret = regmap_update_bits(madera->regmap,
 100                                 MADERA_GPIO1_CTRL_1 + reg_offset,
 101                                 MADERA_GP1_LVL_MASK, reg_val);
 102
 103        /* set() doesn't return an error so log a warning */
 104        if (ret)
 105                dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n",
 106                         MADERA_GPIO1_CTRL_1 + reg_offset, ret);
 107}
 108
 109static const struct gpio_chip madera_gpio_chip = {
 110        .label                  = "madera",
 111        .owner                  = THIS_MODULE,
 112        .request                = gpiochip_generic_request,
 113        .free                   = gpiochip_generic_free,
 114        .get_direction          = madera_gpio_get_direction,
 115        .direction_input        = madera_gpio_direction_in,
 116        .get                    = madera_gpio_get,
 117        .direction_output       = madera_gpio_direction_out,
 118        .set                    = madera_gpio_set,
 119        .set_config             = gpiochip_generic_config,
 120        .can_sleep              = true,
 121};
 122
 123static int madera_gpio_probe(struct platform_device *pdev)
 124{
 125        struct madera *madera = dev_get_drvdata(pdev->dev.parent);
 126        struct madera_pdata *pdata = &madera->pdata;
 127        struct madera_gpio *madera_gpio;
 128        int ret;
 129
 130        madera_gpio = devm_kzalloc(&pdev->dev, sizeof(*madera_gpio),
 131                                   GFP_KERNEL);
 132        if (!madera_gpio)
 133                return -ENOMEM;
 134
 135        madera_gpio->madera = madera;
 136
 137        /* Construct suitable gpio_chip from the template in madera_gpio_chip */
 138        madera_gpio->gpio_chip = madera_gpio_chip;
 139        madera_gpio->gpio_chip.parent = pdev->dev.parent;
 140
 141        switch (madera->type) {
 142        case CS47L15:
 143                madera_gpio->gpio_chip.ngpio = CS47L15_NUM_GPIOS;
 144                break;
 145        case CS47L35:
 146                madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS;
 147                break;
 148        case CS47L85:
 149        case WM1840:
 150                madera_gpio->gpio_chip.ngpio = CS47L85_NUM_GPIOS;
 151                break;
 152        case CS47L90:
 153        case CS47L91:
 154                madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS;
 155                break;
 156        case CS42L92:
 157        case CS47L92:
 158        case CS47L93:
 159                madera_gpio->gpio_chip.ngpio = CS47L92_NUM_GPIOS;
 160                break;
 161        default:
 162                dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type);
 163                return -EINVAL;
 164        }
 165
 166        /* We want to be usable on systems that don't use devicetree or acpi */
 167        if (pdata->gpio_base)
 168                madera_gpio->gpio_chip.base = pdata->gpio_base;
 169        else
 170                madera_gpio->gpio_chip.base = -1;
 171
 172        ret = devm_gpiochip_add_data(&pdev->dev,
 173                                     &madera_gpio->gpio_chip,
 174                                     madera_gpio);
 175        if (ret < 0) {
 176                dev_dbg(&pdev->dev, "Could not register gpiochip, %d\n", ret);
 177                return ret;
 178        }
 179
 180        /*
 181         * This is part of a composite MFD device which can only be used with
 182         * the corresponding pinctrl driver. On all supported silicon the GPIO
 183         * to pinctrl mapping is fixed in the silicon, so we register it
 184         * explicitly instead of requiring a redundant gpio-ranges in the
 185         * devicetree.
 186         * In any case we also want to work on systems that don't use devicetree
 187         * or acpi.
 188         */
 189        ret = gpiochip_add_pin_range(&madera_gpio->gpio_chip, "madera-pinctrl",
 190                                     0, 0, madera_gpio->gpio_chip.ngpio);
 191        if (ret) {
 192                dev_dbg(&pdev->dev, "Failed to add pin range (%d)\n", ret);
 193                return ret;
 194        }
 195
 196        return 0;
 197}
 198
 199static struct platform_driver madera_gpio_driver = {
 200        .driver = {
 201                .name   = "madera-gpio",
 202        },
 203        .probe          = madera_gpio_probe,
 204};
 205
 206module_platform_driver(madera_gpio_driver);
 207
 208MODULE_SOFTDEP("pre: pinctrl-madera");
 209MODULE_DESCRIPTION("GPIO interface for Madera codecs");
 210MODULE_AUTHOR("Nariman Poushin <nariman@opensource.cirrus.com>");
 211MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
 212MODULE_LICENSE("GPL v2");
 213MODULE_ALIAS("platform:madera-gpio");
 214