18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Core driver for the imx pin controller 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2012 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci// Copyright (C) 2012 Linaro Ltd. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Dong Aisheng <dong.aisheng@linaro.org> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 208c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 218c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/regmap.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "../core.h" 268c2ecf20Sopenharmony_ci#include "../pinconf.h" 278c2ecf20Sopenharmony_ci#include "../pinmux.h" 288c2ecf20Sopenharmony_ci#include "pinctrl-imx.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* The bits in CONFIG cell defined in binding doc*/ 318c2ecf20Sopenharmony_ci#define IMX_NO_PAD_CTL 0x80000000 /* no pin config need */ 328c2ecf20Sopenharmony_ci#define IMX_PAD_SION 0x40000000 /* set SION */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline const struct group_desc *imx_pinctrl_find_group_by_name( 358c2ecf20Sopenharmony_ci struct pinctrl_dev *pctldev, 368c2ecf20Sopenharmony_ci const char *name) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci const struct group_desc *grp = NULL; 398c2ecf20Sopenharmony_ci int i; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (i = 0; i < pctldev->num_groups; i++) { 428c2ecf20Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, i); 438c2ecf20Sopenharmony_ci if (grp && !strcmp(grp->name, name)) 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return grp; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void imx_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, 518c2ecf20Sopenharmony_ci unsigned offset) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci seq_printf(s, "%s", dev_name(pctldev->dev)); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int imx_dt_node_to_map(struct pinctrl_dev *pctldev, 578c2ecf20Sopenharmony_ci struct device_node *np, 588c2ecf20Sopenharmony_ci struct pinctrl_map **map, unsigned *num_maps) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 618c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 628c2ecf20Sopenharmony_ci const struct group_desc *grp; 638c2ecf20Sopenharmony_ci struct pinctrl_map *new_map; 648c2ecf20Sopenharmony_ci struct device_node *parent; 658c2ecf20Sopenharmony_ci struct imx_pin *pin; 668c2ecf20Sopenharmony_ci int map_num = 1; 678c2ecf20Sopenharmony_ci int i, j; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * first find the group of this node and check if we need create 718c2ecf20Sopenharmony_ci * config maps for pins 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci grp = imx_pinctrl_find_group_by_name(pctldev, np->name); 748c2ecf20Sopenharmony_ci if (!grp) { 758c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "unable to find group for node %pOFn\n", np); 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 808c2ecf20Sopenharmony_ci map_num += grp->num_pins; 818c2ecf20Sopenharmony_ci } else { 828c2ecf20Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 838c2ecf20Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 848c2ecf20Sopenharmony_ci if (!(pin->conf.mmio.config & IMX_NO_PAD_CTL)) 858c2ecf20Sopenharmony_ci map_num++; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci new_map = kmalloc_array(map_num, sizeof(struct pinctrl_map), 908c2ecf20Sopenharmony_ci GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (!new_map) 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci *map = new_map; 958c2ecf20Sopenharmony_ci *num_maps = map_num; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* create mux map */ 988c2ecf20Sopenharmony_ci parent = of_get_parent(np); 998c2ecf20Sopenharmony_ci if (!parent) { 1008c2ecf20Sopenharmony_ci kfree(new_map); 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; 1048c2ecf20Sopenharmony_ci new_map[0].data.mux.function = parent->name; 1058c2ecf20Sopenharmony_ci new_map[0].data.mux.group = np->name; 1068c2ecf20Sopenharmony_ci of_node_put(parent); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* create config map */ 1098c2ecf20Sopenharmony_ci new_map++; 1108c2ecf20Sopenharmony_ci for (i = j = 0; i < grp->num_pins; i++) { 1118c2ecf20Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * We only create config maps for SCU pads or MMIO pads that 1158c2ecf20Sopenharmony_ci * are not using the default config(a.k.a IMX_NO_PAD_CTL) 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci if (!(info->flags & IMX_USE_SCU) && 1188c2ecf20Sopenharmony_ci (pin->conf.mmio.config & IMX_NO_PAD_CTL)) 1198c2ecf20Sopenharmony_ci continue; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; 1228c2ecf20Sopenharmony_ci new_map[j].data.configs.group_or_pin = 1238c2ecf20Sopenharmony_ci pin_get_name(pctldev, pin->pin); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * For SCU case, we set mux and conf together 1288c2ecf20Sopenharmony_ci * in one IPC call 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci new_map[j].data.configs.configs = 1318c2ecf20Sopenharmony_ci (unsigned long *)&pin->conf.scu; 1328c2ecf20Sopenharmony_ci new_map[j].data.configs.num_configs = 2; 1338c2ecf20Sopenharmony_ci } else { 1348c2ecf20Sopenharmony_ci new_map[j].data.configs.configs = 1358c2ecf20Sopenharmony_ci &pin->conf.mmio.config; 1368c2ecf20Sopenharmony_ci new_map[j].data.configs.num_configs = 1; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci j++; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", 1438c2ecf20Sopenharmony_ci (*map)->data.mux.function, (*map)->data.mux.group, map_num); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void imx_dt_free_map(struct pinctrl_dev *pctldev, 1498c2ecf20Sopenharmony_ci struct pinctrl_map *map, unsigned num_maps) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci kfree(map); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic const struct pinctrl_ops imx_pctrl_ops = { 1558c2ecf20Sopenharmony_ci .get_groups_count = pinctrl_generic_get_group_count, 1568c2ecf20Sopenharmony_ci .get_group_name = pinctrl_generic_get_group_name, 1578c2ecf20Sopenharmony_ci .get_group_pins = pinctrl_generic_get_group_pins, 1588c2ecf20Sopenharmony_ci .pin_dbg_show = imx_pin_dbg_show, 1598c2ecf20Sopenharmony_ci .dt_node_to_map = imx_dt_node_to_map, 1608c2ecf20Sopenharmony_ci .dt_free_map = imx_dt_free_map, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int imx_pmx_set_one_pin_mmio(struct imx_pinctrl *ipctl, 1648c2ecf20Sopenharmony_ci struct imx_pin *pin) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 1678c2ecf20Sopenharmony_ci struct imx_pin_mmio *pin_mmio = &pin->conf.mmio; 1688c2ecf20Sopenharmony_ci const struct imx_pin_reg *pin_reg; 1698c2ecf20Sopenharmony_ci unsigned int pin_id; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pin_id = pin->pin; 1728c2ecf20Sopenharmony_ci pin_reg = &ipctl->pin_regs[pin_id]; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (pin_reg->mux_reg == -1) { 1758c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "Pin(%s) does not support mux function\n", 1768c2ecf20Sopenharmony_ci info->pins[pin_id].name); 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 1818c2ecf20Sopenharmony_ci u32 reg; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci reg = readl(ipctl->base + pin_reg->mux_reg); 1848c2ecf20Sopenharmony_ci reg &= ~info->mux_mask; 1858c2ecf20Sopenharmony_ci reg |= (pin_mmio->mux_mode << info->mux_shift); 1868c2ecf20Sopenharmony_ci writel(reg, ipctl->base + pin_reg->mux_reg); 1878c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 1888c2ecf20Sopenharmony_ci pin_reg->mux_reg, reg); 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci writel(pin_mmio->mux_mode, ipctl->base + pin_reg->mux_reg); 1918c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 1928c2ecf20Sopenharmony_ci pin_reg->mux_reg, pin_mmio->mux_mode); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * If the select input value begins with 0xff, it's a quirky 1978c2ecf20Sopenharmony_ci * select input and the value should be interpreted as below. 1988c2ecf20Sopenharmony_ci * 31 23 15 7 0 1998c2ecf20Sopenharmony_ci * | 0xff | shift | width | select | 2008c2ecf20Sopenharmony_ci * It's used to work around the problem that the select 2018c2ecf20Sopenharmony_ci * input for some pin is not implemented in the select 2028c2ecf20Sopenharmony_ci * input register but in some general purpose register. 2038c2ecf20Sopenharmony_ci * We encode the select input value, width and shift of 2048c2ecf20Sopenharmony_ci * the bit field into input_val cell of pin function ID 2058c2ecf20Sopenharmony_ci * in device tree, and then decode them here for setting 2068c2ecf20Sopenharmony_ci * up the select input bits in general purpose register. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci if (pin_mmio->input_val >> 24 == 0xff) { 2098c2ecf20Sopenharmony_ci u32 val = pin_mmio->input_val; 2108c2ecf20Sopenharmony_ci u8 select = val & 0xff; 2118c2ecf20Sopenharmony_ci u8 width = (val >> 8) & 0xff; 2128c2ecf20Sopenharmony_ci u8 shift = (val >> 16) & 0xff; 2138c2ecf20Sopenharmony_ci u32 mask = ((1 << width) - 1) << shift; 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * The input_reg[i] here is actually some IOMUXC general 2168c2ecf20Sopenharmony_ci * purpose register, not regular select input register. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci val = readl(ipctl->base + pin_mmio->input_reg); 2198c2ecf20Sopenharmony_ci val &= ~mask; 2208c2ecf20Sopenharmony_ci val |= select << shift; 2218c2ecf20Sopenharmony_ci writel(val, ipctl->base + pin_mmio->input_reg); 2228c2ecf20Sopenharmony_ci } else if (pin_mmio->input_reg) { 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * Regular select input register can never be at offset 2258c2ecf20Sopenharmony_ci * 0, and we only print register value for regular case. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci if (ipctl->input_sel_base) 2288c2ecf20Sopenharmony_ci writel(pin_mmio->input_val, ipctl->input_sel_base + 2298c2ecf20Sopenharmony_ci pin_mmio->input_reg); 2308c2ecf20Sopenharmony_ci else 2318c2ecf20Sopenharmony_ci writel(pin_mmio->input_val, ipctl->base + 2328c2ecf20Sopenharmony_ci pin_mmio->input_reg); 2338c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, 2348c2ecf20Sopenharmony_ci "==>select_input: offset 0x%x val 0x%x\n", 2358c2ecf20Sopenharmony_ci pin_mmio->input_reg, pin_mmio->input_val); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, 2428c2ecf20Sopenharmony_ci unsigned group) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 2458c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 2468c2ecf20Sopenharmony_ci struct function_desc *func; 2478c2ecf20Sopenharmony_ci struct group_desc *grp; 2488c2ecf20Sopenharmony_ci struct imx_pin *pin; 2498c2ecf20Sopenharmony_ci unsigned int npins; 2508c2ecf20Sopenharmony_ci int i, err; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Configure the mux mode for each pin in the group for a specific 2548c2ecf20Sopenharmony_ci * function. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, group); 2578c2ecf20Sopenharmony_ci if (!grp) 2588c2ecf20Sopenharmony_ci return -EINVAL; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci func = pinmux_generic_get_function(pctldev, selector); 2618c2ecf20Sopenharmony_ci if (!func) 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci npins = grp->num_pins; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "enable function %s group %s\n", 2678c2ecf20Sopenharmony_ci func->name, grp->name); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for (i = 0; i < npins; i++) { 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * For IMX_USE_SCU case, we postpone the mux setting 2728c2ecf20Sopenharmony_ci * until config is set as we can set them together 2738c2ecf20Sopenharmony_ci * in one IPC call 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 2768c2ecf20Sopenharmony_ci if (!(info->flags & IMX_USE_SCU)) { 2778c2ecf20Sopenharmony_ci err = imx_pmx_set_one_pin_mmio(ipctl, pin); 2788c2ecf20Sopenharmony_ci if (err) 2798c2ecf20Sopenharmony_ci return err; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistruct pinmux_ops imx_pmx_ops = { 2878c2ecf20Sopenharmony_ci .get_functions_count = pinmux_generic_get_function_count, 2888c2ecf20Sopenharmony_ci .get_function_name = pinmux_generic_get_function_name, 2898c2ecf20Sopenharmony_ci .get_function_groups = pinmux_generic_get_function_groups, 2908c2ecf20Sopenharmony_ci .set_mux = imx_pmx_set, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* decode generic config into raw register values */ 2948c2ecf20Sopenharmony_cistatic u32 imx_pinconf_decode_generic_config(struct imx_pinctrl *ipctl, 2958c2ecf20Sopenharmony_ci unsigned long *configs, 2968c2ecf20Sopenharmony_ci unsigned int num_configs) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 2998c2ecf20Sopenharmony_ci const struct imx_cfg_params_decode *decode; 3008c2ecf20Sopenharmony_ci enum pin_config_param param; 3018c2ecf20Sopenharmony_ci u32 raw_config = 0; 3028c2ecf20Sopenharmony_ci u32 param_val; 3038c2ecf20Sopenharmony_ci int i, j; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci WARN_ON(num_configs > info->num_decodes); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 3088c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 3098c2ecf20Sopenharmony_ci param_val = pinconf_to_config_argument(configs[i]); 3108c2ecf20Sopenharmony_ci decode = info->decodes; 3118c2ecf20Sopenharmony_ci for (j = 0; j < info->num_decodes; j++) { 3128c2ecf20Sopenharmony_ci if (param == decode->param) { 3138c2ecf20Sopenharmony_ci if (decode->invert) 3148c2ecf20Sopenharmony_ci param_val = !param_val; 3158c2ecf20Sopenharmony_ci raw_config |= (param_val << decode->shift) 3168c2ecf20Sopenharmony_ci & decode->mask; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci decode++; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (info->fixup) 3248c2ecf20Sopenharmony_ci info->fixup(configs, num_configs, &raw_config); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return raw_config; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic u32 imx_pinconf_parse_generic_config(struct device_node *np, 3308c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 3338c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl = ipctl->pctl; 3348c2ecf20Sopenharmony_ci unsigned int num_configs; 3358c2ecf20Sopenharmony_ci unsigned long *configs; 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!info->generic_pinconf) 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = pinconf_generic_parse_dt_config(np, pctl, &configs, 3428c2ecf20Sopenharmony_ci &num_configs); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return imx_pinconf_decode_generic_config(ipctl, configs, num_configs); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id, 3508c2ecf20Sopenharmony_ci unsigned long *config) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 3538c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 3548c2ecf20Sopenharmony_ci const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id]; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (pin_reg->conf_reg == -1) { 3578c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "Pin(%s) does not support config function\n", 3588c2ecf20Sopenharmony_ci info->pins[pin_id].name); 3598c2ecf20Sopenharmony_ci return -EINVAL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci *config = readl(ipctl->base + pin_reg->conf_reg); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) 3658c2ecf20Sopenharmony_ci *config &= ~info->mux_mask; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int imx_pinconf_get(struct pinctrl_dev *pctldev, 3718c2ecf20Sopenharmony_ci unsigned pin_id, unsigned long *config) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 3748c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) 3778c2ecf20Sopenharmony_ci return info->imx_pinconf_get(pctldev, pin_id, config); 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci return imx_pinconf_get_mmio(pctldev, pin_id, config); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev, 3838c2ecf20Sopenharmony_ci unsigned pin_id, unsigned long *configs, 3848c2ecf20Sopenharmony_ci unsigned num_configs) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 3878c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 3888c2ecf20Sopenharmony_ci const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id]; 3898c2ecf20Sopenharmony_ci int i; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (pin_reg->conf_reg == -1) { 3928c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "Pin(%s) does not support config function\n", 3938c2ecf20Sopenharmony_ci info->pins[pin_id].name); 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "pinconf set pin %s\n", 3988c2ecf20Sopenharmony_ci info->pins[pin_id].name); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 4018c2ecf20Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 4028c2ecf20Sopenharmony_ci u32 reg; 4038c2ecf20Sopenharmony_ci reg = readl(ipctl->base + pin_reg->conf_reg); 4048c2ecf20Sopenharmony_ci reg &= info->mux_mask; 4058c2ecf20Sopenharmony_ci reg |= configs[i]; 4068c2ecf20Sopenharmony_ci writel(reg, ipctl->base + pin_reg->conf_reg); 4078c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 4088c2ecf20Sopenharmony_ci pin_reg->conf_reg, reg); 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci writel(configs[i], ipctl->base + pin_reg->conf_reg); 4118c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", 4128c2ecf20Sopenharmony_ci pin_reg->conf_reg, configs[i]); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } /* for each config */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int imx_pinconf_set(struct pinctrl_dev *pctldev, 4208c2ecf20Sopenharmony_ci unsigned pin_id, unsigned long *configs, 4218c2ecf20Sopenharmony_ci unsigned num_configs) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 4248c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) 4278c2ecf20Sopenharmony_ci return info->imx_pinconf_set(pctldev, pin_id, 4288c2ecf20Sopenharmony_ci configs, num_configs); 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci return imx_pinconf_set_mmio(pctldev, pin_id, 4318c2ecf20Sopenharmony_ci configs, num_configs); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void imx_pinconf_dbg_show(struct pinctrl_dev *pctldev, 4358c2ecf20Sopenharmony_ci struct seq_file *s, unsigned pin_id) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 4388c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 4398c2ecf20Sopenharmony_ci const struct imx_pin_reg *pin_reg; 4408c2ecf20Sopenharmony_ci unsigned long config; 4418c2ecf20Sopenharmony_ci int ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 4448c2ecf20Sopenharmony_ci ret = info->imx_pinconf_get(pctldev, pin_id, &config); 4458c2ecf20Sopenharmony_ci if (ret) { 4468c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "failed to get %s pinconf\n", 4478c2ecf20Sopenharmony_ci pin_get_name(pctldev, pin_id)); 4488c2ecf20Sopenharmony_ci seq_puts(s, "N/A"); 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci pin_reg = &ipctl->pin_regs[pin_id]; 4538c2ecf20Sopenharmony_ci if (pin_reg->conf_reg == -1) { 4548c2ecf20Sopenharmony_ci seq_puts(s, "N/A"); 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci config = readl(ipctl->base + pin_reg->conf_reg); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci seq_printf(s, "0x%lx", config); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 4658c2ecf20Sopenharmony_ci struct seq_file *s, unsigned group) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct group_desc *grp; 4688c2ecf20Sopenharmony_ci unsigned long config; 4698c2ecf20Sopenharmony_ci const char *name; 4708c2ecf20Sopenharmony_ci int i, ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (group >= pctldev->num_groups) 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 4768c2ecf20Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, group); 4778c2ecf20Sopenharmony_ci if (!grp) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 4818c2ecf20Sopenharmony_ci struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i]; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci name = pin_get_name(pctldev, pin->pin); 4848c2ecf20Sopenharmony_ci ret = imx_pinconf_get(pctldev, pin->pin, &config); 4858c2ecf20Sopenharmony_ci if (ret) 4868c2ecf20Sopenharmony_ci return; 4878c2ecf20Sopenharmony_ci seq_printf(s, " %s: 0x%lx\n", name, config); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic const struct pinconf_ops imx_pinconf_ops = { 4928c2ecf20Sopenharmony_ci .pin_config_get = imx_pinconf_get, 4938c2ecf20Sopenharmony_ci .pin_config_set = imx_pinconf_set, 4948c2ecf20Sopenharmony_ci .pin_config_dbg_show = imx_pinconf_dbg_show, 4958c2ecf20Sopenharmony_ci .pin_config_group_dbg_show = imx_pinconf_group_dbg_show, 4968c2ecf20Sopenharmony_ci}; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* 4998c2ecf20Sopenharmony_ci * Each pin represented in fsl,pins consists of a number of u32 PIN_FUNC_ID 5008c2ecf20Sopenharmony_ci * and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for each pin. 5018c2ecf20Sopenharmony_ci * For generic_pinconf case, there's no extra u32 CONFIG. 5028c2ecf20Sopenharmony_ci * 5038c2ecf20Sopenharmony_ci * PIN_FUNC_ID format: 5048c2ecf20Sopenharmony_ci * Default: 5058c2ecf20Sopenharmony_ci * <mux_reg conf_reg input_reg mux_mode input_val> 5068c2ecf20Sopenharmony_ci * SHARE_MUX_CONF_REG: 5078c2ecf20Sopenharmony_ci * <mux_conf_reg input_reg mux_mode input_val> 5088c2ecf20Sopenharmony_ci * IMX_USE_SCU: 5098c2ecf20Sopenharmony_ci * <pin_id mux_mode> 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci#define FSL_PIN_SIZE 24 5128c2ecf20Sopenharmony_ci#define FSL_PIN_SHARE_SIZE 20 5138c2ecf20Sopenharmony_ci#define FSL_SCU_PIN_SIZE 12 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void imx_pinctrl_parse_pin_mmio(struct imx_pinctrl *ipctl, 5168c2ecf20Sopenharmony_ci unsigned int *pin_id, struct imx_pin *pin, 5178c2ecf20Sopenharmony_ci const __be32 **list_p, 5188c2ecf20Sopenharmony_ci struct device_node *np) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 5218c2ecf20Sopenharmony_ci struct imx_pin_mmio *pin_mmio = &pin->conf.mmio; 5228c2ecf20Sopenharmony_ci struct imx_pin_reg *pin_reg; 5238c2ecf20Sopenharmony_ci const __be32 *list = *list_p; 5248c2ecf20Sopenharmony_ci u32 mux_reg, conf_reg; 5258c2ecf20Sopenharmony_ci u32 config; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci mux_reg = be32_to_cpu(*list++); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) 5308c2ecf20Sopenharmony_ci mux_reg = -1; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 5338c2ecf20Sopenharmony_ci conf_reg = mux_reg; 5348c2ecf20Sopenharmony_ci } else { 5358c2ecf20Sopenharmony_ci conf_reg = be32_to_cpu(*list++); 5368c2ecf20Sopenharmony_ci if (!conf_reg) 5378c2ecf20Sopenharmony_ci conf_reg = -1; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci *pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4; 5418c2ecf20Sopenharmony_ci pin_reg = &ipctl->pin_regs[*pin_id]; 5428c2ecf20Sopenharmony_ci pin->pin = *pin_id; 5438c2ecf20Sopenharmony_ci pin_reg->mux_reg = mux_reg; 5448c2ecf20Sopenharmony_ci pin_reg->conf_reg = conf_reg; 5458c2ecf20Sopenharmony_ci pin_mmio->input_reg = be32_to_cpu(*list++); 5468c2ecf20Sopenharmony_ci pin_mmio->mux_mode = be32_to_cpu(*list++); 5478c2ecf20Sopenharmony_ci pin_mmio->input_val = be32_to_cpu(*list++); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (info->generic_pinconf) { 5508c2ecf20Sopenharmony_ci /* generic pin config decoded */ 5518c2ecf20Sopenharmony_ci pin_mmio->config = imx_pinconf_parse_generic_config(np, ipctl); 5528c2ecf20Sopenharmony_ci } else { 5538c2ecf20Sopenharmony_ci /* legacy pin config read from devicetree */ 5548c2ecf20Sopenharmony_ci config = be32_to_cpu(*list++); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* SION bit is in mux register */ 5578c2ecf20Sopenharmony_ci if (config & IMX_PAD_SION) 5588c2ecf20Sopenharmony_ci pin_mmio->mux_mode |= IOMUXC_CONFIG_SION; 5598c2ecf20Sopenharmony_ci pin_mmio->config = config & ~IMX_PAD_SION; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci *list_p = list; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "%s: 0x%x 0x%08lx", info->pins[*pin_id].name, 5658c2ecf20Sopenharmony_ci pin_mmio->mux_mode, pin_mmio->config); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int imx_pinctrl_parse_groups(struct device_node *np, 5698c2ecf20Sopenharmony_ci struct group_desc *grp, 5708c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl, 5718c2ecf20Sopenharmony_ci u32 index) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 5748c2ecf20Sopenharmony_ci struct imx_pin *pin; 5758c2ecf20Sopenharmony_ci int size, pin_size; 5768c2ecf20Sopenharmony_ci const __be32 *list; 5778c2ecf20Sopenharmony_ci int i; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci dev_dbg(ipctl->dev, "group(%d): %pOFn\n", index, np); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) 5828c2ecf20Sopenharmony_ci pin_size = FSL_SCU_PIN_SIZE; 5838c2ecf20Sopenharmony_ci else if (info->flags & SHARE_MUX_CONF_REG) 5848c2ecf20Sopenharmony_ci pin_size = FSL_PIN_SHARE_SIZE; 5858c2ecf20Sopenharmony_ci else 5868c2ecf20Sopenharmony_ci pin_size = FSL_PIN_SIZE; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (info->generic_pinconf) 5898c2ecf20Sopenharmony_ci pin_size -= 4; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* Initialise group */ 5928c2ecf20Sopenharmony_ci grp->name = np->name; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * the binding format is fsl,pins = <PIN_FUNC_ID CONFIG ...>, 5968c2ecf20Sopenharmony_ci * do sanity check and calculate pins number 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * First try legacy 'fsl,pins' property, then fall back to the 5998c2ecf20Sopenharmony_ci * generic 'pinmux'. 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Note: for generic 'pinmux' case, there's no CONFIG part in 6028c2ecf20Sopenharmony_ci * the binding format. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ci list = of_get_property(np, "fsl,pins", &size); 6058c2ecf20Sopenharmony_ci if (!list) { 6068c2ecf20Sopenharmony_ci list = of_get_property(np, "pinmux", &size); 6078c2ecf20Sopenharmony_ci if (!list) { 6088c2ecf20Sopenharmony_ci dev_err(ipctl->dev, 6098c2ecf20Sopenharmony_ci "no fsl,pins and pins property in node %pOF\n", np); 6108c2ecf20Sopenharmony_ci return -EINVAL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* we do not check return since it's safe node passed down */ 6158c2ecf20Sopenharmony_ci if (!size || size % pin_size) { 6168c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "Invalid fsl,pins or pins property in node %pOF\n", np); 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci grp->num_pins = size / pin_size; 6218c2ecf20Sopenharmony_ci grp->data = devm_kcalloc(ipctl->dev, 6228c2ecf20Sopenharmony_ci grp->num_pins, sizeof(struct imx_pin), 6238c2ecf20Sopenharmony_ci GFP_KERNEL); 6248c2ecf20Sopenharmony_ci grp->pins = devm_kcalloc(ipctl->dev, 6258c2ecf20Sopenharmony_ci grp->num_pins, sizeof(unsigned int), 6268c2ecf20Sopenharmony_ci GFP_KERNEL); 6278c2ecf20Sopenharmony_ci if (!grp->pins || !grp->data) 6288c2ecf20Sopenharmony_ci return -ENOMEM; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 6318c2ecf20Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 6328c2ecf20Sopenharmony_ci if (info->flags & IMX_USE_SCU) 6338c2ecf20Sopenharmony_ci info->imx_pinctrl_parse_pin(ipctl, &grp->pins[i], 6348c2ecf20Sopenharmony_ci pin, &list); 6358c2ecf20Sopenharmony_ci else 6368c2ecf20Sopenharmony_ci imx_pinctrl_parse_pin_mmio(ipctl, &grp->pins[i], 6378c2ecf20Sopenharmony_ci pin, &list, np); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int imx_pinctrl_parse_functions(struct device_node *np, 6448c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl, 6458c2ecf20Sopenharmony_ci u32 index) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl = ipctl->pctl; 6488c2ecf20Sopenharmony_ci struct device_node *child; 6498c2ecf20Sopenharmony_ci struct function_desc *func; 6508c2ecf20Sopenharmony_ci struct group_desc *grp; 6518c2ecf20Sopenharmony_ci u32 i = 0; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci dev_dbg(pctl->dev, "parse function(%d): %pOFn\n", index, np); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci func = pinmux_generic_get_function(pctl, index); 6568c2ecf20Sopenharmony_ci if (!func) 6578c2ecf20Sopenharmony_ci return -EINVAL; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Initialise function */ 6608c2ecf20Sopenharmony_ci func->name = np->name; 6618c2ecf20Sopenharmony_ci func->num_group_names = of_get_child_count(np); 6628c2ecf20Sopenharmony_ci if (func->num_group_names == 0) { 6638c2ecf20Sopenharmony_ci dev_err(ipctl->dev, "no groups defined in %pOF\n", np); 6648c2ecf20Sopenharmony_ci return -EINVAL; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci func->group_names = devm_kcalloc(ipctl->dev, func->num_group_names, 6678c2ecf20Sopenharmony_ci sizeof(char *), GFP_KERNEL); 6688c2ecf20Sopenharmony_ci if (!func->group_names) 6698c2ecf20Sopenharmony_ci return -ENOMEM; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 6728c2ecf20Sopenharmony_ci func->group_names[i] = child->name; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci grp = devm_kzalloc(ipctl->dev, sizeof(struct group_desc), 6758c2ecf20Sopenharmony_ci GFP_KERNEL); 6768c2ecf20Sopenharmony_ci if (!grp) { 6778c2ecf20Sopenharmony_ci of_node_put(child); 6788c2ecf20Sopenharmony_ci return -ENOMEM; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci mutex_lock(&ipctl->mutex); 6828c2ecf20Sopenharmony_ci radix_tree_insert(&pctl->pin_group_tree, 6838c2ecf20Sopenharmony_ci ipctl->group_index++, grp); 6848c2ecf20Sopenharmony_ci mutex_unlock(&ipctl->mutex); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci imx_pinctrl_parse_groups(child, grp, ipctl, i++); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * Check if the DT contains pins in the direct child nodes. This indicates the 6948c2ecf20Sopenharmony_ci * newer DT format to store pins. This function returns true if the first found 6958c2ecf20Sopenharmony_ci * fsl,pins property is in a child of np. Otherwise false is returned. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_cistatic bool imx_pinctrl_dt_is_flat_functions(struct device_node *np) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct device_node *function_np; 7008c2ecf20Sopenharmony_ci struct device_node *pinctrl_np; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci for_each_child_of_node(np, function_np) { 7038c2ecf20Sopenharmony_ci if (of_property_read_bool(function_np, "fsl,pins")) { 7048c2ecf20Sopenharmony_ci of_node_put(function_np); 7058c2ecf20Sopenharmony_ci return true; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci for_each_child_of_node(function_np, pinctrl_np) { 7098c2ecf20Sopenharmony_ci if (of_property_read_bool(pinctrl_np, "fsl,pins")) { 7108c2ecf20Sopenharmony_ci of_node_put(pinctrl_np); 7118c2ecf20Sopenharmony_ci of_node_put(function_np); 7128c2ecf20Sopenharmony_ci return false; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return true; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int imx_pinctrl_probe_dt(struct platform_device *pdev, 7218c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 7248c2ecf20Sopenharmony_ci struct device_node *child; 7258c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl = ipctl->pctl; 7268c2ecf20Sopenharmony_ci u32 nfuncs = 0; 7278c2ecf20Sopenharmony_ci u32 i = 0; 7288c2ecf20Sopenharmony_ci bool flat_funcs; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (!np) 7318c2ecf20Sopenharmony_ci return -ENODEV; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci flat_funcs = imx_pinctrl_dt_is_flat_functions(np); 7348c2ecf20Sopenharmony_ci if (flat_funcs) { 7358c2ecf20Sopenharmony_ci nfuncs = 1; 7368c2ecf20Sopenharmony_ci } else { 7378c2ecf20Sopenharmony_ci nfuncs = of_get_child_count(np); 7388c2ecf20Sopenharmony_ci if (nfuncs == 0) { 7398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no functions defined\n"); 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci for (i = 0; i < nfuncs; i++) { 7458c2ecf20Sopenharmony_ci struct function_desc *function; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci function = devm_kzalloc(&pdev->dev, sizeof(*function), 7488c2ecf20Sopenharmony_ci GFP_KERNEL); 7498c2ecf20Sopenharmony_ci if (!function) 7508c2ecf20Sopenharmony_ci return -ENOMEM; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci mutex_lock(&ipctl->mutex); 7538c2ecf20Sopenharmony_ci radix_tree_insert(&pctl->pin_function_tree, i, function); 7548c2ecf20Sopenharmony_ci mutex_unlock(&ipctl->mutex); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci pctl->num_functions = nfuncs; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci ipctl->group_index = 0; 7598c2ecf20Sopenharmony_ci if (flat_funcs) { 7608c2ecf20Sopenharmony_ci pctl->num_groups = of_get_child_count(np); 7618c2ecf20Sopenharmony_ci } else { 7628c2ecf20Sopenharmony_ci pctl->num_groups = 0; 7638c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) 7648c2ecf20Sopenharmony_ci pctl->num_groups += of_get_child_count(child); 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (flat_funcs) { 7688c2ecf20Sopenharmony_ci imx_pinctrl_parse_functions(np, ipctl, 0); 7698c2ecf20Sopenharmony_ci } else { 7708c2ecf20Sopenharmony_ci i = 0; 7718c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) 7728c2ecf20Sopenharmony_ci imx_pinctrl_parse_functions(child, ipctl, i++); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return 0; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ciint imx_pinctrl_probe(struct platform_device *pdev, 7798c2ecf20Sopenharmony_ci const struct imx_pinctrl_soc_info *info) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct regmap_config config = { .name = "gpr" }; 7828c2ecf20Sopenharmony_ci struct device_node *dev_np = pdev->dev.of_node; 7838c2ecf20Sopenharmony_ci struct pinctrl_desc *imx_pinctrl_desc; 7848c2ecf20Sopenharmony_ci struct device_node *np; 7858c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl; 7868c2ecf20Sopenharmony_ci struct regmap *gpr; 7878c2ecf20Sopenharmony_ci int ret, i; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (!info || !info->pins || !info->npins) { 7908c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "wrong pinctrl info\n"); 7918c2ecf20Sopenharmony_ci return -EINVAL; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (info->gpr_compatible) { 7958c2ecf20Sopenharmony_ci gpr = syscon_regmap_lookup_by_compatible(info->gpr_compatible); 7968c2ecf20Sopenharmony_ci if (!IS_ERR(gpr)) 7978c2ecf20Sopenharmony_ci regmap_attach_dev(&pdev->dev, gpr, &config); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* Create state holders etc for this driver */ 8018c2ecf20Sopenharmony_ci ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL); 8028c2ecf20Sopenharmony_ci if (!ipctl) 8038c2ecf20Sopenharmony_ci return -ENOMEM; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (!(info->flags & IMX_USE_SCU)) { 8068c2ecf20Sopenharmony_ci ipctl->pin_regs = devm_kmalloc_array(&pdev->dev, info->npins, 8078c2ecf20Sopenharmony_ci sizeof(*ipctl->pin_regs), 8088c2ecf20Sopenharmony_ci GFP_KERNEL); 8098c2ecf20Sopenharmony_ci if (!ipctl->pin_regs) 8108c2ecf20Sopenharmony_ci return -ENOMEM; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci for (i = 0; i < info->npins; i++) { 8138c2ecf20Sopenharmony_ci ipctl->pin_regs[i].mux_reg = -1; 8148c2ecf20Sopenharmony_ci ipctl->pin_regs[i].conf_reg = -1; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci ipctl->base = devm_platform_ioremap_resource(pdev, 0); 8188c2ecf20Sopenharmony_ci if (IS_ERR(ipctl->base)) 8198c2ecf20Sopenharmony_ci return PTR_ERR(ipctl->base); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (of_property_read_bool(dev_np, "fsl,input-sel")) { 8228c2ecf20Sopenharmony_ci np = of_parse_phandle(dev_np, "fsl,input-sel", 0); 8238c2ecf20Sopenharmony_ci if (!np) { 8248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n"); 8258c2ecf20Sopenharmony_ci return -EINVAL; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci ipctl->input_sel_base = of_iomap(np, 0); 8298c2ecf20Sopenharmony_ci of_node_put(np); 8308c2ecf20Sopenharmony_ci if (!ipctl->input_sel_base) { 8318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8328c2ecf20Sopenharmony_ci "iomuxc input select base address not found\n"); 8338c2ecf20Sopenharmony_ci return -ENOMEM; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc), 8398c2ecf20Sopenharmony_ci GFP_KERNEL); 8408c2ecf20Sopenharmony_ci if (!imx_pinctrl_desc) 8418c2ecf20Sopenharmony_ci return -ENOMEM; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci imx_pinctrl_desc->name = dev_name(&pdev->dev); 8448c2ecf20Sopenharmony_ci imx_pinctrl_desc->pins = info->pins; 8458c2ecf20Sopenharmony_ci imx_pinctrl_desc->npins = info->npins; 8468c2ecf20Sopenharmony_ci imx_pinctrl_desc->pctlops = &imx_pctrl_ops; 8478c2ecf20Sopenharmony_ci imx_pinctrl_desc->pmxops = &imx_pmx_ops; 8488c2ecf20Sopenharmony_ci imx_pinctrl_desc->confops = &imx_pinconf_ops; 8498c2ecf20Sopenharmony_ci imx_pinctrl_desc->owner = THIS_MODULE; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* for generic pinconf */ 8528c2ecf20Sopenharmony_ci imx_pinctrl_desc->custom_params = info->custom_params; 8538c2ecf20Sopenharmony_ci imx_pinctrl_desc->num_custom_params = info->num_custom_params; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* platform specific callback */ 8568c2ecf20Sopenharmony_ci imx_pmx_ops.gpio_set_direction = info->gpio_set_direction; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci mutex_init(&ipctl->mutex); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci ipctl->info = info; 8618c2ecf20Sopenharmony_ci ipctl->dev = &pdev->dev; 8628c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ipctl); 8638c2ecf20Sopenharmony_ci ret = devm_pinctrl_register_and_init(&pdev->dev, 8648c2ecf20Sopenharmony_ci imx_pinctrl_desc, ipctl, 8658c2ecf20Sopenharmony_ci &ipctl->pctl); 8668c2ecf20Sopenharmony_ci if (ret) { 8678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register IMX pinctrl driver\n"); 8688c2ecf20Sopenharmony_ci return ret; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci ret = imx_pinctrl_probe_dt(pdev, ipctl); 8728c2ecf20Sopenharmony_ci if (ret) { 8738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fail to probe dt properties\n"); 8748c2ecf20Sopenharmony_ci return ret; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "initialized IMX pinctrl driver\n"); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return pinctrl_enable(ipctl->pctl); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_probe); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic int __maybe_unused imx_pinctrl_suspend(struct device *dev) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = dev_get_drvdata(dev); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return pinctrl_force_sleep(ipctl->pctl); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int __maybe_unused imx_pinctrl_resume(struct device *dev) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct imx_pinctrl *ipctl = dev_get_drvdata(dev); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return pinctrl_force_default(ipctl->pctl); 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ciconst struct dev_pm_ops imx_pinctrl_pm_ops = { 8988c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(imx_pinctrl_suspend, 8998c2ecf20Sopenharmony_ci imx_pinctrl_resume) 9008c2ecf20Sopenharmony_ci}; 9018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_pm_ops); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); 9048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NXP i.MX common pinctrl driver"); 9058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 906