[RFC PATCH 3/7] pinctrl: sh-pfc: r8a7790: Add regulators for SD voltage switch

Ben Hutchings
2015-04-30T12:31:40Z

Model the choice of 1.8V or 3.3V signalling for each SD interface as a
regulator.

Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
---
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