File Index
Files and directories
Sign in
FILE

pinctrl-sg2042-ops.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pinctrl/sophgo/pinctrl-sg2042-ops.c C
  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
 43struct sg2042_priv {
 44	void __iomem				*regs;
 45};
 46
 47static u8 sg2042_dt_get_pin_mux(u32 value)
 48{
 49	return value >> 16;
 50}
 51
 52static 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
 65static 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
 87static 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
 99const 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};
107EXPORT_SYMBOL_GPL(sg2042_pctrl_ops);
108
109static 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
118const 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};
125EXPORT_SYMBOL_GPL(sg2042_pmx_ops);
126
127static 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
190static 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
264const 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};
270EXPORT_SYMBOL_GPL(sg2042_pconf_ops);
271
272static 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
290const 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};
296EXPORT_SYMBOL_GPL(sg2042_cfg_ops);
~/ Read-only file preview.