159 lines
4.5 KiB
Diff
159 lines
4.5 KiB
Diff
|
From c5fda170e87a4bdaeb278f7e50f7a1f654e94eb5 Mon Sep 17 00:00:00 2001
|
||
|
From: Chen-Yu Tsai <wens@csie.org>
|
||
|
Date: Fri, 11 Nov 2016 17:50:35 +0800
|
||
|
Subject: pinctrl: sunxi: Add support for fetching pinconf settings from
|
||
|
hardware
|
||
|
|
||
|
The sunxi pinctrl driver only caches whatever pinconf setting was last
|
||
|
set on a given pingroup. This is not particularly helpful, nor is it
|
||
|
correct.
|
||
|
|
||
|
Fix this by actually reading the hardware registers and returning
|
||
|
the correct results or error codes. Also filter out unsupported
|
||
|
pinconf settings. Since this driver has a peculiar setup of 1 pin
|
||
|
per group, we can support both pin and pingroup pinconf setting
|
||
|
read back with the same code. The sunxi_pconf_reg helper and code
|
||
|
structure is inspired by pinctrl-msm.
|
||
|
|
||
|
With this done we can also claim to support generic pinconf, by
|
||
|
setting .is_generic = true in pinconf_ops.
|
||
|
|
||
|
Also remove the cached config value. The behavior of this was never
|
||
|
correct, as it only cached 1 setting instead of all of them. Since
|
||
|
we can now read back settings directly from the hardware, it is no
|
||
|
longer required.
|
||
|
|
||
|
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
|
||
|
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
|
||
|
---
|
||
|
drivers/pinctrl/sunxi/pinctrl-sunxi.c | 86 +++++++++++++++++++++++++++++++++--
|
||
|
drivers/pinctrl/sunxi/pinctrl-sunxi.h | 1 -
|
||
|
2 files changed, 81 insertions(+), 6 deletions(-)
|
||
|
|
||
|
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
|
||
|
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
|
||
|
@@ -438,15 +438,91 @@ static const struct pinctrl_ops sunxi_pc
|
||
|
.get_group_pins = sunxi_pctrl_get_group_pins,
|
||
|
};
|
||
|
|
||
|
+static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param,
|
||
|
+ u32 *offset, u32 *shift, u32 *mask)
|
||
|
+{
|
||
|
+ switch (param) {
|
||
|
+ case PIN_CONFIG_DRIVE_STRENGTH:
|
||
|
+ *offset = sunxi_dlevel_reg(pin);
|
||
|
+ *shift = sunxi_dlevel_offset(pin);
|
||
|
+ *mask = DLEVEL_PINS_MASK;
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_UP:
|
||
|
+ case PIN_CONFIG_BIAS_PULL_DOWN:
|
||
|
+ case PIN_CONFIG_BIAS_DISABLE:
|
||
|
+ *offset = sunxi_pull_reg(pin);
|
||
|
+ *shift = sunxi_pull_offset(pin);
|
||
|
+ *mask = PULL_PINS_MASK;
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ return -ENOTSUPP;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin,
|
||
|
+ unsigned long *config)
|
||
|
+{
|
||
|
+ struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||
|
+ enum pin_config_param param = pinconf_to_config_param(*config);
|
||
|
+ u32 offset, shift, mask, val;
|
||
|
+ u16 arg;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ pin -= pctl->desc->pin_base;
|
||
|
+
|
||
|
+ ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ val = (readl(pctl->membase + offset) >> shift) & mask;
|
||
|
+
|
||
|
+ switch (pinconf_to_config_param(*config)) {
|
||
|
+ case PIN_CONFIG_DRIVE_STRENGTH:
|
||
|
+ arg = (val + 1) * 10;
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_UP:
|
||
|
+ if (val != SUN4I_PINCTRL_PULL_UP)
|
||
|
+ return -EINVAL;
|
||
|
+ arg = 1; /* hardware is weak pull-up */
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_PULL_DOWN:
|
||
|
+ if (val != SUN4I_PINCTRL_PULL_DOWN)
|
||
|
+ return -EINVAL;
|
||
|
+ arg = 1; /* hardware is weak pull-down */
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PIN_CONFIG_BIAS_DISABLE:
|
||
|
+ if (val != SUN4I_PINCTRL_NO_PULL)
|
||
|
+ return -EINVAL;
|
||
|
+ arg = 0;
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ /* sunxi_pconf_reg should catch anything unsupported */
|
||
|
+ WARN_ON(1);
|
||
|
+ return -ENOTSUPP;
|
||
|
+ }
|
||
|
+
|
||
|
+ *config = pinconf_to_config_packed(param, arg);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev,
|
||
|
unsigned group,
|
||
|
unsigned long *config)
|
||
|
{
|
||
|
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
|
||
|
+ struct sunxi_pinctrl_group *g = &pctl->groups[group];
|
||
|
|
||
|
- *config = pctl->groups[group].config;
|
||
|
-
|
||
|
- return 0;
|
||
|
+ /* We only support 1 pin per group. Chain it to the pin callback */
|
||
|
+ return sunxi_pconf_get(pctldev, g->pin, config);
|
||
|
}
|
||
|
|
||
|
static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
|
||
|
@@ -508,8 +584,6 @@ static int sunxi_pconf_group_set(struct
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
- /* cache the config value */
|
||
|
- g->config = configs[i];
|
||
|
} /* for each config */
|
||
|
|
||
|
spin_unlock_irqrestore(&pctl->lock, flags);
|
||
|
@@ -518,6 +592,8 @@ static int sunxi_pconf_group_set(struct
|
||
|
}
|
||
|
|
||
|
static const struct pinconf_ops sunxi_pconf_ops = {
|
||
|
+ .is_generic = true,
|
||
|
+ .pin_config_get = sunxi_pconf_get,
|
||
|
.pin_config_group_get = sunxi_pconf_group_get,
|
||
|
.pin_config_group_set = sunxi_pconf_group_set,
|
||
|
};
|
||
|
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
|
||
|
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
|
||
|
@@ -109,7 +109,6 @@ struct sunxi_pinctrl_function {
|
||
|
|
||
|
struct sunxi_pinctrl_group {
|
||
|
const char *name;
|
||
|
- unsigned long config;
|
||
|
unsigned pin;
|
||
|
};
|
||
|
|