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", ®)) 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