| From 79998aa5ef8dbec4b49324259fe9381bd81fc9e8 Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Date: Sun, 21 Apr 2013 23:26:26 +0200 |
| Subject: sh-pfc: sh73a0: Add VCCQ MC0 regulator |
| |
| The sh73a0 has an internal power gate on the VCCQ power supply for the |
| SDHI0 device that is controlled (for some strange reason) by a bit in a |
| PFC register. This feature should be exposed as a regulator. |
| |
| As the same register is also used for pin control purposes there is no |
| way to achieve atomic read/write sequences with a separate regulator |
| driver. We thus need to implement the regulator here. |
| |
| Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> |
| Acked-by: Linus Walleij <linus.walleij@linaro.org> |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| (cherry picked from commit ea770ad2ec054e26076d677f2e87add53712941c) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/pinctrl/sh-pfc/Kconfig | 1 + |
| drivers/pinctrl/sh-pfc/pfc-sh73a0.c | 134 ++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 135 insertions(+) |
| |
| diff --git a/drivers/pinctrl/sh-pfc/Kconfig b/drivers/pinctrl/sh-pfc/Kconfig |
| index 32161c4f..636a882b 100644 |
| --- a/drivers/pinctrl/sh-pfc/Kconfig |
| +++ b/drivers/pinctrl/sh-pfc/Kconfig |
| @@ -72,6 +72,7 @@ config PINCTRL_PFC_SH73A0 |
| def_bool y |
| depends on ARCH_SH73A0 |
| select PINCTRL_SH_PFC |
| + select REGULATOR |
| |
| config PINCTRL_PFC_SH7720 |
| def_bool y |
| diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c |
| index 587f7772..b7837248 100644 |
| --- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c |
| +++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c |
| @@ -20,7 +20,11 @@ |
| */ |
| #include <linux/io.h> |
| #include <linux/kernel.h> |
| +#include <linux/module.h> |
| #include <linux/pinctrl/pinconf-generic.h> |
| +#include <linux/regulator/driver.h> |
| +#include <linux/regulator/machine.h> |
| +#include <linux/slab.h> |
| |
| #include <mach/sh73a0.h> |
| #include <mach/irqs.h> |
| @@ -3888,6 +3892,92 @@ static const struct pinmux_irq pinmux_irqs[] = { |
| PINMUX_IRQ(EXT_IRQ16L(9), 308), |
| }; |
| |
| +/* ----------------------------------------------------------------------------- |
| + * VCCQ MC0 regulator |
| + */ |
| + |
| +static void sh73a0_vccq_mc0_endisable(struct regulator_dev *reg, bool enable) |
| +{ |
| + struct sh_pfc *pfc = reg->reg_data; |
| + void __iomem *addr = pfc->window[1].virt + 4; |
| + unsigned long flags; |
| + u32 value; |
| + |
| + spin_lock_irqsave(&pfc->lock, flags); |
| + |
| + value = ioread32(addr); |
| + |
| + if (enable) |
| + value |= BIT(28); |
| + else |
| + value &= ~BIT(28); |
| + |
| + iowrite32(value, addr); |
| + |
| + spin_unlock_irqrestore(&pfc->lock, flags); |
| +} |
| + |
| +static int sh73a0_vccq_mc0_enable(struct regulator_dev *reg) |
| +{ |
| + sh73a0_vccq_mc0_endisable(reg, true); |
| + return 0; |
| +} |
| + |
| +static int sh73a0_vccq_mc0_disable(struct regulator_dev *reg) |
| +{ |
| + sh73a0_vccq_mc0_endisable(reg, false); |
| + return 0; |
| +} |
| + |
| +static int sh73a0_vccq_mc0_is_enabled(struct regulator_dev *reg) |
| +{ |
| + struct sh_pfc *pfc = reg->reg_data; |
| + void __iomem *addr = pfc->window[1].virt + 4; |
| + unsigned long flags; |
| + u32 value; |
| + |
| + spin_lock_irqsave(&pfc->lock, flags); |
| + value = ioread32(addr); |
| + spin_unlock_irqrestore(&pfc->lock, flags); |
| + |
| + return !!(value & BIT(28)); |
| +} |
| + |
| +static int sh73a0_vccq_mc0_get_voltage(struct regulator_dev *reg) |
| +{ |
| + return 3300000; |
| +} |
| + |
| +static struct regulator_ops sh73a0_vccq_mc0_ops = { |
| + .enable = sh73a0_vccq_mc0_enable, |
| + .disable = sh73a0_vccq_mc0_disable, |
| + .is_enabled = sh73a0_vccq_mc0_is_enabled, |
| + .get_voltage = sh73a0_vccq_mc0_get_voltage, |
| +}; |
| + |
| +static const struct regulator_desc sh73a0_vccq_mc0_desc = { |
| + .owner = THIS_MODULE, |
| + .name = "vccq_mc0", |
| + .type = REGULATOR_VOLTAGE, |
| + .ops = &sh73a0_vccq_mc0_ops, |
| +}; |
| + |
| +static struct regulator_consumer_supply sh73a0_vccq_mc0_consumers[] = { |
| + REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"), |
| +}; |
| + |
| +static const struct regulator_init_data sh73a0_vccq_mc0_init_data = { |
| + .constraints = { |
| + .valid_ops_mask = REGULATOR_CHANGE_STATUS, |
| + }, |
| + .num_consumer_supplies = ARRAY_SIZE(sh73a0_vccq_mc0_consumers), |
| + .consumer_supplies = sh73a0_vccq_mc0_consumers, |
| +}; |
| + |
| +/* ----------------------------------------------------------------------------- |
| + * Pin bias |
| + */ |
| + |
| #define PORTnCR_PULMD_OFF (0 << 6) |
| #define PORTnCR_PULMD_DOWN (2 << 6) |
| #define PORTnCR_PULMD_UP (3 << 6) |
| @@ -3934,7 +4024,51 @@ static void sh73a0_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin, |
| iowrite8(value, addr); |
| } |
| |
| +/* ----------------------------------------------------------------------------- |
| + * SoC information |
| + */ |
| + |
| +struct sh73a0_pinmux_data { |
| + struct regulator_dev *vccq_mc0; |
| +}; |
| + |
| +static int sh73a0_pinmux_soc_init(struct sh_pfc *pfc) |
| +{ |
| + struct sh73a0_pinmux_data *data; |
| + struct regulator_config cfg = { }; |
| + int ret; |
| + |
| + data = devm_kzalloc(pfc->dev, sizeof(*data), GFP_KERNEL); |
| + if (data == NULL) |
| + return -ENOMEM; |
| + |
| + cfg.dev = pfc->dev; |
| + cfg.init_data = &sh73a0_vccq_mc0_init_data; |
| + cfg.driver_data = pfc; |
| + |
| + data->vccq_mc0 = regulator_register(&sh73a0_vccq_mc0_desc, &cfg); |
| + if (IS_ERR(data->vccq_mc0)) { |
| + ret = PTR_ERR(data->vccq_mc0); |
| + dev_err(pfc->dev, "Failed to register VCCQ MC0 regulator: %d\n", |
| + ret); |
| + return ret; |
| + } |
| + |
| + pfc->soc_data = data; |
| + |
| + return 0; |
| +} |
| + |
| +static void sh73a0_pinmux_soc_exit(struct sh_pfc *pfc) |
| +{ |
| + struct sh73a0_pinmux_data *data = pfc->soc_data; |
| + |
| + regulator_unregister(data->vccq_mc0); |
| +} |
| + |
| static const struct sh_pfc_soc_operations sh73a0_pinmux_ops = { |
| + .init = sh73a0_pinmux_soc_init, |
| + .exit = sh73a0_pinmux_soc_exit, |
| .get_bias = sh73a0_pinmux_get_bias, |
| .set_bias = sh73a0_pinmux_set_bias, |
| }; |
| -- |
| 1.8.4.3.gca3854a |
| |