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