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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux