summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/aspeed/pinctrl-aspeed.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/aspeed/pinctrl-aspeed.c')
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed.c120
1 files changed, 102 insertions, 18 deletions
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
index b625a657171e..7e0ebf11af16 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
@@ -5,6 +5,7 @@
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "../core.h"
@@ -76,6 +77,9 @@ static int aspeed_sig_expr_enable(struct aspeed_pinmux_data *ctx,
{
int ret;
+ pr_debug("Enabling signal %s for %s\n", expr->signal,
+ expr->function);
+
ret = aspeed_sig_expr_eval(ctx, expr, true);
if (ret < 0)
return ret;
@@ -91,6 +95,9 @@ static int aspeed_sig_expr_disable(struct aspeed_pinmux_data *ctx,
{
int ret;
+ pr_debug("Disabling signal %s for %s\n", expr->signal,
+ expr->function);
+
ret = aspeed_sig_expr_eval(ctx, expr, true);
if (ret < 0)
return ret;
@@ -102,7 +109,8 @@ static int aspeed_sig_expr_disable(struct aspeed_pinmux_data *ctx,
}
/**
- * Disable a signal on a pin by disabling all provided signal expressions.
+ * aspeed_disable_sig() - Disable a signal on a pin by disabling all provided
+ * signal expressions.
*
* @ctx: The pinmux context
* @exprs: The list of signal expressions (from a priority level on a pin)
@@ -115,7 +123,7 @@ static int aspeed_disable_sig(struct aspeed_pinmux_data *ctx,
int ret = 0;
if (!exprs)
- return true;
+ return -EINVAL;
while (*exprs && !ret) {
ret = aspeed_sig_expr_disable(ctx, *exprs);
@@ -126,8 +134,8 @@ static int aspeed_disable_sig(struct aspeed_pinmux_data *ctx,
}
/**
- * Search for the signal expression needed to enable the pin's signal for the
- * requested function.
+ * aspeed_find_expr_by_name - Search for the signal expression needed to
+ * enable the pin's signal for the requested function.
*
* @exprs: List of signal expressions (haystack)
* @name: The name of the requested function (needle)
@@ -229,11 +237,11 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
const struct aspeed_sig_expr **funcs;
const struct aspeed_sig_expr ***prios;
- pr_debug("Muxing pin %d for %s\n", pin, pfunc->name);
-
if (!pdesc)
return -EINVAL;
+ pr_debug("Muxing pin %s for %s\n", pdesc->name, pfunc->name);
+
prios = pdesc->prios;
if (!prios)
@@ -269,6 +277,9 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
ret = aspeed_sig_expr_enable(&pdata->pinmux, expr);
if (ret)
return ret;
+
+ pr_debug("Muxed pin %s as %s for %s\n", pdesc->name, expr->signal,
+ expr->function);
}
return 0;
@@ -277,13 +288,76 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
static bool aspeed_expr_is_gpio(const struct aspeed_sig_expr *expr)
{
/*
- * The signal type is GPIO if the signal name has "GPIO" as a prefix.
- * strncmp (rather than strcmp) is used to implement the prefix
- * requirement.
+ * We need to differentiate between GPIO and non-GPIO signals to
+ * implement the gpio_request_enable() interface. For better or worse
+ * the ASPEED pinctrl driver uses the expression names to determine
+ * whether an expression will mux a pin for GPIO.
+ *
+ * Generally we have the following - A GPIO such as B1 has:
+ *
+ * - expr->signal set to "GPIOB1"
+ * - expr->function set to "GPIOB1"
+ *
+ * Using this fact we can determine whether the provided expression is
+ * a GPIO expression by testing the signal name for the string prefix
+ * "GPIO".
+ *
+ * However, some GPIOs are input-only, and the ASPEED datasheets name
+ * them differently. An input-only GPIO such as T0 has:
+ *
+ * - expr->signal set to "GPIT0"
+ * - expr->function set to "GPIT0"
+ *
+ * It's tempting to generalise the prefix test from "GPIO" to "GPI" to
+ * account for both GPIOs and GPIs, but in doing so we run aground on
+ * another feature:
+ *
+ * Some pins in the ASPEED BMC SoCs have a "pass-through" GPIO
+ * function where the input state of one pin is replicated as the
+ * output state of another (as if they were shorted together - a mux
+ * configuration that is typically enabled by hardware strapping).
+ * This feature allows the BMC to pass e.g. power button state through
+ * to the host while the BMC is yet to boot, but take control of the
+ * button state once the BMC has booted by muxing each pin as a
+ * separate, pin-specific GPIO.
+ *
+ * Conceptually this pass-through mode is a form of GPIO and is named
+ * as such in the datasheets, e.g. "GPID0". This naming similarity
+ * trips us up with the simple GPI-prefixed-signal-name scheme
+ * discussed above, as the pass-through configuration is not what we
+ * want when muxing a pin as GPIO for the GPIO subsystem.
*
- * expr->signal might look like "GPIOT3" in the GPIO case.
+ * On e.g. the AST2400, a pass-through function "GPID0" is grouped on
+ * balls A18 and D16, where we have:
+ *
+ * For ball A18:
+ * - expr->signal set to "GPID0IN"
+ * - expr->function set to "GPID0"
+ *
+ * For ball D16:
+ * - expr->signal set to "GPID0OUT"
+ * - expr->function set to "GPID0"
+ *
+ * By contrast, the pin-specific GPIO expressions for the same pins are
+ * as follows:
+ *
+ * For ball A18:
+ * - expr->signal looks like "GPIOD0"
+ * - expr->function looks like "GPIOD0"
+ *
+ * For ball D16:
+ * - expr->signal looks like "GPIOD1"
+ * - expr->function looks like "GPIOD1"
+ *
+ * Testing both the signal _and_ function names gives us the means
+ * differentiate the pass-through GPIO pinmux configuration from the
+ * pin-specific configuration that the GPIO subsystem is after: An
+ * expression is a pin-specific (non-pass-through) GPIO configuration
+ * if the signal prefix is "GPI" and the signal name matches the
+ * function name.
*/
- return strncmp(expr->signal, "GPIO", 4) == 0;
+ return !strncmp(expr->signal, "GPI", 3) &&
+ !strcmp(expr->signal, expr->function);
}
static bool aspeed_gpio_in_exprs(const struct aspeed_sig_expr **exprs)
@@ -317,6 +391,8 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
if (!prios)
return -ENXIO;
+ pr_debug("Muxing pin %s for GPIO\n", pdesc->name);
+
/* Disable any functions of higher priority than GPIO */
while ((funcs = *prios)) {
if (aspeed_gpio_in_exprs(funcs))
@@ -346,18 +422,26 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev,
* lowest-priority signal type. As such it has no associated
* expression.
*/
- if (!expr)
+ if (!expr) {
+ pr_debug("Muxed pin %s as GPIO\n", pdesc->name);
return 0;
+ }
/*
* If GPIO is not the lowest priority signal type, assume there is only
* one expression defined to enable the GPIO function
*/
- return aspeed_sig_expr_enable(&pdata->pinmux, expr);
+ ret = aspeed_sig_expr_enable(&pdata->pinmux, expr);
+ if (ret)
+ return ret;
+
+ pr_debug("Muxed pin %s as %s\n", pdesc->name, expr->signal);
+
+ return 0;
}
int aspeed_pinctrl_probe(struct platform_device *pdev,
- struct pinctrl_desc *pdesc,
+ const struct pinctrl_desc *pdesc,
struct aspeed_pinctrl_data *pdata)
{
struct device *parent;
@@ -515,14 +599,14 @@ int aspeed_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset,
val = pmap->val << __ffs(pconf->mask);
rc = regmap_update_bits(pdata->scu, pconf->reg,
- pmap->mask, val);
+ pconf->mask, val);
if (rc < 0)
return rc;
- pr_debug("%s: Set SCU%02X[%lu]=%d for param %d(=%d) on pin %d\n",
- __func__, pconf->reg, __ffs(pconf->mask),
- pmap->val, param, arg, offset);
+ pr_debug("%s: Set SCU%02X[0x%08X]=0x%X for param %d(=%d) on pin %d\n",
+ __func__, pconf->reg, pconf->mask,
+ val, param, arg, offset);
}
return 0;