Model the choice of 1.8V or 3.3V signalling for each SD interface as a regulator. Signed-off-by: Ben Hutchings <ben.hutchings@xxxxxxxxxxxxxxx> --- drivers/pinctrl/sh-pfc/Kconfig | 1 + drivers/pinctrl/sh-pfc/core.c | 2 +- drivers/pinctrl/sh-pfc/core.h | 1 + drivers/pinctrl/sh-pfc/pfc-r8a7790.c | 189 ++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/sh-pfc/Kconfig b/drivers/pinctrl/sh-pfc/Kconfig index 8c4b3d391823..4b1895a6ac69 100644 --- a/drivers/pinctrl/sh-pfc/Kconfig +++ b/drivers/pinctrl/sh-pfc/Kconfig @@ -49,6 +49,7 @@ config PINCTRL_PFC_R8A7790 def_bool y depends on ARCH_R8A7790 select PINCTRL_SH_PFC + select REGULATOR if OF config PINCTRL_PFC_R8A7791 def_bool y diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 7b2c9495c383..7d51f96afc9a 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -92,7 +92,7 @@ static int sh_pfc_map_resources(struct sh_pfc *pfc, return 0; } -static void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg) +void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg) { struct sh_pfc_window *window; phys_addr_t address = reg; diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h index 6dc8a6fc2746..af355629c5d2 100644 --- a/drivers/pinctrl/sh-pfc/core.h +++ b/drivers/pinctrl/sh-pfc/core.h @@ -57,6 +57,7 @@ int sh_pfc_unregister_gpiochip(struct sh_pfc *pfc); int sh_pfc_register_pinctrl(struct sh_pfc *pfc); int sh_pfc_unregister_pinctrl(struct sh_pfc *pfc); +void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 address); u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width); void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width, u32 data); diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index 22a5470889f5..0c9d2c018a10 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -23,6 +23,13 @@ #include <linux/kernel.h> #include <linux/platform_data/gpio-rcar.h> +#ifdef CONFIG_OF +#include <linux/of.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/string.h> +#endif #include "core.h" #include "sh_pfc.h" @@ -5586,8 +5593,190 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { { }, }; +#ifdef CONFIG_OF + +struct r8a7790_sd_regulator_data { + struct regulator_desc desc; + char name[10]; + struct regulator_dev *dev; + int state; +}; + +#define SD_REGULATOR_NAME "regulator-r8a7790-sd" + +#define SD_LOW_VOLTAGE 1800000 +#define SD_STD_VOLTAGE 3300000 + +static int r8a7790_sd_regulator_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned int *selector) +{ + struct r8a7790_sd_regulator_data *drvdata = rdev_get_drvdata(dev); + struct sh_pfc *pfc = dev_get_drvdata(dev->dev.parent); + void __iomem *mapped_reg; + u32 data, mask; + int state; + + if (min_uV <= SD_LOW_VOLTAGE && max_uV >= SD_LOW_VOLTAGE) + state = 0; + else if (min_uV <= SD_STD_VOLTAGE && max_uV >= SD_STD_VOLTAGE) + state = 1; + else + return -EINVAL; + + /* Map IOCTRL6 */ + mapped_reg = sh_pfc_phys_to_virt(pfc, 0xe606008c); + + spin_lock(&pfc->lock); + + data = sh_pfc_read_raw_reg(mapped_reg, 32); + + /* Set I/O voltage for the 8 pins for this SD interface */ + mask = 0xff << (24 - drvdata->desc.id * 8); + if (state) + data |= mask; + else + data &= ~mask; + + sh_pfc_write_raw_reg( + sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32, + ~data); + sh_pfc_write_raw_reg(mapped_reg, 32, data); + + spin_unlock(&pfc->lock); + + drvdata->state = state; + if (selector) + *selector = state; + + return 0; +} + +static int r8a7790_sd_regulator_list_voltage(struct regulator_dev *dev, + unsigned int selector) +{ + switch (selector) { + case 0: + return SD_LOW_VOLTAGE; + case 1: + return SD_STD_VOLTAGE; + default: + return -EINVAL; + } +} + +static int r8a7790_sd_regulator_get_voltage(struct regulator_dev *dev) +{ + struct r8a7790_sd_regulator_data *drvdata = rdev_get_drvdata(dev); + + return r8a7790_sd_regulator_list_voltage(dev, drvdata->state); +} + +static const struct regulator_ops r8a7790_sd_regulator_ops = { + .set_voltage = r8a7790_sd_regulator_set_voltage, + .get_voltage = r8a7790_sd_regulator_get_voltage, + .list_voltage = r8a7790_sd_regulator_list_voltage, +}; + +static const struct regulator_init_data r8a7790_sd_regulator_init = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .min_uV = SD_LOW_VOLTAGE, + .max_uV = SD_STD_VOLTAGE, + }, +}; + +static int r8a7790_sd_regulator_probe(struct sh_pfc *pfc, int index) +{ + char child_name[20]; + struct device_node *np; + struct r8a7790_sd_regulator_data *drvdata; + struct regulator_config cfg = { }; + void __iomem *mapped_reg; + int ret; + + snprintf(child_name, sizeof(child_name), "sd-regulator@%d", index); + np = NULL; + while ((np = of_get_next_available_child(pfc->dev->of_node, np))) { + if (!strcmp(kbasename(np->full_name), child_name)) + break; + } + if (!np) { + dev_dbg(pfc->dev, "no %s child node found\n", child_name); + return -ENODEV; + } + + drvdata = devm_kzalloc(pfc->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + goto out; + } + drvdata->desc.owner = THIS_MODULE; + /* XXX drvdata->desc.enable_time = ???; */ + drvdata->desc.id = index; + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.ops = &r8a7790_sd_regulator_ops; + drvdata->desc.n_voltages = 2; + + snprintf(drvdata->name, sizeof(drvdata->name), "sd%d-vccq", index); + drvdata->desc.name = drvdata->name; + + /* Read initial state from IOCTRL6 */ + mapped_reg = sh_pfc_phys_to_virt(pfc, 0xe606008c); + switch ((sh_pfc_read_raw_reg(mapped_reg, 32) >> (24 - index * 8)) & + 0xff) { + case 0: /* low = 1.8V */ + drvdata->state = 0; + break; + case 0xff: /* standard = 3.3V */ + drvdata->state = 1; + break; + default: /* mixed?! */ + drvdata->state = -1; + break; + } + + cfg.dev = pfc->dev; + cfg.of_node = np; + cfg.driver_data = drvdata; + cfg.init_data = &r8a7790_sd_regulator_init; + + drvdata->dev = devm_regulator_register(pfc->dev, &drvdata->desc, &cfg); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + dev_err(pfc->dev, "Failed to register regulator: %d\n", ret); + } + +out: + of_node_put(np); + return ret; +} + +static int r8a7790_pinmux_soc_init(struct sh_pfc *pfc) +{ + int i, ret; + + for (i = 0; i < 4; ++i) { + ret = r8a7790_sd_regulator_probe(pfc, i); + if (ret && ret != -ENODEV) + return ret; + } + + return 0; +} + +#endif /* CONFIG_OF */ + +static const struct sh_pfc_soc_operations pinmux_ops = { +#ifdef CONFIG_OF + .init = r8a7790_pinmux_soc_init, +#endif +}; + const struct sh_pfc_soc_info r8a7790_pinmux_info = { .name = "r8a77900_pfc", + .ops = &pinmux_ops, + .unlock_reg = 0xe6060000, /* PMMR */ .function = { PINMUX_FUNCTION_BEGIN, PINMUX_FUNCTION_END }, -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html