Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6: (27 commits)
  regulator: wm831x_reg_read() failure unnoticed in wm831x_aldo_get_mode()
  twl-regulator: Fix reg_disable functionality for 4030 and 6030
  twl-regulator: Add turnon delay to reg_enable
  twl-regulator: Restore REMAP configuration in regulator probe
  twl-regulator: Add turnon-delay and REMAP config to twlreg_info struct
  twl-regulator: Define critical regulators as always_on
  twl-regulator: Add all twl4030 regulators to twlreg_info
  regulator: mc13783-regulator: correct the probing time.
  regulator: Fix unbalanced disables/enables in regulator_bulk_{enable,disable} error path
  regulator: core.c: Small coding style cleanup (indentation fixup)
  drivers/regulator: use PTR_ERR to get error code
  regulator: consumer.h - fix build when consumer.h is #included first.
  regulator/mc13783: various cleanups
  regulator/mc13783: rename source file to match other drivers
  Fix some AB3100 regulator issues
  regulator: keep index within bounds in da9034_get_ldo12_voltage()
  regulator: Ensure val is initialised in 88pm8607 choose_voltage()
  regulator: Remove duplicate consts from ab3100
  regulator: Handle regulators without suspend mode configuration
  regulator: Factor out regulator name pretty printing
  ...
This commit is contained in:
Linus Torvalds 2009-12-17 08:08:36 -08:00
commit 22a8059359
17 changed files with 1982 additions and 612 deletions

View file

@ -0,0 +1,685 @@
/*
* Regulators driver for Marvell 88PM8607
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/88pm8607.h>
struct pm8607_regulator_info {
struct regulator_desc desc;
struct pm8607_chip *chip;
struct regulator_dev *regulator;
int min_uV;
int max_uV;
int step_uV;
int vol_reg;
int vol_shift;
int vol_nbits;
int update_reg;
int update_bit;
int enable_reg;
int enable_bit;
int slope_double;
};
static inline int check_range(struct pm8607_regulator_info *info,
int min_uV, int max_uV)
{
if (max_uV < info->min_uV || min_uV > info->max_uV || min_uV > max_uV)
return -EINVAL;
return 0;
}
static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
uint8_t chip_id = info->chip->chip_id;
int ret = -EINVAL;
switch (info->desc.id) {
case PM8607_ID_BUCK1:
ret = (index < 0x1d) ? (index * 25000 + 800000) :
((index < 0x20) ? 1500000 :
((index < 0x40) ? ((index - 0x20) * 25000) :
-EINVAL));
break;
case PM8607_ID_BUCK3:
ret = (index < 0x3d) ? (index * 25000) :
((index < 0x40) ? 1500000 : -EINVAL);
if (ret < 0)
break;
if (info->slope_double)
ret <<= 1;
break;
case PM8607_ID_LDO1:
ret = (index == 0) ? 1800000 :
((index == 1) ? 1200000 :
((index == 2) ? 2800000 : -EINVAL));
break;
case PM8607_ID_LDO5:
ret = (index == 0) ? 2900000 :
((index == 1) ? 3000000 :
((index == 2) ? 3100000 : 3300000));
break;
case PM8607_ID_LDO7:
case PM8607_ID_LDO8:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_ID_LDO12:
ret = (index < 2) ? (index * 100000 + 1800000) :
((index < 7) ? (index * 100000 + 2500000) :
((index == 7) ? 3300000 : 1200000));
break;
case PM8607_ID_LDO2:
case PM8607_ID_LDO3:
case PM8607_ID_LDO9:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
3300000);
break;
}
break;
case PM8607_ID_LDO4:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 6) ? (index * 50000 + 2550000) :
((index == 6) ? 2900000 : 3300000));
break;
}
break;
case PM8607_ID_LDO6:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2450000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2500000) :
3300000);
break;
}
break;
case PM8607_ID_LDO10:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
1200000);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
((index == 7) ? 3300000 : 1200000));
break;
}
break;
case PM8607_ID_LDO14:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2600000) :
3300000);
break;
}
break;
}
return ret;
}
static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
uint8_t chip_id = info->chip->chip_id;
int val = -ENOENT;
int ret;
switch (info->desc.id) {
case PM8607_ID_BUCK1:
if (min_uV >= 800000) /* 800mV ~ 1500mV / 25mV */
val = (min_uV - 775001) / 25000;
else { /* 25mV ~ 775mV / 25mV */
val = (min_uV + 249999) / 25000;
val += 32;
}
break;
case PM8607_ID_BUCK3:
if (info->slope_double)
min_uV = min_uV >> 1;
val = (min_uV + 249999) / 25000; /* 0mV ~ 1500mV / 25mV */
break;
case PM8607_ID_LDO1:
if (min_uV > 1800000)
val = 2;
else if (min_uV > 1200000)
val = 0;
else
val = 1;
break;
case PM8607_ID_LDO5:
if (min_uV > 3100000)
val = 3;
else /* 2900mV ~ 3100mV / 100mV */
val = (min_uV - 2800001) / 100000;
break;
case PM8607_ID_LDO7:
case PM8607_ID_LDO8:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0; /* 1800mv */
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_ID_LDO10:
if (min_uV > 2850000)
val = 7;
else if (min_uV <= 1200000)
val = 8;
else if (min_uV < 2700000) /* 1800mV ~ 1900mV / 50mV */
val = (min_uV - 1750001) / 50000;
else { /* 2700mV ~ 2850mV / 50mV */
val = (min_uV - 2650001) / 50000;
val += 3;
}
break;
case PM8607_ID_LDO12:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 100mV */
if (min_uV <= 1200000)
val = 8; /* 1200mV */
else if (min_uV <= 1800000)
val = 0; /* 1800mV */
else if (min_uV <= 1900000)
val = (min_uV - 1700001) / 100000;
else
val = 2; /* 2700mV */
} else { /* 2700mV ~ 3100mV / 100mV */
if (min_uV <= 3100000) {
val = (min_uV - 2600001) / 100000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
case PM8607_ID_LDO2:
case PM8607_ID_LDO3:
case PM8607_ID_LDO9:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2850mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
}
break;
case PM8607_ID_LDO4:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2800mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 2900000)
val = 6;
else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
}
break;
case PM8607_ID_LDO6:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2600000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2600mV */
} else { /* 2600mV ~ 2800mV / 50mV */
if (min_uV <= 2800000) {
val = (min_uV - 2550001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2600000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2600mV */
} else { /* 2600mV ~ 2800mV / 50mV */
if (min_uV <= 2800000) {
val = (min_uV - 2550001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
}
break;
case PM8607_ID_LDO14:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
}
break;
}
if (val >= 0) {
ret = pm8607_list_voltage(rdev, val);
if (ret > max_uV) {
pr_err("exceed voltage range (%d %d) uV",
min_uV, max_uV);
return -EINVAL;
}
} else
pr_err("invalid voltage range (%d %d) uV", min_uV, max_uV);
return val;
}
static int pm8607_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
uint8_t val, mask;
int ret;
if (check_range(info, min_uV, max_uV)) {
pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
return -EINVAL;
}
ret = choose_voltage(rdev, min_uV, max_uV);
if (ret < 0)
return -EINVAL;
val = (uint8_t)(ret << info->vol_shift);
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
ret = pm8607_set_bits(chip, info->vol_reg, mask, val);
if (ret)
return ret;
switch (info->desc.id) {
case PM8607_ID_BUCK1:
case PM8607_ID_BUCK3:
ret = pm8607_set_bits(chip, info->update_reg,
1 << info->update_bit,
1 << info->update_bit);
break;
}
return ret;
}
static int pm8607_get_voltage(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
uint8_t val, mask;
int ret;
ret = pm8607_reg_read(chip, info->vol_reg);
if (ret < 0)
return ret;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
val = ((unsigned char)ret & mask) >> info->vol_shift;
return pm8607_list_voltage(rdev, val);
}
static int pm8607_enable(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
return pm8607_set_bits(chip, info->enable_reg,
1 << info->enable_bit,
1 << info->enable_bit);
}
static int pm8607_disable(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
return pm8607_set_bits(chip, info->enable_reg,
1 << info->enable_bit, 0);
}
static int pm8607_is_enabled(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
int ret;
ret = pm8607_reg_read(chip, info->enable_reg);
if (ret < 0)
return ret;
return !!((unsigned char)ret & (1 << info->enable_bit));
}
static struct regulator_ops pm8607_regulator_ops = {
.set_voltage = pm8607_set_voltage,
.get_voltage = pm8607_get_voltage,
.enable = pm8607_enable,
.disable = pm8607_disable,
.is_enabled = pm8607_is_enabled,
};
#define PM8607_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
{ \
.desc = { \
.name = "BUCK" #_id, \
.ops = &pm8607_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PM8607_ID_BUCK##_id, \
.owner = THIS_MODULE, \
}, \
.min_uV = (min) * 1000, \
.max_uV = (max) * 1000, \
.step_uV = (step) * 1000, \
.vol_reg = PM8607_##vreg, \
.vol_shift = (0), \
.vol_nbits = (nbits), \
.update_reg = PM8607_##ureg, \
.update_bit = (ubit), \
.enable_reg = PM8607_##ereg, \
.enable_bit = (ebit), \
.slope_double = (0), \
}
#define PM8607_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \
{ \
.desc = { \
.name = "LDO" #_id, \
.ops = &pm8607_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PM8607_ID_LDO##_id, \
.owner = THIS_MODULE, \
}, \
.min_uV = (min) * 1000, \
.max_uV = (max) * 1000, \
.step_uV = (step) * 1000, \
.vol_reg = PM8607_##vreg, \
.vol_shift = (shift), \
.vol_nbits = (nbits), \
.enable_reg = PM8607_##ereg, \
.enable_bit = (ebit), \
.slope_double = (0), \
}
static struct pm8607_regulator_info pm8607_regulator_info[] = {
PM8607_DVC(1, 0, 1500, 25, BUCK1, 6, GO, 0, SUPPLIES_EN11, 0),
PM8607_DVC(3, 0, 1500, 25, BUCK3, 6, GO, 2, SUPPLIES_EN11, 2),
PM8607_LDO(1 , 1200, 2800, 0, LDO1 , 0, 2, SUPPLIES_EN11, 3),
PM8607_LDO(2 , 1800, 3300, 0, LDO2 , 0, 3, SUPPLIES_EN11, 4),
PM8607_LDO(3 , 1800, 3300, 0, LDO3 , 0, 3, SUPPLIES_EN11, 5),
PM8607_LDO(4 , 1800, 3300, 0, LDO4 , 0, 3, SUPPLIES_EN11, 6),
PM8607_LDO(5 , 2900, 3300, 0, LDO5 , 0, 2, SUPPLIES_EN11, 7),
PM8607_LDO(6 , 1800, 3300, 0, LDO6 , 0, 3, SUPPLIES_EN12, 0),
PM8607_LDO(7 , 1800, 2900, 0, LDO7 , 0, 3, SUPPLIES_EN12, 1),
PM8607_LDO(8 , 1800, 2900, 0, LDO8 , 0, 3, SUPPLIES_EN12, 2),
PM8607_LDO(9 , 1800, 3300, 0, LDO9 , 0, 3, SUPPLIES_EN12, 3),
PM8607_LDO(10, 1200, 3300, 0, LDO10, 0, 4, SUPPLIES_EN11, 4),
PM8607_LDO(12, 1200, 3300, 0, LDO12, 0, 4, SUPPLIES_EN11, 5),
PM8607_LDO(14, 1800, 3300, 0, LDO14, 0, 3, SUPPLIES_EN11, 6),
};
static inline struct pm8607_regulator_info *find_regulator_info(int id)
{
struct pm8607_regulator_info *info;
int i;
for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
info = &pm8607_regulator_info[i];
if (info->desc.id == id)
return info;
}
return NULL;
}
static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
{
struct pm8607_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm8607_platform_data *pdata = chip->dev->platform_data;
struct pm8607_regulator_info *info = NULL;
info = find_regulator_info(pdev->id);
if (info == NULL) {
dev_err(&pdev->dev, "invalid regulator ID specified\n");
return -EINVAL;
}
info->chip = chip;
info->regulator = regulator_register(&info->desc, &pdev->dev,
pdata->regulator[pdev->id], info);
if (IS_ERR(info->regulator)) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
info->desc.name);
return PTR_ERR(info->regulator);
}
/* check DVC ramp slope double */
if (info->desc.id == PM8607_ID_BUCK3)
if (info->chip->buck3_double)
info->slope_double = 1;
platform_set_drvdata(pdev, info);
return 0;
}
static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
{
struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
regulator_unregister(info->regulator);
return 0;
}
#define PM8607_REGULATOR_DRIVER(_name) \
{ \
.driver = { \
.name = "88pm8607-" #_name, \
.owner = THIS_MODULE, \
}, \
.probe = pm8607_regulator_probe, \
.remove = __devexit_p(pm8607_regulator_remove), \
}
static struct platform_driver pm8607_regulator_driver[] = {
PM8607_REGULATOR_DRIVER(buck1),
PM8607_REGULATOR_DRIVER(buck2),
PM8607_REGULATOR_DRIVER(buck3),
PM8607_REGULATOR_DRIVER(ldo1),
PM8607_REGULATOR_DRIVER(ldo2),
PM8607_REGULATOR_DRIVER(ldo3),
PM8607_REGULATOR_DRIVER(ldo4),
PM8607_REGULATOR_DRIVER(ldo5),
PM8607_REGULATOR_DRIVER(ldo6),
PM8607_REGULATOR_DRIVER(ldo7),
PM8607_REGULATOR_DRIVER(ldo8),
PM8607_REGULATOR_DRIVER(ldo9),
PM8607_REGULATOR_DRIVER(ldo10),
PM8607_REGULATOR_DRIVER(ldo12),
PM8607_REGULATOR_DRIVER(ldo14),
};
static int __init pm8607_regulator_init(void)
{
int i, count, ret;
count = ARRAY_SIZE(pm8607_regulator_driver);
for (i = 0; i < count; i++) {
ret = platform_driver_register(&pm8607_regulator_driver[i]);
if (ret != 0)
pr_err("Failed to register regulator driver: %d\n",
ret);
}
return 0;
}
subsys_initcall(pm8607_regulator_init);
static void __exit pm8607_regulator_exit(void)
{
int i, count;
count = ARRAY_SIZE(pm8607_regulator_driver);
for (i = 0; i < count; i++)
platform_driver_unregister(&pm8607_regulator_driver[i]);
}
module_exit(pm8607_regulator_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
MODULE_ALIAS("platform:88pm8607-regulator");

View file

@ -69,6 +69,13 @@ config REGULATOR_MAX1586
regulator via I2C bus. The provided regulator is suitable regulator via I2C bus. The provided regulator is suitable
for PXA27x chips to control VCC_CORE and VCC_USIM voltages. for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
config REGULATOR_MAX8660
tristate "Maxim 8660/8661 voltage regulator"
depends on I2C
help
This driver controls a Maxim 8660/8661 voltage output
regulator via I2C bus.
config REGULATOR_TWL4030 config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC" bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE depends on TWL4030_CORE
@ -157,5 +164,11 @@ config REGULATOR_TPS6507X
three step-down converters and two general-purpose LDO voltage regulators. three step-down converters and two general-purpose LDO voltage regulators.
It supports TI's software based Class-2 SmartReflex implementation. It supports TI's software based Class-2 SmartReflex implementation.
config REGULATOR_88PM8607
bool "Marvell 88PM8607 Power regulators"
depends on MFD_88PM8607=y
help
This driver supports 88PM8607 voltage regulator chips.
endif endif

View file

@ -12,6 +12,7 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
@ -20,10 +21,11 @@ obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG

View file

@ -81,7 +81,7 @@ static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = {
#define LDO_C_VOLTAGE 2650000 #define LDO_C_VOLTAGE 2650000
#define LDO_D_VOLTAGE 2650000 #define LDO_D_VOLTAGE 2650000
static const int const ldo_e_buck_typ_voltages[] = { static const int ldo_e_buck_typ_voltages[] = {
1800000, 1800000,
1400000, 1400000,
1300000, 1300000,
@ -91,7 +91,7 @@ static const int const ldo_e_buck_typ_voltages[] = {
900000, 900000,
}; };
static const int const ldo_f_typ_voltages[] = { static const int ldo_f_typ_voltages[] = {
1800000, 1800000,
1400000, 1400000,
1300000, 1300000,
@ -102,21 +102,21 @@ static const int const ldo_f_typ_voltages[] = {
2650000, 2650000,
}; };
static const int const ldo_g_typ_voltages[] = { static const int ldo_g_typ_voltages[] = {
2850000, 2850000,
2750000, 2750000,
1800000, 1800000,
1500000, 1500000,
}; };
static const int const ldo_h_typ_voltages[] = { static const int ldo_h_typ_voltages[] = {
2750000, 2750000,
1800000, 1800000,
1500000, 1500000,
1200000, 1200000,
}; };
static const int const ldo_k_typ_voltages[] = { static const int ldo_k_typ_voltages[] = {
2750000, 2750000,
1800000, 1800000,
}; };
@ -241,24 +241,12 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
* LDO D is a special regulator. When it is disabled, the entire * LDO D is a special regulator. When it is disabled, the entire
* system is shut down. So this is handled specially. * system is shut down. So this is handled specially.
*/ */
pr_info("Called ab3100_disable_regulator\n");
if (abreg->regreg == AB3100_LDO_D) { if (abreg->regreg == AB3100_LDO_D) {
int i;
dev_info(&reg->dev, "disabling LDO D - shut down system\n"); dev_info(&reg->dev, "disabling LDO D - shut down system\n");
/*
* Set regulators to default values, ignore any errors,
* we're going DOWN
*/
for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
(void) ab3100_set_register_interruptible(abreg->ab3100,
ab3100_reg_init_order[i],
abreg->plfdata->reg_initvals[i]);
}
/* Setting LDO D to 0x00 cuts the power to the SoC */ /* Setting LDO D to 0x00 cuts the power to the SoC */
return ab3100_set_register_interruptible(abreg->ab3100, return ab3100_set_register_interruptible(abreg->ab3100,
AB3100_LDO_D, 0x00U); AB3100_LDO_D, 0x00U);
} }
/* /*
@ -607,13 +595,6 @@ static int __init ab3100_regulators_probe(struct platform_device *pdev)
} }
} }
if (err) {
dev_err(&pdev->dev,
"LDO D regulator initialization failed with error %d\n",
err);
return err;
}
/* Register the regulators */ /* Register the regulators */
for (i = 0; i < AB3100_NUM_REGULATORS; i++) { for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
struct ab3100_regulator *reg = &ab3100_regulators[i]; struct ab3100_regulator *reg = &ab3100_regulators[i];
@ -688,7 +669,7 @@ static __init int ab3100_regulators_init(void)
static __exit void ab3100_regulators_exit(void) static __exit void ab3100_regulators_exit(void)
{ {
platform_driver_register(&ab3100_regulators_driver); platform_driver_unregister(&ab3100_regulators_driver);
} }
subsys_initcall(ab3100_regulators_init); subsys_initcall(ab3100_regulators_init);

View file

@ -66,6 +66,16 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
static void _notifier_call_chain(struct regulator_dev *rdev, static void _notifier_call_chain(struct regulator_dev *rdev,
unsigned long event, void *data); unsigned long event, void *data);
static const char *rdev_get_name(struct regulator_dev *rdev)
{
if (rdev->constraints && rdev->constraints->name)
return rdev->constraints->name;
else if (rdev->desc->name)
return rdev->desc->name;
else
return "";
}
/* gets the regulator for a given consumer device */ /* gets the regulator for a given consumer device */
static struct regulator *get_device_regulator(struct device *dev) static struct regulator *get_device_regulator(struct device *dev)
{ {
@ -96,12 +106,12 @@ static int regulator_check_voltage(struct regulator_dev *rdev,
if (!rdev->constraints) { if (!rdev->constraints) {
printk(KERN_ERR "%s: no constraints for %s\n", __func__, printk(KERN_ERR "%s: no constraints for %s\n", __func__,
rdev->desc->name); rdev_get_name(rdev));
return -ENODEV; return -ENODEV;
} }
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
printk(KERN_ERR "%s: operation not allowed for %s\n", printk(KERN_ERR "%s: operation not allowed for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return -EPERM; return -EPERM;
} }
@ -124,12 +134,12 @@ static int regulator_check_current_limit(struct regulator_dev *rdev,
if (!rdev->constraints) { if (!rdev->constraints) {
printk(KERN_ERR "%s: no constraints for %s\n", __func__, printk(KERN_ERR "%s: no constraints for %s\n", __func__,
rdev->desc->name); rdev_get_name(rdev));
return -ENODEV; return -ENODEV;
} }
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) { if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) {
printk(KERN_ERR "%s: operation not allowed for %s\n", printk(KERN_ERR "%s: operation not allowed for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return -EPERM; return -EPERM;
} }
@ -159,17 +169,17 @@ static int regulator_check_mode(struct regulator_dev *rdev, int mode)
if (!rdev->constraints) { if (!rdev->constraints) {
printk(KERN_ERR "%s: no constraints for %s\n", __func__, printk(KERN_ERR "%s: no constraints for %s\n", __func__,
rdev->desc->name); rdev_get_name(rdev));
return -ENODEV; return -ENODEV;
} }
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) { if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) {
printk(KERN_ERR "%s: operation not allowed for %s\n", printk(KERN_ERR "%s: operation not allowed for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return -EPERM; return -EPERM;
} }
if (!(rdev->constraints->valid_modes_mask & mode)) { if (!(rdev->constraints->valid_modes_mask & mode)) {
printk(KERN_ERR "%s: invalid mode %x for %s\n", printk(KERN_ERR "%s: invalid mode %x for %s\n",
__func__, mode, rdev->desc->name); __func__, mode, rdev_get_name(rdev));
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
@ -180,12 +190,12 @@ static int regulator_check_drms(struct regulator_dev *rdev)
{ {
if (!rdev->constraints) { if (!rdev->constraints) {
printk(KERN_ERR "%s: no constraints for %s\n", __func__, printk(KERN_ERR "%s: no constraints for %s\n", __func__,
rdev->desc->name); rdev_get_name(rdev));
return -ENODEV; return -ENODEV;
} }
if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) {
printk(KERN_ERR "%s: operation not allowed for %s\n", printk(KERN_ERR "%s: operation not allowed for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return -EPERM; return -EPERM;
} }
return 0; return 0;
@ -230,16 +240,8 @@ static ssize_t regulator_name_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct regulator_dev *rdev = dev_get_drvdata(dev); struct regulator_dev *rdev = dev_get_drvdata(dev);
const char *name;
if (rdev->constraints && rdev->constraints->name) return sprintf(buf, "%s\n", rdev_get_name(rdev));
name = rdev->constraints->name;
else if (rdev->desc->name)
name = rdev->desc->name;
else
name = "";
return sprintf(buf, "%s\n", name);
} }
static ssize_t regulator_print_opmode(char *buf, int mode) static ssize_t regulator_print_opmode(char *buf, int mode)
@ -388,7 +390,7 @@ static ssize_t regulator_total_uA_show(struct device *dev,
mutex_lock(&rdev->mutex); mutex_lock(&rdev->mutex);
list_for_each_entry(regulator, &rdev->consumer_list, list) list_for_each_entry(regulator, &rdev->consumer_list, list)
uA += regulator->uA_load; uA += regulator->uA_load;
mutex_unlock(&rdev->mutex); mutex_unlock(&rdev->mutex);
return sprintf(buf, "%d\n", uA); return sprintf(buf, "%d\n", uA);
} }
@ -563,7 +565,7 @@ static void drms_uA_update(struct regulator_dev *rdev)
/* calc total requested load */ /* calc total requested load */
list_for_each_entry(sibling, &rdev->consumer_list, list) list_for_each_entry(sibling, &rdev->consumer_list, list)
current_uA += sibling->uA_load; current_uA += sibling->uA_load;
/* now get the optimum mode for our new total regulator load */ /* now get the optimum mode for our new total regulator load */
mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV,
@ -579,10 +581,29 @@ static int suspend_set_state(struct regulator_dev *rdev,
struct regulator_state *rstate) struct regulator_state *rstate)
{ {
int ret = 0; int ret = 0;
bool can_set_state;
/* enable & disable are mandatory for suspend control */ can_set_state = rdev->desc->ops->set_suspend_enable &&
if (!rdev->desc->ops->set_suspend_enable || rdev->desc->ops->set_suspend_disable;
!rdev->desc->ops->set_suspend_disable) {
/* If we have no suspend mode configration don't set anything;
* only warn if the driver actually makes the suspend mode
* configurable.
*/
if (!rstate->enabled && !rstate->disabled) {
if (can_set_state)
printk(KERN_WARNING "%s: No configuration for %s\n",
__func__, rdev_get_name(rdev));
return 0;
}
if (rstate->enabled && rstate->disabled) {
printk(KERN_ERR "%s: invalid configuration for %s\n",
__func__, rdev_get_name(rdev));
return -EINVAL;
}
if (!can_set_state) {
printk(KERN_ERR "%s: no way to set suspend state\n", printk(KERN_ERR "%s: no way to set suspend state\n",
__func__); __func__);
return -EINVAL; return -EINVAL;
@ -641,25 +662,43 @@ static void print_constraints(struct regulator_dev *rdev)
{ {
struct regulation_constraints *constraints = rdev->constraints; struct regulation_constraints *constraints = rdev->constraints;
char buf[80]; char buf[80];
int count; int count = 0;
int ret;
if (rdev->desc->type == REGULATOR_VOLTAGE) { if (constraints->min_uV && constraints->max_uV) {
if (constraints->min_uV == constraints->max_uV) if (constraints->min_uV == constraints->max_uV)
count = sprintf(buf, "%d mV ", count += sprintf(buf + count, "%d mV ",
constraints->min_uV / 1000); constraints->min_uV / 1000);
else else
count = sprintf(buf, "%d <--> %d mV ", count += sprintf(buf + count, "%d <--> %d mV ",
constraints->min_uV / 1000, constraints->min_uV / 1000,
constraints->max_uV / 1000); constraints->max_uV / 1000);
} else {
if (constraints->min_uA == constraints->max_uA)
count = sprintf(buf, "%d mA ",
constraints->min_uA / 1000);
else
count = sprintf(buf, "%d <--> %d mA ",
constraints->min_uA / 1000,
constraints->max_uA / 1000);
} }
if (!constraints->min_uV ||
constraints->min_uV != constraints->max_uV) {
ret = _regulator_get_voltage(rdev);
if (ret > 0)
count += sprintf(buf + count, "at %d mV ", ret / 1000);
}
if (constraints->min_uA && constraints->max_uA) {
if (constraints->min_uA == constraints->max_uA)
count += sprintf(buf + count, "%d mA ",
constraints->min_uA / 1000);
else
count += sprintf(buf + count, "%d <--> %d mA ",
constraints->min_uA / 1000,
constraints->max_uA / 1000);
}
if (!constraints->min_uA ||
constraints->min_uA != constraints->max_uA) {
ret = _regulator_get_current_limit(rdev);
if (ret > 0)
count += sprintf(buf + count, "at %d uA ", ret / 1000);
}
if (constraints->valid_modes_mask & REGULATOR_MODE_FAST) if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
count += sprintf(buf + count, "fast "); count += sprintf(buf + count, "fast ");
if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL) if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
@ -669,33 +708,30 @@ static void print_constraints(struct regulator_dev *rdev)
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
count += sprintf(buf + count, "standby"); count += sprintf(buf + count, "standby");
printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf); printk(KERN_INFO "regulator: %s: %s\n", rdev_get_name(rdev), buf);
} }
/** static int machine_constraints_voltage(struct regulator_dev *rdev,
* set_machine_constraints - sets regulator constraints
* @rdev: regulator source
* @constraints: constraints to apply
*
* Allows platform initialisation code to define and constrain
* regulator circuits e.g. valid voltage/current ranges, etc. NOTE:
* Constraints *must* be set by platform code in order for some
* regulator operations to proceed i.e. set_voltage, set_current_limit,
* set_mode.
*/
static int set_machine_constraints(struct regulator_dev *rdev,
struct regulation_constraints *constraints) struct regulation_constraints *constraints)
{ {
int ret = 0;
const char *name;
struct regulator_ops *ops = rdev->desc->ops; struct regulator_ops *ops = rdev->desc->ops;
const char *name = rdev_get_name(rdev);
int ret;
if (constraints->name) /* do we need to apply the constraint voltage */
name = constraints->name; if (rdev->constraints->apply_uV &&
else if (rdev->desc->name) rdev->constraints->min_uV == rdev->constraints->max_uV &&
name = rdev->desc->name; ops->set_voltage) {
else ret = ops->set_voltage(rdev,
name = "regulator"; rdev->constraints->min_uV, rdev->constraints->max_uV);
if (ret < 0) {
printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
__func__,
rdev->constraints->min_uV, name);
rdev->constraints = NULL;
return ret;
}
}
/* constrain machine-level voltage specs to fit /* constrain machine-level voltage specs to fit
* the actual range supported by this regulator. * the actual range supported by this regulator.
@ -719,14 +755,13 @@ static int set_machine_constraints(struct regulator_dev *rdev,
/* voltage constraints are optional */ /* voltage constraints are optional */
if ((cmin == 0) && (cmax == 0)) if ((cmin == 0) && (cmax == 0))
goto out; return 0;
/* else require explicit machine-level constraints */ /* else require explicit machine-level constraints */
if (cmin <= 0 || cmax <= 0 || cmax < cmin) { if (cmin <= 0 || cmax <= 0 || cmax < cmin) {
pr_err("%s: %s '%s' voltage constraints\n", pr_err("%s: %s '%s' voltage constraints\n",
__func__, "invalid", name); __func__, "invalid", name);
ret = -EINVAL; return -EINVAL;
goto out;
} }
/* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */
@ -748,8 +783,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
if (max_uV < min_uV) { if (max_uV < min_uV) {
pr_err("%s: %s '%s' voltage constraints\n", pr_err("%s: %s '%s' voltage constraints\n",
__func__, "unsupportable", name); __func__, "unsupportable", name);
ret = -EINVAL; return -EINVAL;
goto out;
} }
/* use regulator's subset of machine constraints */ /* use regulator's subset of machine constraints */
@ -767,22 +801,34 @@ static int set_machine_constraints(struct regulator_dev *rdev,
} }
} }
return 0;
}
/**
* set_machine_constraints - sets regulator constraints
* @rdev: regulator source
* @constraints: constraints to apply
*
* Allows platform initialisation code to define and constrain
* regulator circuits e.g. valid voltage/current ranges, etc. NOTE:
* Constraints *must* be set by platform code in order for some
* regulator operations to proceed i.e. set_voltage, set_current_limit,
* set_mode.
*/
static int set_machine_constraints(struct regulator_dev *rdev,
struct regulation_constraints *constraints)
{
int ret = 0;
const char *name;
struct regulator_ops *ops = rdev->desc->ops;
rdev->constraints = constraints; rdev->constraints = constraints;
/* do we need to apply the constraint voltage */ name = rdev_get_name(rdev);
if (rdev->constraints->apply_uV &&
rdev->constraints->min_uV == rdev->constraints->max_uV && ret = machine_constraints_voltage(rdev, constraints);
ops->set_voltage) { if (ret != 0)
ret = ops->set_voltage(rdev, goto out;
rdev->constraints->min_uV, rdev->constraints->max_uV);
if (ret < 0) {
printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
__func__,
rdev->constraints->min_uV, name);
rdev->constraints = NULL;
goto out;
}
}
/* do we need to setup our suspend state */ /* do we need to setup our suspend state */
if (constraints->initial_state) { if (constraints->initial_state) {
@ -903,7 +949,7 @@ static int set_consumer_device_supply(struct regulator_dev *rdev,
dev_name(&node->regulator->dev), dev_name(&node->regulator->dev),
node->regulator->desc->name, node->regulator->desc->name,
supply, supply,
dev_name(&rdev->dev), rdev->desc->name); dev_name(&rdev->dev), rdev_get_name(rdev));
return -EBUSY; return -EBUSY;
} }
@ -1212,7 +1258,7 @@ static int _regulator_enable(struct regulator_dev *rdev)
ret = _regulator_enable(rdev->supply); ret = _regulator_enable(rdev->supply);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to enable %s: %d\n", printk(KERN_ERR "%s: failed to enable %s: %d\n",
__func__, rdev->desc->name, ret); __func__, rdev_get_name(rdev), ret);
return ret; return ret;
} }
} }
@ -1238,7 +1284,7 @@ static int _regulator_enable(struct regulator_dev *rdev)
} }
} else if (ret < 0) { } else if (ret < 0) {
printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n",
__func__, rdev->desc->name, ret); __func__, rdev_get_name(rdev), ret);
return ret; return ret;
} }
/* Fallthrough on positive return values - already enabled */ /* Fallthrough on positive return values - already enabled */
@ -1279,7 +1325,7 @@ static int _regulator_disable(struct regulator_dev *rdev)
if (WARN(rdev->use_count <= 0, if (WARN(rdev->use_count <= 0,
"unbalanced disables for %s\n", "unbalanced disables for %s\n",
rdev->desc->name)) rdev_get_name(rdev)))
return -EIO; return -EIO;
/* are we the last user and permitted to disable ? */ /* are we the last user and permitted to disable ? */
@ -1292,7 +1338,7 @@ static int _regulator_disable(struct regulator_dev *rdev)
ret = rdev->desc->ops->disable(rdev); ret = rdev->desc->ops->disable(rdev);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to disable %s\n", printk(KERN_ERR "%s: failed to disable %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return ret; return ret;
} }
} }
@ -1349,7 +1395,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
ret = rdev->desc->ops->disable(rdev); ret = rdev->desc->ops->disable(rdev);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to force disable %s\n", printk(KERN_ERR "%s: failed to force disable %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
return ret; return ret;
} }
/* notify other consumers that power has been forced off */ /* notify other consumers that power has been forced off */
@ -1766,7 +1812,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
output_uV = rdev->desc->ops->get_voltage(rdev); output_uV = rdev->desc->ops->get_voltage(rdev);
if (output_uV <= 0) { if (output_uV <= 0) {
printk(KERN_ERR "%s: invalid output voltage found for %s\n", printk(KERN_ERR "%s: invalid output voltage found for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
goto out; goto out;
} }
@ -1777,13 +1823,13 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
input_uV = rdev->constraints->input_uV; input_uV = rdev->constraints->input_uV;
if (input_uV <= 0) { if (input_uV <= 0) {
printk(KERN_ERR "%s: invalid input voltage found for %s\n", printk(KERN_ERR "%s: invalid input voltage found for %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
goto out; goto out;
} }
/* calc total requested load for this regulator */ /* calc total requested load for this regulator */
list_for_each_entry(consumer, &rdev->consumer_list, list) list_for_each_entry(consumer, &rdev->consumer_list, list)
total_uA_load += consumer->uA_load; total_uA_load += consumer->uA_load;
mode = rdev->desc->ops->get_optimum_mode(rdev, mode = rdev->desc->ops->get_optimum_mode(rdev,
input_uV, output_uV, input_uV, output_uV,
@ -1791,7 +1837,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
ret = regulator_check_mode(rdev, mode); ret = regulator_check_mode(rdev, mode);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to get optimum mode for %s @" printk(KERN_ERR "%s: failed to get optimum mode for %s @"
" %d uA %d -> %d uV\n", __func__, rdev->desc->name, " %d uA %d -> %d uV\n", __func__, rdev_get_name(rdev),
total_uA_load, input_uV, output_uV); total_uA_load, input_uV, output_uV);
goto out; goto out;
} }
@ -1799,7 +1845,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
ret = rdev->desc->ops->set_mode(rdev, mode); ret = rdev->desc->ops->set_mode(rdev, mode);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n", printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n",
__func__, mode, rdev->desc->name); __func__, mode, rdev_get_name(rdev));
goto out; goto out;
} }
ret = mode; ret = mode;
@ -1852,9 +1898,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
/* now notify regulator we supply */ /* now notify regulator we supply */
list_for_each_entry(_rdev, &rdev->supply_list, slist) { list_for_each_entry(_rdev, &rdev->supply_list, slist) {
mutex_lock(&_rdev->mutex); mutex_lock(&_rdev->mutex);
_notifier_call_chain(_rdev, event, data); _notifier_call_chain(_rdev, event, data);
mutex_unlock(&_rdev->mutex); mutex_unlock(&_rdev->mutex);
} }
} }
@ -1885,9 +1931,9 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = regulator_get(dev, consumers[i].consumer = regulator_get(dev,
consumers[i].supply); consumers[i].supply);
if (IS_ERR(consumers[i].consumer)) { if (IS_ERR(consumers[i].consumer)) {
dev_err(dev, "Failed to get supply '%s'\n",
consumers[i].supply);
ret = PTR_ERR(consumers[i].consumer); ret = PTR_ERR(consumers[i].consumer);
dev_err(dev, "Failed to get supply '%s': %d\n",
consumers[i].supply, ret);
consumers[i].consumer = NULL; consumers[i].consumer = NULL;
goto err; goto err;
} }
@ -1930,8 +1976,8 @@ int regulator_bulk_enable(int num_consumers,
return 0; return 0;
err: err:
printk(KERN_ERR "Failed to enable %s\n", consumers[i].supply); printk(KERN_ERR "Failed to enable %s: %d\n", consumers[i].supply, ret);
for (i = 0; i < num_consumers; i++) for (--i; i >= 0; --i)
regulator_disable(consumers[i].consumer); regulator_disable(consumers[i].consumer);
return ret; return ret;
@ -1965,8 +2011,9 @@ int regulator_bulk_disable(int num_consumers,
return 0; return 0;
err: err:
printk(KERN_ERR "Failed to disable %s\n", consumers[i].supply); printk(KERN_ERR "Failed to disable %s: %d\n", consumers[i].supply,
for (i = 0; i < num_consumers; i++) ret);
for (--i; i >= 0; --i)
regulator_enable(consumers[i].consumer); regulator_enable(consumers[i].consumer);
return ret; return ret;
@ -2316,7 +2363,7 @@ int regulator_suspend_prepare(suspend_state_t state)
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: failed to prepare %s\n", printk(KERN_ERR "%s: failed to prepare %s\n",
__func__, rdev->desc->name); __func__, rdev_get_name(rdev));
goto out; goto out;
} }
} }
@ -2429,12 +2476,7 @@ static int __init regulator_init_complete(void)
ops = rdev->desc->ops; ops = rdev->desc->ops;
c = rdev->constraints; c = rdev->constraints;
if (c && c->name) name = rdev_get_name(rdev);
name = c->name;
else if (rdev->desc->name)
name = rdev->desc->name;
else
name = "regulator";
if (!ops->disable || (c && c->always_on)) if (!ops->disable || (c && c->always_on))
continue; continue;

View file

@ -331,7 +331,7 @@ static int da9034_get_ldo12_voltage(struct regulator_dev *rdev)
static int da9034_list_ldo12_voltage(struct regulator_dev *rdev, static int da9034_list_ldo12_voltage(struct regulator_dev *rdev,
unsigned selector) unsigned selector)
{ {
if (selector > ARRAY_SIZE(da9034_ldo12_data)) if (selector >= ARRAY_SIZE(da9034_ldo12_data))
return -EINVAL; return -EINVAL;
return da9034_ldo12_data[selector] * 1000; return da9034_ldo12_data[selector] * 1000;
} }

View file

@ -446,8 +446,8 @@ static int setup_regulators(struct lp3971 *lp3971,
lp3971->rdev[i] = regulator_register(&regulators[id], lp3971->rdev[i] = regulator_register(&regulators[id],
lp3971->dev, pdata->regulators[i].initdata, lp3971); lp3971->dev, pdata->regulators[i].initdata, lp3971);
err = IS_ERR(lp3971->rdev[i]); if (IS_ERR(lp3971->rdev[i])) {
if (err) { err = PTR_ERR(lp3971->rdev[i]);
dev_err(lp3971->dev, "regulator init failed: %d\n", dev_err(lp3971->dev, "regulator init failed: %d\n",
err); err);
goto error; goto error;

510
drivers/regulator/max8660.c Normal file
View file

@ -0,0 +1,510 @@
/*
* max8660.c -- Voltage regulation for the Maxim 8660/8661
*
* based on max1586.c and wm8400-regulator.c
*
* Copyright (C) 2009 Wolfram Sang, Pengutronix e.K.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*
* Some info:
*
* Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf
*
* This chip is a bit nasty because it is a write-only device. Thus, the driver
* uses shadow registers to keep track of its values. The main problem appears
* to be the initialization: When Linux boots up, we cannot know if the chip is
* in the default state or not, so we would have to pass such information in
* platform_data. As this adds a bit of complexity to the driver, this is left
* out for now until it is really needed.
*
* [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2.
*
* If the driver is feature complete, it might be worth to check if one set of
* functions for V3-V7 is sufficient. For maximum flexibility during
* development, they are separated for now.
*
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/max8660.h>
#define MAX8660_DCDC_MIN_UV 725000
#define MAX8660_DCDC_MAX_UV 1800000
#define MAX8660_DCDC_STEP 25000
#define MAX8660_DCDC_MAX_SEL 0x2b
#define MAX8660_LDO5_MIN_UV 1700000
#define MAX8660_LDO5_MAX_UV 2000000
#define MAX8660_LDO5_STEP 25000
#define MAX8660_LDO5_MAX_SEL 0x0c
#define MAX8660_LDO67_MIN_UV 1800000
#define MAX8660_LDO67_MAX_UV 3300000
#define MAX8660_LDO67_STEP 100000
#define MAX8660_LDO67_MAX_SEL 0x0f
enum {
MAX8660_OVER1,
MAX8660_OVER2,
MAX8660_VCC1,
MAX8660_ADTV1,
MAX8660_ADTV2,
MAX8660_SDTV1,
MAX8660_SDTV2,
MAX8660_MDTV1,
MAX8660_MDTV2,
MAX8660_L12VCR,
MAX8660_FPWM,
MAX8660_N_REGS, /* not a real register */
};
struct max8660 {
struct i2c_client *client;
u8 shadow_regs[MAX8660_N_REGS]; /* as chip is write only */
struct regulator_dev *rdev[];
};
static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val)
{
static const u8 max8660_addresses[MAX8660_N_REGS] =
{ 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 };
int ret;
u8 reg_val = (max8660->shadow_regs[reg] & mask) | val;
dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n",
max8660_addresses[reg], reg_val);
ret = i2c_smbus_write_byte_data(max8660->client,
max8660_addresses[reg], reg_val);
if (ret == 0)
max8660->shadow_regs[reg] = reg_val;
return ret;
}
/*
* DCDC functions
*/
static int max8660_dcdc_is_enabled(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 val = max8660->shadow_regs[MAX8660_OVER1];
u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
return !!(val & mask);
}
static int max8660_dcdc_enable(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
return max8660_write(max8660, MAX8660_OVER1, 0xff, bit);
}
static int max8660_dcdc_disable(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4;
return max8660_write(max8660, MAX8660_OVER1, mask, 0);
}
static int max8660_dcdc_list(struct regulator_dev *rdev, unsigned selector)
{
if (selector > MAX8660_DCDC_MAX_SEL)
return -EINVAL;
return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP;
}
static int max8660_dcdc_get(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
u8 selector = max8660->shadow_regs[reg];
return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP;
}
static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 reg, selector, bits;
int ret;
if (min_uV < MAX8660_DCDC_MIN_UV || min_uV > MAX8660_DCDC_MAX_UV)
return -EINVAL;
if (max_uV < MAX8660_DCDC_MIN_UV || max_uV > MAX8660_DCDC_MAX_UV)
return -EINVAL;
selector = (min_uV - (MAX8660_DCDC_MIN_UV - MAX8660_DCDC_STEP + 1))
/ MAX8660_DCDC_STEP;
ret = max8660_dcdc_list(rdev, selector);
if (ret < 0 || ret > max_uV)
return -EINVAL;
reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
ret = max8660_write(max8660, reg, 0, selector);
if (ret)
return ret;
/* Select target voltage register and activate regulation */
bits = (rdev_get_id(rdev) == MAX8660_V3) ? 0x03 : 0x30;
return max8660_write(max8660, MAX8660_VCC1, 0xff, bits);
}
static struct regulator_ops max8660_dcdc_ops = {
.is_enabled = max8660_dcdc_is_enabled,
.list_voltage = max8660_dcdc_list,
.set_voltage = max8660_dcdc_set,
.get_voltage = max8660_dcdc_get,
};
/*
* LDO5 functions
*/
static int max8660_ldo5_list(struct regulator_dev *rdev, unsigned selector)
{
if (selector > MAX8660_LDO5_MAX_SEL)
return -EINVAL;
return MAX8660_LDO5_MIN_UV + selector * MAX8660_LDO5_STEP;
}
static int max8660_ldo5_get(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 selector = max8660->shadow_regs[MAX8660_MDTV2];
return MAX8660_LDO5_MIN_UV + selector * MAX8660_LDO5_STEP;
}
static int max8660_ldo5_set(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 selector;
int ret;
if (min_uV < MAX8660_LDO5_MIN_UV || min_uV > MAX8660_LDO5_MAX_UV)
return -EINVAL;
if (max_uV < MAX8660_LDO5_MIN_UV || max_uV > MAX8660_LDO5_MAX_UV)
return -EINVAL;
selector = (min_uV - (MAX8660_LDO5_MIN_UV - MAX8660_LDO5_STEP + 1))
/ MAX8660_LDO5_STEP;
ret = max8660_ldo5_list(rdev, selector);
if (ret < 0 || ret > max_uV)
return -EINVAL;
ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector);
if (ret)
return ret;
/* Select target voltage register and activate regulation */
return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0);
}
static struct regulator_ops max8660_ldo5_ops = {
.list_voltage = max8660_ldo5_list,
.set_voltage = max8660_ldo5_set,
.get_voltage = max8660_ldo5_get,
};
/*
* LDO67 functions
*/
static int max8660_ldo67_is_enabled(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 val = max8660->shadow_regs[MAX8660_OVER2];
u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
return !!(val & mask);
}
static int max8660_ldo67_enable(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
return max8660_write(max8660, MAX8660_OVER2, 0xff, bit);
}
static int max8660_ldo67_disable(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4;
return max8660_write(max8660, MAX8660_OVER2, mask, 0);
}
static int max8660_ldo67_list(struct regulator_dev *rdev, unsigned selector)
{
if (selector > MAX8660_LDO67_MAX_SEL)
return -EINVAL;
return MAX8660_LDO67_MIN_UV + selector * MAX8660_LDO67_STEP;
}
static int max8660_ldo67_get(struct regulator_dev *rdev)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4;
u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf;
return MAX8660_LDO67_MIN_UV + selector * MAX8660_LDO67_STEP;
}
static int max8660_ldo67_set(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct max8660 *max8660 = rdev_get_drvdata(rdev);
u8 selector;
int ret;
if (min_uV < MAX8660_LDO67_MIN_UV || min_uV > MAX8660_LDO67_MAX_UV)
return -EINVAL;
if (max_uV < MAX8660_LDO67_MIN_UV || max_uV > MAX8660_LDO67_MAX_UV)
return -EINVAL;
selector = (min_uV - (MAX8660_LDO67_MIN_UV - MAX8660_LDO67_STEP + 1))
/ MAX8660_LDO67_STEP;
ret = max8660_ldo67_list(rdev, selector);
if (ret < 0 || ret > max_uV)
return -EINVAL;
if (rdev_get_id(rdev) == MAX8660_V6)
return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector);
else
return max8660_write(max8660, MAX8660_L12VCR, 0x0f, selector << 4);
}
static struct regulator_ops max8660_ldo67_ops = {
.is_enabled = max8660_ldo67_is_enabled,
.enable = max8660_ldo67_enable,
.disable = max8660_ldo67_disable,
.list_voltage = max8660_ldo67_list,
.get_voltage = max8660_ldo67_get,
.set_voltage = max8660_ldo67_set,
};
static struct regulator_desc max8660_reg[] = {
{
.name = "V3(DCDC)",
.id = MAX8660_V3,
.ops = &max8660_dcdc_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = MAX8660_DCDC_MAX_SEL + 1,
.owner = THIS_MODULE,
},
{
.name = "V4(DCDC)",
.id = MAX8660_V4,
.ops = &max8660_dcdc_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = MAX8660_DCDC_MAX_SEL + 1,
.owner = THIS_MODULE,
},
{
.name = "V5(LDO)",
.id = MAX8660_V5,
.ops = &max8660_ldo5_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = MAX8660_LDO5_MAX_SEL + 1,
.owner = THIS_MODULE,
},
{
.name = "V6(LDO)",
.id = MAX8660_V6,
.ops = &max8660_ldo67_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = MAX8660_LDO67_MAX_SEL + 1,
.owner = THIS_MODULE,
},
{
.name = "V7(LDO)",
.id = MAX8660_V7,
.ops = &max8660_ldo67_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = MAX8660_LDO67_MAX_SEL + 1,
.owner = THIS_MODULE,
},
};
static int max8660_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct regulator_dev **rdev;
struct max8660_platform_data *pdata = client->dev.platform_data;
struct max8660 *max8660;
int boot_on, i, id, ret = -EINVAL;
if (pdata->num_subdevs > MAX8660_V_END) {
dev_err(&client->dev, "Too much regulators found!\n");
goto out;
}
max8660 = kzalloc(sizeof(struct max8660) +
sizeof(struct regulator_dev *) * MAX8660_V_END,
GFP_KERNEL);
if (!max8660) {
ret = -ENOMEM;
goto out;
}
max8660->client = client;
rdev = max8660->rdev;
if (pdata->en34_is_high) {
/* Simulate always on */
max8660->shadow_regs[MAX8660_OVER1] = 5;
} else {
/* Otherwise devices can be toggled via software */
max8660_dcdc_ops.enable = max8660_dcdc_enable;
max8660_dcdc_ops.disable = max8660_dcdc_disable;
}
/*
* First, set up shadow registers to prevent glitches. As some
* registers are shared between regulators, everything must be properly
* set up for all regulators in advance.
*/
max8660->shadow_regs[MAX8660_ADTV1] =
max8660->shadow_regs[MAX8660_ADTV2] =
max8660->shadow_regs[MAX8660_SDTV1] =
max8660->shadow_regs[MAX8660_SDTV2] = 0x1b;
max8660->shadow_regs[MAX8660_MDTV1] =
max8660->shadow_regs[MAX8660_MDTV2] = 0x04;
for (i = 0; i < pdata->num_subdevs; i++) {
if (!pdata->subdevs[i].platform_data)
goto err_free;
boot_on = pdata->subdevs[i].platform_data->constraints.boot_on;
switch (pdata->subdevs[i].id) {
case MAX8660_V3:
if (boot_on)
max8660->shadow_regs[MAX8660_OVER1] |= 1;
break;
case MAX8660_V4:
if (boot_on)
max8660->shadow_regs[MAX8660_OVER1] |= 4;
break;
case MAX8660_V5:
break;
case MAX8660_V6:
if (boot_on)
max8660->shadow_regs[MAX8660_OVER2] |= 2;
break;
case MAX8660_V7:
if (!strcmp(i2c_id->name, "max8661")) {
dev_err(&client->dev, "Regulator not on this chip!\n");
goto err_free;
}
if (boot_on)
max8660->shadow_regs[MAX8660_OVER2] |= 4;
break;
default:
dev_err(&client->dev, "invalid regulator %s\n",
pdata->subdevs[i].name);
goto err_free;
}
}
/* Finally register devices */
for (i = 0; i < pdata->num_subdevs; i++) {
id = pdata->subdevs[i].id;
rdev[i] = regulator_register(&max8660_reg[id], &client->dev,
pdata->subdevs[i].platform_data,
max8660);
if (IS_ERR(rdev[i])) {
ret = PTR_ERR(rdev[i]);
dev_err(&client->dev, "failed to register %s\n",
max8660_reg[id].name);
goto err_unregister;
}
}
i2c_set_clientdata(client, rdev);
dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n");
return 0;
err_unregister:
while (--i >= 0)
regulator_unregister(rdev[i]);
err_free:
kfree(max8660);
out:
return ret;
}
static int max8660_remove(struct i2c_client *client)
{
struct regulator_dev **rdev = i2c_get_clientdata(client);
int i;
for (i = 0; i < MAX8660_V_END; i++)
if (rdev[i])
regulator_unregister(rdev[i]);
kfree(rdev);
i2c_set_clientdata(client, NULL);
return 0;
}
static const struct i2c_device_id max8660_id[] = {
{ "max8660", 0 },
{ "max8661", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max8660_id);
static struct i2c_driver max8660_driver = {
.probe = max8660_probe,
.remove = max8660_remove,
.driver = {
.name = "max8660",
},
.id_table = max8660_id,
};
static int __init max8660_init(void)
{
return i2c_add_driver(&max8660_driver);
}
subsys_initcall(max8660_init);
static void __exit max8660_exit(void)
{
i2c_del_driver(&max8660_driver);
}
module_exit(max8660_exit);
/* Module information */
MODULE_DESCRIPTION("MAXIM 8660/8661 voltage regulator driver");
MODULE_AUTHOR("Wolfram Sang");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,245 @@
/*
* Regulator Driver for Freescale MC13783 PMIC
*
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/mfd/mc13783.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/driver.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#define MC13783_REG_SWITCHERS4 28
#define MC13783_REG_SWITCHERS4_PLLEN (1 << 18)
#define MC13783_REG_SWITCHERS5 29
#define MC13783_REG_SWITCHERS5_SW3EN (1 << 20)
#define MC13783_REG_REGULATORMODE0 32
#define MC13783_REG_REGULATORMODE0_VAUDIOEN (1 << 0)
#define MC13783_REG_REGULATORMODE0_VIOHIEN (1 << 3)
#define MC13783_REG_REGULATORMODE0_VIOLOEN (1 << 6)
#define MC13783_REG_REGULATORMODE0_VDIGEN (1 << 9)
#define MC13783_REG_REGULATORMODE0_VGENEN (1 << 12)
#define MC13783_REG_REGULATORMODE0_VRFDIGEN (1 << 15)
#define MC13783_REG_REGULATORMODE0_VRFREFEN (1 << 18)
#define MC13783_REG_REGULATORMODE0_VRFCPEN (1 << 21)
#define MC13783_REG_REGULATORMODE1 33
#define MC13783_REG_REGULATORMODE1_VSIMEN (1 << 0)
#define MC13783_REG_REGULATORMODE1_VESIMEN (1 << 3)
#define MC13783_REG_REGULATORMODE1_VCAMEN (1 << 6)
#define MC13783_REG_REGULATORMODE1_VRFBGEN (1 << 9)
#define MC13783_REG_REGULATORMODE1_VVIBEN (1 << 11)
#define MC13783_REG_REGULATORMODE1_VRF1EN (1 << 12)
#define MC13783_REG_REGULATORMODE1_VRF2EN (1 << 15)
#define MC13783_REG_REGULATORMODE1_VMMC1EN (1 << 18)
#define MC13783_REG_REGULATORMODE1_VMMC2EN (1 << 21)
#define MC13783_REG_POWERMISC 34
#define MC13783_REG_POWERMISC_GPO1EN (1 << 6)
#define MC13783_REG_POWERMISC_GPO2EN (1 << 8)
#define MC13783_REG_POWERMISC_GPO3EN (1 << 10)
#define MC13783_REG_POWERMISC_GPO4EN (1 << 12)
struct mc13783_regulator {
struct regulator_desc desc;
int reg;
int enable_bit;
};
static struct regulator_ops mc13783_regulator_ops;
#define MC13783_DEFINE(prefix, _name, _reg) \
[MC13783_ ## prefix ## _ ## _name] = { \
.desc = { \
.name = #prefix "_" #_name, \
.ops = &mc13783_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = MC13783_ ## prefix ## _ ## _name, \
.owner = THIS_MODULE, \
}, \
.reg = MC13783_REG_ ## _reg, \
.enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
}
#define MC13783_DEFINE_SW(_name, _reg) MC13783_DEFINE(SW, _name, _reg)
#define MC13783_DEFINE_REGU(_name, _reg) MC13783_DEFINE(REGU, _name, _reg)
static struct mc13783_regulator mc13783_regulators[] = {
MC13783_DEFINE_SW(SW3, SWITCHERS5),
MC13783_DEFINE_SW(PLL, SWITCHERS4),
MC13783_DEFINE_REGU(VAUDIO, REGULATORMODE0),
MC13783_DEFINE_REGU(VIOHI, REGULATORMODE0),
MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0),
MC13783_DEFINE_REGU(VDIG, REGULATORMODE0),
MC13783_DEFINE_REGU(VGEN, REGULATORMODE0),
MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0),
MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0),
MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0),
MC13783_DEFINE_REGU(VSIM, REGULATORMODE1),
MC13783_DEFINE_REGU(VESIM, REGULATORMODE1),
MC13783_DEFINE_REGU(VCAM, REGULATORMODE1),
MC13783_DEFINE_REGU(VRFBG, REGULATORMODE1),
MC13783_DEFINE_REGU(VVIB, REGULATORMODE1),
MC13783_DEFINE_REGU(VRF1, REGULATORMODE1),
MC13783_DEFINE_REGU(VRF2, REGULATORMODE1),
MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1),
MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1),
MC13783_DEFINE_REGU(GPO1, POWERMISC),
MC13783_DEFINE_REGU(GPO2, POWERMISC),
MC13783_DEFINE_REGU(GPO3, POWERMISC),
MC13783_DEFINE_REGU(GPO4, POWERMISC),
};
struct mc13783_regulator_priv {
struct mc13783 *mc13783;
struct regulator_dev *regulators[];
};
static int mc13783_regulator_enable(struct regulator_dev *rdev)
{
struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
int ret;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
mc13783_lock(priv->mc13783);
ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
mc13783_regulators[id].enable_bit,
mc13783_regulators[id].enable_bit);
mc13783_unlock(priv->mc13783);
return ret;
}
static int mc13783_regulator_disable(struct regulator_dev *rdev)
{
struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
int ret;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
mc13783_lock(priv->mc13783);
ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
mc13783_regulators[id].enable_bit, 0);
mc13783_unlock(priv->mc13783);
return ret;
}
static int mc13783_regulator_is_enabled(struct regulator_dev *rdev)
{
struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
int ret, id = rdev_get_id(rdev);
unsigned int val;
mc13783_lock(priv->mc13783);
ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
mc13783_unlock(priv->mc13783);
if (ret)
return ret;
return (val & mc13783_regulators[id].enable_bit) != 0;
}
static struct regulator_ops mc13783_regulator_ops = {
.enable = mc13783_regulator_enable,
.disable = mc13783_regulator_disable,
.is_enabled = mc13783_regulator_is_enabled,
};
static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
{
struct mc13783_regulator_priv *priv;
struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
struct mc13783_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct mc13783_regulator_init_data *init_data;
int i, ret;
dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
priv = kzalloc(sizeof(*priv) +
pdata->num_regulators * sizeof(priv->regulators[0]),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mc13783 = mc13783;
for (i = 0; i < pdata->num_regulators; i++) {
init_data = &pdata->regulators[i];
priv->regulators[i] = regulator_register(
&mc13783_regulators[init_data->id].desc,
&pdev->dev, init_data->init_data, priv);
if (IS_ERR(priv->regulators[i])) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
mc13783_regulators[i].desc.name);
ret = PTR_ERR(priv->regulators[i]);
goto err;
}
}
platform_set_drvdata(pdev, priv);
return 0;
err:
while (--i >= 0)
regulator_unregister(priv->regulators[i]);
kfree(priv);
return ret;
}
static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
{
struct mc13783_regulator_priv *priv = platform_get_drvdata(pdev);
struct mc13783_regulator_platform_data *pdata =
dev_get_platdata(&pdev->dev);
int i;
for (i = 0; i < pdata->num_regulators; i++)
regulator_unregister(priv->regulators[i]);
return 0;
}
static struct platform_driver mc13783_regulator_driver = {
.driver = {
.name = "mc13783-regulator",
.owner = THIS_MODULE,
},
.remove = __devexit_p(mc13783_regulator_remove),
.probe = mc13783_regulator_probe,
};
static int __init mc13783_regulator_init(void)
{
return platform_driver_register(&mc13783_regulator_driver);
}
subsys_initcall(mc13783_regulator_init);
static void __exit mc13783_regulator_exit(void)
{
platform_driver_unregister(&mc13783_regulator_driver);
}
module_exit(mc13783_regulator_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
MODULE_ALIAS("platform:mc13783-regulator");

View file

@ -1,410 +0,0 @@
/*
* Regulator Driver for Freescale MC13783 PMIC
*
* Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/mfd/mc13783-private.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/driver.h>
#include <linux/platform_device.h>
#include <linux/mfd/mc13783.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
struct mc13783_regulator {
struct regulator_desc desc;
int reg;
int enable_bit;
};
static struct regulator_ops mc13783_regulator_ops;
static struct mc13783_regulator mc13783_regulators[] = {
[MC13783_SW_SW3] = {
.desc = {
.name = "SW_SW3",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_SW_SW3,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_SWITCHERS_5,
.enable_bit = MC13783_SWCTRL_SW3_EN,
},
[MC13783_SW_PLL] = {
.desc = {
.name = "SW_PLL",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_SW_PLL,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_SWITCHERS_4,
.enable_bit = MC13783_SWCTRL_PLL_EN,
},
[MC13783_REGU_VAUDIO] = {
.desc = {
.name = "REGU_VAUDIO",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VAUDIO,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VAUDIO_EN,
},
[MC13783_REGU_VIOHI] = {
.desc = {
.name = "REGU_VIOHI",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VIOHI,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VIOHI_EN,
},
[MC13783_REGU_VIOLO] = {
.desc = {
.name = "REGU_VIOLO",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VIOLO,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VIOLO_EN,
},
[MC13783_REGU_VDIG] = {
.desc = {
.name = "REGU_VDIG",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VDIG,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VDIG_EN,
},
[MC13783_REGU_VGEN] = {
.desc = {
.name = "REGU_VGEN",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VGEN,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VGEN_EN,
},
[MC13783_REGU_VRFDIG] = {
.desc = {
.name = "REGU_VRFDIG",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRFDIG,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VRFDIG_EN,
},
[MC13783_REGU_VRFREF] = {
.desc = {
.name = "REGU_VRFREF",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRFREF,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VRFREF_EN,
},
[MC13783_REGU_VRFCP] = {
.desc = {
.name = "REGU_VRFCP",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRFCP,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_0,
.enable_bit = MC13783_REGCTRL_VRFCP_EN,
},
[MC13783_REGU_VSIM] = {
.desc = {
.name = "REGU_VSIM",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VSIM,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VSIM_EN,
},
[MC13783_REGU_VESIM] = {
.desc = {
.name = "REGU_VESIM",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VESIM,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VESIM_EN,
},
[MC13783_REGU_VCAM] = {
.desc = {
.name = "REGU_VCAM",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VCAM,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VCAM_EN,
},
[MC13783_REGU_VRFBG] = {
.desc = {
.name = "REGU_VRFBG",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRFBG,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VRFBG_EN,
},
[MC13783_REGU_VVIB] = {
.desc = {
.name = "REGU_VVIB",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VVIB,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VVIB_EN,
},
[MC13783_REGU_VRF1] = {
.desc = {
.name = "REGU_VRF1",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRF1,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VRF1_EN,
},
[MC13783_REGU_VRF2] = {
.desc = {
.name = "REGU_VRF2",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VRF2,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VRF2_EN,
},
[MC13783_REGU_VMMC1] = {
.desc = {
.name = "REGU_VMMC1",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VMMC1,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VMMC1_EN,
},
[MC13783_REGU_VMMC2] = {
.desc = {
.name = "REGU_VMMC2",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_VMMC2,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_REGULATOR_MODE_1,
.enable_bit = MC13783_REGCTRL_VMMC2_EN,
},
[MC13783_REGU_GPO1] = {
.desc = {
.name = "REGU_GPO1",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_GPO1,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_POWER_MISCELLANEOUS,
.enable_bit = MC13783_REGCTRL_GPO1_EN,
},
[MC13783_REGU_GPO2] = {
.desc = {
.name = "REGU_GPO2",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_GPO2,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_POWER_MISCELLANEOUS,
.enable_bit = MC13783_REGCTRL_GPO2_EN,
},
[MC13783_REGU_GPO3] = {
.desc = {
.name = "REGU_GPO3",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_GPO3,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_POWER_MISCELLANEOUS,
.enable_bit = MC13783_REGCTRL_GPO3_EN,
},
[MC13783_REGU_GPO4] = {
.desc = {
.name = "REGU_GPO4",
.ops = &mc13783_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = MC13783_REGU_GPO4,
.owner = THIS_MODULE,
},
.reg = MC13783_REG_POWER_MISCELLANEOUS,
.enable_bit = MC13783_REGCTRL_GPO4_EN,
},
};
struct mc13783_priv {
struct regulator_desc desc[ARRAY_SIZE(mc13783_regulators)];
struct mc13783 *mc13783;
struct regulator_dev *regulators[0];
};
static int mc13783_enable(struct regulator_dev *rdev)
{
struct mc13783_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
mc13783_regulators[id].enable_bit,
mc13783_regulators[id].enable_bit);
}
static int mc13783_disable(struct regulator_dev *rdev)
{
struct mc13783_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
mc13783_regulators[id].enable_bit, 0);
}
static int mc13783_is_enabled(struct regulator_dev *rdev)
{
struct mc13783_priv *priv = rdev_get_drvdata(rdev);
int ret, id = rdev_get_id(rdev);
unsigned int val;
ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
if (ret)
return ret;
return (val & mc13783_regulators[id].enable_bit) != 0;
}
static struct regulator_ops mc13783_regulator_ops = {
.enable = mc13783_enable,
.disable = mc13783_disable,
.is_enabled = mc13783_is_enabled,
};
static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
{
struct mc13783_priv *priv;
struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
struct mc13783_regulator_init_data *init_data;
int i, ret;
dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
priv = kzalloc(sizeof(*priv) + mc13783->num_regulators * sizeof(void *),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mc13783 = mc13783;
for (i = 0; i < mc13783->num_regulators; i++) {
init_data = &mc13783->regulators[i];
priv->regulators[i] = regulator_register(
&mc13783_regulators[init_data->id].desc,
&pdev->dev, init_data->init_data, priv);
if (IS_ERR(priv->regulators[i])) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
mc13783_regulators[i].desc.name);
ret = PTR_ERR(priv->regulators[i]);
goto err;
}
}
platform_set_drvdata(pdev, priv);
return 0;
err:
while (--i >= 0)
regulator_unregister(priv->regulators[i]);
kfree(priv);
return ret;
}
static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
{
struct mc13783_priv *priv = platform_get_drvdata(pdev);
struct mc13783 *mc13783 = priv->mc13783;
int i;
for (i = 0; i < mc13783->num_regulators; i++)
regulator_unregister(priv->regulators[i]);
return 0;
}
static struct platform_driver mc13783_regulator_driver = {
.driver = {
.name = "mc13783-regulator",
.owner = THIS_MODULE,
},
.remove = __devexit_p(mc13783_regulator_remove),
};
static int __init mc13783_regulator_init(void)
{
return platform_driver_probe(&mc13783_regulator_driver,
mc13783_regulator_probe);
}
subsys_initcall(mc13783_regulator_init);
static void __exit mc13783_regulator_exit(void)
{
platform_driver_unregister(&mc13783_regulator_driver);
}
module_exit(mc13783_regulator_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
MODULE_ALIAS("platform:mc13783-regulator");

View file

@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
@ -40,6 +41,12 @@ struct twlreg_info {
u8 table_len; u8 table_len;
const u16 *table; const u16 *table;
/* regulator specific turn-on delay */
u16 delay;
/* State REMAP default configuration */
u8 remap;
/* chip constraints on regulator behavior */ /* chip constraints on regulator behavior */
u16 min_mV; u16 min_mV;
@ -128,6 +135,7 @@ static int twlreg_enable(struct regulator_dev *rdev)
{ {
struct twlreg_info *info = rdev_get_drvdata(rdev); struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp; int grp;
int ret;
grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0) if (grp < 0)
@ -138,7 +146,11 @@ static int twlreg_enable(struct regulator_dev *rdev)
else else
grp |= P1_GRP_6030; grp |= P1_GRP_6030;
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
udelay(info->delay);
return ret;
} }
static int twlreg_disable(struct regulator_dev *rdev) static int twlreg_disable(struct regulator_dev *rdev)
@ -151,9 +163,9 @@ static int twlreg_disable(struct regulator_dev *rdev)
return grp; return grp;
if (twl_class_is_4030()) if (twl_class_is_4030())
grp &= ~P1_GRP_4030; grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030);
else else
grp &= ~P1_GRP_6030; grp &= ~(P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030);
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
} }
@ -294,6 +306,18 @@ static const u16 VSIM_VSEL_table[] = {
static const u16 VDAC_VSEL_table[] = { static const u16 VDAC_VSEL_table[] = {
1200, 1300, 1800, 1800, 1200, 1300, 1800, 1800,
}; };
static const u16 VDD1_VSEL_table[] = {
800, 1450,
};
static const u16 VDD2_VSEL_table[] = {
800, 1450, 1500,
};
static const u16 VIO_VSEL_table[] = {
1800, 1850,
};
static const u16 VINTANA2_VSEL_table[] = {
2500, 2750,
};
static const u16 VAUX1_6030_VSEL_table[] = { static const u16 VAUX1_6030_VSEL_table[] = {
1000, 1300, 1800, 2500, 1000, 1300, 1800, 2500,
2800, 2900, 3000, 3000, 2800, 2900, 3000, 3000,
@ -414,20 +438,30 @@ static struct regulator_ops twlfixed_ops = {
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
#define TWL4030_ADJUSTABLE_LDO(label, offset, num) \ #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \
TWL_ADJUSTABLE_LDO(label, offset, num, TWL4030) TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
#define TWL4030_FIXED_LDO(label, offset, mVolts, num) \ remap_conf, TWL4030)
TWL_FIXED_LDO(label, offset, mVolts, num, TWL4030) #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
#define TWL6030_ADJUSTABLE_LDO(label, offset, num) \ remap_conf) \
TWL_ADJUSTABLE_LDO(label, offset, num, TWL6030) TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
#define TWL6030_FIXED_LDO(label, offset, mVolts, num) \ remap_conf, TWL4030)
TWL_FIXED_LDO(label, offset, mVolts, num, TWL6030) #define TWL6030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
remap_conf) \
TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
remap_conf, TWL6030)
#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
remap_conf) \
TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
remap_conf, TWL6030)
#define TWL_ADJUSTABLE_LDO(label, offset, num, family) { \ #define TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf, \
family) { \
.base = offset, \ .base = offset, \
.id = num, \ .id = num, \
.table_len = ARRAY_SIZE(label##_VSEL_table), \ .table_len = ARRAY_SIZE(label##_VSEL_table), \
.table = label##_VSEL_table, \ .table = label##_VSEL_table, \
.delay = turnon_delay, \
.remap = remap_conf, \
.desc = { \ .desc = { \
.name = #label, \ .name = #label, \
.id = family##_REG_##label, \ .id = family##_REG_##label, \
@ -438,10 +472,13 @@ static struct regulator_ops twlfixed_ops = {
}, \ }, \
} }
#define TWL_FIXED_LDO(label, offset, mVolts, num, family) { \ #define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
family) { \
.base = offset, \ .base = offset, \
.id = num, \ .id = num, \
.min_mV = mVolts, \ .min_mV = mVolts, \
.delay = turnon_delay, \
.remap = remap_conf, \
.desc = { \ .desc = { \
.name = #label, \ .name = #label, \
.id = family##_REG_##label, \ .id = family##_REG_##label, \
@ -457,43 +494,41 @@ static struct regulator_ops twlfixed_ops = {
* software control over them after boot. * software control over them after boot.
*/ */
static struct twlreg_info twl_regs[] = { static struct twlreg_info twl_regs[] = {
TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1), TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2), TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2), TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3), TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4), TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5), TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6), TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6, 100, 0x08),
/* TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7, 100, 0x00),
TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7), TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8, 100, 0x08),
*/ TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9, 100, 0x00),
TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8), TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9), TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10), TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12, 100, 0x08),
/* TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08),
TWL4030_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11), TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08),
TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12), TWL4030_ADJUSTABLE_LDO(VDD1, 0x55, 15, 1000, 0x08),
TWL4030_ADJUSTABLE_LDO(VINTDIG, 0x47, 13), TWL4030_ADJUSTABLE_LDO(VDD2, 0x63, 16, 1000, 0x08),
TWL4030_SMPS(VIO, 0x4b, 14), TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08),
TWL4030_SMPS(VDD1, 0x55, 15), TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08),
TWL4030_SMPS(VDD2, 0x63, 16), TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19, 150, 0x08),
*/
TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
/* VUSBCP is managed *only* by the USB subchip */ /* VUSBCP is managed *only* by the USB subchip */
/* 6030 REG with base as PMC Slave Misc : 0x0030 */ /* 6030 REG with base as PMC Slave Misc : 0x0030 */
TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1), /* Turnon-delay and remap configuration values for 6030 are not
TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2), verified since the specification is not public */
TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3), TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1, 0, 0x08),
TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4), TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2, 0, 0x08),
TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5), TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3, 0, 0x08),
TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7), TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4, 0, 0x08),
TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15), TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5, 0, 0x08),
TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16), TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7, 0, 0x08),
TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17), TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x08),
TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18) TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x08),
TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x08),
TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x08)
}; };
static int twlreg_probe(struct platform_device *pdev) static int twlreg_probe(struct platform_device *pdev)
@ -525,6 +560,19 @@ static int twlreg_probe(struct platform_device *pdev)
c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE
| REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_MODE
| REGULATOR_CHANGE_STATUS; | REGULATOR_CHANGE_STATUS;
switch (pdev->id) {
case TWL4030_REG_VIO:
case TWL4030_REG_VDD1:
case TWL4030_REG_VDD2:
case TWL4030_REG_VPLL1:
case TWL4030_REG_VINTANA1:
case TWL4030_REG_VINTANA2:
case TWL4030_REG_VINTDIG:
c->always_on = true;
break;
default:
break;
}
rdev = regulator_register(&info->desc, &pdev->dev, initdata, info); rdev = regulator_register(&info->desc, &pdev->dev, initdata, info);
if (IS_ERR(rdev)) { if (IS_ERR(rdev)) {
@ -534,6 +582,9 @@ static int twlreg_probe(struct platform_device *pdev)
} }
platform_set_drvdata(pdev, rdev); platform_set_drvdata(pdev, rdev);
twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP,
info->remap);
/* NOTE: many regulators support short-circuit IRQs (presentable /* NOTE: many regulators support short-circuit IRQs (presentable
* as REGULATOR_OVER_CURRENT notifications?) configured via: * as REGULATOR_OVER_CURRENT notifications?) configured via:
* - SC_CONFIG * - SC_CONFIG

View file

@ -19,6 +19,8 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/gpio.h>
#include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/regulator.h> #include <linux/mfd/wm831x/regulator.h>
@ -39,6 +41,7 @@
#define WM831X_DCDC_CONTROL_2 1 #define WM831X_DCDC_CONTROL_2 1
#define WM831X_DCDC_ON_CONFIG 2 #define WM831X_DCDC_ON_CONFIG 2
#define WM831X_DCDC_SLEEP_CONTROL 3 #define WM831X_DCDC_SLEEP_CONTROL 3
#define WM831X_DCDC_DVS_CONTROL 4
/* /*
* Shared * Shared
@ -50,6 +53,10 @@ struct wm831x_dcdc {
int base; int base;
struct wm831x *wm831x; struct wm831x *wm831x;
struct regulator_dev *regulator; struct regulator_dev *regulator;
int dvs_gpio;
int dvs_gpio_state;
int on_vsel;
int dvs_vsel;
}; };
static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev) static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
@ -240,11 +247,9 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
return -EINVAL; return -EINVAL;
} }
static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg, static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV) int min_uV, int max_uV)
{ {
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 vsel; u16 vsel;
if (min_uV < 600000) if (min_uV < 600000)
@ -257,39 +262,126 @@ static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV) if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
return -EINVAL; return -EINVAL;
return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel); return vsel;
}
static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
u16 vsel;
if (max_uV < 600000 || max_uV > 1800000)
return -EINVAL;
vsel = ((max_uV - 600000) / 12500) + 8;
if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
return -EINVAL;
return vsel;
}
static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
if (state == dcdc->dvs_gpio_state)
return 0;
dcdc->dvs_gpio_state = state;
gpio_set_value(dcdc->dvs_gpio, state);
/* Should wait for DVS state change to be asserted if we have
* a GPIO for it, for now assume the device is configured
* for the fastest possible transition.
*/
return 0;
} }
static int wm831x_buckv_set_voltage(struct regulator_dev *rdev, static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV) int min_uV, int max_uV)
{ {
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; struct wm831x *wm831x = dcdc->wm831x;
int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL;
int vsel, ret;
return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV); vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV);
if (vsel < 0)
return vsel;
/* If this value is already set then do a GPIO update if we can */
if (dcdc->dvs_gpio && dcdc->on_vsel == vsel)
return wm831x_buckv_set_dvs(rdev, 0);
if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel)
return wm831x_buckv_set_dvs(rdev, 1);
/* Always set the ON status to the minimum voltage */
ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel);
if (ret < 0)
return ret;
dcdc->on_vsel = vsel;
if (!dcdc->dvs_gpio)
return ret;
/* Kick the voltage transition now */
ret = wm831x_buckv_set_dvs(rdev, 0);
if (ret < 0)
return ret;
/* Set the high voltage as the DVS voltage. This is optimised
* for CPUfreq usage, most processors will keep the maximum
* voltage constant and lower the minimum with the frequency. */
vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
if (vsel < 0) {
/* This should never happen - at worst the same vsel
* should be chosen */
WARN_ON(vsel < 0);
return 0;
}
/* Don't bother if it's the same VSEL we're already using */
if (vsel == dcdc->on_vsel)
return 0;
ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
if (ret == 0)
dcdc->dvs_vsel = vsel;
else
dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
ret);
return 0;
} }
static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
int uV) int uV)
{ {
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
int vsel;
return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV); vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV);
if (vsel < 0)
return vsel;
return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel);
} }
static int wm831x_buckv_get_voltage(struct regulator_dev *rdev) static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
{ {
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
int val;
val = wm831x_reg_read(wm831x, reg); if (dcdc->dvs_gpio && dcdc->dvs_gpio_state)
if (val < 0) return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel);
return val; else
return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel);
return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
} }
/* Current limit options */ /* Current limit options */
@ -346,6 +438,64 @@ static struct regulator_ops wm831x_buckv_ops = {
.set_suspend_mode = wm831x_dcdc_set_suspend_mode, .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
}; };
/*
* Set up DVS control. We just log errors since we can still run
* (with reduced performance) if we fail.
*/
static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
struct wm831x_buckv_pdata *pdata)
{
struct wm831x *wm831x = dcdc->wm831x;
int ret;
u16 ctrl;
if (!pdata || !pdata->dvs_gpio)
return;
switch (pdata->dvs_control_src) {
case 1:
ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
break;
case 2:
ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
break;
default:
dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
pdata->dvs_control_src, dcdc->name);
return;
}
ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
WM831X_DC1_DVS_SRC_MASK, ctrl);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
dcdc->name, ret);
return;
}
ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
if (ret < 0) {
dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
dcdc->name, ret);
return;
}
/* gpiolib won't let us read the GPIO status so pick the higher
* of the two existing voltages so we take it as platform data.
*/
dcdc->dvs_gpio_state = pdata->dvs_init_state;
ret = gpio_direction_output(pdata->dvs_gpio, dcdc->dvs_gpio_state);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to enable %s DVS GPIO: %d\n",
dcdc->name, ret);
gpio_free(pdata->dvs_gpio);
return;
}
dcdc->dvs_gpio = pdata->dvs_gpio;
}
static __devinit int wm831x_buckv_probe(struct platform_device *pdev) static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
{ {
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@ -384,6 +534,23 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
dcdc->desc.ops = &wm831x_buckv_ops; dcdc->desc.ops = &wm831x_buckv_ops;
dcdc->desc.owner = THIS_MODULE; dcdc->desc.owner = THIS_MODULE;
ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret);
goto err;
}
dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
goto err;
}
dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK;
if (pdata->dcdc[id])
wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data);
dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
pdata->dcdc[id], dcdc); pdata->dcdc[id], dcdc);
if (IS_ERR(dcdc->regulator)) { if (IS_ERR(dcdc->regulator)) {
@ -422,6 +589,8 @@ err_uv:
err_regulator: err_regulator:
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
err: err:
if (dcdc->dvs_gpio)
gpio_free(dcdc->dvs_gpio);
kfree(dcdc); kfree(dcdc);
return ret; return ret;
} }
@ -434,6 +603,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc); wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
if (dcdc->dvs_gpio)
gpio_free(dcdc->dvs_gpio);
kfree(dcdc); kfree(dcdc);
return 0; return 0;

View file

@ -470,7 +470,7 @@ static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x; struct wm831x *wm831x = ldo->wm831x;
int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
unsigned int ret; int ret;
ret = wm831x_reg_read(wm831x, on_reg); ret = wm831x_reg_read(wm831x, on_reg);
if (ret < 0) if (ret < 0)

View file

@ -41,6 +41,23 @@ struct wm831x_battery_pdata {
int timeout; /** Charge cycle timeout, in minutes */ int timeout; /** Charge cycle timeout, in minutes */
}; };
/**
* Configuration for the WM831x DC-DC BuckWise convertors. This
* should be passed as driver_data in the regulator_init_data.
*
* Currently all the configuration is for the fast DVS switching
* support of the devices. This allows MFPs on the device to be
* configured as an input to switch between two output voltages,
* allowing voltage transitions without the expense of an access over
* I2C or SPI buses.
*/
struct wm831x_buckv_pdata {
int dvs_gpio; /** CPU GPIO to use for DVS switching */
int dvs_control_src; /** Hardware DVS source to use (1 or 2) */
int dvs_init_state; /** DVS state to expect on startup */
int dvs_state_gpio; /** CPU GPIO to use for monitoring status */
};
/* Sources for status LED configuration. Values are register values /* Sources for status LED configuration. Values are register values
* plus 1 to allow for a zero default for preserve. * plus 1 to allow for a zero default for preserve.
*/ */

View file

@ -35,6 +35,8 @@
#ifndef __LINUX_REGULATOR_CONSUMER_H_ #ifndef __LINUX_REGULATOR_CONSUMER_H_
#define __LINUX_REGULATOR_CONSUMER_H_ #define __LINUX_REGULATOR_CONSUMER_H_
#include <linux/device.h>
/* /*
* Regulator operating modes. * Regulator operating modes.
* *

View file

@ -43,16 +43,20 @@ struct regulator;
/** /**
* struct regulator_state - regulator state during low power system states * struct regulator_state - regulator state during low power system states
* *
* This describes a regulators state during a system wide low power state. * This describes a regulators state during a system wide low power
* state. One of enabled or disabled must be set for the
* configuration to be applied.
* *
* @uV: Operating voltage during suspend. * @uV: Operating voltage during suspend.
* @mode: Operating mode during suspend. * @mode: Operating mode during suspend.
* @enabled: Enabled during suspend. * @enabled: Enabled during suspend.
* @disabled: Disabled during suspend.
*/ */
struct regulator_state { struct regulator_state {
int uV; /* suspend voltage */ int uV; /* suspend voltage */
unsigned int mode; /* suspend regulator operating mode */ unsigned int mode; /* suspend regulator operating mode */
int enabled; /* is regulator enabled in this suspend state */ int enabled; /* is regulator enabled in this suspend state */
int disabled; /* is the regulator disbled in this suspend state */
}; };
/** /**

View file

@ -0,0 +1,57 @@
/*
* max8660.h -- Voltage regulation for the Maxim 8660/8661
*
* Copyright (C) 2009 Wolfram Sang, Pengutronix e.K.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __LINUX_REGULATOR_MAX8660_H
#define __LINUX_REGULATOR_MAX8660_H
#include <linux/regulator/machine.h>
enum {
MAX8660_V3,
MAX8660_V4,
MAX8660_V5,
MAX8660_V6,
MAX8660_V7,
MAX8660_V_END,
};
/**
* max8660_subdev_data - regulator subdev data
* @id: regulator id
* @name: regulator name
* @platform_data: regulator init data
*/
struct max8660_subdev_data {
int id;
char *name;
struct regulator_init_data *platform_data;
};
/**
* max8660_platform_data - platform data for max8660
* @num_subdevs: number of regulators used
* @subdevs: pointer to regulators used
* @en34_is_high: if EN34 is driven high, regulators cannot be en-/disabled.
*/
struct max8660_platform_data {
int num_subdevs;
struct max8660_subdev_data *subdevs;
unsigned en34_is_high:1;
};
#endif