forked from Minki/linux
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>
This commit is contained in:
parent
0c151062f3
commit
ea770ad2ec
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user