File Index
Files and directories
Sign in
FILE

pinctrl-cv18xx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pinctrl/sophgo/pinctrl-cv18xx.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Sophgo CV18XX SoCs pinctrl driver.
  4 *
  5 * Copyright (C) 2024 Inochi Amaoto <inochiama@outlook.com>
  6 *
  7 */
  8
  9#include <linux/bitfield.h>
 10#include <linux/export.h>
 11#include <linux/io.h>
 12#include <linux/of.h>
 13#include <linux/platform_device.h>
 14#include <linux/bsearch.h>
 15#include <linux/seq_file.h>
 16#include <linux/spinlock.h>
 17
 18#include <linux/pinctrl/pinconf-generic.h>
 19#include <linux/pinctrl/pinconf.h>
 20#include <linux/pinctrl/pinctrl.h>
 21#include <linux/pinctrl/pinmux.h>
 22
 23#include <dt-bindings/pinctrl/pinctrl-cv18xx.h>
 24
 25#include "../pinctrl-utils.h"
 26#include "../pinmux.h"
 27#include "pinctrl-cv18xx.h"
 28
 29struct cv1800_priv {
 30	u32					*power_cfg;
 31	void __iomem				*regs[2];
 32};
 33
 34static unsigned int cv1800_dt_get_pin_mux(u32 value)
 35{
 36	return (value >> 16) & GENMASK(7, 0);
 37}
 38
 39static unsigned int cv1800_dt_get_pin_mux2(u32 value)
 40{
 41	return (value >> 24) & GENMASK(7, 0);
 42}
 43
 44#define cv1800_pinctrl_get_component_addr(pctrl, _comp)		\
 45	((pctrl)->regs[(_comp)->area] + (_comp)->offset)
 46
 47static int cv1800_set_power_cfg(struct sophgo_pinctrl *pctrl,
 48				u8 domain, u32 cfg)
 49{
 50	struct cv1800_priv *priv = pctrl->priv_ctrl;
 51
 52	if (domain >= pctrl->data->npds)
 53		return -ENOTSUPP;
 54
 55	if (priv->power_cfg[domain] && priv->power_cfg[domain] != cfg)
 56		return -EINVAL;
 57
 58	priv->power_cfg[domain] = cfg;
 59
 60	return 0;
 61}
 62
 63static int cv1800_get_power_cfg(struct sophgo_pinctrl *pctrl,
 64				u8 domain)
 65{
 66	struct cv1800_priv *priv = pctrl->priv_ctrl;
 67
 68	return priv->power_cfg[domain];
 69}
 70
 71#define PIN_BGA_ID_OFFSET		8
 72#define PIN_BGA_ID_MASK			0xff
 73
 74static const char *const io_type_desc[] = {
 75	"1V8",
 76	"18OD33",
 77	"AUDIO",
 78	"ETH"
 79};
 80
 81static const char *cv1800_get_power_cfg_desc(struct sophgo_pinctrl *pctrl,
 82					     u8 domain)
 83{
 84	return pctrl->data->pdnames[domain];
 85}
 86
 87static void cv1800_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	struct cv1800_priv *priv = pctrl->priv_ctrl;
 92	const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id);
 93	const struct cv1800_pin *pin = sophgo_to_cv1800_pin(sp);
 94	enum cv1800_pin_io_type type = cv1800_pin_io_type(pin);
 95	u32 pin_hwid = pin->pin.id;
 96	u32 value;
 97	void __iomem *reg;
 98
 99	if (pin_hwid >> PIN_BGA_ID_OFFSET)
100		seq_printf(seq, "pos: %c%u ",
101			   'A' + (pin_hwid >> PIN_BGA_ID_OFFSET) - 1,
102			   pin_hwid & PIN_BGA_ID_MASK);
103	else
104		seq_printf(seq, "pos: %u ", pin_hwid);
105
106	seq_printf(seq, "power-domain: %s ",
107		   cv1800_get_power_cfg_desc(pctrl, pin->power_domain));
108	seq_printf(seq, "type: %s ", io_type_desc[type]);
109
110	reg = cv1800_pinctrl_get_component_addr(priv, &pin->mux);
111	value = readl(reg);
112	seq_printf(seq, "mux: 0x%08x ", value);
113
114	if (pin->pin.flags & CV1800_PIN_HAVE_MUX2) {
115		reg = cv1800_pinctrl_get_component_addr(priv, &pin->mux2);
116		value = readl(reg);
117		seq_printf(seq, "mux2: 0x%08x ", value);
118	}
119
120	if (type == IO_TYPE_1V8_ONLY || type == IO_TYPE_1V8_OR_3V3) {
121		reg = cv1800_pinctrl_get_component_addr(priv, &pin->conf);
122		value = readl(reg);
123		seq_printf(seq, "conf: 0x%08x ", value);
124	}
125}
126
127static int cv1800_verify_pinmux_config(const struct sophgo_pin_mux_config *config)
128{
129	struct cv1800_pin *pin = sophgo_to_cv1800_pin(config->pin);
130	unsigned int mux = cv1800_dt_get_pin_mux(config->config);
131	unsigned int mux2 = cv1800_dt_get_pin_mux2(config->config);
132
133	if (mux > pin->mux.max)
134		return -EINVAL;
135
136	if (pin->pin.flags & CV1800_PIN_HAVE_MUX2) {
137		if (mux != pin->mux2.pfunc)
138			return -EINVAL;
139
140		if (mux2 > pin->mux2.max)
141			return -EINVAL;
142	} else {
143		if (mux2 != PIN_MUX_INVALD)
144			return -ENOTSUPP;
145	}
146
147	return 0;
148}
149
150static int cv1800_verify_pin_group(const struct sophgo_pin_mux_config *mux,
151				   unsigned int npins)
152{
153	struct cv1800_pin *pin;
154	enum cv1800_pin_io_type type;
155	u8 power_domain;
156	int i;
157
158	if (npins == 1)
159		return 0;
160
161	pin = sophgo_to_cv1800_pin(mux[0].pin);
162	type = cv1800_pin_io_type(pin);
163	power_domain = pin->power_domain;
164
165	for (i = 0; i < npins; i++) {
166		pin = sophgo_to_cv1800_pin(mux[i].pin);
167
168		if (type != cv1800_pin_io_type(pin) ||
169		    power_domain != pin->power_domain)
170			return -ENOTSUPP;
171	}
172
173	return 0;
174}
175
176static int cv1800_dt_node_to_map_post(struct device_node *cur,
177				      struct sophgo_pinctrl *pctrl,
178				      struct sophgo_pin_mux_config *pinmuxs,
179				      unsigned int npins)
180{
181	const struct cv1800_pin *pin = sophgo_to_cv1800_pin(pinmuxs[0].pin);
182	u32 power;
183	int ret;
184
185	ret = of_property_read_u32(cur, "power-source", &power);
186	if (ret)
187		return ret;
188
189	if (!(power == PIN_POWER_STATE_3V3 || power == PIN_POWER_STATE_1V8))
190		return -ENOTSUPP;
191
192	return cv1800_set_power_cfg(pctrl, pin->power_domain, power);
193}
194
195const struct pinctrl_ops cv1800_pctrl_ops = {
196	.get_groups_count	= pinctrl_generic_get_group_count,
197	.get_group_name		= pinctrl_generic_get_group_name,
198	.get_group_pins		= pinctrl_generic_get_group_pins,
199	.pin_dbg_show		= cv1800_pctrl_dbg_show,
200	.dt_node_to_map		= sophgo_pctrl_dt_node_to_map,
201	.dt_free_map		= pinctrl_utils_free_map,
202};
203EXPORT_SYMBOL_GPL(cv1800_pctrl_ops);
204
205static void cv1800_set_pinmux_config(struct sophgo_pinctrl *pctrl,
206				     const struct sophgo_pin *sp, u32 config)
207{
208	const struct cv1800_pin *pin = sophgo_to_cv1800_pin(sp);
209	struct cv1800_priv *priv = pctrl->priv_ctrl;
210	void __iomem *reg_mux;
211	void __iomem *reg_mux2;
212	u32 mux;
213	u32 mux2;
214
215	reg_mux = cv1800_pinctrl_get_component_addr(priv, &pin->mux);
216	reg_mux2 = cv1800_pinctrl_get_component_addr(priv, &pin->mux2);
217	mux = cv1800_dt_get_pin_mux(config);
218	mux2 = cv1800_dt_get_pin_mux2(config);
219
220	writel_relaxed(mux, reg_mux);
221	if (mux2 != PIN_MUX_INVALD)
222		writel_relaxed(mux2, reg_mux2);
223}
224
225const struct pinmux_ops cv1800_pmx_ops = {
226	.get_functions_count	= pinmux_generic_get_function_count,
227	.get_function_name	= pinmux_generic_get_function_name,
228	.get_function_groups	= pinmux_generic_get_function_groups,
229	.set_mux		= sophgo_pmx_set_mux,
230	.strict			= true,
231};
232EXPORT_SYMBOL_GPL(cv1800_pmx_ops);
233
234#define PIN_IO_PULLUP		BIT(2)
235#define PIN_IO_PULLDOWN		BIT(3)
236#define PIN_IO_DRIVE		GENMASK(7, 5)
237#define PIN_IO_SCHMITT		GENMASK(9, 8)
238#define PIN_IO_BUS_HOLD		BIT(10)
239#define PIN_IO_OUT_FAST_SLEW	BIT(11)
240
241static int cv1800_pconf_get(struct pinctrl_dev *pctldev,
242			    unsigned int pin_id, unsigned long *config)
243{
244	struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
245	struct cv1800_priv *priv = pctrl->priv_ctrl;
246	int param = pinconf_to_config_param(*config);
247	const struct sophgo_pin *sp = sophgo_get_pin(pctrl, pin_id);
248	const struct cv1800_pin *pin = sophgo_to_cv1800_pin(sp);
249	enum cv1800_pin_io_type type;
250	u32 value;
251	u32 arg;
252	bool enabled;
253	int ret;
254
255	if (!pin)
256		return -EINVAL;
257
258	type = cv1800_pin_io_type(pin);
259	if (type == IO_TYPE_ETH || type == IO_TYPE_AUDIO)
260		return -ENOTSUPP;
261
262	value = readl(cv1800_pinctrl_get_component_addr(priv, &pin->conf));
263
264	switch (param) {
265	case PIN_CONFIG_BIAS_PULL_DOWN:
266		enabled = FIELD_GET(PIN_IO_PULLDOWN, value);
267		arg = sophgo_pinctrl_typical_pull_down(pctrl, sp, priv->power_cfg);
268		break;
269	case PIN_CONFIG_BIAS_PULL_UP:
270		enabled = FIELD_GET(PIN_IO_PULLUP, value);
271		arg = sophgo_pinctrl_typical_pull_up(pctrl, sp, priv->power_cfg);
272		break;
273	case PIN_CONFIG_DRIVE_STRENGTH_UA:
274		enabled = true;
275		arg = FIELD_GET(PIN_IO_DRIVE, value);
276		ret = sophgo_pinctrl_reg2oc(pctrl, sp, priv->power_cfg, arg);
277		if (ret < 0)
278			return ret;
279		arg = ret;
280		break;
281	case PIN_CONFIG_INPUT_SCHMITT_UV:
282		arg = FIELD_GET(PIN_IO_SCHMITT, value);
283		ret = sophgo_pinctrl_reg2schmitt(pctrl, sp, priv->power_cfg, arg);
284		if (ret < 0)
285			return ret;
286		arg = ret;
287		enabled = arg != 0;
288		break;
289	case PIN_CONFIG_POWER_SOURCE:
290		enabled = true;
291		arg = cv1800_get_power_cfg(pctrl, pin->power_domain);
292		break;
293	case PIN_CONFIG_SLEW_RATE:
294		enabled = true;
295		arg = FIELD_GET(PIN_IO_OUT_FAST_SLEW, value);
296		break;
297	case PIN_CONFIG_BIAS_BUS_HOLD:
298		arg = FIELD_GET(PIN_IO_BUS_HOLD, value);
299		enabled = arg != 0;
300		break;
301	default:
302		return -ENOTSUPP;
303	}
304
305	*config = pinconf_to_config_packed(param, arg);
306
307	return enabled ? 0 : -EINVAL;
308}
309
310static int cv1800_pinconf_compute_config(struct sophgo_pinctrl *pctrl,
311					 const struct sophgo_pin *sp,
312					 unsigned long *configs,
313					 unsigned int num_configs,
314					 u32 *value, u32 *mask)
315{
316	struct cv1800_priv *priv = pctrl->priv_ctrl;
317	const struct cv1800_pin *pin = sophgo_to_cv1800_pin(sp);
318	int i;
319	u32 v = 0, m = 0;
320	enum cv1800_pin_io_type type;
321	int ret;
322
323	if (!pin)
324		return -EINVAL;
325
326	type = cv1800_pin_io_type(pin);
327	if (type == IO_TYPE_ETH || type == IO_TYPE_AUDIO)
328		return -ENOTSUPP;
329
330	for (i = 0; i < num_configs; i++) {
331		int param = pinconf_to_config_param(configs[i]);
332		u32 arg = pinconf_to_config_argument(configs[i]);
333
334		switch (param) {
335		case PIN_CONFIG_BIAS_PULL_DOWN:
336			v &= ~PIN_IO_PULLDOWN;
337			v |= FIELD_PREP(PIN_IO_PULLDOWN, arg);
338			m |= PIN_IO_PULLDOWN;
339			break;
340		case PIN_CONFIG_BIAS_PULL_UP:
341			v &= ~PIN_IO_PULLUP;
342			v |= FIELD_PREP(PIN_IO_PULLUP, arg);
343			m |= PIN_IO_PULLUP;
344			break;
345		case PIN_CONFIG_DRIVE_STRENGTH_UA:
346			ret = sophgo_pinctrl_oc2reg(pctrl, sp,
347						    priv->power_cfg, arg);
348			if (ret < 0)
349				return ret;
350			v &= ~PIN_IO_DRIVE;
351			v |= FIELD_PREP(PIN_IO_DRIVE, ret);
352			m |= PIN_IO_DRIVE;
353			break;
354		case PIN_CONFIG_INPUT_SCHMITT_UV:
355			ret = sophgo_pinctrl_schmitt2reg(pctrl, sp,
356							 priv->power_cfg, arg);
357			if (ret < 0)
358				return ret;
359			v &= ~PIN_IO_SCHMITT;
360			v |= FIELD_PREP(PIN_IO_SCHMITT, ret);
361			m |= PIN_IO_SCHMITT;
362			break;
363		case PIN_CONFIG_POWER_SOURCE:
364			/* Ignore power source as it is always fixed */
365			break;
366		case PIN_CONFIG_SLEW_RATE:
367			v &= ~PIN_IO_OUT_FAST_SLEW;
368			v |= FIELD_PREP(PIN_IO_OUT_FAST_SLEW, arg);
369			m |= PIN_IO_OUT_FAST_SLEW;
370			break;
371		case PIN_CONFIG_BIAS_BUS_HOLD:
372			v &= ~PIN_IO_BUS_HOLD;
373			v |= FIELD_PREP(PIN_IO_BUS_HOLD, arg);
374			m |= PIN_IO_BUS_HOLD;
375			break;
376		default:
377			return -ENOTSUPP;
378		}
379	}
380
381	*value = v;
382	*mask = m;
383
384	return 0;
385}
386
387static int cv1800_set_pinconf_config(struct sophgo_pinctrl *pctrl,
388				     const struct sophgo_pin *sp,
389				     u32 value, u32 mask)
390{
391	struct cv1800_priv *priv = pctrl->priv_ctrl;
392	struct cv1800_pin *pin = sophgo_to_cv1800_pin(sp);
393	void __iomem *addr;
394	u32 reg;
395
396	addr = cv1800_pinctrl_get_component_addr(priv, &pin->conf);
397
398	reg = readl(addr);
399	reg &= ~mask;
400	reg |= value;
401	writel(reg, addr);
402
403	return 0;
404}
405
406const struct pinconf_ops cv1800_pconf_ops = {
407	.pin_config_get			= cv1800_pconf_get,
408	.pin_config_set			= sophgo_pconf_set,
409	.pin_config_group_set		= sophgo_pconf_group_set,
410	.is_generic			= true,
411};
412EXPORT_SYMBOL_GPL(cv1800_pconf_ops);
413
414static int cv1800_pinctrl_init(struct platform_device *pdev,
415			       struct sophgo_pinctrl *pctrl)
416{
417	const struct sophgo_pinctrl_data *pctrl_data = pctrl->data;
418	struct cv1800_priv *priv;
419
420	priv = devm_kzalloc(&pdev->dev, sizeof(struct cv1800_priv), GFP_KERNEL);
421	if (!priv)
422		return -ENOMEM;
423
424	priv->power_cfg = devm_kcalloc(&pdev->dev, pctrl_data->npds,
425				       sizeof(u32), GFP_KERNEL);
426	if (!priv->power_cfg)
427		return -ENOMEM;
428
429	priv->regs[0] = devm_platform_ioremap_resource_byname(pdev, "sys");
430	if (IS_ERR(priv->regs[0]))
431		return PTR_ERR(priv->regs[0]);
432
433	priv->regs[1] = devm_platform_ioremap_resource_byname(pdev, "rtc");
434	if (IS_ERR(priv->regs[1]))
435		return PTR_ERR(priv->regs[1]);
436
437	pctrl->priv_ctrl = priv;
438
439	return 0;
440}
441
442const struct sophgo_cfg_ops cv1800_cfg_ops = {
443	.pctrl_init = cv1800_pinctrl_init,
444	.verify_pinmux_config = cv1800_verify_pinmux_config,
445	.verify_pin_group = cv1800_verify_pin_group,
446	.dt_node_to_map_post = cv1800_dt_node_to_map_post,
447	.compute_pinconf_config = cv1800_pinconf_compute_config,
448	.set_pinconf_config = cv1800_set_pinconf_config,
449	.set_pinmux_config = cv1800_set_pinmux_config,
450};
451EXPORT_SYMBOL_GPL(cv1800_cfg_ops);
~/ Read-only file preview.