1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Sophgo sg2042 SoCs pinctrl driver. 4 * 5 * Copyright (C) 2024 Inochi Amaoto <inochiama@outlook.com> 6 * 7 */ 8 9 #include <linux/bitfield.h> 10 #include <linux/bits.h> 11 #include <linux/cleanup.h> 12 #include <linux/export.h> 13 #include <linux/io.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/seq_file.h> 17 #include <linux/spinlock.h> 18 19 #include <linux/pinctrl/pinconf-generic.h> 20 #include <linux/pinctrl/pinconf.h> 21 #include <linux/pinctrl/pinctrl.h> 22 #include <linux/pinctrl/pinmux.h> 23 24 #include "../pinctrl-utils.h" 25 #include "../pinmux.h" 26 27 #include "pinctrl-sg2042.h" 28 29 #define PIN_IO_PULL_ONE_ENABLE BIT(0) 30 #define PIN_IO_PULL_DIR_UP (BIT(1) | PIN_IO_PULL_ONE_ENABLE) 31 #define PIN_IO_PULL_DIR_DOWN (0 | PIN_IO_PULL_ONE_ENABLE) 32 #define PIN_IO_PULL_ONE_MASK GENMASK(1, 0) 33 34 #define PIN_IO_PULL_UP BIT(2) 35 #define PIN_IO_PULL_UP_DONW BIT(3) 36 #define PIN_IO_PULL_UP_MASK GENMASK(3, 2) 37 38 #define PIN_IO_MUX GENMASK(5, 4) 39 #define PIN_IO_DRIVE GENMASK(9, 6) 40 #define PIN_IO_SCHMITT_ENABLE BIT(10) 41 #define PIN_IO_OUTPUT_ENABLE BIT(11) 42 43 struct sg2042_priv { 44 void __iomem *regs; 45 }; 46 47 static u8 sg2042_dt_get_pin_mux(u32 value) 48 { 49 return value >> 16; 50 } 51 52 static inline u32 sg2042_get_pin_reg(struct sophgo_pinctrl *pctrl, 53 const struct sophgo_pin *sp) 54 { 55 struct sg2042_priv *priv = pctrl->priv_ctrl; 56 const struct sg2042_pin *pin = sophgo_to_sg2042_pin(sp); 57 void __iomem *reg = priv->regs + pin->offset; 58 59 if (sp->flags & PIN_FLAG_WRITE_HIGH) 60 return readl(reg) >> 16; 61 else 62 return readl(reg) & 0xffff; 63 } 64 65 static int sg2042_set_pin_reg(struct sophgo_pinctrl *pctrl, 66 const struct sophgo_pin *sp, 67 u32 value, u32 mask) 68 { 69 struct sg2042_priv *priv = pctrl->priv_ctrl; 70 const struct sg2042_pin *pin = sophgo_to_sg2042_pin(sp); 71 void __iomem *reg = priv->regs + pin->offset; 72 u32 v = readl(reg); 73 74 if (sp->flags & PIN_FLAG_WRITE_HIGH) { 75 v &= ~(mask << 16); 76 v |= value << 16; 77 } else { 78 v &= ~mask; 79 v |= value; 80 } 81 82 writel(v, reg); 83 84 return 0; 85 } 86 87 static void sg2042_pctrl_dbg_show(struct pinctrl_dev *pctldev, 88 struct seq_file *seq, unsigned int pin_id) 89 { 90 struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 91 const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id); 92 u32 value, mux; 93 94 value = sg2042_get_pin_reg(pctrl, sp); 95 mux = FIELD_GET(PIN_IO_MUX, value); 96 seq_printf(seq, "mux:%u reg:0x%04x ", mux, value); 97 } 98 99 const struct pinctrl_ops sg2042_pctrl_ops = { 100 .get_groups_count = pinctrl_generic_get_group_count, 101 .get_group_name = pinctrl_generic_get_group_name, 102 .get_group_pins = pinctrl_generic_get_group_pins, 103 .pin_dbg_show = sg2042_pctrl_dbg_show, 104 .dt_node_to_map = sophgo_pctrl_dt_node_to_map, 105 .dt_free_map = pinctrl_utils_free_map, 106 }; 107 EXPORT_SYMBOL_GPL(sg2042_pctrl_ops); 108 109 static void sg2042_set_pinmux_config(struct sophgo_pinctrl *pctrl, 110 const struct sophgo_pin *sp, u32 config) 111 { 112 u32 mux = sg2042_dt_get_pin_mux(config); 113 114 if (!(sp->flags & PIN_FLAG_NO_PINMUX)) 115 sg2042_set_pin_reg(pctrl, sp, mux, PIN_IO_MUX); 116 } 117 118 const struct pinmux_ops sg2042_pmx_ops = { 119 .get_functions_count = pinmux_generic_get_function_count, 120 .get_function_name = pinmux_generic_get_function_name, 121 .get_function_groups = pinmux_generic_get_function_groups, 122 .set_mux = sophgo_pmx_set_mux, 123 .strict = true, 124 }; 125 EXPORT_SYMBOL_GPL(sg2042_pmx_ops); 126 127 static int sg2042_pconf_get(struct pinctrl_dev *pctldev, 128 unsigned int pin_id, unsigned long *config) 129 { 130 struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); 131 int param = pinconf_to_config_param(*config); 132 const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id); 133 u32 value; 134 u32 arg; 135 bool enabled; 136 int ret; 137 138 if (!sp) 139 return -EINVAL; 140 141 value = sg2042_get_pin_reg(pctrl, sp); 142 143 switch (param) { 144 case PIN_CONFIG_BIAS_DISABLE: 145 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) 146 arg = FIELD_GET(PIN_IO_PULL_ONE_ENABLE, value); 147 else 148 arg = FIELD_GET(PIN_IO_PULL_UP_MASK, value); 149 enabled = arg == 0; 150 break; 151 case PIN_CONFIG_BIAS_PULL_DOWN: 152 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) { 153 arg = FIELD_GET(PIN_IO_PULL_ONE_MASK, value); 154 enabled = arg == PIN_IO_PULL_DIR_DOWN; 155 } else { 156 enabled = FIELD_GET(PIN_IO_PULL_UP_DONW, value) != 0; 157 } 158 arg = sophgo_pinctrl_typical_pull_down(pctrl, sp, NULL); 159 break; 160 case PIN_CONFIG_BIAS_PULL_UP: 161 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) { 162 arg = FIELD_GET(PIN_IO_PULL_ONE_MASK, value); 163 enabled = arg == PIN_IO_PULL_DIR_UP; 164 } else { 165 enabled = FIELD_GET(PIN_IO_PULL_UP, value) != 0; 166 } 167 arg = sophgo_pinctrl_typical_pull_up(pctrl, sp, NULL); 168 break; 169 case PIN_CONFIG_DRIVE_STRENGTH_UA: 170 enabled = FIELD_GET(PIN_IO_OUTPUT_ENABLE, value) != 0; 171 arg = FIELD_GET(PIN_IO_DRIVE, value); 172 ret = sophgo_pinctrl_reg2oc(pctrl, sp, NULL, arg); 173 if (ret < 0) 174 return ret; 175 arg = ret; 176 break; 177 case PIN_CONFIG_INPUT_SCHMITT_ENABLE: 178 arg = FIELD_GET(PIN_IO_SCHMITT_ENABLE, value); 179 enabled = arg != 0; 180 break; 181 default: 182 return -ENOTSUPP; 183 } 184 185 *config = pinconf_to_config_packed(param, arg); 186 187 return enabled ? 0 : -EINVAL; 188 } 189 190 static int sg2042_pinconf_compute_config(struct sophgo_pinctrl *pctrl, 191 const struct sophgo_pin *sp, 192 unsigned long *configs, 193 unsigned int num_configs, 194 u32 *value, u32 *mask) 195 { 196 int i; 197 u16 v = 0, m = 0; 198 int ret; 199 200 if (!sp) 201 return -EINVAL; 202 203 for (i = 0; i < num_configs; i++) { 204 int param = pinconf_to_config_param(configs[i]); 205 u32 arg = pinconf_to_config_argument(configs[i]); 206 207 switch (param) { 208 case PIN_CONFIG_BIAS_DISABLE: 209 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) { 210 v &= ~PIN_IO_PULL_ONE_ENABLE; 211 m |= PIN_IO_PULL_ONE_ENABLE; 212 } else { 213 v &= ~PIN_IO_PULL_UP_MASK; 214 m |= PIN_IO_PULL_UP_MASK; 215 } 216 break; 217 case PIN_CONFIG_BIAS_PULL_DOWN: 218 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) { 219 v &= ~PIN_IO_PULL_ONE_MASK; 220 v |= PIN_IO_PULL_DIR_DOWN; 221 m |= PIN_IO_PULL_ONE_MASK; 222 } else { 223 v |= PIN_IO_PULL_UP_DONW; 224 m |= PIN_IO_PULL_UP_DONW; 225 } 226 break; 227 case PIN_CONFIG_BIAS_PULL_UP: 228 if (sp->flags & PIN_FLAG_ONLY_ONE_PULL) { 229 v &= ~PIN_IO_PULL_ONE_MASK; 230 v |= PIN_IO_PULL_DIR_UP; 231 m |= PIN_IO_PULL_ONE_MASK; 232 } else { 233 v |= PIN_IO_PULL_UP; 234 m |= PIN_IO_PULL_UP; 235 } 236 break; 237 case PIN_CONFIG_DRIVE_STRENGTH_UA: 238 v &= ~(PIN_IO_DRIVE | PIN_IO_OUTPUT_ENABLE); 239 if (arg != 0) { 240 ret = sophgo_pinctrl_oc2reg(pctrl, sp, NULL, arg); 241 if (ret < 0) 242 return ret; 243 if (!(sp->flags & PIN_FLAG_NO_OEX_EN)) 244 v |= PIN_IO_OUTPUT_ENABLE; 245 v |= FIELD_PREP(PIN_IO_DRIVE, ret); 246 } 247 m |= PIN_IO_DRIVE | PIN_IO_OUTPUT_ENABLE; 248 break; 249 case PIN_CONFIG_INPUT_SCHMITT_ENABLE: 250 v |= PIN_IO_SCHMITT_ENABLE; 251 m |= PIN_IO_SCHMITT_ENABLE; 252 break; 253 default: 254 return -ENOTSUPP; 255 } 256 } 257 258 *value = v; 259 *mask = m; 260 261 return 0; 262 } 263 264 const struct pinconf_ops sg2042_pconf_ops = { 265 .pin_config_get = sg2042_pconf_get, 266 .pin_config_set = sophgo_pconf_set, 267 .pin_config_group_set = sophgo_pconf_group_set, 268 .is_generic = true, 269 }; 270 EXPORT_SYMBOL_GPL(sg2042_pconf_ops); 271 272 static int sophgo_pinctrl_init(struct platform_device *pdev, 273 struct sophgo_pinctrl *pctrl) 274 { 275 struct sg2042_priv *priv; 276 277 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 278 if (!priv) 279 return -ENOMEM; 280 281 priv->regs = devm_platform_ioremap_resource(pdev, 0); 282 if (IS_ERR(priv->regs)) 283 return PTR_ERR(priv->regs); 284 285 pctrl->priv_ctrl = priv; 286 287 return 0; 288 } 289 290 const struct sophgo_cfg_ops sg2042_cfg_ops = { 291 .pctrl_init = sophgo_pinctrl_init, 292 .compute_pinconf_config = sg2042_pinconf_compute_config, 293 .set_pinconf_config = sg2042_set_pin_reg, 294 .set_pinmux_config = sg2042_set_pinmux_config, 295 }; 296 EXPORT_SYMBOL_GPL(sg2042_cfg_ops); 297