mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
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:
commit
22a8059359
17 changed files with 1982 additions and 612 deletions
685
drivers/regulator/88pm8607.c
Normal file
685
drivers/regulator/88pm8607.c
Normal 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");
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(®->dev, "disabling LDO D - shut down system\n");
|
dev_info(®->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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,8 +446,8 @@ static int setup_regulators(struct lp3971 *lp3971,
|
||||||
lp3971->rdev[i] = regulator_register(®ulators[id],
|
lp3971->rdev[i] = regulator_register(®ulators[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
510
drivers/regulator/max8660.c
Normal 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");
|
245
drivers/regulator/mc13783-regulator.c
Normal file
245
drivers/regulator/mc13783-regulator.c
Normal 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");
|
|
@ -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");
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
57
include/linux/regulator/max8660.h
Normal file
57
include/linux/regulator/max8660.h
Normal 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
|
Loading…
Reference in a new issue