18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright 2012 Freescale Semiconductor, Inc. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/err.h> 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/of_address.h> 108c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 118c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 138c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include "../core.h" 178c2ecf20Sopenharmony_ci#include "pinctrl-mxs.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define SUFFIX_LEN 4 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct mxs_pinctrl_data { 228c2ecf20Sopenharmony_ci struct device *dev; 238c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl; 248c2ecf20Sopenharmony_ci void __iomem *base; 258c2ecf20Sopenharmony_ci struct mxs_pinctrl_soc_data *soc; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int mxs_get_groups_count(struct pinctrl_dev *pctldev) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return d->soc->ngroups; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const char *mxs_get_group_name(struct pinctrl_dev *pctldev, 368c2ecf20Sopenharmony_ci unsigned group) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return d->soc->groups[group].name; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int mxs_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, 448c2ecf20Sopenharmony_ci const unsigned **pins, unsigned *num_pins) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci *pins = d->soc->groups[group].pins; 498c2ecf20Sopenharmony_ci *num_pins = d->soc->groups[group].npins; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void mxs_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, 558c2ecf20Sopenharmony_ci unsigned offset) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci seq_printf(s, " %s", dev_name(pctldev->dev)); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int mxs_dt_node_to_map(struct pinctrl_dev *pctldev, 618c2ecf20Sopenharmony_ci struct device_node *np, 628c2ecf20Sopenharmony_ci struct pinctrl_map **map, unsigned *num_maps) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct pinctrl_map *new_map; 658c2ecf20Sopenharmony_ci char *group = NULL; 668c2ecf20Sopenharmony_ci unsigned new_num = 1; 678c2ecf20Sopenharmony_ci unsigned long config = 0; 688c2ecf20Sopenharmony_ci unsigned long *pconfig; 698c2ecf20Sopenharmony_ci int length = strlen(np->name) + SUFFIX_LEN; 708c2ecf20Sopenharmony_ci bool purecfg = false; 718c2ecf20Sopenharmony_ci u32 val, reg; 728c2ecf20Sopenharmony_ci int ret, i = 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Check for pin config node which has no 'reg' property */ 758c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg", ®)) 768c2ecf20Sopenharmony_ci purecfg = true; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "fsl,drive-strength", &val); 798c2ecf20Sopenharmony_ci if (!ret) 808c2ecf20Sopenharmony_ci config = val | MA_PRESENT; 818c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "fsl,voltage", &val); 828c2ecf20Sopenharmony_ci if (!ret) 838c2ecf20Sopenharmony_ci config |= val << VOL_SHIFT | VOL_PRESENT; 848c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "fsl,pull-up", &val); 858c2ecf20Sopenharmony_ci if (!ret) 868c2ecf20Sopenharmony_ci config |= val << PULL_SHIFT | PULL_PRESENT; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Check for group node which has both mux and config settings */ 898c2ecf20Sopenharmony_ci if (!purecfg && config) 908c2ecf20Sopenharmony_ci new_num = 2; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci new_map = kcalloc(new_num, sizeof(*new_map), GFP_KERNEL); 938c2ecf20Sopenharmony_ci if (!new_map) 948c2ecf20Sopenharmony_ci return -ENOMEM; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!purecfg) { 978c2ecf20Sopenharmony_ci new_map[i].type = PIN_MAP_TYPE_MUX_GROUP; 988c2ecf20Sopenharmony_ci new_map[i].data.mux.function = np->name; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Compose group name */ 1018c2ecf20Sopenharmony_ci group = kzalloc(length, GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (!group) { 1038c2ecf20Sopenharmony_ci ret = -ENOMEM; 1048c2ecf20Sopenharmony_ci goto free; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci snprintf(group, length, "%s.%d", np->name, reg); 1078c2ecf20Sopenharmony_ci new_map[i].data.mux.group = group; 1088c2ecf20Sopenharmony_ci i++; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (config) { 1128c2ecf20Sopenharmony_ci pconfig = kmemdup(&config, sizeof(config), GFP_KERNEL); 1138c2ecf20Sopenharmony_ci if (!pconfig) { 1148c2ecf20Sopenharmony_ci ret = -ENOMEM; 1158c2ecf20Sopenharmony_ci goto free_group; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci new_map[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; 1198c2ecf20Sopenharmony_ci new_map[i].data.configs.group_or_pin = purecfg ? np->name : 1208c2ecf20Sopenharmony_ci group; 1218c2ecf20Sopenharmony_ci new_map[i].data.configs.configs = pconfig; 1228c2ecf20Sopenharmony_ci new_map[i].data.configs.num_configs = 1; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci *map = new_map; 1268c2ecf20Sopenharmony_ci *num_maps = new_num; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cifree_group: 1318c2ecf20Sopenharmony_ci if (!purecfg) 1328c2ecf20Sopenharmony_ci kfree(group); 1338c2ecf20Sopenharmony_cifree: 1348c2ecf20Sopenharmony_ci kfree(new_map); 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void mxs_dt_free_map(struct pinctrl_dev *pctldev, 1398c2ecf20Sopenharmony_ci struct pinctrl_map *map, unsigned num_maps) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci u32 i; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (i = 0; i < num_maps; i++) { 1448c2ecf20Sopenharmony_ci if (map[i].type == PIN_MAP_TYPE_MUX_GROUP) 1458c2ecf20Sopenharmony_ci kfree(map[i].data.mux.group); 1468c2ecf20Sopenharmony_ci if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) 1478c2ecf20Sopenharmony_ci kfree(map[i].data.configs.configs); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci kfree(map); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct pinctrl_ops mxs_pinctrl_ops = { 1548c2ecf20Sopenharmony_ci .get_groups_count = mxs_get_groups_count, 1558c2ecf20Sopenharmony_ci .get_group_name = mxs_get_group_name, 1568c2ecf20Sopenharmony_ci .get_group_pins = mxs_get_group_pins, 1578c2ecf20Sopenharmony_ci .pin_dbg_show = mxs_pin_dbg_show, 1588c2ecf20Sopenharmony_ci .dt_node_to_map = mxs_dt_node_to_map, 1598c2ecf20Sopenharmony_ci .dt_free_map = mxs_dt_free_map, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int mxs_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return d->soc->nfunctions; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const char *mxs_pinctrl_get_func_name(struct pinctrl_dev *pctldev, 1708c2ecf20Sopenharmony_ci unsigned function) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return d->soc->functions[function].name; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int mxs_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, 1788c2ecf20Sopenharmony_ci unsigned group, 1798c2ecf20Sopenharmony_ci const char * const **groups, 1808c2ecf20Sopenharmony_ci unsigned * const num_groups) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci *groups = d->soc->functions[group].groups; 1858c2ecf20Sopenharmony_ci *num_groups = d->soc->functions[group].ngroups; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void mxs_pinctrl_rmwl(u32 value, u32 mask, u8 shift, void __iomem *reg) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u32 tmp; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci tmp = readl(reg); 1958c2ecf20Sopenharmony_ci tmp &= ~(mask << shift); 1968c2ecf20Sopenharmony_ci tmp |= value << shift; 1978c2ecf20Sopenharmony_ci writel(tmp, reg); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int mxs_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned selector, 2018c2ecf20Sopenharmony_ci unsigned group) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 2048c2ecf20Sopenharmony_ci struct mxs_group *g = &d->soc->groups[group]; 2058c2ecf20Sopenharmony_ci void __iomem *reg; 2068c2ecf20Sopenharmony_ci u8 bank, shift; 2078c2ecf20Sopenharmony_ci u16 pin; 2088c2ecf20Sopenharmony_ci u32 i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < g->npins; i++) { 2118c2ecf20Sopenharmony_ci bank = PINID_TO_BANK(g->pins[i]); 2128c2ecf20Sopenharmony_ci pin = PINID_TO_PIN(g->pins[i]); 2138c2ecf20Sopenharmony_ci reg = d->base + d->soc->regs->muxsel; 2148c2ecf20Sopenharmony_ci reg += bank * 0x20 + pin / 16 * 0x10; 2158c2ecf20Sopenharmony_ci shift = pin % 16 * 2; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci mxs_pinctrl_rmwl(g->muxsel[i], 0x3, shift, reg); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic const struct pinmux_ops mxs_pinmux_ops = { 2248c2ecf20Sopenharmony_ci .get_functions_count = mxs_pinctrl_get_funcs_count, 2258c2ecf20Sopenharmony_ci .get_function_name = mxs_pinctrl_get_func_name, 2268c2ecf20Sopenharmony_ci .get_function_groups = mxs_pinctrl_get_func_groups, 2278c2ecf20Sopenharmony_ci .set_mux = mxs_pinctrl_set_mux, 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int mxs_pinconf_get(struct pinctrl_dev *pctldev, 2318c2ecf20Sopenharmony_ci unsigned pin, unsigned long *config) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci return -ENOTSUPP; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int mxs_pinconf_set(struct pinctrl_dev *pctldev, 2378c2ecf20Sopenharmony_ci unsigned pin, unsigned long *configs, 2388c2ecf20Sopenharmony_ci unsigned num_configs) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return -ENOTSUPP; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int mxs_pinconf_group_get(struct pinctrl_dev *pctldev, 2448c2ecf20Sopenharmony_ci unsigned group, unsigned long *config) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci *config = d->soc->groups[group].config; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int mxs_pinconf_group_set(struct pinctrl_dev *pctldev, 2548c2ecf20Sopenharmony_ci unsigned group, unsigned long *configs, 2558c2ecf20Sopenharmony_ci unsigned num_configs) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); 2588c2ecf20Sopenharmony_ci struct mxs_group *g = &d->soc->groups[group]; 2598c2ecf20Sopenharmony_ci void __iomem *reg; 2608c2ecf20Sopenharmony_ci u8 ma, vol, pull, bank, shift; 2618c2ecf20Sopenharmony_ci u16 pin; 2628c2ecf20Sopenharmony_ci u32 i; 2638c2ecf20Sopenharmony_ci int n; 2648c2ecf20Sopenharmony_ci unsigned long config; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci for (n = 0; n < num_configs; n++) { 2678c2ecf20Sopenharmony_ci config = configs[n]; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ma = CONFIG_TO_MA(config); 2708c2ecf20Sopenharmony_ci vol = CONFIG_TO_VOL(config); 2718c2ecf20Sopenharmony_ci pull = CONFIG_TO_PULL(config); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci for (i = 0; i < g->npins; i++) { 2748c2ecf20Sopenharmony_ci bank = PINID_TO_BANK(g->pins[i]); 2758c2ecf20Sopenharmony_ci pin = PINID_TO_PIN(g->pins[i]); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* drive */ 2788c2ecf20Sopenharmony_ci reg = d->base + d->soc->regs->drive; 2798c2ecf20Sopenharmony_ci reg += bank * 0x40 + pin / 8 * 0x10; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* mA */ 2828c2ecf20Sopenharmony_ci if (config & MA_PRESENT) { 2838c2ecf20Sopenharmony_ci shift = pin % 8 * 4; 2848c2ecf20Sopenharmony_ci mxs_pinctrl_rmwl(ma, 0x3, shift, reg); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* vol */ 2888c2ecf20Sopenharmony_ci if (config & VOL_PRESENT) { 2898c2ecf20Sopenharmony_ci shift = pin % 8 * 4 + 2; 2908c2ecf20Sopenharmony_ci if (vol) 2918c2ecf20Sopenharmony_ci writel(1 << shift, reg + SET); 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci writel(1 << shift, reg + CLR); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* pull */ 2978c2ecf20Sopenharmony_ci if (config & PULL_PRESENT) { 2988c2ecf20Sopenharmony_ci reg = d->base + d->soc->regs->pull; 2998c2ecf20Sopenharmony_ci reg += bank * 0x10; 3008c2ecf20Sopenharmony_ci shift = pin; 3018c2ecf20Sopenharmony_ci if (pull) 3028c2ecf20Sopenharmony_ci writel(1 << shift, reg + SET); 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci writel(1 << shift, reg + CLR); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* cache the config value for mxs_pinconf_group_get() */ 3098c2ecf20Sopenharmony_ci g->config = config; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci } /* for each config */ 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void mxs_pinconf_dbg_show(struct pinctrl_dev *pctldev, 3178c2ecf20Sopenharmony_ci struct seq_file *s, unsigned pin) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci /* Not support */ 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void mxs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 3238c2ecf20Sopenharmony_ci struct seq_file *s, unsigned group) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci unsigned long config; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!mxs_pinconf_group_get(pctldev, group, &config)) 3288c2ecf20Sopenharmony_ci seq_printf(s, "0x%lx", config); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct pinconf_ops mxs_pinconf_ops = { 3328c2ecf20Sopenharmony_ci .pin_config_get = mxs_pinconf_get, 3338c2ecf20Sopenharmony_ci .pin_config_set = mxs_pinconf_set, 3348c2ecf20Sopenharmony_ci .pin_config_group_get = mxs_pinconf_group_get, 3358c2ecf20Sopenharmony_ci .pin_config_group_set = mxs_pinconf_group_set, 3368c2ecf20Sopenharmony_ci .pin_config_dbg_show = mxs_pinconf_dbg_show, 3378c2ecf20Sopenharmony_ci .pin_config_group_dbg_show = mxs_pinconf_group_dbg_show, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic struct pinctrl_desc mxs_pinctrl_desc = { 3418c2ecf20Sopenharmony_ci .pctlops = &mxs_pinctrl_ops, 3428c2ecf20Sopenharmony_ci .pmxops = &mxs_pinmux_ops, 3438c2ecf20Sopenharmony_ci .confops = &mxs_pinconf_ops, 3448c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int mxs_pinctrl_parse_group(struct platform_device *pdev, 3488c2ecf20Sopenharmony_ci struct device_node *np, int idx, 3498c2ecf20Sopenharmony_ci const char **out_name) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d = platform_get_drvdata(pdev); 3528c2ecf20Sopenharmony_ci struct mxs_group *g = &d->soc->groups[idx]; 3538c2ecf20Sopenharmony_ci struct property *prop; 3548c2ecf20Sopenharmony_ci const char *propname = "fsl,pinmux-ids"; 3558c2ecf20Sopenharmony_ci char *group; 3568c2ecf20Sopenharmony_ci int length = strlen(np->name) + SUFFIX_LEN; 3578c2ecf20Sopenharmony_ci u32 val, i; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci group = devm_kzalloc(&pdev->dev, length, GFP_KERNEL); 3608c2ecf20Sopenharmony_ci if (!group) 3618c2ecf20Sopenharmony_ci return -ENOMEM; 3628c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg", &val)) 3638c2ecf20Sopenharmony_ci snprintf(group, length, "%s", np->name); 3648c2ecf20Sopenharmony_ci else 3658c2ecf20Sopenharmony_ci snprintf(group, length, "%s.%d", np->name, val); 3668c2ecf20Sopenharmony_ci g->name = group; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci prop = of_find_property(np, propname, &length); 3698c2ecf20Sopenharmony_ci if (!prop) 3708c2ecf20Sopenharmony_ci return -EINVAL; 3718c2ecf20Sopenharmony_ci g->npins = length / sizeof(u32); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci g->pins = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->pins), 3748c2ecf20Sopenharmony_ci GFP_KERNEL); 3758c2ecf20Sopenharmony_ci if (!g->pins) 3768c2ecf20Sopenharmony_ci return -ENOMEM; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci g->muxsel = devm_kcalloc(&pdev->dev, g->npins, sizeof(*g->muxsel), 3798c2ecf20Sopenharmony_ci GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (!g->muxsel) 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci of_property_read_u32_array(np, propname, g->pins, g->npins); 3848c2ecf20Sopenharmony_ci for (i = 0; i < g->npins; i++) { 3858c2ecf20Sopenharmony_ci g->muxsel[i] = MUXID_TO_MUXSEL(g->pins[i]); 3868c2ecf20Sopenharmony_ci g->pins[i] = MUXID_TO_PINID(g->pins[i]); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (out_name) 3908c2ecf20Sopenharmony_ci *out_name = g->name; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int mxs_pinctrl_probe_dt(struct platform_device *pdev, 3968c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct mxs_pinctrl_soc_data *soc = d->soc; 3998c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4008c2ecf20Sopenharmony_ci struct device_node *child; 4018c2ecf20Sopenharmony_ci struct mxs_function *f; 4028c2ecf20Sopenharmony_ci const char *gpio_compat = "fsl,mxs-gpio"; 4038c2ecf20Sopenharmony_ci const char *fn, *fnull = ""; 4048c2ecf20Sopenharmony_ci int i = 0, idxf = 0, idxg = 0; 4058c2ecf20Sopenharmony_ci int ret; 4068c2ecf20Sopenharmony_ci u32 val; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci child = of_get_next_child(np, NULL); 4098c2ecf20Sopenharmony_ci if (!child) { 4108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no group is defined\n"); 4118c2ecf20Sopenharmony_ci return -ENOENT; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Count total functions and groups */ 4158c2ecf20Sopenharmony_ci fn = fnull; 4168c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 4178c2ecf20Sopenharmony_ci if (of_device_is_compatible(child, gpio_compat)) 4188c2ecf20Sopenharmony_ci continue; 4198c2ecf20Sopenharmony_ci soc->ngroups++; 4208c2ecf20Sopenharmony_ci /* Skip pure pinconf node */ 4218c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &val)) 4228c2ecf20Sopenharmony_ci continue; 4238c2ecf20Sopenharmony_ci if (strcmp(fn, child->name)) { 4248c2ecf20Sopenharmony_ci fn = child->name; 4258c2ecf20Sopenharmony_ci soc->nfunctions++; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci soc->functions = devm_kcalloc(&pdev->dev, 4308c2ecf20Sopenharmony_ci soc->nfunctions, 4318c2ecf20Sopenharmony_ci sizeof(*soc->functions), 4328c2ecf20Sopenharmony_ci GFP_KERNEL); 4338c2ecf20Sopenharmony_ci if (!soc->functions) 4348c2ecf20Sopenharmony_ci return -ENOMEM; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci soc->groups = devm_kcalloc(&pdev->dev, 4378c2ecf20Sopenharmony_ci soc->ngroups, sizeof(*soc->groups), 4388c2ecf20Sopenharmony_ci GFP_KERNEL); 4398c2ecf20Sopenharmony_ci if (!soc->groups) 4408c2ecf20Sopenharmony_ci return -ENOMEM; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Count groups for each function */ 4438c2ecf20Sopenharmony_ci fn = fnull; 4448c2ecf20Sopenharmony_ci f = &soc->functions[idxf]; 4458c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 4468c2ecf20Sopenharmony_ci if (of_device_is_compatible(child, gpio_compat)) 4478c2ecf20Sopenharmony_ci continue; 4488c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &val)) 4498c2ecf20Sopenharmony_ci continue; 4508c2ecf20Sopenharmony_ci if (strcmp(fn, child->name)) { 4518c2ecf20Sopenharmony_ci struct device_node *child2; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * This reference is dropped by 4558c2ecf20Sopenharmony_ci * of_get_next_child(np, * child) 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci of_node_get(child); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * The logic parsing the functions from dt currently 4618c2ecf20Sopenharmony_ci * doesn't handle if functions with the same name are 4628c2ecf20Sopenharmony_ci * not grouped together. Only the first contiguous 4638c2ecf20Sopenharmony_ci * cluster is usable for each function name. This is a 4648c2ecf20Sopenharmony_ci * bug that is not trivial to fix, but at least warn 4658c2ecf20Sopenharmony_ci * about it. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci for (child2 = of_get_next_child(np, child); 4688c2ecf20Sopenharmony_ci child2 != NULL; 4698c2ecf20Sopenharmony_ci child2 = of_get_next_child(np, child2)) { 4708c2ecf20Sopenharmony_ci if (!strcmp(child2->name, fn)) 4718c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 4728c2ecf20Sopenharmony_ci "function nodes must be grouped by name (failed for: %s)", 4738c2ecf20Sopenharmony_ci fn); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci f = &soc->functions[idxf++]; 4778c2ecf20Sopenharmony_ci f->name = fn = child->name; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci f->ngroups++; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Get groups for each function */ 4838c2ecf20Sopenharmony_ci idxf = 0; 4848c2ecf20Sopenharmony_ci fn = fnull; 4858c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 4868c2ecf20Sopenharmony_ci if (of_device_is_compatible(child, gpio_compat)) 4878c2ecf20Sopenharmony_ci continue; 4888c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &val)) { 4898c2ecf20Sopenharmony_ci ret = mxs_pinctrl_parse_group(pdev, child, 4908c2ecf20Sopenharmony_ci idxg++, NULL); 4918c2ecf20Sopenharmony_ci if (ret) { 4928c2ecf20Sopenharmony_ci of_node_put(child); 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci continue; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (strcmp(fn, child->name)) { 4998c2ecf20Sopenharmony_ci f = &soc->functions[idxf++]; 5008c2ecf20Sopenharmony_ci f->groups = devm_kcalloc(&pdev->dev, 5018c2ecf20Sopenharmony_ci f->ngroups, 5028c2ecf20Sopenharmony_ci sizeof(*f->groups), 5038c2ecf20Sopenharmony_ci GFP_KERNEL); 5048c2ecf20Sopenharmony_ci if (!f->groups) { 5058c2ecf20Sopenharmony_ci of_node_put(child); 5068c2ecf20Sopenharmony_ci return -ENOMEM; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci fn = child->name; 5098c2ecf20Sopenharmony_ci i = 0; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci ret = mxs_pinctrl_parse_group(pdev, child, idxg++, 5128c2ecf20Sopenharmony_ci &f->groups[i++]); 5138c2ecf20Sopenharmony_ci if (ret) { 5148c2ecf20Sopenharmony_ci of_node_put(child); 5158c2ecf20Sopenharmony_ci return ret; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ciint mxs_pinctrl_probe(struct platform_device *pdev, 5238c2ecf20Sopenharmony_ci struct mxs_pinctrl_soc_data *soc) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 5268c2ecf20Sopenharmony_ci struct mxs_pinctrl_data *d; 5278c2ecf20Sopenharmony_ci int ret; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); 5308c2ecf20Sopenharmony_ci if (!d) 5318c2ecf20Sopenharmony_ci return -ENOMEM; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci d->dev = &pdev->dev; 5348c2ecf20Sopenharmony_ci d->soc = soc; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci d->base = of_iomap(np, 0); 5378c2ecf20Sopenharmony_ci if (!d->base) 5388c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci mxs_pinctrl_desc.pins = d->soc->pins; 5418c2ecf20Sopenharmony_ci mxs_pinctrl_desc.npins = d->soc->npins; 5428c2ecf20Sopenharmony_ci mxs_pinctrl_desc.name = dev_name(&pdev->dev); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, d); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ret = mxs_pinctrl_probe_dt(pdev, d); 5478c2ecf20Sopenharmony_ci if (ret) { 5488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "dt probe failed: %d\n", ret); 5498c2ecf20Sopenharmony_ci goto err; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci d->pctl = pinctrl_register(&mxs_pinctrl_desc, &pdev->dev, d); 5538c2ecf20Sopenharmony_ci if (IS_ERR(d->pctl)) { 5548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register MXS pinctrl driver\n"); 5558c2ecf20Sopenharmony_ci ret = PTR_ERR(d->pctl); 5568c2ecf20Sopenharmony_ci goto err; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cierr: 5628c2ecf20Sopenharmony_ci iounmap(d->base); 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci} 565