summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/pinctrl-imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/pinctrl-imx.c')
-rw-r--r--drivers/pinctrl/pinctrl-imx.c179
1 files changed, 94 insertions, 85 deletions
diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c
index 57a4eb0add2..d78dd813bff 100644
--- a/drivers/pinctrl/pinctrl-imx.c
+++ b/drivers/pinctrl/pinctrl-imx.c
@@ -27,18 +27,6 @@
#include "core.h"
#include "pinctrl-imx.h"
-#define IMX_PMX_DUMP(info, p, m, c, n) \
-{ \
- int i, j; \
- printk(KERN_DEBUG "Format: Pin Mux Config\n"); \
- for (i = 0; i < n; i++) { \
- j = p[i]; \
- printk(KERN_DEBUG "%s %d 0x%lx\n", \
- info->pins[j].name, \
- m[i], c[i]); \
- } \
-}
-
/* The bits in CONFIG cell defined in binding doc*/
#define IMX_NO_PAD_CTL 0x80000000 /* no pin config need */
#define IMX_PAD_SION 0x40000000 /* set SION */
@@ -98,7 +86,7 @@ static int imx_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
if (selector >= info->ngroups)
return -EINVAL;
- *pins = info->groups[selector].pins;
+ *pins = info->groups[selector].pin_ids;
*npins = info->groups[selector].npins;
return 0;
@@ -134,7 +122,7 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
}
for (i = 0; i < grp->npins; i++) {
- if (!(grp->configs[i] & IMX_NO_PAD_CTL))
+ if (!(grp->pins[i].config & IMX_NO_PAD_CTL))
map_num++;
}
@@ -159,11 +147,11 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev,
/* create config map */
new_map++;
for (i = j = 0; i < grp->npins; i++) {
- if (!(grp->configs[i] & IMX_NO_PAD_CTL)) {
+ if (!(grp->pins[i].config & IMX_NO_PAD_CTL)) {
new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN;
new_map[j].data.configs.group_or_pin =
- pin_get_name(pctldev, grp->pins[i]);
- new_map[j].data.configs.configs = &grp->configs[i];
+ pin_get_name(pctldev, grp->pins[i].pin);
+ new_map[j].data.configs.configs = &grp->pins[i].config;
new_map[j].data.configs.num_configs = 1;
j++;
}
@@ -197,28 +185,23 @@ static int imx_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector,
struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
const struct imx_pinctrl_soc_info *info = ipctl->info;
const struct imx_pin_reg *pin_reg;
- const unsigned *pins, *mux, *input_val;
- u16 *input_reg;
unsigned int npins, pin_id;
int i;
+ struct imx_pin_group *grp;
/*
* Configure the mux mode for each pin in the group for a specific
* function.
*/
- pins = info->groups[group].pins;
- npins = info->groups[group].npins;
- mux = info->groups[group].mux_mode;
- input_val = info->groups[group].input_val;
- input_reg = info->groups[group].input_reg;
-
- WARN_ON(!pins || !npins || !mux || !input_val || !input_reg);
+ grp = &info->groups[group];
+ npins = grp->npins;
dev_dbg(ipctl->dev, "enable function %s group %s\n",
- info->functions[selector].name, info->groups[group].name);
+ info->functions[selector].name, grp->name);
for (i = 0; i < npins; i++) {
- pin_id = pins[i];
+ struct imx_pin *pin = &grp->pins[i];
+ pin_id = pin->pin;
pin_reg = &info->pin_regs[pin_id];
if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->mux_reg) {
@@ -231,20 +214,50 @@ static int imx_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector,
u32 reg;
reg = readl(ipctl->base + pin_reg->mux_reg);
reg &= ~(0x7 << 20);
- reg |= (mux[i] << 20);
+ reg |= (pin->mux_mode << 20);
writel(reg, ipctl->base + pin_reg->mux_reg);
} else {
- writel(mux[i], ipctl->base + pin_reg->mux_reg);
+ writel(pin->mux_mode, ipctl->base + pin_reg->mux_reg);
}
dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n",
- pin_reg->mux_reg, mux[i]);
-
- /* some pins also need select input setting, set it if found */
- if (input_reg[i]) {
- writel(input_val[i], ipctl->base + input_reg[i]);
+ pin_reg->mux_reg, pin->mux_mode);
+
+ /*
+ * If the select input value begins with 0xff, it's a quirky
+ * select input and the value should be interpreted as below.
+ * 31 23 15 7 0
+ * | 0xff | shift | width | select |
+ * It's used to work around the problem that the select
+ * input for some pin is not implemented in the select
+ * input register but in some general purpose register.
+ * We encode the select input value, width and shift of
+ * the bit field into input_val cell of pin function ID
+ * in device tree, and then decode them here for setting
+ * up the select input bits in general purpose register.
+ */
+ if (pin->input_val >> 24 == 0xff) {
+ u32 val = pin->input_val;
+ u8 select = val & 0xff;
+ u8 width = (val >> 8) & 0xff;
+ u8 shift = (val >> 16) & 0xff;
+ u32 mask = ((1 << width) - 1) << shift;
+ /*
+ * The input_reg[i] here is actually some IOMUXC general
+ * purpose register, not regular select input register.
+ */
+ val = readl(ipctl->base + pin->input_val);
+ val &= ~mask;
+ val |= select << shift;
+ writel(val, ipctl->base + pin->input_val);
+ } else if (pin->input_val) {
+ /*
+ * Regular select input register can never be at offset
+ * 0, and we only print register value for regular case.
+ */
+ writel(pin->input_val, ipctl->base + pin->input_reg);
dev_dbg(ipctl->dev,
"==>select_input: offset 0x%x val 0x%x\n",
- input_reg[i], input_val[i]);
+ pin->input_reg, pin->input_val);
}
}
@@ -310,11 +323,13 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev,
}
static int imx_pinconf_set(struct pinctrl_dev *pctldev,
- unsigned pin_id, unsigned long config)
+ unsigned pin_id, unsigned long *configs,
+ unsigned num_configs)
{
struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
const struct imx_pinctrl_soc_info *info = ipctl->info;
const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id];
+ int i;
if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->conf_reg) {
dev_err(info->dev, "Pin(%s) does not support config function\n",
@@ -325,17 +340,19 @@ static int imx_pinconf_set(struct pinctrl_dev *pctldev,
dev_dbg(ipctl->dev, "pinconf set pin %s\n",
info->pins[pin_id].name);
- if (info->flags & SHARE_MUX_CONF_REG) {
- u32 reg;
- reg = readl(ipctl->base + pin_reg->conf_reg);
- reg &= ~0xffff;
- reg |= config;
- writel(reg, ipctl->base + pin_reg->conf_reg);
- } else {
- writel(config, ipctl->base + pin_reg->conf_reg);
- }
- dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n",
- pin_reg->conf_reg, config);
+ for (i = 0; i < num_configs; i++) {
+ if (info->flags & SHARE_MUX_CONF_REG) {
+ u32 reg;
+ reg = readl(ipctl->base + pin_reg->conf_reg);
+ reg &= ~0xffff;
+ reg |= configs[i];
+ writel(reg, ipctl->base + pin_reg->conf_reg);
+ } else {
+ writel(configs[i], ipctl->base + pin_reg->conf_reg);
+ }
+ dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n",
+ pin_reg->conf_reg, configs[i]);
+ } /* for each config */
return 0;
}
@@ -373,8 +390,9 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
seq_printf(s, "\n");
grp = &info->groups[group];
for (i = 0; i < grp->npins; i++) {
- name = pin_get_name(pctldev, grp->pins[i]);
- ret = imx_pinconf_get(pctldev, grp->pins[i], &config);
+ struct imx_pin *pin = &grp->pins[i];
+ name = pin_get_name(pctldev, pin->pin);
+ ret = imx_pinconf_get(pctldev, pin->pin, &config);
if (ret)
return;
seq_printf(s, "%s: 0x%lx", name, config);
@@ -426,28 +444,31 @@ static int imx_pinctrl_parse_groups(struct device_node *np,
* do sanity check and calculate pins number
*/
list = of_get_property(np, "fsl,pins", &size);
+ if (!list) {
+ dev_err(info->dev, "no fsl,pins property in node %s\n", np->full_name);
+ return -EINVAL;
+ }
+
/* we do not check return since it's safe node passed down */
if (!size || size % pin_size) {
- dev_err(info->dev, "Invalid fsl,pins property\n");
+ dev_err(info->dev, "Invalid fsl,pins property in node %s\n", np->full_name);
return -EINVAL;
}
grp->npins = size / pin_size;
- grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(struct imx_pin),
GFP_KERNEL);
- grp->mux_mode = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
- GFP_KERNEL);
- grp->input_reg = devm_kzalloc(info->dev, grp->npins * sizeof(u16),
- GFP_KERNEL);
- grp->input_val = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
- GFP_KERNEL);
- grp->configs = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned long),
+ grp->pin_ids = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
GFP_KERNEL);
+ if (!grp->pins || ! grp->pin_ids)
+ return -ENOMEM;
+
for (i = 0; i < grp->npins; i++) {
u32 mux_reg = be32_to_cpu(*list++);
u32 conf_reg;
unsigned int pin_id;
struct imx_pin_reg *pin_reg;
+ struct imx_pin *pin = &grp->pins[i];
if (info->flags & SHARE_MUX_CONF_REG)
conf_reg = mux_reg;
@@ -456,23 +477,23 @@ static int imx_pinctrl_parse_groups(struct device_node *np,
pin_id = mux_reg ? mux_reg / 4 : conf_reg / 4;
pin_reg = &info->pin_regs[pin_id];
- grp->pins[i] = pin_id;
+ pin->pin = pin_id;
+ grp->pin_ids[i] = pin_id;
pin_reg->mux_reg = mux_reg;
pin_reg->conf_reg = conf_reg;
- grp->input_reg[i] = be32_to_cpu(*list++);
- grp->mux_mode[i] = be32_to_cpu(*list++);
- grp->input_val[i] = be32_to_cpu(*list++);
+ pin->input_reg = be32_to_cpu(*list++);
+ pin->mux_mode = be32_to_cpu(*list++);
+ pin->input_val = be32_to_cpu(*list++);
/* SION bit is in mux register */
config = be32_to_cpu(*list++);
if (config & IMX_PAD_SION)
- grp->mux_mode[i] |= IOMUXC_CONFIG_SION;
- grp->configs[i] = config & ~IMX_PAD_SION;
- }
+ pin->mux_mode |= IOMUXC_CONFIG_SION;
+ pin->config = config & ~IMX_PAD_SION;
-#ifdef DEBUG
- IMX_PMX_DUMP(info, grp->pins, grp->mux_mode, grp->configs, grp->npins);
-#endif
+ dev_dbg(info->dev, "%s: %d 0x%08lx", info->pins[i].name,
+ pin->mux_mode, pin->config);
+ }
return 0;
}
@@ -484,7 +505,6 @@ static int imx_pinctrl_parse_functions(struct device_node *np,
struct device_node *child;
struct imx_pmx_func *func;
struct imx_pin_group *grp;
- int ret;
static u32 grp_index;
u32 i = 0;
@@ -496,7 +516,7 @@ static int imx_pinctrl_parse_functions(struct device_node *np,
func->name = np->name;
func->num_groups = of_get_child_count(np);
if (func->num_groups <= 0) {
- dev_err(info->dev, "no groups defined\n");
+ dev_err(info->dev, "no groups defined in %s\n", np->full_name);
return -EINVAL;
}
func->groups = devm_kzalloc(info->dev,
@@ -505,9 +525,7 @@ static int imx_pinctrl_parse_functions(struct device_node *np,
for_each_child_of_node(np, child) {
func->groups[i] = child->name;
grp = &info->groups[grp_index++];
- ret = imx_pinctrl_parse_groups(child, grp, info, i++);
- if (ret)
- return ret;
+ imx_pinctrl_parse_groups(child, grp, info, i++);
}
return 0;
@@ -518,7 +536,6 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev,
{
struct device_node *np = pdev->dev.of_node;
struct device_node *child;
- int ret;
u32 nfuncs = 0;
u32 i = 0;
@@ -545,13 +562,8 @@ static int imx_pinctrl_probe_dt(struct platform_device *pdev,
if (!info->groups)
return -ENOMEM;
- for_each_child_of_node(np, child) {
- ret = imx_pinctrl_parse_functions(child, info, i++);
- if (ret) {
- dev_err(&pdev->dev, "failed to parse function\n");
- return ret;
- }
- }
+ for_each_child_of_node(np, child)
+ imx_pinctrl_parse_functions(child, info, i++);
return 0;
}
@@ -580,9 +592,6 @@ int imx_pinctrl_probe(struct platform_device *pdev,
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
ipctl->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ipctl->base))
return PTR_ERR(ipctl->base);