diff options
Diffstat (limited to 'drivers/pinctrl/renesas/core.c')
-rw-r--r-- | drivers/pinctrl/renesas/core.c | 219 |
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); |