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"); 280