2009-01-09 08:51:01 +08:00
|
|
|
/* NXP PCF50633 PMIC Driver
|
|
|
|
*
|
|
|
|
* (C) 2006-2008 by Openmoko, Inc.
|
|
|
|
* Author: Balaji Rao <balajirrao@openmoko.org>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Broken down from monstrous PCF50633 driver mainly by
|
|
|
|
* Harald Welte and Andy Green and Werner Almesberger
|
|
|
|
*
|
|
|
|
* 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; either version 2 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
|
|
|
#include <linux/mfd/pcf50633/core.h>
|
|
|
|
#include <linux/mfd/pcf50633/pmic.h>
|
|
|
|
|
2012-04-18 10:50:37 +08:00
|
|
|
#define PCF50633_REGULATOR(_name, _id, _n) \
|
|
|
|
{ \
|
|
|
|
.name = _name, \
|
|
|
|
.id = PCF50633_REGULATOR_##_id, \
|
|
|
|
.ops = &pcf50633_regulator_ops, \
|
|
|
|
.n_voltages = _n, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.owner = THIS_MODULE, \
|
2012-04-18 10:51:59 +08:00
|
|
|
.vsel_reg = PCF50633_REG_##_id##OUT, \
|
|
|
|
.vsel_mask = 0xff, \
|
2012-04-18 10:50:37 +08:00
|
|
|
.enable_reg = PCF50633_REG_##_id##OUT + 1, \
|
|
|
|
.enable_mask = PCF50633_REGULATOR_ON, \
|
2009-01-09 08:51:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Bits from voltage value */
|
|
|
|
static u8 auto_voltage_bits(unsigned int millivolts)
|
|
|
|
{
|
|
|
|
if (millivolts < 1800)
|
regulator: pcf50633: Don't write to reserved bits of AUTO output voltage select register
The datasheet says 00000000 to 00101110 are reserved, and the min value of the
voltage setting is 1.8 V.
Thus don't write 0 to AUTO output voltage select register (address 1Ah).
Table 50. AUTOOUT - AUTO output voltage select register (address 1Ah) bit description[1]
Bit Symbol Access Description
7:0 auto_out R/W VO(prog) = 0.625 + auto_out × 0.025 V
eg. 00000000 to 00101110: reserved
00101111: 1.8 V (min)
01010011: 2.7 V
01101010: 3.275 V
01101011: 3.300 V
01101100: 3.325 V
01111111 : 3.800 V (max)
..... .....
11111110 : 3.800 V
11111111 : 3.800 V
This patch also fixes a bug in pcf50633_regulator_list_voltage:
In regulator core _regulator_do_set_voltage function:
if (rdev->desc->ops->set_voltage) {
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV,
&selector);
if (rdev->desc->ops->list_voltage)
selector = rdev->desc->ops->list_voltage(rdev,
selector);
else
selector = -1;
The list_voltage call here takes the selector got from set_voltage callback.
Thus adding 0x2f to the index in pcf50633_regulator_list_voltage looks wrong to me.
e.g.
If min_uV < 1.8V, pcf50633_regulator_set_voltage sets 0 to selector.
For this case, adding 0x2f to the index in pcf50633_regulator_list_voltage is correct.
However, if min_uV == 1.8V, pcf50633_regulator_set_voltage sets 0x2f to selector.
Adding 0x2f to the index in pcf50633_regulator_list_voltage in this case is wrong.
What this patch does is:
The minimal voltage setting for AUTOOUT is 0x2f.
Thus for the case min_uV < 1.8, set the voltage setting to 1.8V by writting
0x2f to AUTOOUT register and set selector = 0x2f.
So we don't write the rserved range to AUTOOUT register.
Which means the possible range of AUTOOUT register value is 0x2f ~ 0xff.
We have no problem in regulator_get_voltage.
Since we won't write 0~0x2e to AUTOOUT register, we have no problem converting
the bits we read to voltage. The equation in auto_voltage_value works fine.
For list_voltage, we need to take into account the case selector is 0 ~ 0x2e
because the regulator core assumes the selector is starting from 0.
This patch returns 0 for the cases selector is 0 ~ 0x2e, which means
"this selector code can't be used on this system".
The regulator core iterates from 0 to n_voltages to find the small voltage
in the specific range. The n_voltages settings for AUTOOUT should be 128 now,
including the reserved range of AUTOOUT.
Signed-off-by: Axel Lin <axel.lin@gmail.com>
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
2012-03-19 10:55:24 +08:00
|
|
|
return 0x2f;
|
2009-01-09 08:51:01 +08:00
|
|
|
if (millivolts > 3800)
|
|
|
|
return 0xff;
|
|
|
|
|
|
|
|
millivolts -= 625;
|
|
|
|
|
|
|
|
return millivolts / 25;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 down_voltage_bits(unsigned int millivolts)
|
|
|
|
{
|
|
|
|
if (millivolts < 625)
|
|
|
|
return 0;
|
|
|
|
else if (millivolts > 3000)
|
|
|
|
return 0xff;
|
|
|
|
|
|
|
|
millivolts -= 625;
|
|
|
|
|
|
|
|
return millivolts / 25;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 ldo_voltage_bits(unsigned int millivolts)
|
|
|
|
{
|
|
|
|
if (millivolts < 900)
|
|
|
|
return 0;
|
|
|
|
else if (millivolts > 3600)
|
|
|
|
return 0x1f;
|
|
|
|
|
|
|
|
millivolts -= 900;
|
|
|
|
return millivolts / 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Obtain voltage value from bits */
|
|
|
|
static unsigned int auto_voltage_value(u8 bits)
|
|
|
|
{
|
regulator: pcf50633: Don't write to reserved bits of AUTO output voltage select register
The datasheet says 00000000 to 00101110 are reserved, and the min value of the
voltage setting is 1.8 V.
Thus don't write 0 to AUTO output voltage select register (address 1Ah).
Table 50. AUTOOUT - AUTO output voltage select register (address 1Ah) bit description[1]
Bit Symbol Access Description
7:0 auto_out R/W VO(prog) = 0.625 + auto_out × 0.025 V
eg. 00000000 to 00101110: reserved
00101111: 1.8 V (min)
01010011: 2.7 V
01101010: 3.275 V
01101011: 3.300 V
01101100: 3.325 V
01111111 : 3.800 V (max)
..... .....
11111110 : 3.800 V
11111111 : 3.800 V
This patch also fixes a bug in pcf50633_regulator_list_voltage:
In regulator core _regulator_do_set_voltage function:
if (rdev->desc->ops->set_voltage) {
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV,
&selector);
if (rdev->desc->ops->list_voltage)
selector = rdev->desc->ops->list_voltage(rdev,
selector);
else
selector = -1;
The list_voltage call here takes the selector got from set_voltage callback.
Thus adding 0x2f to the index in pcf50633_regulator_list_voltage looks wrong to me.
e.g.
If min_uV < 1.8V, pcf50633_regulator_set_voltage sets 0 to selector.
For this case, adding 0x2f to the index in pcf50633_regulator_list_voltage is correct.
However, if min_uV == 1.8V, pcf50633_regulator_set_voltage sets 0x2f to selector.
Adding 0x2f to the index in pcf50633_regulator_list_voltage in this case is wrong.
What this patch does is:
The minimal voltage setting for AUTOOUT is 0x2f.
Thus for the case min_uV < 1.8, set the voltage setting to 1.8V by writting
0x2f to AUTOOUT register and set selector = 0x2f.
So we don't write the rserved range to AUTOOUT register.
Which means the possible range of AUTOOUT register value is 0x2f ~ 0xff.
We have no problem in regulator_get_voltage.
Since we won't write 0~0x2e to AUTOOUT register, we have no problem converting
the bits we read to voltage. The equation in auto_voltage_value works fine.
For list_voltage, we need to take into account the case selector is 0 ~ 0x2e
because the regulator core assumes the selector is starting from 0.
This patch returns 0 for the cases selector is 0 ~ 0x2e, which means
"this selector code can't be used on this system".
The regulator core iterates from 0 to n_voltages to find the small voltage
in the specific range. The n_voltages settings for AUTOOUT should be 128 now,
including the reserved range of AUTOOUT.
Signed-off-by: Axel Lin <axel.lin@gmail.com>
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
2012-03-19 10:55:24 +08:00
|
|
|
/* AUTOOUT: 00000000 to 00101110 are reserved.
|
|
|
|
* Return 0 for bits in reserved range, which means this selector code
|
|
|
|
* can't be used on this system */
|
2009-01-09 08:51:01 +08:00
|
|
|
if (bits < 0x2f)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 625 + (bits * 25);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int down_voltage_value(u8 bits)
|
|
|
|
{
|
|
|
|
return 625 + (bits * 25);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int ldo_voltage_value(u8 bits)
|
|
|
|
{
|
|
|
|
bits &= 0x1f;
|
|
|
|
|
|
|
|
return 900 + (bits * 100);
|
|
|
|
}
|
|
|
|
|
2012-06-11 14:43:57 +08:00
|
|
|
static int pcf50633_regulator_map_voltage(struct regulator_dev *rdev,
|
|
|
|
int min_uV, int max_uV)
|
2009-01-09 08:51:01 +08:00
|
|
|
{
|
|
|
|
struct pcf50633 *pcf;
|
|
|
|
int regulator_id, millivolts;
|
2012-06-11 14:43:57 +08:00
|
|
|
u8 volt_bits;
|
2009-01-09 08:51:01 +08:00
|
|
|
|
|
|
|
pcf = rdev_get_drvdata(rdev);
|
|
|
|
|
|
|
|
regulator_id = rdev_get_id(rdev);
|
|
|
|
if (regulator_id >= PCF50633_NUM_REGULATORS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
millivolts = min_uV / 1000;
|
|
|
|
|
|
|
|
switch (regulator_id) {
|
|
|
|
case PCF50633_REGULATOR_AUTO:
|
|
|
|
volt_bits = auto_voltage_bits(millivolts);
|
|
|
|
break;
|
|
|
|
case PCF50633_REGULATOR_DOWN1:
|
|
|
|
case PCF50633_REGULATOR_DOWN2:
|
|
|
|
volt_bits = down_voltage_bits(millivolts);
|
|
|
|
break;
|
|
|
|
case PCF50633_REGULATOR_LDO1:
|
|
|
|
case PCF50633_REGULATOR_LDO2:
|
|
|
|
case PCF50633_REGULATOR_LDO3:
|
|
|
|
case PCF50633_REGULATOR_LDO4:
|
|
|
|
case PCF50633_REGULATOR_LDO5:
|
|
|
|
case PCF50633_REGULATOR_LDO6:
|
|
|
|
case PCF50633_REGULATOR_HCLDO:
|
2012-02-29 12:45:35 +08:00
|
|
|
case PCF50633_REGULATOR_MEMLDO:
|
2009-01-09 08:51:01 +08:00
|
|
|
volt_bits = ldo_voltage_bits(millivolts);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-06-11 14:43:57 +08:00
|
|
|
return volt_bits;
|
2009-01-09 08:51:01 +08:00
|
|
|
}
|
|
|
|
|
2012-03-19 10:57:06 +08:00
|
|
|
static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev,
|
|
|
|
unsigned int index)
|
|
|
|
{
|
|
|
|
int regulator_id = rdev_get_id(rdev);
|
|
|
|
|
2009-08-04 08:03:52 +08:00
|
|
|
int millivolts;
|
|
|
|
|
2012-03-19 10:57:06 +08:00
|
|
|
switch (regulator_id) {
|
2009-08-04 08:03:52 +08:00
|
|
|
case PCF50633_REGULATOR_AUTO:
|
2012-03-19 10:57:06 +08:00
|
|
|
millivolts = auto_voltage_value(index);
|
2009-08-04 08:03:52 +08:00
|
|
|
break;
|
|
|
|
case PCF50633_REGULATOR_DOWN1:
|
|
|
|
case PCF50633_REGULATOR_DOWN2:
|
2012-03-19 10:57:06 +08:00
|
|
|
millivolts = down_voltage_value(index);
|
2009-08-04 08:03:52 +08:00
|
|
|
break;
|
|
|
|
case PCF50633_REGULATOR_LDO1:
|
|
|
|
case PCF50633_REGULATOR_LDO2:
|
|
|
|
case PCF50633_REGULATOR_LDO3:
|
|
|
|
case PCF50633_REGULATOR_LDO4:
|
|
|
|
case PCF50633_REGULATOR_LDO5:
|
|
|
|
case PCF50633_REGULATOR_LDO6:
|
|
|
|
case PCF50633_REGULATOR_HCLDO:
|
2012-02-29 12:45:35 +08:00
|
|
|
case PCF50633_REGULATOR_MEMLDO:
|
2012-03-19 10:57:06 +08:00
|
|
|
millivolts = ldo_voltage_value(index);
|
2009-08-04 08:03:52 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return millivolts * 1000;
|
|
|
|
}
|
|
|
|
|
2009-01-09 08:51:01 +08:00
|
|
|
static struct regulator_ops pcf50633_regulator_ops = {
|
2012-06-11 14:43:57 +08:00
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
2012-04-18 10:51:59 +08:00
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
2009-08-04 08:03:52 +08:00
|
|
|
.list_voltage = pcf50633_regulator_list_voltage,
|
2012-06-11 14:43:57 +08:00
|
|
|
.map_voltage = pcf50633_regulator_map_voltage,
|
2012-04-18 10:50:37 +08:00
|
|
|
.enable = regulator_enable_regmap,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
2009-01-09 08:51:01 +08:00
|
|
|
};
|
|
|
|
|
2012-04-05 12:02:36 +08:00
|
|
|
static const struct regulator_desc regulators[] = {
|
2012-04-18 10:50:37 +08:00
|
|
|
[PCF50633_REGULATOR_AUTO] = PCF50633_REGULATOR("auto", AUTO, 128),
|
|
|
|
[PCF50633_REGULATOR_DOWN1] = PCF50633_REGULATOR("down1", DOWN1, 96),
|
|
|
|
[PCF50633_REGULATOR_DOWN2] = PCF50633_REGULATOR("down2", DOWN2, 96),
|
|
|
|
[PCF50633_REGULATOR_LDO1] = PCF50633_REGULATOR("ldo1", LDO1, 28),
|
|
|
|
[PCF50633_REGULATOR_LDO2] = PCF50633_REGULATOR("ldo2", LDO2, 28),
|
|
|
|
[PCF50633_REGULATOR_LDO3] = PCF50633_REGULATOR("ldo3", LDO3, 28),
|
|
|
|
[PCF50633_REGULATOR_LDO4] = PCF50633_REGULATOR("ldo4", LDO4, 28),
|
|
|
|
[PCF50633_REGULATOR_LDO5] = PCF50633_REGULATOR("ldo5", LDO5, 28),
|
|
|
|
[PCF50633_REGULATOR_LDO6] = PCF50633_REGULATOR("ldo6", LDO6, 28),
|
|
|
|
[PCF50633_REGULATOR_HCLDO] = PCF50633_REGULATOR("hcldo", HCLDO, 28),
|
|
|
|
[PCF50633_REGULATOR_MEMLDO] = PCF50633_REGULATOR("memldo", MEMLDO, 28),
|
2009-01-09 08:51:01 +08:00
|
|
|
};
|
|
|
|
|
2012-11-20 02:22:22 +08:00
|
|
|
static int pcf50633_regulator_probe(struct platform_device *pdev)
|
2009-01-09 08:51:01 +08:00
|
|
|
{
|
|
|
|
struct regulator_dev *rdev;
|
|
|
|
struct pcf50633 *pcf;
|
2012-04-04 07:50:22 +08:00
|
|
|
struct regulator_config config = { };
|
2009-01-09 08:51:01 +08:00
|
|
|
|
|
|
|
/* Already set by core driver */
|
2009-10-14 06:12:36 +08:00
|
|
|
pcf = dev_to_pcf50633(pdev->dev.parent);
|
2009-01-09 08:51:01 +08:00
|
|
|
|
2012-04-04 07:50:22 +08:00
|
|
|
config.dev = &pdev->dev;
|
|
|
|
config.init_data = pdev->dev.platform_data;
|
|
|
|
config.driver_data = pcf;
|
2012-04-18 10:50:37 +08:00
|
|
|
config.regmap = pcf->regmap;
|
2012-04-04 07:50:22 +08:00
|
|
|
|
|
|
|
rdev = regulator_register(®ulators[pdev->id], &config);
|
2009-01-09 08:51:01 +08:00
|
|
|
if (IS_ERR(rdev))
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
|
2009-10-14 06:12:36 +08:00
|
|
|
platform_set_drvdata(pdev, rdev);
|
|
|
|
|
2009-01-09 08:51:01 +08:00
|
|
|
if (pcf->pdata->regulator_registered)
|
|
|
|
pcf->pdata->regulator_registered(pcf, pdev->id);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct regulator_dev *rdev = platform_get_drvdata(pdev);
|
|
|
|
|
2009-10-14 06:12:36 +08:00
|
|
|
platform_set_drvdata(pdev, NULL);
|
2009-01-09 08:51:01 +08:00
|
|
|
regulator_unregister(rdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver pcf50633_regulator_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "pcf50633-regltr",
|
|
|
|
},
|
|
|
|
.probe = pcf50633_regulator_probe,
|
2012-11-20 02:20:42 +08:00
|
|
|
.remove = pcf50633_regulator_remove,
|
2009-01-09 08:51:01 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init pcf50633_regulator_init(void)
|
|
|
|
{
|
|
|
|
return platform_driver_register(&pcf50633_regulator_driver);
|
|
|
|
}
|
2009-04-28 01:21:18 +08:00
|
|
|
subsys_initcall(pcf50633_regulator_init);
|
2009-01-09 08:51:01 +08:00
|
|
|
|
|
|
|
static void __exit pcf50633_regulator_exit(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&pcf50633_regulator_driver);
|
|
|
|
}
|
|
|
|
module_exit(pcf50633_regulator_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
|
|
|
|
MODULE_DESCRIPTION("PCF50633 regulator driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS("platform:pcf50633-regulator");
|