linux/drivers/gpio/gpio-syscon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  SYSCON GPIO driver
   4 *
   5 *  Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
   6 */
   7
   8#include <linux/err.h>
   9#include <linux/gpio/driver.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/of_device.h>
  13#include <linux/platform_device.h>
  14#include <linux/regmap.h>
  15#include <linux/mfd/syscon.h>
  16
  17#define GPIO_SYSCON_FEAT_IN     BIT(0)
  18#define GPIO_SYSCON_FEAT_OUT    BIT(1)
  19#define GPIO_SYSCON_FEAT_DIR    BIT(2)
  20
  21/* SYSCON driver is designed to use 32-bit wide registers */
  22#define SYSCON_REG_SIZE         (4)
  23#define SYSCON_REG_BITS         (SYSCON_REG_SIZE * 8)
  24
  25/**
  26 * struct syscon_gpio_data - Configuration for the device.
  27 * @compatible:         SYSCON driver compatible string.
  28 * @flags:              Set of GPIO_SYSCON_FEAT_ flags:
  29 *                      GPIO_SYSCON_FEAT_IN:    GPIOs supports input,
  30 *                      GPIO_SYSCON_FEAT_OUT:   GPIOs supports output,
  31 *                      GPIO_SYSCON_FEAT_DIR:   GPIOs supports switch direction.
  32 * @bit_count:          Number of bits used as GPIOs.
  33 * @dat_bit_offset:     Offset (in bits) to the first GPIO bit.
  34 * @dir_bit_offset:     Optional offset (in bits) to the first bit to switch
  35 *                      GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
  36 * @set:                HW specific callback to assigns output value
  37 *                      for signal "offset"
  38 */
  39
  40struct syscon_gpio_data {
  41        const char      *compatible;
  42        unsigned int    flags;
  43        unsigned int    bit_count;
  44        unsigned int    dat_bit_offset;
  45        unsigned int    dir_bit_offset;
  46        void            (*set)(struct gpio_chip *chip,
  47                               unsigned offset, int value);
  48};
  49
  50struct syscon_gpio_priv {
  51        struct gpio_chip                chip;
  52        struct regmap                   *syscon;
  53        const struct syscon_gpio_data   *data;
  54        u32                             dreg_offset;
  55        u32                             dir_reg_offset;
  56};
  57
  58static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
  59{
  60        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
  61        unsigned int val, offs;
  62        int ret;
  63
  64        offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
  65
  66        ret = regmap_read(priv->syscon,
  67                          (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
  68        if (ret)
  69                return ret;
  70
  71        return !!(val & BIT(offs % SYSCON_REG_BITS));
  72}
  73
  74static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
  75{
  76        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
  77        unsigned int offs;
  78
  79        offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
  80
  81        regmap_update_bits(priv->syscon,
  82                           (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
  83                           BIT(offs % SYSCON_REG_BITS),
  84                           val ? BIT(offs % SYSCON_REG_BITS) : 0);
  85}
  86
  87static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
  88{
  89        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
  90
  91        if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
  92                unsigned int offs;
  93
  94                offs = priv->dir_reg_offset +
  95                       priv->data->dir_bit_offset + offset;
  96
  97                regmap_update_bits(priv->syscon,
  98                                   (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
  99                                   BIT(offs % SYSCON_REG_BITS), 0);
 100        }
 101
 102        return 0;
 103}
 104
 105static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
 106{
 107        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
 108
 109        if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
 110                unsigned int offs;
 111
 112                offs = priv->dir_reg_offset +
 113                       priv->data->dir_bit_offset + offset;
 114
 115                regmap_update_bits(priv->syscon,
 116                                   (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
 117                                   BIT(offs % SYSCON_REG_BITS),
 118                                   BIT(offs % SYSCON_REG_BITS));
 119        }
 120
 121        chip->set(chip, offset, val);
 122
 123        return 0;
 124}
 125
 126static const struct syscon_gpio_data clps711x_mctrl_gpio = {
 127        /* ARM CLPS711X SYSFLG1 Bits 8-10 */
 128        .compatible     = "cirrus,ep7209-syscon1",
 129        .flags          = GPIO_SYSCON_FEAT_IN,
 130        .bit_count      = 3,
 131        .dat_bit_offset = 0x40 * 8 + 8,
 132};
 133
 134static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset,
 135                              int val)
 136{
 137        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
 138        unsigned int offs;
 139        u8 bit;
 140        u32 data;
 141        int ret;
 142
 143        offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
 144        bit = offs % SYSCON_REG_BITS;
 145        data = (val ? BIT(bit) : 0) | BIT(bit + 16);
 146        ret = regmap_write(priv->syscon,
 147                           (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
 148                           data);
 149        if (ret < 0)
 150                dev_err(chip->parent, "gpio write failed ret(%d)\n", ret);
 151}
 152
 153static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = {
 154        /* RK3328 GPIO_MUTE is an output only pin at GRF_SOC_CON10[1] */
 155        .flags          = GPIO_SYSCON_FEAT_OUT,
 156        .bit_count      = 1,
 157        .dat_bit_offset = 0x0428 * 8 + 1,
 158        .set            = rockchip_gpio_set,
 159};
 160
 161#define KEYSTONE_LOCK_BIT BIT(0)
 162
 163static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
 164{
 165        struct syscon_gpio_priv *priv = gpiochip_get_data(chip);
 166        unsigned int offs;
 167        int ret;
 168
 169        offs = priv->dreg_offset + priv->data->dat_bit_offset + offset;
 170
 171        if (!val)
 172                return;
 173
 174        ret = regmap_update_bits(
 175                        priv->syscon,
 176                        (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
 177                        BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT,
 178                        BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT);
 179        if (ret < 0)
 180                dev_err(chip->parent, "gpio write failed ret(%d)\n", ret);
 181}
 182
 183static const struct syscon_gpio_data keystone_dsp_gpio = {
 184        /* ARM Keystone 2 */
 185        .compatible     = NULL,
 186        .flags          = GPIO_SYSCON_FEAT_OUT,
 187        .bit_count      = 28,
 188        .dat_bit_offset = 4,
 189        .set            = keystone_gpio_set,
 190};
 191
 192static const struct of_device_id syscon_gpio_ids[] = {
 193        {
 194                .compatible     = "cirrus,ep7209-mctrl-gpio",
 195                .data           = &clps711x_mctrl_gpio,
 196        },
 197        {
 198                .compatible     = "ti,keystone-dsp-gpio",
 199                .data           = &keystone_dsp_gpio,
 200        },
 201        {
 202                .compatible     = "rockchip,rk3328-grf-gpio",
 203                .data           = &rockchip_rk3328_gpio_mute,
 204        },
 205        { }
 206};
 207MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
 208
 209static int syscon_gpio_probe(struct platform_device *pdev)
 210{
 211        struct device *dev = &pdev->dev;
 212        struct syscon_gpio_priv *priv;
 213        struct device_node *np = dev->of_node;
 214        int ret;
 215
 216        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 217        if (!priv)
 218                return -ENOMEM;
 219
 220        priv->data = of_device_get_match_data(dev);
 221
 222        if (priv->data->compatible) {
 223                priv->syscon = syscon_regmap_lookup_by_compatible(
 224                                        priv->data->compatible);
 225                if (IS_ERR(priv->syscon))
 226                        return PTR_ERR(priv->syscon);
 227        } else {
 228                priv->syscon =
 229                        syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
 230                if (IS_ERR(priv->syscon) && np->parent)
 231                        priv->syscon = syscon_node_to_regmap(np->parent);
 232                if (IS_ERR(priv->syscon))
 233                        return PTR_ERR(priv->syscon);
 234
 235                ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
 236                                                 &priv->dreg_offset);
 237                if (ret)
 238                        dev_err(dev, "can't read the data register offset!\n");
 239
 240                priv->dreg_offset <<= 3;
 241
 242                ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
 243                                                 &priv->dir_reg_offset);
 244                if (ret)
 245                        dev_dbg(dev, "can't read the dir register offset!\n");
 246
 247                priv->dir_reg_offset <<= 3;
 248        }
 249
 250        priv->chip.parent = dev;
 251        priv->chip.owner = THIS_MODULE;
 252        priv->chip.label = dev_name(dev);
 253        priv->chip.base = -1;
 254        priv->chip.ngpio = priv->data->bit_count;
 255        priv->chip.get = syscon_gpio_get;
 256        if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
 257                priv->chip.direction_input = syscon_gpio_dir_in;
 258        if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
 259                priv->chip.set = priv->data->set ? : syscon_gpio_set;
 260                priv->chip.direction_output = syscon_gpio_dir_out;
 261        }
 262
 263        platform_set_drvdata(pdev, priv);
 264
 265        return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
 266}
 267
 268static struct platform_driver syscon_gpio_driver = {
 269        .driver = {
 270                .name           = "gpio-syscon",
 271                .of_match_table = syscon_gpio_ids,
 272        },
 273        .probe  = syscon_gpio_probe,
 274};
 275module_platform_driver(syscon_gpio_driver);
 276
 277MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 278MODULE_DESCRIPTION("SYSCON GPIO driver");
 279MODULE_LICENSE("GPL");
 280