162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright 2012 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/err.h>
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <linux/of_address.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/seq_file.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/pinctrl/machine.h>
1562306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
1662306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
1762306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../core.h"
2062306a36Sopenharmony_ci#include "pinctrl-mxs.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define SUFFIX_LEN	4
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct mxs_pinctrl_data {
2562306a36Sopenharmony_ci	struct device *dev;
2662306a36Sopenharmony_ci	struct pinctrl_dev *pctl;
2762306a36Sopenharmony_ci	void __iomem *base;
2862306a36Sopenharmony_ci	struct mxs_pinctrl_soc_data *soc;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int mxs_get_groups_count(struct pinctrl_dev *pctldev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return d->soc->ngroups;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const char *mxs_get_group_name(struct pinctrl_dev *pctldev,
3962306a36Sopenharmony_ci				      unsigned group)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return d->soc->groups[group].name;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int mxs_get_group_pins(struct pinctrl_dev *pctldev, unsigned group,
4762306a36Sopenharmony_ci			      const unsigned **pins, unsigned *num_pins)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	*pins = d->soc->groups[group].pins;
5262306a36Sopenharmony_ci	*num_pins = d->soc->groups[group].npins;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void mxs_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
5862306a36Sopenharmony_ci			     unsigned offset)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	seq_printf(s, " %s", dev_name(pctldev->dev));
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int mxs_dt_node_to_map(struct pinctrl_dev *pctldev,
6462306a36Sopenharmony_ci			      struct device_node *np,
6562306a36Sopenharmony_ci			      struct pinctrl_map **map, unsigned *num_maps)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct pinctrl_map *new_map;
6862306a36Sopenharmony_ci	char *group = NULL;
6962306a36Sopenharmony_ci	unsigned new_num = 1;
7062306a36Sopenharmony_ci	unsigned long config = 0;
7162306a36Sopenharmony_ci	unsigned long *pconfig;
7262306a36Sopenharmony_ci	int length = strlen(np->name) + SUFFIX_LEN;
7362306a36Sopenharmony_ci	bool purecfg = false;
7462306a36Sopenharmony_ci	u32 val, reg;
7562306a36Sopenharmony_ci	int ret, i = 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Check for pin config node which has no 'reg' property */
7862306a36Sopenharmony_ci	if (of_property_read_u32(np, "reg", &reg))
7962306a36Sopenharmony_ci		purecfg = true;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ret = of_property_read_u32(np, "fsl,drive-strength", &val);
8262306a36Sopenharmony_ci	if (!ret)
8362306a36Sopenharmony_ci		config = val | MA_PRESENT;
8462306a36Sopenharmony_ci	ret = of_property_read_u32(np, "fsl,voltage", &val);
8562306a36Sopenharmony_ci	if (!ret)
8662306a36Sopenharmony_ci		config |= val << VOL_SHIFT | VOL_PRESENT;
8762306a36Sopenharmony_ci	ret = of_property_read_u32(np, "fsl,pull-up", &val);
8862306a36Sopenharmony_ci	if (!ret)
8962306a36Sopenharmony_ci		config |= val << PULL_SHIFT | PULL_PRESENT;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* Check for group node which has both mux and config settings */
9262306a36Sopenharmony_ci	if (!purecfg && config)
9362306a36Sopenharmony_ci		new_num = 2;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	new_map = kcalloc(new_num, sizeof(*new_map), GFP_KERNEL);
9662306a36Sopenharmony_ci	if (!new_map)
9762306a36Sopenharmony_ci		return -ENOMEM;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!purecfg) {
10062306a36Sopenharmony_ci		new_map[i].type = PIN_MAP_TYPE_MUX_GROUP;
10162306a36Sopenharmony_ci		new_map[i].data.mux.function = np->name;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		/* Compose group name */
10462306a36Sopenharmony_ci		group = kzalloc(length, GFP_KERNEL);
10562306a36Sopenharmony_ci		if (!group) {
10662306a36Sopenharmony_ci			ret = -ENOMEM;
10762306a36Sopenharmony_ci			goto free;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci		snprintf(group, length, "%s.%d", np->name, reg);
11062306a36Sopenharmony_ci		new_map[i].data.mux.group = group;
11162306a36Sopenharmony_ci		i++;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (config) {
11562306a36Sopenharmony_ci		pconfig = kmemdup(&config, sizeof(config), GFP_KERNEL);
11662306a36Sopenharmony_ci		if (!pconfig) {
11762306a36Sopenharmony_ci			ret = -ENOMEM;
11862306a36Sopenharmony_ci			goto free_group;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		new_map[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
12262306a36Sopenharmony_ci		new_map[i].data.configs.group_or_pin = purecfg ? np->name :
12362306a36Sopenharmony_ci								 group;
12462306a36Sopenharmony_ci		new_map[i].data.configs.configs = pconfig;
12562306a36Sopenharmony_ci		new_map[i].data.configs.num_configs = 1;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	*map = new_map;
12962306a36Sopenharmony_ci	*num_maps = new_num;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cifree_group:
13462306a36Sopenharmony_ci	if (!purecfg)
13562306a36Sopenharmony_ci		kfree(group);
13662306a36Sopenharmony_cifree:
13762306a36Sopenharmony_ci	kfree(new_map);
13862306a36Sopenharmony_ci	return ret;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void mxs_dt_free_map(struct pinctrl_dev *pctldev,
14262306a36Sopenharmony_ci			    struct pinctrl_map *map, unsigned num_maps)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	u32 i;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (i = 0; i < num_maps; i++) {
14762306a36Sopenharmony_ci		if (map[i].type == PIN_MAP_TYPE_MUX_GROUP)
14862306a36Sopenharmony_ci			kfree(map[i].data.mux.group);
14962306a36Sopenharmony_ci		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
15062306a36Sopenharmony_ci			kfree(map[i].data.configs.configs);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	kfree(map);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic const struct pinctrl_ops mxs_pinctrl_ops = {
15762306a36Sopenharmony_ci	.get_groups_count = mxs_get_groups_count,
15862306a36Sopenharmony_ci	.get_group_name = mxs_get_group_name,
15962306a36Sopenharmony_ci	.get_group_pins = mxs_get_group_pins,
16062306a36Sopenharmony_ci	.pin_dbg_show = mxs_pin_dbg_show,
16162306a36Sopenharmony_ci	.dt_node_to_map = mxs_dt_node_to_map,
16262306a36Sopenharmony_ci	.dt_free_map = mxs_dt_free_map,
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int mxs_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return d->soc->nfunctions;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic const char *mxs_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
17362306a36Sopenharmony_ci					     unsigned function)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return d->soc->functions[function].name;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int mxs_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
18162306a36Sopenharmony_ci				       unsigned group,
18262306a36Sopenharmony_ci				       const char * const **groups,
18362306a36Sopenharmony_ci				       unsigned * const num_groups)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	*groups = d->soc->functions[group].groups;
18862306a36Sopenharmony_ci	*num_groups = d->soc->functions[group].ngroups;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return 0;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	u32 tmp;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	tmp = readl(reg);
19862306a36Sopenharmony_ci	tmp &= ~(mask << shift);
19962306a36Sopenharmony_ci	tmp |= value << shift;
20062306a36Sopenharmony_ci	writel(tmp, reg);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
20462306a36Sopenharmony_ci			       unsigned group)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
20762306a36Sopenharmony_ci	struct mxs_group *g = &d->soc->groups[group];
20862306a36Sopenharmony_ci	void __iomem *reg;
20962306a36Sopenharmony_ci	u8 bank, shift;
21062306a36Sopenharmony_ci	u16 pin;
21162306a36Sopenharmony_ci	u32 i;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i = 0; i < g->npins; i++) {
21462306a36Sopenharmony_ci		bank = PINID_TO_BANK(g->pins[i]);
21562306a36Sopenharmony_ci		pin = PINID_TO_PIN(g->pins[i]);
21662306a36Sopenharmony_ci		reg = d->base + d->soc->regs->muxsel;
21762306a36Sopenharmony_ci		reg += bank * 0x20 + pin / 16 * 0x10;
21862306a36Sopenharmony_ci		shift = pin % 16 * 2;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic const struct pinmux_ops mxs_pinmux_ops = {
22762306a36Sopenharmony_ci	.get_functions_count = mxs_pinctrl_get_funcs_count,
22862306a36Sopenharmony_ci	.get_function_name = mxs_pinctrl_get_func_name,
22962306a36Sopenharmony_ci	.get_function_groups = mxs_pinctrl_get_func_groups,
23062306a36Sopenharmony_ci	.set_mux = mxs_pinctrl_set_mux,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int mxs_pinconf_get(struct pinctrl_dev *pctldev,
23462306a36Sopenharmony_ci			   unsigned pin, unsigned long *config)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return -ENOTSUPP;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int mxs_pinconf_set(struct pinctrl_dev *pctldev,
24062306a36Sopenharmony_ci			   unsigned pin, unsigned long *configs,
24162306a36Sopenharmony_ci			   unsigned num_configs)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	return -ENOTSUPP;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int mxs_pinconf_group_get(struct pinctrl_dev *pctldev,
24762306a36Sopenharmony_ci				 unsigned group, unsigned long *config)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	*config = d->soc->groups[group].config;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int mxs_pinconf_group_set(struct pinctrl_dev *pctldev,
25762306a36Sopenharmony_ci				 unsigned group, unsigned long *configs,
25862306a36Sopenharmony_ci				 unsigned num_configs)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev);
26162306a36Sopenharmony_ci	struct mxs_group *g = &d->soc->groups[group];
26262306a36Sopenharmony_ci	void __iomem *reg;
26362306a36Sopenharmony_ci	u8 ma, vol, pull, bank, shift;
26462306a36Sopenharmony_ci	u16 pin;
26562306a36Sopenharmony_ci	u32 i;
26662306a36Sopenharmony_ci	int n;
26762306a36Sopenharmony_ci	unsigned long config;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	for (n = 0; n < num_configs; n++) {
27062306a36Sopenharmony_ci		config = configs[n];
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		ma = PIN_CONFIG_TO_MA(config);
27362306a36Sopenharmony_ci		vol = PIN_CONFIG_TO_VOL(config);
27462306a36Sopenharmony_ci		pull = PIN_CONFIG_TO_PULL(config);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		for (i = 0; i < g->npins; i++) {
27762306a36Sopenharmony_ci			bank = PINID_TO_BANK(g->pins[i]);
27862306a36Sopenharmony_ci			pin = PINID_TO_PIN(g->pins[i]);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			/* drive */
28162306a36Sopenharmony_ci			reg = d->base + d->soc->regs->drive;
28262306a36Sopenharmony_ci			reg += bank * 0x40 + pin / 8 * 0x10;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci			/* mA */
28562306a36Sopenharmony_ci			if (config & MA_PRESENT) {
28662306a36Sopenharmony_ci				shift = pin % 8 * 4;
28762306a36Sopenharmony_ci				mxs_pinctrl_rmwl(ma, 0x3, shift, reg);
28862306a36Sopenharmony_ci			}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci			/* vol */
29162306a36Sopenharmony_ci			if (config & VOL_PRESENT) {
29262306a36Sopenharmony_ci				shift = pin % 8 * 4 + 2;
29362306a36Sopenharmony_ci				if (vol)
29462306a36Sopenharmony_ci					writel(1 << shift, reg + SET);
29562306a36Sopenharmony_ci				else
29662306a36Sopenharmony_ci					writel(1 << shift, reg + CLR);
29762306a36Sopenharmony_ci			}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			/* pull */
30062306a36Sopenharmony_ci			if (config & PULL_PRESENT) {
30162306a36Sopenharmony_ci				reg = d->base + d->soc->regs->pull;
30262306a36Sopenharmony_ci				reg += bank * 0x10;
30362306a36Sopenharmony_ci				shift = pin;
30462306a36Sopenharmony_ci				if (pull)
30562306a36Sopenharmony_ci					writel(1 << shift, reg + SET);
30662306a36Sopenharmony_ci				else
30762306a36Sopenharmony_ci					writel(1 << shift, reg + CLR);
30862306a36Sopenharmony_ci			}
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		/* cache the config value for mxs_pinconf_group_get() */
31262306a36Sopenharmony_ci		g->config = config;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	} /* for each config */
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void mxs_pinconf_dbg_show(struct pinctrl_dev *pctldev,
32062306a36Sopenharmony_ci				 struct seq_file *s, unsigned pin)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	/* Not support */
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void mxs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
32662306a36Sopenharmony_ci				       struct seq_file *s, unsigned group)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	unsigned long config;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!mxs_pinconf_group_get(pctldev, group, &config))
33162306a36Sopenharmony_ci		seq_printf(s, "0x%lx", config);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic const struct pinconf_ops mxs_pinconf_ops = {
33562306a36Sopenharmony_ci	.pin_config_get = mxs_pinconf_get,
33662306a36Sopenharmony_ci	.pin_config_set = mxs_pinconf_set,
33762306a36Sopenharmony_ci	.pin_config_group_get = mxs_pinconf_group_get,
33862306a36Sopenharmony_ci	.pin_config_group_set = mxs_pinconf_group_set,
33962306a36Sopenharmony_ci	.pin_config_dbg_show = mxs_pinconf_dbg_show,
34062306a36Sopenharmony_ci	.pin_config_group_dbg_show = mxs_pinconf_group_dbg_show,
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic struct pinctrl_desc mxs_pinctrl_desc = {
34462306a36Sopenharmony_ci	.pctlops = &mxs_pinctrl_ops,
34562306a36Sopenharmony_ci	.pmxops = &mxs_pinmux_ops,
34662306a36Sopenharmony_ci	.confops = &mxs_pinconf_ops,
34762306a36Sopenharmony_ci	.owner = THIS_MODULE,
34862306a36Sopenharmony_ci};
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int mxs_pinctrl_parse_group(struct platform_device *pdev,
35162306a36Sopenharmony_ci				   struct device_node *np, int idx,
35262306a36Sopenharmony_ci				   const char **out_name)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct mxs_pinctrl_data *d = platform_get_drvdata(pdev);
35562306a36Sopenharmony_ci	struct mxs_group *g = &d->soc->groups[idx];
35662306a36Sopenharmony_ci	struct property *prop;
35762306a36Sopenharmony_ci	const char *propname = "fsl,pinmux-ids";
35862306a36Sopenharmony_ci	char *group;
35962306a36Sopenharmony_ci	int length = strlen(np->name) + SUFFIX_LEN;
36062306a36Sopenharmony_ci	u32 val, i;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	group = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
36362306a36Sopenharmony_ci	if (!group)
36462306a36Sopenharmony_ci		return -ENOMEM;
36562306a36Sopenharmony_ci	if (of_property_read_u32(np, "reg", &val))
36662306a36Sopenharmony_ci		snprintf(group, length, "%s", np->name);
36762306a36Sopenharmony_ci	else
36862306a36Sopenharmony_ci		snprintf(group, length, "%s.%d", np->name, val);
36962306a36Sopenharmony_ci	g->name = group;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	prop = of_find_property(np, propname, &length);
37262306a36Sopenharmony_ci	if (!prop)
37362306a36Sopenharmony_ci		return -EINVAL;
37462306a36Sopenharmony_ci	g->npins = length / sizeof(u32);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	g->pins = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->pins),
37762306a36Sopenharmony_ci			       GFP_KERNEL);
37862306a36Sopenharmony_ci	if (!g->pins)
37962306a36Sopenharmony_ci		return -ENOMEM;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	g->muxsel = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->muxsel),
38262306a36Sopenharmony_ci				 GFP_KERNEL);
38362306a36Sopenharmony_ci	if (!g->muxsel)
38462306a36Sopenharmony_ci		return -ENOMEM;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	of_property_read_u32_array(np, propname, g->pins, g->npins);
38762306a36Sopenharmony_ci	for (i = 0; i < g->npins; i++) {
38862306a36Sopenharmony_ci		g->muxsel[i] = MUXID_TO_MUXSEL(g->pins[i]);
38962306a36Sopenharmony_ci		g->pins[i] = MUXID_TO_PINID(g->pins[i]);
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (out_name)
39362306a36Sopenharmony_ci		*out_name = g->name;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int mxs_pinctrl_probe_dt(struct platform_device *pdev,
39962306a36Sopenharmony_ci				struct mxs_pinctrl_data *d)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct mxs_pinctrl_soc_data *soc = d->soc;
40262306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
40362306a36Sopenharmony_ci	struct device_node *child;
40462306a36Sopenharmony_ci	struct mxs_function *f;
40562306a36Sopenharmony_ci	const char *gpio_compat = "fsl,mxs-gpio";
40662306a36Sopenharmony_ci	const char *fn, *fnull = "";
40762306a36Sopenharmony_ci	int i = 0, idxf = 0, idxg = 0;
40862306a36Sopenharmony_ci	int ret;
40962306a36Sopenharmony_ci	u32 val;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	child = of_get_next_child(np, NULL);
41262306a36Sopenharmony_ci	if (!child) {
41362306a36Sopenharmony_ci		dev_err(&pdev->dev, "no group is defined\n");
41462306a36Sopenharmony_ci		return -ENOENT;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Count total functions and groups */
41862306a36Sopenharmony_ci	fn = fnull;
41962306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
42062306a36Sopenharmony_ci		if (of_device_is_compatible(child, gpio_compat))
42162306a36Sopenharmony_ci			continue;
42262306a36Sopenharmony_ci		soc->ngroups++;
42362306a36Sopenharmony_ci		/* Skip pure pinconf node */
42462306a36Sopenharmony_ci		if (of_property_read_u32(child, "reg", &val))
42562306a36Sopenharmony_ci			continue;
42662306a36Sopenharmony_ci		if (strcmp(fn, child->name)) {
42762306a36Sopenharmony_ci			fn = child->name;
42862306a36Sopenharmony_ci			soc->nfunctions++;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	soc->functions = devm_kcalloc(&pdev->dev,
43362306a36Sopenharmony_ci				      soc->nfunctions,
43462306a36Sopenharmony_ci				      sizeof(*soc->functions),
43562306a36Sopenharmony_ci				      GFP_KERNEL);
43662306a36Sopenharmony_ci	if (!soc->functions)
43762306a36Sopenharmony_ci		return -ENOMEM;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	soc->groups = devm_kcalloc(&pdev->dev,
44062306a36Sopenharmony_ci				   soc->ngroups, sizeof(*soc->groups),
44162306a36Sopenharmony_ci				   GFP_KERNEL);
44262306a36Sopenharmony_ci	if (!soc->groups)
44362306a36Sopenharmony_ci		return -ENOMEM;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Count groups for each function */
44662306a36Sopenharmony_ci	fn = fnull;
44762306a36Sopenharmony_ci	f = &soc->functions[idxf];
44862306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
44962306a36Sopenharmony_ci		if (of_device_is_compatible(child, gpio_compat))
45062306a36Sopenharmony_ci			continue;
45162306a36Sopenharmony_ci		if (of_property_read_u32(child, "reg", &val))
45262306a36Sopenharmony_ci			continue;
45362306a36Sopenharmony_ci		if (strcmp(fn, child->name)) {
45462306a36Sopenharmony_ci			struct device_node *child2;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci			/*
45762306a36Sopenharmony_ci			 * This reference is dropped by
45862306a36Sopenharmony_ci			 * of_get_next_child(np, * child)
45962306a36Sopenharmony_ci			 */
46062306a36Sopenharmony_ci			of_node_get(child);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci			/*
46362306a36Sopenharmony_ci			 * The logic parsing the functions from dt currently
46462306a36Sopenharmony_ci			 * doesn't handle if functions with the same name are
46562306a36Sopenharmony_ci			 * not grouped together. Only the first contiguous
46662306a36Sopenharmony_ci			 * cluster is usable for each function name. This is a
46762306a36Sopenharmony_ci			 * bug that is not trivial to fix, but at least warn
46862306a36Sopenharmony_ci			 * about it.
46962306a36Sopenharmony_ci			 */
47062306a36Sopenharmony_ci			for (child2 = of_get_next_child(np, child);
47162306a36Sopenharmony_ci			     child2 != NULL;
47262306a36Sopenharmony_ci			     child2 = of_get_next_child(np, child2)) {
47362306a36Sopenharmony_ci				if (!strcmp(child2->name, fn))
47462306a36Sopenharmony_ci					dev_warn(&pdev->dev,
47562306a36Sopenharmony_ci						 "function nodes must be grouped by name (failed for: %s)",
47662306a36Sopenharmony_ci						 fn);
47762306a36Sopenharmony_ci			}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci			f = &soc->functions[idxf++];
48062306a36Sopenharmony_ci			f->name = fn = child->name;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci		f->ngroups++;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Get groups for each function */
48662306a36Sopenharmony_ci	idxf = 0;
48762306a36Sopenharmony_ci	fn = fnull;
48862306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
48962306a36Sopenharmony_ci		if (of_device_is_compatible(child, gpio_compat))
49062306a36Sopenharmony_ci			continue;
49162306a36Sopenharmony_ci		if (of_property_read_u32(child, "reg", &val)) {
49262306a36Sopenharmony_ci			ret = mxs_pinctrl_parse_group(pdev, child,
49362306a36Sopenharmony_ci						      idxg++, NULL);
49462306a36Sopenharmony_ci			if (ret) {
49562306a36Sopenharmony_ci				of_node_put(child);
49662306a36Sopenharmony_ci				return ret;
49762306a36Sopenharmony_ci			}
49862306a36Sopenharmony_ci			continue;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		if (strcmp(fn, child->name)) {
50262306a36Sopenharmony_ci			f = &soc->functions[idxf++];
50362306a36Sopenharmony_ci			f->groups = devm_kcalloc(&pdev->dev,
50462306a36Sopenharmony_ci						 f->ngroups,
50562306a36Sopenharmony_ci						 sizeof(*f->groups),
50662306a36Sopenharmony_ci						 GFP_KERNEL);
50762306a36Sopenharmony_ci			if (!f->groups) {
50862306a36Sopenharmony_ci				of_node_put(child);
50962306a36Sopenharmony_ci				return -ENOMEM;
51062306a36Sopenharmony_ci			}
51162306a36Sopenharmony_ci			fn = child->name;
51262306a36Sopenharmony_ci			i = 0;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci		ret = mxs_pinctrl_parse_group(pdev, child, idxg++,
51562306a36Sopenharmony_ci					      &f->groups[i++]);
51662306a36Sopenharmony_ci		if (ret) {
51762306a36Sopenharmony_ci			of_node_put(child);
51862306a36Sopenharmony_ci			return ret;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciint mxs_pinctrl_probe(struct platform_device *pdev,
52662306a36Sopenharmony_ci		      struct mxs_pinctrl_soc_data *soc)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
52962306a36Sopenharmony_ci	struct mxs_pinctrl_data *d;
53062306a36Sopenharmony_ci	int ret;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
53362306a36Sopenharmony_ci	if (!d)
53462306a36Sopenharmony_ci		return -ENOMEM;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	d->dev = &pdev->dev;
53762306a36Sopenharmony_ci	d->soc = soc;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	d->base = of_iomap(np, 0);
54062306a36Sopenharmony_ci	if (!d->base)
54162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	mxs_pinctrl_desc.pins = d->soc->pins;
54462306a36Sopenharmony_ci	mxs_pinctrl_desc.npins = d->soc->npins;
54562306a36Sopenharmony_ci	mxs_pinctrl_desc.name = dev_name(&pdev->dev);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	platform_set_drvdata(pdev, d);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = mxs_pinctrl_probe_dt(pdev, d);
55062306a36Sopenharmony_ci	if (ret) {
55162306a36Sopenharmony_ci		dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
55262306a36Sopenharmony_ci		goto err;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	d->pctl = pinctrl_register(&mxs_pinctrl_desc, &pdev->dev, d);
55662306a36Sopenharmony_ci	if (IS_ERR(d->pctl)) {
55762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't register MXS pinctrl driver\n");
55862306a36Sopenharmony_ci		ret = PTR_ERR(d->pctl);
55962306a36Sopenharmony_ci		goto err;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cierr:
56562306a36Sopenharmony_ci	iounmap(d->base);
56662306a36Sopenharmony_ci	return ret;
56762306a36Sopenharmony_ci}
568