162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Core driver for the imx pin controller 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2012 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci// Copyright (C) 2012 Linaro Ltd. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Dong Aisheng <dong.aisheng@linaro.org> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/seq_file.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/pinctrl/machine.h> 2362306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 2462306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 2562306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "../core.h" 2862306a36Sopenharmony_ci#include "../pinconf.h" 2962306a36Sopenharmony_ci#include "../pinmux.h" 3062306a36Sopenharmony_ci#include "pinctrl-imx.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* The bits in CONFIG cell defined in binding doc*/ 3362306a36Sopenharmony_ci#define IMX_NO_PAD_CTL 0x80000000 /* no pin config need */ 3462306a36Sopenharmony_ci#define IMX_PAD_SION 0x40000000 /* set SION */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline const struct group_desc *imx_pinctrl_find_group_by_name( 3762306a36Sopenharmony_ci struct pinctrl_dev *pctldev, 3862306a36Sopenharmony_ci const char *name) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci const struct group_desc *grp = NULL; 4162306a36Sopenharmony_ci int i; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 0; i < pctldev->num_groups; i++) { 4462306a36Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, i); 4562306a36Sopenharmony_ci if (grp && !strcmp(grp->name, name)) 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return grp; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void imx_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, 5362306a36Sopenharmony_ci unsigned offset) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci seq_printf(s, "%s", dev_name(pctldev->dev)); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int imx_dt_node_to_map(struct pinctrl_dev *pctldev, 5962306a36Sopenharmony_ci struct device_node *np, 6062306a36Sopenharmony_ci struct pinctrl_map **map, unsigned *num_maps) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 6362306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 6462306a36Sopenharmony_ci const struct group_desc *grp; 6562306a36Sopenharmony_ci struct pinctrl_map *new_map; 6662306a36Sopenharmony_ci struct device_node *parent; 6762306a36Sopenharmony_ci struct imx_pin *pin; 6862306a36Sopenharmony_ci int map_num = 1; 6962306a36Sopenharmony_ci int i, j; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * first find the group of this node and check if we need create 7362306a36Sopenharmony_ci * config maps for pins 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci grp = imx_pinctrl_find_group_by_name(pctldev, np->name); 7662306a36Sopenharmony_ci if (!grp) { 7762306a36Sopenharmony_ci dev_err(ipctl->dev, "unable to find group for node %pOFn\n", np); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 8262306a36Sopenharmony_ci map_num += grp->num_pins; 8362306a36Sopenharmony_ci } else { 8462306a36Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 8562306a36Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 8662306a36Sopenharmony_ci if (!(pin->conf.mmio.config & IMX_NO_PAD_CTL)) 8762306a36Sopenharmony_ci map_num++; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci new_map = kmalloc_array(map_num, sizeof(struct pinctrl_map), 9262306a36Sopenharmony_ci GFP_KERNEL); 9362306a36Sopenharmony_ci if (!new_map) 9462306a36Sopenharmony_ci return -ENOMEM; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci *map = new_map; 9762306a36Sopenharmony_ci *num_maps = map_num; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* create mux map */ 10062306a36Sopenharmony_ci parent = of_get_parent(np); 10162306a36Sopenharmony_ci if (!parent) { 10262306a36Sopenharmony_ci kfree(new_map); 10362306a36Sopenharmony_ci return -EINVAL; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; 10662306a36Sopenharmony_ci new_map[0].data.mux.function = parent->name; 10762306a36Sopenharmony_ci new_map[0].data.mux.group = np->name; 10862306a36Sopenharmony_ci of_node_put(parent); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* create config map */ 11162306a36Sopenharmony_ci new_map++; 11262306a36Sopenharmony_ci for (i = j = 0; i < grp->num_pins; i++) { 11362306a36Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * We only create config maps for SCU pads or MMIO pads that 11762306a36Sopenharmony_ci * are not using the default config(a.k.a IMX_NO_PAD_CTL) 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if (!(info->flags & IMX_USE_SCU) && 12062306a36Sopenharmony_ci (pin->conf.mmio.config & IMX_NO_PAD_CTL)) 12162306a36Sopenharmony_ci continue; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; 12462306a36Sopenharmony_ci new_map[j].data.configs.group_or_pin = 12562306a36Sopenharmony_ci pin_get_name(pctldev, pin->pin); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * For SCU case, we set mux and conf together 13062306a36Sopenharmony_ci * in one IPC call 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci new_map[j].data.configs.configs = 13362306a36Sopenharmony_ci (unsigned long *)&pin->conf.scu; 13462306a36Sopenharmony_ci new_map[j].data.configs.num_configs = 2; 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci new_map[j].data.configs.configs = 13762306a36Sopenharmony_ci &pin->conf.mmio.config; 13862306a36Sopenharmony_ci new_map[j].data.configs.num_configs = 1; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci j++; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", 14562306a36Sopenharmony_ci (*map)->data.mux.function, (*map)->data.mux.group, map_num); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void imx_dt_free_map(struct pinctrl_dev *pctldev, 15162306a36Sopenharmony_ci struct pinctrl_map *map, unsigned num_maps) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci kfree(map); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct pinctrl_ops imx_pctrl_ops = { 15762306a36Sopenharmony_ci .get_groups_count = pinctrl_generic_get_group_count, 15862306a36Sopenharmony_ci .get_group_name = pinctrl_generic_get_group_name, 15962306a36Sopenharmony_ci .get_group_pins = pinctrl_generic_get_group_pins, 16062306a36Sopenharmony_ci .pin_dbg_show = imx_pin_dbg_show, 16162306a36Sopenharmony_ci .dt_node_to_map = imx_dt_node_to_map, 16262306a36Sopenharmony_ci .dt_free_map = imx_dt_free_map, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int imx_pmx_set_one_pin_mmio(struct imx_pinctrl *ipctl, 16662306a36Sopenharmony_ci struct imx_pin *pin) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 16962306a36Sopenharmony_ci struct imx_pin_mmio *pin_mmio = &pin->conf.mmio; 17062306a36Sopenharmony_ci const struct imx_pin_reg *pin_reg; 17162306a36Sopenharmony_ci unsigned int pin_id; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pin_id = pin->pin; 17462306a36Sopenharmony_ci pin_reg = &ipctl->pin_regs[pin_id]; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (pin_reg->mux_reg == -1) { 17762306a36Sopenharmony_ci dev_dbg(ipctl->dev, "Pin(%s) does not support mux function\n", 17862306a36Sopenharmony_ci info->pins[pin_id].name); 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 18362306a36Sopenharmony_ci u32 reg; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci reg = readl(ipctl->base + pin_reg->mux_reg); 18662306a36Sopenharmony_ci reg &= ~info->mux_mask; 18762306a36Sopenharmony_ci reg |= (pin_mmio->mux_mode << info->mux_shift); 18862306a36Sopenharmony_ci writel(reg, ipctl->base + pin_reg->mux_reg); 18962306a36Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 19062306a36Sopenharmony_ci pin_reg->mux_reg, reg); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci writel(pin_mmio->mux_mode, ipctl->base + pin_reg->mux_reg); 19362306a36Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 19462306a36Sopenharmony_ci pin_reg->mux_reg, pin_mmio->mux_mode); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * If the select input value begins with 0xff, it's a quirky 19962306a36Sopenharmony_ci * select input and the value should be interpreted as below. 20062306a36Sopenharmony_ci * 31 23 15 7 0 20162306a36Sopenharmony_ci * | 0xff | shift | width | select | 20262306a36Sopenharmony_ci * It's used to work around the problem that the select 20362306a36Sopenharmony_ci * input for some pin is not implemented in the select 20462306a36Sopenharmony_ci * input register but in some general purpose register. 20562306a36Sopenharmony_ci * We encode the select input value, width and shift of 20662306a36Sopenharmony_ci * the bit field into input_val cell of pin function ID 20762306a36Sopenharmony_ci * in device tree, and then decode them here for setting 20862306a36Sopenharmony_ci * up the select input bits in general purpose register. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (pin_mmio->input_val >> 24 == 0xff) { 21162306a36Sopenharmony_ci u32 val = pin_mmio->input_val; 21262306a36Sopenharmony_ci u8 select = val & 0xff; 21362306a36Sopenharmony_ci u8 width = (val >> 8) & 0xff; 21462306a36Sopenharmony_ci u8 shift = (val >> 16) & 0xff; 21562306a36Sopenharmony_ci u32 mask = ((1 << width) - 1) << shift; 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * The input_reg[i] here is actually some IOMUXC general 21862306a36Sopenharmony_ci * purpose register, not regular select input register. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci val = readl(ipctl->base + pin_mmio->input_reg); 22162306a36Sopenharmony_ci val &= ~mask; 22262306a36Sopenharmony_ci val |= select << shift; 22362306a36Sopenharmony_ci writel(val, ipctl->base + pin_mmio->input_reg); 22462306a36Sopenharmony_ci } else if (pin_mmio->input_reg) { 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * Regular select input register can never be at offset 22762306a36Sopenharmony_ci * 0, and we only print register value for regular case. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci if (ipctl->input_sel_base) 23062306a36Sopenharmony_ci writel(pin_mmio->input_val, ipctl->input_sel_base + 23162306a36Sopenharmony_ci pin_mmio->input_reg); 23262306a36Sopenharmony_ci else 23362306a36Sopenharmony_ci writel(pin_mmio->input_val, ipctl->base + 23462306a36Sopenharmony_ci pin_mmio->input_reg); 23562306a36Sopenharmony_ci dev_dbg(ipctl->dev, 23662306a36Sopenharmony_ci "==>select_input: offset 0x%x val 0x%x\n", 23762306a36Sopenharmony_ci pin_mmio->input_reg, pin_mmio->input_val); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, 24462306a36Sopenharmony_ci unsigned group) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 24762306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 24862306a36Sopenharmony_ci struct function_desc *func; 24962306a36Sopenharmony_ci struct group_desc *grp; 25062306a36Sopenharmony_ci struct imx_pin *pin; 25162306a36Sopenharmony_ci unsigned int npins; 25262306a36Sopenharmony_ci int i, err; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Configure the mux mode for each pin in the group for a specific 25662306a36Sopenharmony_ci * function. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, group); 25962306a36Sopenharmony_ci if (!grp) 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci func = pinmux_generic_get_function(pctldev, selector); 26362306a36Sopenharmony_ci if (!func) 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci npins = grp->num_pins; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci dev_dbg(ipctl->dev, "enable function %s group %s\n", 26962306a36Sopenharmony_ci func->name, grp->name); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci for (i = 0; i < npins; i++) { 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * For IMX_USE_SCU case, we postpone the mux setting 27462306a36Sopenharmony_ci * until config is set as we can set them together 27562306a36Sopenharmony_ci * in one IPC call 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 27862306a36Sopenharmony_ci if (!(info->flags & IMX_USE_SCU)) { 27962306a36Sopenharmony_ci err = imx_pmx_set_one_pin_mmio(ipctl, pin); 28062306a36Sopenharmony_ci if (err) 28162306a36Sopenharmony_ci return err; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistruct pinmux_ops imx_pmx_ops = { 28962306a36Sopenharmony_ci .get_functions_count = pinmux_generic_get_function_count, 29062306a36Sopenharmony_ci .get_function_name = pinmux_generic_get_function_name, 29162306a36Sopenharmony_ci .get_function_groups = pinmux_generic_get_function_groups, 29262306a36Sopenharmony_ci .set_mux = imx_pmx_set, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id, 29662306a36Sopenharmony_ci unsigned long *config) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 29962306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 30062306a36Sopenharmony_ci const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id]; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (pin_reg->conf_reg == -1) { 30362306a36Sopenharmony_ci dev_err(ipctl->dev, "Pin(%s) does not support config function\n", 30462306a36Sopenharmony_ci info->pins[pin_id].name); 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci *config = readl(ipctl->base + pin_reg->conf_reg); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) 31162306a36Sopenharmony_ci *config &= ~info->mux_mask; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int imx_pinconf_get(struct pinctrl_dev *pctldev, 31762306a36Sopenharmony_ci unsigned pin_id, unsigned long *config) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 32062306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) 32362306a36Sopenharmony_ci return info->imx_pinconf_get(pctldev, pin_id, config); 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci return imx_pinconf_get_mmio(pctldev, pin_id, config); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev, 32962306a36Sopenharmony_ci unsigned pin_id, unsigned long *configs, 33062306a36Sopenharmony_ci unsigned num_configs) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 33362306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 33462306a36Sopenharmony_ci const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id]; 33562306a36Sopenharmony_ci int i; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (pin_reg->conf_reg == -1) { 33862306a36Sopenharmony_ci dev_err(ipctl->dev, "Pin(%s) does not support config function\n", 33962306a36Sopenharmony_ci info->pins[pin_id].name); 34062306a36Sopenharmony_ci return -EINVAL; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci dev_dbg(ipctl->dev, "pinconf set pin %s\n", 34462306a36Sopenharmony_ci info->pins[pin_id].name); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 34762306a36Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 34862306a36Sopenharmony_ci u32 reg; 34962306a36Sopenharmony_ci reg = readl(ipctl->base + pin_reg->conf_reg); 35062306a36Sopenharmony_ci reg &= info->mux_mask; 35162306a36Sopenharmony_ci reg |= configs[i]; 35262306a36Sopenharmony_ci writel(reg, ipctl->base + pin_reg->conf_reg); 35362306a36Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", 35462306a36Sopenharmony_ci pin_reg->conf_reg, reg); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci writel(configs[i], ipctl->base + pin_reg->conf_reg); 35762306a36Sopenharmony_ci dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", 35862306a36Sopenharmony_ci pin_reg->conf_reg, configs[i]); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } /* for each config */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int imx_pinconf_set(struct pinctrl_dev *pctldev, 36662306a36Sopenharmony_ci unsigned pin_id, unsigned long *configs, 36762306a36Sopenharmony_ci unsigned num_configs) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 37062306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) 37362306a36Sopenharmony_ci return info->imx_pinconf_set(pctldev, pin_id, 37462306a36Sopenharmony_ci configs, num_configs); 37562306a36Sopenharmony_ci else 37662306a36Sopenharmony_ci return imx_pinconf_set_mmio(pctldev, pin_id, 37762306a36Sopenharmony_ci configs, num_configs); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void imx_pinconf_dbg_show(struct pinctrl_dev *pctldev, 38162306a36Sopenharmony_ci struct seq_file *s, unsigned pin_id) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); 38462306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 38562306a36Sopenharmony_ci const struct imx_pin_reg *pin_reg; 38662306a36Sopenharmony_ci unsigned long config; 38762306a36Sopenharmony_ci int ret; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) { 39062306a36Sopenharmony_ci ret = info->imx_pinconf_get(pctldev, pin_id, &config); 39162306a36Sopenharmony_ci if (ret) { 39262306a36Sopenharmony_ci dev_err(ipctl->dev, "failed to get %s pinconf\n", 39362306a36Sopenharmony_ci pin_get_name(pctldev, pin_id)); 39462306a36Sopenharmony_ci seq_puts(s, "N/A"); 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } else { 39862306a36Sopenharmony_ci pin_reg = &ipctl->pin_regs[pin_id]; 39962306a36Sopenharmony_ci if (pin_reg->conf_reg == -1) { 40062306a36Sopenharmony_ci seq_puts(s, "N/A"); 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci config = readl(ipctl->base + pin_reg->conf_reg); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci seq_printf(s, "0x%lx", config); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 41162306a36Sopenharmony_ci struct seq_file *s, unsigned group) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct group_desc *grp; 41462306a36Sopenharmony_ci unsigned long config; 41562306a36Sopenharmony_ci const char *name; 41662306a36Sopenharmony_ci int i, ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (group >= pctldev->num_groups) 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci seq_puts(s, "\n"); 42262306a36Sopenharmony_ci grp = pinctrl_generic_get_group(pctldev, group); 42362306a36Sopenharmony_ci if (!grp) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 42762306a36Sopenharmony_ci struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i]; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci name = pin_get_name(pctldev, pin->pin); 43062306a36Sopenharmony_ci ret = imx_pinconf_get(pctldev, pin->pin, &config); 43162306a36Sopenharmony_ci if (ret) 43262306a36Sopenharmony_ci return; 43362306a36Sopenharmony_ci seq_printf(s, " %s: 0x%lx\n", name, config); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const struct pinconf_ops imx_pinconf_ops = { 43862306a36Sopenharmony_ci .pin_config_get = imx_pinconf_get, 43962306a36Sopenharmony_ci .pin_config_set = imx_pinconf_set, 44062306a36Sopenharmony_ci .pin_config_dbg_show = imx_pinconf_dbg_show, 44162306a36Sopenharmony_ci .pin_config_group_dbg_show = imx_pinconf_group_dbg_show, 44262306a36Sopenharmony_ci}; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* 44562306a36Sopenharmony_ci * Each pin represented in fsl,pins consists of a number of u32 PIN_FUNC_ID 44662306a36Sopenharmony_ci * and 1 u32 CONFIG, the total size is PIN_FUNC_ID + CONFIG for each pin. 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * PIN_FUNC_ID format: 44962306a36Sopenharmony_ci * Default: 45062306a36Sopenharmony_ci * <mux_reg conf_reg input_reg mux_mode input_val> 45162306a36Sopenharmony_ci * SHARE_MUX_CONF_REG: 45262306a36Sopenharmony_ci * <mux_conf_reg input_reg mux_mode input_val> 45362306a36Sopenharmony_ci * IMX_USE_SCU: 45462306a36Sopenharmony_ci * <pin_id mux_mode> 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci#define FSL_PIN_SIZE 24 45762306a36Sopenharmony_ci#define FSL_PIN_SHARE_SIZE 20 45862306a36Sopenharmony_ci#define FSL_SCU_PIN_SIZE 12 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void imx_pinctrl_parse_pin_mmio(struct imx_pinctrl *ipctl, 46162306a36Sopenharmony_ci unsigned int *pin_id, struct imx_pin *pin, 46262306a36Sopenharmony_ci const __be32 **list_p, 46362306a36Sopenharmony_ci struct device_node *np) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 46662306a36Sopenharmony_ci struct imx_pin_mmio *pin_mmio = &pin->conf.mmio; 46762306a36Sopenharmony_ci struct imx_pin_reg *pin_reg; 46862306a36Sopenharmony_ci const __be32 *list = *list_p; 46962306a36Sopenharmony_ci u32 mux_reg, conf_reg; 47062306a36Sopenharmony_ci u32 config; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci mux_reg = be32_to_cpu(*list++); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg) 47562306a36Sopenharmony_ci mux_reg = -1; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (info->flags & SHARE_MUX_CONF_REG) { 47862306a36Sopenharmony_ci conf_reg = mux_reg; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci conf_reg = be32_to_cpu(*list++); 48162306a36Sopenharmony_ci if (!conf_reg) 48262306a36Sopenharmony_ci conf_reg = -1; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci *pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4; 48662306a36Sopenharmony_ci pin_reg = &ipctl->pin_regs[*pin_id]; 48762306a36Sopenharmony_ci pin->pin = *pin_id; 48862306a36Sopenharmony_ci pin_reg->mux_reg = mux_reg; 48962306a36Sopenharmony_ci pin_reg->conf_reg = conf_reg; 49062306a36Sopenharmony_ci pin_mmio->input_reg = be32_to_cpu(*list++); 49162306a36Sopenharmony_ci pin_mmio->mux_mode = be32_to_cpu(*list++); 49262306a36Sopenharmony_ci pin_mmio->input_val = be32_to_cpu(*list++); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci config = be32_to_cpu(*list++); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* SION bit is in mux register */ 49762306a36Sopenharmony_ci if (config & IMX_PAD_SION) 49862306a36Sopenharmony_ci pin_mmio->mux_mode |= IOMUXC_CONFIG_SION; 49962306a36Sopenharmony_ci pin_mmio->config = config & ~IMX_PAD_SION; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci *list_p = list; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci dev_dbg(ipctl->dev, "%s: 0x%x 0x%08lx", info->pins[*pin_id].name, 50462306a36Sopenharmony_ci pin_mmio->mux_mode, pin_mmio->config); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int imx_pinctrl_parse_groups(struct device_node *np, 50862306a36Sopenharmony_ci struct group_desc *grp, 50962306a36Sopenharmony_ci struct imx_pinctrl *ipctl, 51062306a36Sopenharmony_ci u32 index) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info = ipctl->info; 51362306a36Sopenharmony_ci struct imx_pin *pin; 51462306a36Sopenharmony_ci int size, pin_size; 51562306a36Sopenharmony_ci const __be32 *list; 51662306a36Sopenharmony_ci int i; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci dev_dbg(ipctl->dev, "group(%d): %pOFn\n", index, np); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) 52162306a36Sopenharmony_ci pin_size = FSL_SCU_PIN_SIZE; 52262306a36Sopenharmony_ci else if (info->flags & SHARE_MUX_CONF_REG) 52362306a36Sopenharmony_ci pin_size = FSL_PIN_SHARE_SIZE; 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci pin_size = FSL_PIN_SIZE; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Initialise group */ 52862306a36Sopenharmony_ci grp->name = np->name; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * the binding format is fsl,pins = <PIN_FUNC_ID CONFIG ...>, 53262306a36Sopenharmony_ci * do sanity check and calculate pins number 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * First try legacy 'fsl,pins' property, then fall back to the 53562306a36Sopenharmony_ci * generic 'pinmux'. 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * Note: for generic 'pinmux' case, there's no CONFIG part in 53862306a36Sopenharmony_ci * the binding format. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci list = of_get_property(np, "fsl,pins", &size); 54162306a36Sopenharmony_ci if (!list) { 54262306a36Sopenharmony_ci list = of_get_property(np, "pinmux", &size); 54362306a36Sopenharmony_ci if (!list) { 54462306a36Sopenharmony_ci dev_err(ipctl->dev, 54562306a36Sopenharmony_ci "no fsl,pins and pins property in node %pOF\n", np); 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* we do not check return since it's safe node passed down */ 55162306a36Sopenharmony_ci if (!size || size % pin_size) { 55262306a36Sopenharmony_ci dev_err(ipctl->dev, "Invalid fsl,pins or pins property in node %pOF\n", np); 55362306a36Sopenharmony_ci return -EINVAL; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci grp->num_pins = size / pin_size; 55762306a36Sopenharmony_ci grp->data = devm_kcalloc(ipctl->dev, 55862306a36Sopenharmony_ci grp->num_pins, sizeof(struct imx_pin), 55962306a36Sopenharmony_ci GFP_KERNEL); 56062306a36Sopenharmony_ci grp->pins = devm_kcalloc(ipctl->dev, 56162306a36Sopenharmony_ci grp->num_pins, sizeof(unsigned int), 56262306a36Sopenharmony_ci GFP_KERNEL); 56362306a36Sopenharmony_ci if (!grp->pins || !grp->data) 56462306a36Sopenharmony_ci return -ENOMEM; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 56762306a36Sopenharmony_ci pin = &((struct imx_pin *)(grp->data))[i]; 56862306a36Sopenharmony_ci if (info->flags & IMX_USE_SCU) 56962306a36Sopenharmony_ci info->imx_pinctrl_parse_pin(ipctl, &grp->pins[i], 57062306a36Sopenharmony_ci pin, &list); 57162306a36Sopenharmony_ci else 57262306a36Sopenharmony_ci imx_pinctrl_parse_pin_mmio(ipctl, &grp->pins[i], 57362306a36Sopenharmony_ci pin, &list, np); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int imx_pinctrl_parse_functions(struct device_node *np, 58062306a36Sopenharmony_ci struct imx_pinctrl *ipctl, 58162306a36Sopenharmony_ci u32 index) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct pinctrl_dev *pctl = ipctl->pctl; 58462306a36Sopenharmony_ci struct device_node *child; 58562306a36Sopenharmony_ci struct function_desc *func; 58662306a36Sopenharmony_ci struct group_desc *grp; 58762306a36Sopenharmony_ci const char **group_names; 58862306a36Sopenharmony_ci u32 i; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci dev_dbg(pctl->dev, "parse function(%d): %pOFn\n", index, np); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci func = pinmux_generic_get_function(pctl, index); 59362306a36Sopenharmony_ci if (!func) 59462306a36Sopenharmony_ci return -EINVAL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Initialise function */ 59762306a36Sopenharmony_ci func->name = np->name; 59862306a36Sopenharmony_ci func->num_group_names = of_get_child_count(np); 59962306a36Sopenharmony_ci if (func->num_group_names == 0) { 60062306a36Sopenharmony_ci dev_info(ipctl->dev, "no groups defined in %pOF\n", np); 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci group_names = devm_kcalloc(ipctl->dev, func->num_group_names, 60562306a36Sopenharmony_ci sizeof(char *), GFP_KERNEL); 60662306a36Sopenharmony_ci if (!group_names) 60762306a36Sopenharmony_ci return -ENOMEM; 60862306a36Sopenharmony_ci i = 0; 60962306a36Sopenharmony_ci for_each_child_of_node(np, child) 61062306a36Sopenharmony_ci group_names[i++] = child->name; 61162306a36Sopenharmony_ci func->group_names = group_names; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci i = 0; 61462306a36Sopenharmony_ci for_each_child_of_node(np, child) { 61562306a36Sopenharmony_ci grp = devm_kzalloc(ipctl->dev, sizeof(struct group_desc), 61662306a36Sopenharmony_ci GFP_KERNEL); 61762306a36Sopenharmony_ci if (!grp) { 61862306a36Sopenharmony_ci of_node_put(child); 61962306a36Sopenharmony_ci return -ENOMEM; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mutex_lock(&ipctl->mutex); 62362306a36Sopenharmony_ci radix_tree_insert(&pctl->pin_group_tree, 62462306a36Sopenharmony_ci ipctl->group_index++, grp); 62562306a36Sopenharmony_ci mutex_unlock(&ipctl->mutex); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci imx_pinctrl_parse_groups(child, grp, ipctl, i++); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/* 63462306a36Sopenharmony_ci * Check if the DT contains pins in the direct child nodes. This indicates the 63562306a36Sopenharmony_ci * newer DT format to store pins. This function returns true if the first found 63662306a36Sopenharmony_ci * fsl,pins property is in a child of np. Otherwise false is returned. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistatic bool imx_pinctrl_dt_is_flat_functions(struct device_node *np) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct device_node *function_np; 64162306a36Sopenharmony_ci struct device_node *pinctrl_np; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci for_each_child_of_node(np, function_np) { 64462306a36Sopenharmony_ci if (of_property_read_bool(function_np, "fsl,pins")) { 64562306a36Sopenharmony_ci of_node_put(function_np); 64662306a36Sopenharmony_ci return true; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci for_each_child_of_node(function_np, pinctrl_np) { 65062306a36Sopenharmony_ci if (of_property_read_bool(pinctrl_np, "fsl,pins")) { 65162306a36Sopenharmony_ci of_node_put(pinctrl_np); 65262306a36Sopenharmony_ci of_node_put(function_np); 65362306a36Sopenharmony_ci return false; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return true; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int imx_pinctrl_probe_dt(struct platform_device *pdev, 66262306a36Sopenharmony_ci struct imx_pinctrl *ipctl) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 66562306a36Sopenharmony_ci struct device_node *child; 66662306a36Sopenharmony_ci struct pinctrl_dev *pctl = ipctl->pctl; 66762306a36Sopenharmony_ci u32 nfuncs = 0; 66862306a36Sopenharmony_ci u32 i = 0; 66962306a36Sopenharmony_ci bool flat_funcs; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!np) 67262306a36Sopenharmony_ci return -ENODEV; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci flat_funcs = imx_pinctrl_dt_is_flat_functions(np); 67562306a36Sopenharmony_ci if (flat_funcs) { 67662306a36Sopenharmony_ci nfuncs = 1; 67762306a36Sopenharmony_ci } else { 67862306a36Sopenharmony_ci nfuncs = of_get_child_count(np); 67962306a36Sopenharmony_ci if (nfuncs == 0) { 68062306a36Sopenharmony_ci dev_err(&pdev->dev, "no functions defined\n"); 68162306a36Sopenharmony_ci return -EINVAL; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; i < nfuncs; i++) { 68662306a36Sopenharmony_ci struct function_desc *function; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci function = devm_kzalloc(&pdev->dev, sizeof(*function), 68962306a36Sopenharmony_ci GFP_KERNEL); 69062306a36Sopenharmony_ci if (!function) 69162306a36Sopenharmony_ci return -ENOMEM; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci mutex_lock(&ipctl->mutex); 69462306a36Sopenharmony_ci radix_tree_insert(&pctl->pin_function_tree, i, function); 69562306a36Sopenharmony_ci mutex_unlock(&ipctl->mutex); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci pctl->num_functions = nfuncs; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ipctl->group_index = 0; 70062306a36Sopenharmony_ci if (flat_funcs) { 70162306a36Sopenharmony_ci pctl->num_groups = of_get_child_count(np); 70262306a36Sopenharmony_ci } else { 70362306a36Sopenharmony_ci pctl->num_groups = 0; 70462306a36Sopenharmony_ci for_each_child_of_node(np, child) 70562306a36Sopenharmony_ci pctl->num_groups += of_get_child_count(child); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (flat_funcs) { 70962306a36Sopenharmony_ci imx_pinctrl_parse_functions(np, ipctl, 0); 71062306a36Sopenharmony_ci } else { 71162306a36Sopenharmony_ci i = 0; 71262306a36Sopenharmony_ci for_each_child_of_node(np, child) 71362306a36Sopenharmony_ci imx_pinctrl_parse_functions(child, ipctl, i++); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciint imx_pinctrl_probe(struct platform_device *pdev, 72062306a36Sopenharmony_ci const struct imx_pinctrl_soc_info *info) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct regmap_config config = { .name = "gpr" }; 72362306a36Sopenharmony_ci struct device_node *dev_np = pdev->dev.of_node; 72462306a36Sopenharmony_ci struct pinctrl_desc *imx_pinctrl_desc; 72562306a36Sopenharmony_ci struct device_node *np; 72662306a36Sopenharmony_ci struct imx_pinctrl *ipctl; 72762306a36Sopenharmony_ci struct regmap *gpr; 72862306a36Sopenharmony_ci int ret, i; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (!info || !info->pins || !info->npins) { 73162306a36Sopenharmony_ci dev_err(&pdev->dev, "wrong pinctrl info\n"); 73262306a36Sopenharmony_ci return -EINVAL; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (info->gpr_compatible) { 73662306a36Sopenharmony_ci gpr = syscon_regmap_lookup_by_compatible(info->gpr_compatible); 73762306a36Sopenharmony_ci if (!IS_ERR(gpr)) 73862306a36Sopenharmony_ci regmap_attach_dev(&pdev->dev, gpr, &config); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Create state holders etc for this driver */ 74262306a36Sopenharmony_ci ipctl = devm_kzalloc(&pdev->dev, sizeof(*ipctl), GFP_KERNEL); 74362306a36Sopenharmony_ci if (!ipctl) 74462306a36Sopenharmony_ci return -ENOMEM; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (!(info->flags & IMX_USE_SCU)) { 74762306a36Sopenharmony_ci ipctl->pin_regs = devm_kmalloc_array(&pdev->dev, info->npins, 74862306a36Sopenharmony_ci sizeof(*ipctl->pin_regs), 74962306a36Sopenharmony_ci GFP_KERNEL); 75062306a36Sopenharmony_ci if (!ipctl->pin_regs) 75162306a36Sopenharmony_ci return -ENOMEM; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci for (i = 0; i < info->npins; i++) { 75462306a36Sopenharmony_ci ipctl->pin_regs[i].mux_reg = -1; 75562306a36Sopenharmony_ci ipctl->pin_regs[i].conf_reg = -1; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ipctl->base = devm_platform_ioremap_resource(pdev, 0); 75962306a36Sopenharmony_ci if (IS_ERR(ipctl->base)) 76062306a36Sopenharmony_ci return PTR_ERR(ipctl->base); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (of_property_read_bool(dev_np, "fsl,input-sel")) { 76362306a36Sopenharmony_ci np = of_parse_phandle(dev_np, "fsl,input-sel", 0); 76462306a36Sopenharmony_ci if (!np) { 76562306a36Sopenharmony_ci dev_err(&pdev->dev, "iomuxc fsl,input-sel property not found\n"); 76662306a36Sopenharmony_ci return -EINVAL; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci ipctl->input_sel_base = of_iomap(np, 0); 77062306a36Sopenharmony_ci of_node_put(np); 77162306a36Sopenharmony_ci if (!ipctl->input_sel_base) { 77262306a36Sopenharmony_ci dev_err(&pdev->dev, 77362306a36Sopenharmony_ci "iomuxc input select base address not found\n"); 77462306a36Sopenharmony_ci return -ENOMEM; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc), 78062306a36Sopenharmony_ci GFP_KERNEL); 78162306a36Sopenharmony_ci if (!imx_pinctrl_desc) 78262306a36Sopenharmony_ci return -ENOMEM; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci imx_pinctrl_desc->name = dev_name(&pdev->dev); 78562306a36Sopenharmony_ci imx_pinctrl_desc->pins = info->pins; 78662306a36Sopenharmony_ci imx_pinctrl_desc->npins = info->npins; 78762306a36Sopenharmony_ci imx_pinctrl_desc->pctlops = &imx_pctrl_ops; 78862306a36Sopenharmony_ci imx_pinctrl_desc->pmxops = &imx_pmx_ops; 78962306a36Sopenharmony_ci imx_pinctrl_desc->confops = &imx_pinconf_ops; 79062306a36Sopenharmony_ci imx_pinctrl_desc->owner = THIS_MODULE; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* platform specific callback */ 79362306a36Sopenharmony_ci imx_pmx_ops.gpio_set_direction = info->gpio_set_direction; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci mutex_init(&ipctl->mutex); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ipctl->info = info; 79862306a36Sopenharmony_ci ipctl->dev = &pdev->dev; 79962306a36Sopenharmony_ci platform_set_drvdata(pdev, ipctl); 80062306a36Sopenharmony_ci ret = devm_pinctrl_register_and_init(&pdev->dev, 80162306a36Sopenharmony_ci imx_pinctrl_desc, ipctl, 80262306a36Sopenharmony_ci &ipctl->pctl); 80362306a36Sopenharmony_ci if (ret) { 80462306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register IMX pinctrl driver\n"); 80562306a36Sopenharmony_ci return ret; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ret = imx_pinctrl_probe_dt(pdev, ipctl); 80962306a36Sopenharmony_ci if (ret) { 81062306a36Sopenharmony_ci dev_err(&pdev->dev, "fail to probe dt properties\n"); 81162306a36Sopenharmony_ci return ret; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci dev_info(&pdev->dev, "initialized IMX pinctrl driver\n"); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return pinctrl_enable(ipctl->pctl); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_probe); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int __maybe_unused imx_pinctrl_suspend(struct device *dev) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct imx_pinctrl *ipctl = dev_get_drvdata(dev); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return pinctrl_force_sleep(ipctl->pctl); 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int __maybe_unused imx_pinctrl_resume(struct device *dev) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct imx_pinctrl *ipctl = dev_get_drvdata(dev); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return pinctrl_force_default(ipctl->pctl); 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ciconst struct dev_pm_ops imx_pinctrl_pm_ops = { 83562306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(imx_pinctrl_suspend, 83662306a36Sopenharmony_ci imx_pinctrl_resume) 83762306a36Sopenharmony_ci}; 83862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_pinctrl_pm_ops); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ciMODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); 84162306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP i.MX common pinctrl driver"); 84262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 843