diff mbox

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

Message ID 1430397095.5802.42.camel@xylophone.i.decadent.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Ben Hutchings April 30, 2015, 12:31 p.m. UTC
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(-)

Comments

Ulf Hansson May 5, 2015, 7:52 a.m. UTC | #1
On 30 April 2015 at 14:31, Ben Hutchings <ben.hutchings@codethink.co.uk> wrote:
> 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>

You need also to send this to the pinctrl maintainer and the corresponding list.

Kind regards
Uffe

> ---
>  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@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings May 6, 2015, 1:12 a.m. UTC | #2
On Tue, 2015-05-05 at 09:52 +0200, Ulf Hansson wrote:
> On 30 April 2015 at 14:31, Ben Hutchings <ben.hutchings@codethink.co.uk> wrote:
> > 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>
> 
> You need also to send this to the pinctrl maintainer and the corresponding list.
[...]

OK, will do next time.

Ben.


--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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 },