File Index
Files and directories
Sign in
FILE

pinctrl-sophgo-common.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pinctrl/sophgo/pinctrl-sophgo-common.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Sophgo SoCs pinctrl common ops.
  4 *
  5 * Copyright (C) 2024 Inochi Amaoto <inochiama@outlook.com>
  6 *
  7 */
  8
  9#include <linux/bsearch.h>
 10#include <linux/cleanup.h>
 11#include <linux/export.h>
 12#include <linux/io.h>
 13#include <linux/of.h>
 14#include <linux/module.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/pinctrl.h>
 21
 22#include "../pinctrl-utils.h"
 23#include "../pinconf.h"
 24#include "../pinmux.h"
 25
 26#include "pinctrl-sophgo.h"
 27
 28static u16 sophgo_dt_get_pin(u32 value)
 29{
 30	return value;
 31}
 32
 33static int sophgo_cmp_pin(const void *key, const void *pivot)
 34{
 35	const struct sophgo_pin *pin = pivot;
 36	int pin_id = (long)key;
 37	int pivid = pin->id;
 38
 39	return pin_id - pivid;
 40}
 41
 42const struct sophgo_pin *sophgo_get_pin(struct sophgo_pinctrl *pctrl,
 43					unsigned long pin_id)
 44{
 45	return bsearch((void *)pin_id, pctrl->data->pindata, pctrl->data->npins,
 46		       pctrl->data->pinsize, sophgo_cmp_pin);
 47}
 48
 49static int sophgo_verify_pinmux_config(struct sophgo_pinctrl *pctrl,
 50				       const struct sophgo_pin_mux_config *config)
 51{
 52	if (pctrl->data->cfg_ops->verify_pinmux_config)
 53		return pctrl->data->cfg_ops->verify_pinmux_config(config);
 54	return 0;
 55}
 56
 57static int sophgo_verify_pin_group(struct sophgo_pinctrl *pctrl,
 58				   const struct sophgo_pin_mux_config *config,
 59				   unsigned int npins)
 60{
 61	if (pctrl->data->cfg_ops->verify_pin_group)
 62		return pctrl->data->cfg_ops->verify_pin_group(config, npins);
 63	return 0;
 64}
 65
 66static int sophgo_dt_node_to_map_post(struct device_node *cur,
 67				      struct sophgo_pinctrl *pctrl,
 68				      struct sophgo_pin_mux_config *config,
 69				      unsigned int npins)
 70{
 71	if (pctrl->data->cfg_ops->dt_node_to_map_post)
 72		return pctrl->data->cfg_ops->dt_node_to_map_post(cur, pctrl,
 73								 config, npins);
 74	return 0;
 75}
 76
 77int sophgo_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
 78				struct pinctrl_map **maps, unsigned int *num_maps)
 79{
 80	struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
 81	struct device *dev = pctrl->dev;
 82	struct device_node *child;
 83	struct pinctrl_map *map;
 84	const char **grpnames;
 85	const char *grpname;
 86	int ngroups = 0;
 87	int nmaps = 0;
 88	int ret;
 89
 90	for_each_available_child_of_node(np, child)
 91		ngroups += 1;
 92
 93	grpnames = devm_kcalloc(dev, ngroups, sizeof(*grpnames), GFP_KERNEL);
 94	if (!grpnames)
 95		return -ENOMEM;
 96
 97	map = kcalloc(ngroups * 2, sizeof(*map), GFP_KERNEL);
 98	if (!map)
 99		return -ENOMEM;
100
101	ngroups = 0;
102	guard(mutex)(&pctrl->mutex);
103	for_each_available_child_of_node(np, child) {
104		int npins = of_property_count_u32_elems(child, "pinmux");
105		unsigned int *pins;
106		struct sophgo_pin_mux_config *pinmuxs;
107		u32 config;
108		int i;
109
110		if (npins < 1) {
111			dev_err(dev, "invalid pinctrl group %pOFn.%pOFn\n",
112				np, child);
113			ret = -EINVAL;
114			goto dt_failed;
115		}
116
117		grpname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn.%pOFn",
118					 np, child);
119		if (!grpname) {
120			ret = -ENOMEM;
121			goto dt_failed;
122		}
123
124		grpnames[ngroups++] = grpname;
125
126		pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
127		if (!pins) {
128			ret = -ENOMEM;
129			goto dt_failed;
130		}
131
132		pinmuxs = devm_kcalloc(dev, npins, sizeof(*pinmuxs), GFP_KERNEL);
133		if (!pinmuxs) {
134			ret = -ENOMEM;
135			goto dt_failed;
136		}
137
138		for (i = 0; i < npins; i++) {
139			ret = of_property_read_u32_index(child, "pinmux",
140							 i, &config);
141			if (ret)
142				goto dt_failed;
143
144			pins[i] = sophgo_dt_get_pin(config);
145			pinmuxs[i].config = config;
146			pinmuxs[i].pin = sophgo_get_pin(pctrl, pins[i]);
147
148			if (!pinmuxs[i].pin) {
149				dev_err(dev, "failed to get pin %d\n", pins[i]);
150				ret = -ENODEV;
151				goto dt_failed;
152			}
153
154			ret = sophgo_verify_pinmux_config(pctrl, &pinmuxs[i]);
155			if (ret) {
156				dev_err(dev, "group %s pin %d is invalid\n",
157					grpname, i);
158				goto dt_failed;
159			}
160		}
161
162		ret = sophgo_verify_pin_group(pctrl, pinmuxs, npins);
163		if (ret) {
164			dev_err(dev, "group %s is invalid\n", grpname);
165			goto dt_failed;
166		}
167
168		ret = sophgo_dt_node_to_map_post(child, pctrl, pinmuxs, npins);
169		if (ret)
170			goto dt_failed;
171
172		map[nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
173		map[nmaps].data.mux.function = np->name;
174		map[nmaps].data.mux.group = grpname;
175		nmaps += 1;
176
177		ret = pinconf_generic_parse_dt_config(child, pctldev,
178						      &map[nmaps].data.configs.configs,
179						      &map[nmaps].data.configs.num_configs);
180		if (ret) {
181			dev_err(dev, "failed to parse pin config of group %s: %d\n",
182				grpname, ret);
183			goto dt_failed;
184		}
185
186		ret = pinctrl_generic_add_group(pctldev, grpname,
187						pins, npins, pinmuxs);
188		if (ret < 0) {
189			dev_err(dev, "failed to add group %s: %d\n", grpname, ret);
190			goto dt_failed;
191		}
192
193		/* don't create a map if there are no pinconf settings */
194		if (map[nmaps].data.configs.num_configs == 0)
195			continue;
196
197		map[nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
198		map[nmaps].data.configs.group_or_pin = grpname;
199		nmaps += 1;
200	}
201
202	ret = pinmux_generic_add_function(pctldev, np->name,
203					  grpnames, ngroups, NULL);
204	if (ret < 0) {
205		dev_err(dev, "error adding function %s: %d\n", np->name, ret);
206		goto function_failed;
207	}
208
209	*maps = map;
210	*num_maps = nmaps;
211
212	return 0;
213
214dt_failed:
215	of_node_put(child);
216function_failed:
217	pinctrl_utils_free_map(pctldev, map, nmaps);
218	return ret;
219}
220
221int sophgo_pmx_set_mux(struct pinctrl_dev *pctldev,
222		       unsigned int fsel, unsigned int gsel)
223{
224	struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
225	const struct group_desc *group;
226	const struct sophgo_pin_mux_config *configs;
227	unsigned int i;
228
229	group = pinctrl_generic_get_group(pctldev, gsel);
230	if (!group)
231		return -EINVAL;
232
233	configs = group->data;
234
235	for (i = 0; i < group->grp.npins; i++) {
236		const struct sophgo_pin *pin = configs[i].pin;
237		u32 value = configs[i].config;
238
239		guard(raw_spinlock_irqsave)(&pctrl->lock);
240
241		pctrl->data->cfg_ops->set_pinmux_config(pctrl, pin, value);
242	}
243
244	return 0;
245}
246
247static int sophgo_pin_set_config(struct sophgo_pinctrl *pctrl,
248				 unsigned int pin_id,
249				 u32 value, u32 mask)
250{
251	const struct sophgo_pin *pin = sophgo_get_pin(pctrl, pin_id);
252
253	if (!pin)
254		return -EINVAL;
255
256	guard(raw_spinlock_irqsave)(&pctrl->lock);
257
258	return pctrl->data->cfg_ops->set_pinconf_config(pctrl, pin, value, mask);
259}
260
261int sophgo_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin_id,
262		     unsigned long *configs, unsigned int num_configs)
263{
264	struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
265	const struct sophgo_pin *pin = sophgo_get_pin(pctrl, pin_id);
266	u32 value, mask;
267
268	if (!pin)
269		return -ENODEV;
270
271	if (pctrl->data->cfg_ops->compute_pinconf_config(pctrl, pin,
272							 configs, num_configs,
273							 &value, &mask))
274		return -ENOTSUPP;
275
276	return sophgo_pin_set_config(pctrl, pin_id, value, mask);
277}
278
279int sophgo_pconf_group_set(struct pinctrl_dev *pctldev, unsigned int gsel,
280			   unsigned long *configs, unsigned int num_configs)
281{
282	struct sophgo_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
283	const struct group_desc *group;
284	const struct sophgo_pin_mux_config *pinmuxs;
285	u32 value, mask;
286	int i;
287
288	group = pinctrl_generic_get_group(pctldev, gsel);
289	if (!group)
290		return -EINVAL;
291
292	pinmuxs = group->data;
293
294	if (pctrl->data->cfg_ops->compute_pinconf_config(pctrl, pinmuxs[0].pin,
295							 configs, num_configs,
296							 &value, &mask))
297		return -ENOTSUPP;
298
299	for (i = 0; i < group->grp.npins; i++)
300		sophgo_pin_set_config(pctrl,  group->grp.pins[i], value, mask);
301
302	return 0;
303}
304
305u32 sophgo_pinctrl_typical_pull_down(struct sophgo_pinctrl *pctrl,
306				     const struct sophgo_pin *pin,
307				     const u32 *power_cfg)
308{
309	return pctrl->data->vddio_ops->get_pull_down(pin, power_cfg);
310}
311
312u32 sophgo_pinctrl_typical_pull_up(struct sophgo_pinctrl *pctrl,
313				   const struct sophgo_pin *pin,
314				   const u32 *power_cfg)
315{
316	return pctrl->data->vddio_ops->get_pull_up(pin, power_cfg);
317}
318
319int sophgo_pinctrl_oc2reg(struct sophgo_pinctrl *pctrl,
320			  const struct sophgo_pin *pin,
321			  const u32 *power_cfg, u32 target)
322{
323	const u32 *map;
324	int i, len;
325
326	if (!pctrl->data->vddio_ops->get_oc_map)
327		return -ENOTSUPP;
328
329	len = pctrl->data->vddio_ops->get_oc_map(pin, power_cfg, &map);
330	if (len < 0)
331		return len;
332
333	for (i = 0; i < len; i++) {
334		if (map[i] >= target)
335			return i;
336	}
337
338	return -EINVAL;
339}
340
341int sophgo_pinctrl_reg2oc(struct sophgo_pinctrl *pctrl,
342			  const struct sophgo_pin *pin,
343			  const u32 *power_cfg, u32 reg)
344{
345	const u32 *map;
346	int len;
347
348	if (!pctrl->data->vddio_ops->get_oc_map)
349		return -ENOTSUPP;
350
351	len = pctrl->data->vddio_ops->get_oc_map(pin, power_cfg, &map);
352	if (len < 0)
353		return len;
354
355	if (reg >= len)
356		return -EINVAL;
357
358	return map[reg];
359}
360
361int sophgo_pinctrl_schmitt2reg(struct sophgo_pinctrl *pctrl,
362			       const struct sophgo_pin *pin,
363			       const u32 *power_cfg, u32 target)
364{
365	const u32 *map;
366	int i, len;
367
368	if (!pctrl->data->vddio_ops->get_schmitt_map)
369		return -ENOTSUPP;
370
371	len = pctrl->data->vddio_ops->get_schmitt_map(pin, power_cfg, &map);
372	if (len < 0)
373		return len;
374
375	for (i = 0; i < len; i++) {
376		if (map[i] == target)
377			return i;
378	}
379
380	return -EINVAL;
381}
382
383int sophgo_pinctrl_reg2schmitt(struct sophgo_pinctrl *pctrl,
384			       const struct sophgo_pin *pin,
385			       const u32 *power_cfg, u32 reg)
386{
387	const u32 *map;
388	int len;
389
390	if (!pctrl->data->vddio_ops->get_schmitt_map)
391		return -ENOTSUPP;
392
393	len = pctrl->data->vddio_ops->get_schmitt_map(pin, power_cfg, &map);
394	if (len < 0)
395		return len;
396
397	if (reg >= len)
398		return -EINVAL;
399
400	return map[reg];
401}
402
403int sophgo_pinctrl_probe(struct platform_device *pdev)
404{
405	struct device *dev = &pdev->dev;
406	struct sophgo_pinctrl *pctrl;
407	const struct sophgo_pinctrl_data *pctrl_data;
408	int ret;
409
410	pctrl_data = device_get_match_data(dev);
411	if (!pctrl_data)
412		return -ENODEV;
413
414	if (pctrl_data->npins == 0)
415		return dev_err_probe(dev, -EINVAL, "invalid pin data\n");
416
417	pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
418	if (!pctrl)
419		return -ENOMEM;
420
421	pctrl->pdesc.name = dev_name(dev);
422	pctrl->pdesc.pins = pctrl_data->pins;
423	pctrl->pdesc.npins = pctrl_data->npins;
424	pctrl->pdesc.pctlops = pctrl_data->pctl_ops;
425	pctrl->pdesc.pmxops = pctrl_data->pmx_ops;
426	pctrl->pdesc.confops = pctrl_data->pconf_ops;
427	pctrl->pdesc.owner = THIS_MODULE;
428
429	pctrl->data = pctrl_data;
430	pctrl->dev = dev;
431	raw_spin_lock_init(&pctrl->lock);
432	mutex_init(&pctrl->mutex);
433
434	ret = pctrl->data->cfg_ops->pctrl_init(pdev, pctrl);
435	if (ret)
436		return ret;
437
438	platform_set_drvdata(pdev, pctrl);
439
440	ret = devm_pinctrl_register_and_init(dev, &pctrl->pdesc,
441					     pctrl, &pctrl->pctrl_dev);
442	if (ret)
443		return dev_err_probe(dev, ret,
444				     "fail to register pinctrl driver\n");
445
446	return pinctrl_enable(pctrl->pctrl_dev);
447}
448EXPORT_SYMBOL_GPL(sophgo_pinctrl_probe);
449
450MODULE_DESCRIPTION("Common pinctrl helper function for the Sophgo SoC");
451MODULE_LICENSE("GPL");
~/ Read-only file preview.