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