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
sg2042_dt_get_pin_mux(u32 value)47 static u8 sg2042_dt_get_pin_mux(u32 value)
48 {
49 return value >> 16;
50 }
51
sg2042_get_pin_reg(struct sophgo_pinctrl * pctrl,const struct sophgo_pin * sp)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
sg2042_set_pin_reg(struct sophgo_pinctrl * pctrl,const struct sophgo_pin * sp,u32 value,u32 mask)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
sg2042_pctrl_dbg_show(struct pinctrl_dev * pctldev,struct seq_file * seq,unsigned int pin_id)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
sg2042_set_pinmux_config(struct sophgo_pinctrl * pctrl,const struct sophgo_pin * sp,u32 config)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
sg2042_pconf_get(struct pinctrl_dev * pctldev,unsigned int pin_id,unsigned long * config)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
sg2042_pinconf_compute_config(struct sophgo_pinctrl * pctrl,const struct sophgo_pin * sp,unsigned long * configs,unsigned int num_configs,u32 * value,u32 * mask)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
sophgo_pinctrl_init(struct platform_device * pdev,struct sophgo_pinctrl * pctrl)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