root/drivers/gpio/gpio-syscon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. syscon_gpio_get
  2. syscon_gpio_set
  3. syscon_gpio_dir_in
  4. syscon_gpio_dir_out
  5. rockchip_gpio_set
  6. keystone_gpio_set
  7. syscon_gpio_probe

   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 
  40 struct 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 
  50 struct 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 
  58 static 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 
  74 static 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 
  87 static 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 
 105 static 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 
 126 static 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 
 134 static 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 
 153 static 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 
 163 static 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 
 183 static 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 
 192 static 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 };
 207 MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
 208 
 209 static 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 
 268 static 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 };
 275 module_platform_driver(syscon_gpio_driver);
 276 
 277 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
 278 MODULE_DESCRIPTION("SYSCON GPIO driver");
 279 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */