summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/renesas/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/renesas/core.c')
-rw-r--r--drivers/pinctrl/renesas/core.c219
1 files changed, 199 insertions, 20 deletions
diff --git a/drivers/pinctrl/renesas/core.c b/drivers/pinctrl/renesas/core.c
index 0d4ea2e22a53..d0d4714731c1 100644
--- a/drivers/pinctrl/renesas/core.c
+++ b/drivers/pinctrl/renesas/core.c
@@ -636,6 +636,12 @@ static const struct of_device_id sh_pfc_of_table[] = {
.data = &r8a779a0_pinmux_info,
},
#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A779F0
+ {
+ .compatible = "renesas,pfc-r8a779f0",
+ .data = &r8a779f0_pinmux_info,
+ },
+#endif
#ifdef CONFIG_PINCTRL_PFC_SH73A0
{
.compatible = "renesas,pfc-sh73a0",
@@ -741,10 +747,13 @@ static int sh_pfc_suspend_init(struct sh_pfc *pfc) { return 0; }
#ifdef DEBUG
#define SH_PFC_MAX_REGS 300
-#define SH_PFC_MAX_ENUMS 3000
+#define SH_PFC_MAX_ENUMS 5000
static unsigned int sh_pfc_errors __initdata;
static unsigned int sh_pfc_warnings __initdata;
+static bool sh_pfc_bias_done __initdata;
+static bool sh_pfc_drive_done __initdata;
+static bool sh_pfc_power_done __initdata;
static struct {
u32 reg;
u32 bits;
@@ -758,6 +767,15 @@ static u32 sh_pfc_num_enums __initdata;
pr_err("%s: " fmt, drvname, ##__VA_ARGS__); \
sh_pfc_errors++; \
} while (0)
+
+#define sh_pfc_err_once(type, fmt, ...) \
+ do { \
+ if (!sh_pfc_ ## type ## _done) { \
+ sh_pfc_ ## type ## _done = true; \
+ sh_pfc_err(fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
#define sh_pfc_warn(fmt, ...) \
do { \
pr_warn("%s: " fmt, drvname, ##__VA_ARGS__); \
@@ -777,10 +795,7 @@ static bool __init is0s(const u16 *enum_ids, unsigned int n)
static bool __init same_name(const char *a, const char *b)
{
- if (!a || !b)
- return false;
-
- return !strcmp(a, b);
+ return a && b && !strcmp(a, b);
}
static void __init sh_pfc_check_reg(const char *drvname, u32 reg, u32 bits)
@@ -839,21 +854,22 @@ static void __init sh_pfc_check_reg_enums(const char *drvname, u32 reg,
}
}
-static void __init sh_pfc_check_pin(const struct sh_pfc_soc_info *info,
- u32 reg, unsigned int pin)
+static const struct sh_pfc_pin __init *sh_pfc_find_pin(
+ const struct sh_pfc_soc_info *info, u32 reg, unsigned int pin)
{
const char *drvname = info->name;
unsigned int i;
if (pin == SH_PFC_PIN_NONE)
- return;
+ return NULL;
for (i = 0; i < info->nr_pins; i++) {
if (pin == info->pins[i].pin)
- return;
+ return &info->pins[i];
}
sh_pfc_err("reg 0x%x: pin %u not found\n", reg, pin);
+ return NULL;
}
static void __init sh_pfc_check_cfg_reg(const char *drvname,
@@ -865,7 +881,8 @@ static void __init sh_pfc_check_cfg_reg(const char *drvname,
GENMASK(cfg_reg->reg_width - 1, 0));
if (cfg_reg->field_width) {
- n = cfg_reg->reg_width / cfg_reg->field_width;
+ fw = cfg_reg->field_width;
+ n = (cfg_reg->reg_width / fw) << fw;
/* Skip field checks (done at build time) */
goto check_enum_ids;
}
@@ -893,6 +910,8 @@ check_enum_ids:
static void __init sh_pfc_check_drive_reg(const struct sh_pfc_soc_info *info,
const struct pinmux_drive_reg *drive)
{
+ const char *drvname = info->name;
+ const struct sh_pfc_pin *pin;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(drive->fields); i++) {
@@ -905,13 +924,18 @@ static void __init sh_pfc_check_drive_reg(const struct sh_pfc_soc_info *info,
GENMASK(field->offset + field->size - 1,
field->offset));
- sh_pfc_check_pin(info, drive->reg, field->pin);
+ pin = sh_pfc_find_pin(info, drive->reg, field->pin);
+ if (pin && !(pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH))
+ sh_pfc_err("drive_reg 0x%x: field %u: pin %s lacks SH_PFC_PIN_CFG_DRIVE_STRENGTH flag\n",
+ drive->reg, i, pin->name);
}
}
static void __init sh_pfc_check_bias_reg(const struct sh_pfc_soc_info *info,
const struct pinmux_bias_reg *bias)
{
+ const char *drvname = info->name;
+ const struct sh_pfc_pin *pin;
unsigned int i;
u32 bits;
@@ -923,12 +947,66 @@ static void __init sh_pfc_check_bias_reg(const struct sh_pfc_soc_info *info,
sh_pfc_check_reg(info->name, bias->puen, bits);
if (bias->pud)
sh_pfc_check_reg(info->name, bias->pud, bits);
- for (i = 0; i < ARRAY_SIZE(bias->pins); i++)
- sh_pfc_check_pin(info, bias->puen, bias->pins[i]);
+ for (i = 0; i < ARRAY_SIZE(bias->pins); i++) {
+ pin = sh_pfc_find_pin(info, bias->puen, bias->pins[i]);
+ if (!pin)
+ continue;
+
+ if (bias->puen && bias->pud) {
+ /*
+ * Pull-enable and pull-up/down control registers
+ * As some SoCs have pins that support only pull-up
+ * or pull-down, we just check for one of them
+ */
+ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN))
+ sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks one or more SH_PFC_PIN_CFG_PULL_* flags\n",
+ bias->puen, i, pin->name);
+ } else if (bias->puen) {
+ /* Pull-up control register only */
+ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_UP))
+ sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_UP flag\n",
+ bias->puen, i, pin->name);
+ } else if (bias->pud) {
+ /* Pull-down control register only */
+ if (!(pin->configs & SH_PFC_PIN_CFG_PULL_DOWN))
+ sh_pfc_err("bias_reg 0x%x:%u: pin %s lacks SH_PFC_PIN_CFG_PULL_DOWN flag\n",
+ bias->pud, i, pin->name);
+ }
+ }
+}
+
+static void __init sh_pfc_compare_groups(const char *drvname,
+ const struct sh_pfc_pin_group *a,
+ const struct sh_pfc_pin_group *b)
+{
+ unsigned int i;
+ size_t len;
+
+ if (same_name(a->name, b->name))
+ sh_pfc_err("group %s: name conflict\n", a->name);
+
+ if (a->nr_pins > b->nr_pins)
+ swap(a, b);
+
+ len = a->nr_pins * sizeof(a->pins[0]);
+ for (i = 0; i <= b->nr_pins - a->nr_pins; i++) {
+ if (a->pins == b->pins + i || a->mux == b->mux + i ||
+ memcmp(a->pins, b->pins + i, len) ||
+ memcmp(a->mux, b->mux + i, len))
+ continue;
+
+ if (a->nr_pins == b->nr_pins)
+ sh_pfc_warn("group %s can be an alias for %s\n",
+ a->name, b->name);
+ else
+ sh_pfc_warn("group %s is a subset of %s\n", a->name,
+ b->name);
+ }
}
static void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info)
{
+ const struct pinmux_drive_reg *drive_regs = info->drive_regs;
const struct pinmux_bias_reg *bias_regs = info->bias_regs;
const char *drvname = info->name;
unsigned int *refcnts;
@@ -937,10 +1015,14 @@ static void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info)
pr_info("sh_pfc: Checking %s\n", drvname);
sh_pfc_num_regs = 0;
sh_pfc_num_enums = 0;
+ sh_pfc_bias_done = false;
+ sh_pfc_drive_done = false;
+ sh_pfc_power_done = false;
/* Check pins */
for (i = 0; i < info->nr_pins; i++) {
const struct sh_pfc_pin *pin = &info->pins[i];
+ unsigned int x;
if (!pin->name) {
sh_pfc_err("empty pin %u\n", i);
@@ -962,6 +1044,65 @@ static void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info)
pin->name, pin2->name,
pin->enum_id);
}
+
+ if (pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN) {
+ if (!info->ops || !info->ops->get_bias ||
+ !info->ops->set_bias)
+ sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_* flag set but .[gs]et_bias() not implemented\n");
+
+ if (!bias_regs &&
+ (!info->ops || !info->ops->pin_to_portcr))
+ sh_pfc_err_once(bias, "SH_PFC_PIN_CFG_PULL_UP flag set but no bias_regs defined and .pin_to_portcr() not implemented\n");
+ }
+
+ if ((pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN) && bias_regs) {
+ const struct pinmux_bias_reg *bias_reg =
+ rcar_pin_to_bias_reg(info, pin->pin, &x);
+
+ if (!bias_reg ||
+ ((pin->configs & SH_PFC_PIN_CFG_PULL_UP) &&
+ !bias_reg->puen))
+ sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_UP flag set but pin not in bias_regs\n",
+ pin->name);
+
+ if (!bias_reg ||
+ ((pin->configs & SH_PFC_PIN_CFG_PULL_DOWN) &&
+ !bias_reg->pud))
+ sh_pfc_err("pin %s: SH_PFC_PIN_CFG_PULL_DOWN flag set but pin not in bias_regs\n",
+ pin->name);
+ }
+
+ if (pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH) {
+ if (!drive_regs) {
+ sh_pfc_err_once(drive, "SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but drive_regs missing\n");
+ } else {
+ for (j = 0; drive_regs[j / 8].reg; j++) {
+ if (!drive_regs[j / 8].fields[j % 8].pin &&
+ !drive_regs[j / 8].fields[j % 8].offset &&
+ !drive_regs[j / 8].fields[j % 8].size)
+ continue;
+
+ if (drive_regs[j / 8].fields[j % 8].pin == pin->pin)
+ break;
+ }
+
+ if (!drive_regs[j / 8].reg)
+ sh_pfc_err("pin %s: SH_PFC_PIN_CFG_DRIVE_STRENGTH flag set but not in drive_regs\n",
+ pin->name);
+ }
+ }
+
+ if (pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE) {
+ if (!info->ops || !info->ops->pin_to_pocctrl)
+ sh_pfc_err_once(power, "SH_PFC_PIN_CFG_IO_VOLTAGE flag set but .pin_to_pocctrl() not implemented\n");
+ else if (info->ops->pin_to_pocctrl(pin->pin, &x) < 0)
+ sh_pfc_err("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE set but invalid pin_to_pocctrl()\n",
+ pin->name);
+ } else if (info->ops && info->ops->pin_to_pocctrl &&
+ info->ops->pin_to_pocctrl(pin->pin, &x) >= 0) {
+ sh_pfc_warn("pin %s: SH_PFC_PIN_CFG_IO_VOLTAGE not set but valid pin_to_pocctrl()\n",
+ pin->name);
+ }
}
/* Check groups and functions */
@@ -1003,11 +1144,9 @@ static void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info)
sh_pfc_err("empty group %u\n", i);
continue;
}
- for (j = 0; j < i; j++) {
- if (same_name(group->name, info->groups[j].name))
- sh_pfc_err("group %s: name conflict\n",
- group->name);
- }
+ for (j = 0; j < i; j++)
+ sh_pfc_compare_groups(drvname, group, &info->groups[j]);
+
if (!refcnts[i])
sh_pfc_err("orphan group %s\n", group->name);
else if (refcnts[i] > 1)
@@ -1022,13 +1161,53 @@ static void __init sh_pfc_check_info(const struct sh_pfc_soc_info *info)
sh_pfc_check_cfg_reg(drvname, &info->cfg_regs[i]);
/* Check drive strength registers */
- for (i = 0; info->drive_regs && info->drive_regs[i].reg; i++)
- sh_pfc_check_drive_reg(info, &info->drive_regs[i]);
+ for (i = 0; drive_regs && drive_regs[i].reg; i++)
+ sh_pfc_check_drive_reg(info, &drive_regs[i]);
+
+ for (i = 0; drive_regs && drive_regs[i / 8].reg; i++) {
+ if (!drive_regs[i / 8].fields[i % 8].pin &&
+ !drive_regs[i / 8].fields[i % 8].offset &&
+ !drive_regs[i / 8].fields[i % 8].size)
+ continue;
+
+ for (j = 0; j < i; j++) {
+ if (drive_regs[i / 8].fields[i % 8].pin ==
+ drive_regs[j / 8].fields[j % 8].pin &&
+ drive_regs[j / 8].fields[j % 8].offset &&
+ drive_regs[j / 8].fields[j % 8].size) {
+ sh_pfc_err("drive_reg 0x%x:%u/0x%x:%u: pin conflict\n",
+ drive_regs[i / 8].reg, i % 8,
+ drive_regs[j / 8].reg, j % 8);
+ }
+ }
+ }
/* Check bias registers */
for (i = 0; bias_regs && (bias_regs[i].puen || bias_regs[i].pud); i++)
sh_pfc_check_bias_reg(info, &bias_regs[i]);
+ for (i = 0; bias_regs &&
+ (bias_regs[i / 32].puen || bias_regs[i / 32].pud); i++) {
+ if (bias_regs[i / 32].pins[i % 32] == SH_PFC_PIN_NONE)
+ continue;
+
+ for (j = 0; j < i; j++) {
+ if (bias_regs[i / 32].pins[i % 32] !=
+ bias_regs[j / 32].pins[j % 32])
+ continue;
+
+ if (bias_regs[i / 32].puen && bias_regs[j / 32].puen)
+ sh_pfc_err("bias_reg 0x%x:%u/0x%x:%u: pin conflict\n",
+ bias_regs[i / 32].puen, i % 32,
+ bias_regs[j / 32].puen, j % 32);
+ if (bias_regs[i / 32].pud && bias_regs[j / 32].pud)
+ sh_pfc_err("bias_reg 0x%x:%u/0x%x:%u: pin conflict\n",
+ bias_regs[i / 32].pud, i % 32,
+ bias_regs[j / 32].pud, j % 32);
+ }
+
+ }
+
/* Check ioctrl registers */
for (i = 0; info->ioctrl_regs && info->ioctrl_regs[i].reg; i++)
sh_pfc_check_reg(drvname, info->ioctrl_regs[i].reg, U32_MAX);