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
|
def_bool y
|
||||||
depends on ARCH_SH73A0
|
depends on ARCH_SH73A0
|
||||||
select PINCTRL_SH_PFC
|
select PINCTRL_SH_PFC
|
||||||
|
select REGULATOR
|
||||||
|
|
||||||
config PINCTRL_PFC_SH7720
|
config PINCTRL_PFC_SH7720
|
||||||
def_bool y
|
def_bool y
|
||||||
|
@ -20,7 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/pinctrl/pinconf-generic.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/sh73a0.h>
|
||||||
#include <mach/irqs.h>
|
#include <mach/irqs.h>
|
||||||
@ -3888,6 +3892,92 @@ static const struct pinmux_irq pinmux_irqs[] = {
|
|||||||
PINMUX_IRQ(EXT_IRQ16L(9), 308),
|
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_OFF (0 << 6)
|
||||||
#define PORTnCR_PULMD_DOWN (2 << 6)
|
#define PORTnCR_PULMD_DOWN (2 << 6)
|
||||||
#define PORTnCR_PULMD_UP (3 << 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);
|
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 = {
|
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,
|
.get_bias = sh73a0_pinmux_get_bias,
|
||||||
.set_bias = sh73a0_pinmux_set_bias,
|
.set_bias = sh73a0_pinmux_set_bias,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user