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