+======================
Aspeed Pin Controllers
-----------------------
+======================
The Aspeed SoCs vary in functionality inside a generation but have a common mux
device register layout.
-Required properties:
-- compatible : Should be any one of the following:
- "aspeed,ast2400-pinctrl"
- "aspeed,g4-pinctrl"
- "aspeed,ast2500-pinctrl"
- "aspeed,g5-pinctrl"
+Required properties for g4:
+- compatible : Should be one of the following:
+ "aspeed,ast2400-pinctrl"
+ "aspeed,g4-pinctrl"
+
+Required properties for g5:
+- compatible : Should be one of the following:
+ "aspeed,ast2500-pinctrl"
+ "aspeed,g5-pinctrl"
+
+- aspeed,external-nodes: A cell of phandles to external controller nodes:
+ 0: compatible with "aspeed,ast2500-gfx", "syscon"
+ 1: compatible with "aspeed,ast2500-lhc", "syscon"
The pin controller node should be the child of a syscon node with the required
property:
Documentation/devicetree/bindings/mfd/syscon.txt
Subnode Format
---------------
+==============
The required properties of child nodes are (as defined in pinctrl-bindings):
- function
RGMII1 RGMII2 RMII1 RMII2 SD1 SPI1 SPI1DEBUG SPI1PASSTHRU TIMER4 TIMER5 TIMER6
TIMER7 TIMER8 VGABIOSROM
+Examples
+========
-Examples:
+g4 Example
+----------
syscon: scu@1e6e2000 {
compatible = "aspeed,ast2400-scu", "syscon", "simple-mfd";
};
};
+g5 Example
+----------
+
+ahb {
+ apb {
+ syscon: scu@1e6e2000 {
+ compatible = "aspeed,ast2500-scu", "syscon", "simple-mfd";
+ reg = <0x1e6e2000 0x1a8>;
+
+ pinctrl: pinctrl {
+ compatible = "aspeed,g5-pinctrl";
+ aspeed,external-nodes = <&gfx &lhc>;
+
+ pinctrl_i2c3_default: i2c3_default {
+ function = "I2C3";
+ groups = "I2C3";
+ };
+ };
+ };
+
+ gfx: display@1e6e6000 {
+ compatible = "aspeed,ast2500-gfx", "syscon";
+ reg = <0x1e6e6000 0x1000>;
+ };
+ };
+
+ lpc: lpc@1e789000 {
+ compatible = "aspeed,ast2500-lpc", "simple-mfd";
+ reg = <0x1e789000 0x1000>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e789000 0x1000>;
+
+ lpc_host: lpc-host@80 {
+ compatible = "aspeed,ast2500-lpc-host", "simple-mfd", "syscon";
+ reg = <0x80 0x1e0>;
+ reg-io-width = <4>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x80 0x1e0>;
+
+ lhc: lhc@20 {
+ compatible = "aspeed,ast2500-lhc";
+ reg = <0x20 0x24 0x48 0x8>;
+ };
+ };
+ };
+};
+
Please refer to pinctrl-bindings.txt in this directory for details of the
common pinctrl bindings used by client devices.
#define UART6_DESC SIG_DESC_SET(SCU90, 7)
#define ROM16_DESC SIG_DESC_SET(SCU90, 6)
#define FLASH_WIDE SIG_DESC_SET(HW_STRAP1, 4)
-#define BOOT_SRC_NOR { HW_STRAP1, GENMASK(1, 0), 0, 0 }
+#define BOOT_SRC_NOR { ASPEED_IP_SCU, HW_STRAP1, GENMASK(1, 0), 0, 0 }
#define A8 56
SIG_EXPR_DECL(ROMD8, ROM16, ROM16_DESC);
#define U1 88
SSSF_PIN_DECL(U1, GPIOL0, NCTS1, SIG_DESC_SET(SCU84, 16));
-#define VPI18_DESC { SCU90, GENMASK(5, 4), 1, 0 }
-#define VPI24_DESC { SCU90, GENMASK(5, 4), 2, 0 }
-#define VPI30_DESC { SCU90, GENMASK(5, 4), 3, 0 }
+#define VPI18_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 1, 0 }
+#define VPI24_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 2, 0 }
+#define VPI30_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 3, 0 }
#define T5 89
#define T5_DESC SIG_DESC_SET(SCU84, 17)
#define U19 139
SSSF_PIN_DECL(U19, GPIOR3, ROMCS4, SIG_DESC_SET(SCU88, 27));
-#define VPOOFF0_DESC { SCU94, GENMASK(1, 0), 0, 0 }
-#define VPO12_DESC { SCU94, GENMASK(1, 0), 1, 0 }
-#define VPO24_DESC { SCU94, GENMASK(1, 0), 2, 0 }
-#define VPOOFF1_DESC { SCU94, GENMASK(1, 0), 3, 0 }
-#define VPO_OFF_12 { SCU94, 0x2, 0, 0 }
+#define VPOOFF0_DESC { ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 0, 0 }
+#define VPO12_DESC { ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 1, 0 }
+#define VPO24_DESC { ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 2, 0 }
+#define VPOOFF1_DESC { ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 3, 0 }
+#define VPO_OFF_12 { ASPEED_IP_SCU, SCU94, 0x2, 0, 0 }
#define VPO_24_OFF SIG_DESC_SET(SCU94, 1)
#define V21 140
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#define ASPEED_G5_NR_PINS 228
-#define COND1 { SCU90, BIT(6), 0, 0 }
-#define COND2 { SCU94, GENMASK(1, 0), 0, 0 }
+#define COND1 { ASPEED_IP_SCU, SCU90, BIT(6), 0, 0 }
+#define COND2 { ASPEED_IP_SCU, SCU94, GENMASK(1, 0), 0, 0 }
#define B14 0
SSSF_PIN_DECL(B14, GPIOA0, MAC1LINK, SIG_DESC_SET(SCU80, 0));
FUNC_GROUP_DECL(GPIE0, B20, C20);
-#define SPI1_DESC { HW_STRAP1, GENMASK(13, 12), 1, 0 }
-#define SPI1DEBUG_DESC { HW_STRAP1, GENMASK(13, 12), 2, 0 }
-#define SPI1PASSTHRU_DESC { HW_STRAP1, GENMASK(13, 12), 3, 0 }
+#define SPI1_DESC \
+ { ASPEED_IP_SCU, HW_STRAP1, GENMASK(13, 12), 1, 0 }
+#define SPI1DEBUG_DESC \
+ { ASPEED_IP_SCU, HW_STRAP1, GENMASK(13, 12), 2, 0 }
+#define SPI1PASSTHRU_DESC \
+ { ASPEED_IP_SCU, HW_STRAP1, GENMASK(13, 12), 3, 0 }
#define C18 64
SIG_EXPR_DECL(SYSCS, SPI1DEBUG, COND1, SPI1DEBUG_DESC);
FUNC_GROUP_DECL(I2C8, P2, R1);
-#define VPIOFF0_DESC { SCU90, GENMASK(5, 4), 0, 0 }
-#define VPIOFF1_DESC { SCU90, GENMASK(5, 4), 1, 0 }
-#define VPI24_DESC { SCU90, GENMASK(5, 4), 2, 0 }
-#define VPIRSVD_DESC { SCU90, GENMASK(5, 4), 3, 0 }
+#define VPIOFF0_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 0, 0 }
+#define VPIOFF1_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 1, 0 }
+#define VPI24_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 2, 0 }
+#define VPIRSVD_DESC { ASPEED_IP_SCU, SCU90, GENMASK(5, 4), 3, 0 }
+
#define V2 104
#define V2_DESC SIG_DESC_SET(SCU88, 0)
static int aspeed_g5_pinctrl_probe(struct platform_device *pdev)
{
int i;
+ struct regmap *map;
+ struct device_node *node;
for (i = 0; i < ARRAY_SIZE(aspeed_g5_pins); i++)
aspeed_g5_pins[i].number = i;
+ node = of_parse_phandle(pdev->dev.of_node, "aspeed,external-nodes", 0);
+ map = syscon_node_to_regmap(node);
+ of_node_put(node);
+ if (IS_ERR(map)) {
+ dev_warn(&pdev->dev, "No GFX phandle found, some mux configurations may fail\n");
+ map = NULL;
+ }
+ aspeed_g5_pinctrl_data.maps[ASPEED_IP_GFX] = map;
+
+ node = of_parse_phandle(pdev->dev.of_node, "aspeed,external-nodes", 1);
+ if (node) {
+ map = syscon_node_to_regmap(node->parent);
+ if (IS_ERR(map)) {
+ dev_warn(&pdev->dev, "LHC parent is not a syscon, some mux configurations may fail\n");
+ map = NULL;
+ }
+ } else {
+ dev_warn(&pdev->dev, "No LHC phandle found, some mux configurations may fail\n");
+ map = NULL;
+ }
+ of_node_put(node);
+ aspeed_g5_pinctrl_data.maps[ASPEED_IP_LPC] = map;
+
return aspeed_pinctrl_probe(pdev, &aspeed_g5_pinctrl_desc,
&aspeed_g5_pinctrl_data);
}
#include "../core.h"
#include "pinctrl-aspeed.h"
+static const char *const aspeed_pinmux_ips[] = {
+ [ASPEED_IP_SCU] = "SCU",
+ [ASPEED_IP_GFX] = "GFX",
+ [ASPEED_IP_LPC] = "LPC",
+};
+
int aspeed_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev);
static inline void aspeed_sig_desc_print_val(
const struct aspeed_sig_desc *desc, bool enable, u32 rv)
{
- pr_debug("SCU%x[0x%08x]=0x%x, got 0x%x from 0x%08x\n", desc->reg,
+ pr_debug("Want %s%X[0x%08X]=0x%X, got 0x%X from 0x%08X\n",
+ aspeed_pinmux_ips[desc->ip], desc->reg,
desc->mask, enable ? desc->enable : desc->disable,
(rv & desc->mask) >> __ffs(desc->mask), rv);
}
*
* @desc: The signal descriptor of interest
* @enabled: True to query the enabled state, false to query disabled state
- * @regmap: The SCU regmap instance
+ * @regmap: The IP block's regmap instance
*
- * @return True if the descriptor's bitfield is configured to the state
- * selected by @enabled, false otherwise
+ * @return 1 if the descriptor's bitfield is configured to the state
+ * selected by @enabled, 0 if not, and less than zero if an unrecoverable
+ * failure occurred
*
* Evaluation of descriptor state is non-trivial in that it is not a binary
* outcome: The bitfields can be greater than one bit in size and thus can take
* descriptor (typically this means a different function to the one of interest
* is enabled). Thus we must explicitly test for either condition as required.
*/
-static bool aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc,
+static int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc,
bool enabled, struct regmap *map)
{
+ int ret;
unsigned int raw;
u32 want;
- if (regmap_read(map, desc->reg, &raw) < 0)
- return false;
+ if (!map)
+ return -ENODEV;
+
+ ret = regmap_read(map, desc->reg, &raw);
+ if (ret)
+ return ret;
aspeed_sig_desc_print_val(desc, enabled, raw);
want = enabled ? desc->enable : desc->disable;
*
* @expr: An expression controlling the signal for a mux function on a pin
* @enabled: True to query the enabled state, false to query disabled state
- * @regmap: The SCU regmap instance
+ * @maps: The list of regmap instances
*
- * @return True if the expression composed by @enabled evaluates true, false
- * otherwise
+ * @return 1 if the expression composed by @enabled evaluates true, 0 if not,
+ * and less than zero if an unrecoverable failure occurred.
*
* A mux function is enabled or disabled if the function's signal expression
* for each pin in the function's pin group evaluates true for the desired
* neither the enabled nor disabled state. Thus we must explicitly test for
* either condition as required.
*/
-static bool aspeed_sig_expr_eval(const struct aspeed_sig_expr *expr,
- bool enabled, struct regmap *map)
+static int aspeed_sig_expr_eval(const struct aspeed_sig_expr *expr,
+ bool enabled, struct regmap * const *maps)
{
int i;
+ int ret;
for (i = 0; i < expr->ndescs; i++) {
const struct aspeed_sig_desc *desc = &expr->descs[i];
- if (!aspeed_sig_desc_eval(desc, enabled, map))
- return false;
+ ret = aspeed_sig_desc_eval(desc, enabled, maps[desc->ip]);
+ if (ret <= 0)
+ return ret;
}
- return true;
+ return 1;
}
/**
* configured
* @enable: true to enable an function's signal through a pin's signal
* expression, false to disable the function's signal
- * @map: The SCU's regmap instance for pinmux register access.
+ * @maps: The list of regmap instances for pinmux register access.
*
- * @return true if the expression is configured as requested, false otherwise
+ * @return 0 if the expression is configured as requested and a negative error
+ * code otherwise
*/
-static bool aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
- bool enable, struct regmap *map)
+static int aspeed_sig_expr_set(const struct aspeed_sig_expr *expr,
+ bool enable, struct regmap * const *maps)
{
+ int ret;
int i;
for (i = 0; i < expr->ndescs; i++) {
- bool ret;
const struct aspeed_sig_desc *desc = &expr->descs[i];
u32 pattern = enable ? desc->enable : desc->disable;
+ u32 val = (pattern << __ffs(desc->mask));
+
+ if (!maps[desc->ip])
+ return -ENODEV;
/*
* Strap registers are configured in hardware or by early-boot
* deconfigured and is the reason we re-evaluate after writing
* all descriptor bits.
*/
- if (desc->reg == HW_STRAP1 || desc->reg == HW_STRAP2)
+ if ((desc->reg == HW_STRAP1 || desc->reg == HW_STRAP2) &&
+ desc->ip == ASPEED_IP_SCU)
continue;
- ret = regmap_update_bits(map, desc->reg, desc->mask,
- pattern << __ffs(desc->mask)) == 0;
+ ret = regmap_update_bits(maps[desc->ip], desc->reg,
+ desc->mask, val);
- if (!ret)
+ if (ret)
return ret;
}
- return aspeed_sig_expr_eval(expr, enable, map);
+ ret = aspeed_sig_expr_eval(expr, enable, maps);
+ if (ret < 0)
+ return ret;
+
+ if (!ret)
+ return -EPERM;
+
+ return 0;
}
-static bool aspeed_sig_expr_enable(const struct aspeed_sig_expr *expr,
- struct regmap *map)
+static int aspeed_sig_expr_enable(const struct aspeed_sig_expr *expr,
+ struct regmap * const *maps)
{
- if (aspeed_sig_expr_eval(expr, true, map))
- return true;
+ int ret;
+
+ ret = aspeed_sig_expr_eval(expr, true, maps);
+ if (ret < 0)
+ return ret;
+
+ if (!ret)
+ return aspeed_sig_expr_set(expr, true, maps);
- return aspeed_sig_expr_set(expr, true, map);
+ return 0;
}
-static bool aspeed_sig_expr_disable(const struct aspeed_sig_expr *expr,
- struct regmap *map)
+static int aspeed_sig_expr_disable(const struct aspeed_sig_expr *expr,
+ struct regmap * const *maps)
{
- if (!aspeed_sig_expr_eval(expr, true, map))
- return true;
+ int ret;
+
+ ret = aspeed_sig_expr_eval(expr, true, maps);
+ if (ret < 0)
+ return ret;
+
+ if (ret)
+ return aspeed_sig_expr_set(expr, false, maps);
- return aspeed_sig_expr_set(expr, false, map);
+ return 0;
}
/**
* Disable a signal on a pin by disabling all provided signal expressions.
*
* @exprs: The list of signal expressions (from a priority level on a pin)
- * @map: The SCU's regmap instance for pinmux register access.
+ * @maps: The list of regmap instances for pinmux register access.
*
- * @return true if all expressions in the list are successfully disabled, false
- * otherwise
+ * @return 0 if all expressions are disabled, otherwise a negative error code
*/
-static bool aspeed_disable_sig(const struct aspeed_sig_expr **exprs,
- struct regmap *map)
+static int aspeed_disable_sig(const struct aspeed_sig_expr **exprs,
+ struct regmap * const *maps)
{
- bool disabled = true;
+ int ret = 0;
if (!exprs)
return true;
- while (*exprs) {
- bool ret;
-
- ret = aspeed_sig_expr_disable(*exprs, map);
- disabled = disabled && ret;
-
+ while (*exprs && !ret) {
+ ret = aspeed_sig_expr_disable(*exprs, maps);
exprs++;
}
- return disabled;
+ return ret;
}
/**
unsigned int group)
{
int i;
+ int ret;
const struct aspeed_pinctrl_data *pdata =
pinctrl_dev_get_drvdata(pctldev);
const struct aspeed_pin_group *pgroup = &pdata->groups[group];
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;
if (expr)
break;
- if (!aspeed_disable_sig(funcs, pdata->map))
- return -EPERM;
+ ret = aspeed_disable_sig(funcs, pdata->maps);
+ if (ret)
+ return ret;
prios++;
}
return -ENXIO;
}
- if (!aspeed_sig_expr_enable(expr, pdata->map))
- return -EPERM;
+ ret = aspeed_sig_expr_enable(expr, pdata->maps);
+ if (ret)
+ return ret;
}
return 0;
struct pinctrl_gpio_range *range,
unsigned int offset)
{
+ int ret;
const struct aspeed_pinctrl_data *pdata =
pinctrl_dev_get_drvdata(pctldev);
const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data;
if (aspeed_gpio_in_exprs(funcs))
break;
- if (!aspeed_disable_sig(funcs, pdata->map))
- return -EPERM;
+ ret = aspeed_disable_sig(funcs, pdata->maps);
+ if (ret)
+ return ret;
prios++;
}
* If GPIO is not the lowest priority signal type, assume there is only
* one expression defined to enable the GPIO function
*/
- if (!aspeed_sig_expr_enable(expr, pdata->map))
- return -EPERM;
-
- return 0;
+ return aspeed_sig_expr_enable(expr, pdata->maps);
}
int aspeed_pinctrl_probe(struct platform_device *pdev,
return -ENODEV;
}
- pdata->map = syscon_node_to_regmap(parent->of_node);
- if (IS_ERR(pdata->map)) {
+ pdata->maps[ASPEED_IP_SCU] = syscon_node_to_regmap(parent->of_node);
+ if (IS_ERR(pdata->maps[ASPEED_IP_SCU])) {
dev_err(&pdev->dev, "No regmap for syscon pincontroller parent\n");
- return PTR_ERR(pdata->map);
+ return PTR_ERR(pdata->maps[ASPEED_IP_SCU]);
}
pctl = pinctrl_register(pdesc, &pdev->dev, pdata);
* group.
*/
+#define ASPEED_IP_SCU 0
+#define ASPEED_IP_GFX 1
+#define ASPEED_IP_LPC 2
+#define ASPEED_NR_PINMUX_IPS 3
+
/*
* The "Multi-function Pins Mapping and Control" table in the SoC datasheet
* references registers by the device/offset mnemonic. The register macros
* A signal descriptor, which describes the register, bits and the
* enable/disable values that should be compared or written.
*
- * @reg: The register offset from base in bytes
+ * @ip: The IP block identifier, used as an index into the regmap array in
+ * struct aspeed_pinctrl_data
+ * @reg: The register offset with respect to the base address of the IP block
* @mask: The mask to apply to the register. The lowest set bit of the mask is
* used to derive the shift value.
* @enable: The value that enables the function. Value should be in the LSBs,
* LSBs, not at the position of the mask.
*/
struct aspeed_sig_desc {
+ unsigned int ip;
unsigned int reg;
u32 mask;
u32 enable;
/* Macro hell */
+#define SIG_DESC_IP_BIT(ip, reg, idx, val) \
+ { ip, reg, BIT_MASK(idx), val, (((val) + 1) & 1) }
+
/**
- * Short-hand macro for describing a configuration enabled by the state of one
- * bit. The disable value is derived.
+ * Short-hand macro for describing an SCU descriptor enabled by the state of
+ * one bit. The disable value is derived.
*
* @reg: The signal's associated register, offset from base
* @idx: The signal's bit index in the register
* @val: The value (0 or 1) that enables the function
*/
#define SIG_DESC_BIT(reg, idx, val) \
- { reg, BIT_MASK(idx), val, (((val) + 1) & 1) }
+ SIG_DESC_IP_BIT(ASPEED_IP_SCU, reg, idx, val)
+
+#define SIG_DESC_IP_SET(ip, reg, idx) SIG_DESC_IP_BIT(ip, reg, idx, 1)
/**
- * A further short-hand macro describing a configuration enabled with a set bit.
+ * A further short-hand macro expanding to an SCU descriptor enabled by a set
+ * bit.
*
- * @reg: The configuration's associated register, offset from base
- * @idx: The configuration's bit index in the register
+ * @reg: The register, offset from base
+ * @idx: The bit index in the register
*/
-#define SIG_DESC_SET(reg, idx) SIG_DESC_BIT(reg, idx, 1)
+#define SIG_DESC_SET(reg, idx) SIG_DESC_IP_BIT(ASPEED_IP_SCU, reg, idx, 1)
#define SIG_DESC_LIST_SYM(sig, func) sig_descs_ ## sig ## _ ## func
#define SIG_DESC_LIST_DECL(sig, func, ...) \
MS_PIN_DECL_(pin, SIG_EXPR_LIST_PTR(gpio))
struct aspeed_pinctrl_data {
- struct regmap *map;
+ struct regmap *maps[ASPEED_NR_PINMUX_IPS];
const struct pinctrl_pin_desc *pins;
const unsigned int npins;