linux/drivers/regulator/tps65217-regulator.c
AnilKumar Ch 1922b0f275 mfd: Move tps65217 regulator plat data handling to regulator
Regulator platform data handling was mistakenly added to MFD
driver. So we will see build errors if we compile MFD drivers
without CONFIG_REGULATOR. This patch moves regulator platform
data handling from TPS65217 MFD driver to regulator driver.

This makes MFD driver independent of REGULATOR framework so
build error is fixed if CONFIG_REGULATOR is not set.

drivers/built-in.o: In function `tps65217_probe':
tps65217.c:(.devinit.text+0x13e37): undefined reference
to `of_regulator_match'

This patch also fix allocation size of tps65217 platform data.
Current implementation allocates a struct tps65217_board for each
regulator specified in the device tree. But the structure itself
provides array of regulators so one instance of it is sufficient.

Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
2012-08-22 10:55:25 +02:00

437 lines
12 KiB
C

/*
* tps65217-regulator.c
*
* Regulator driver for TPS65217 PMIC
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* 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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/tps65217.h>
#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \
{ \
.name = _name, \
.id = _id, \
.ops = &_ops, \
.n_voltages = _n, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.vsel_reg = _vr, \
.vsel_mask = _vm, \
.enable_reg = TPS65217_REG_ENABLE, \
.enable_mask = _em, \
.volt_table = _t, \
} \
#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \
{ \
.name = _nm, \
.min_uV = _min, \
.max_uV = _max, \
.vsel_to_uv = _f1, \
.uv_to_vsel = _f2, \
}
static const unsigned int LDO1_VSEL_table[] = {
1000000, 1100000, 1200000, 1250000,
1300000, 1350000, 1400000, 1500000,
1600000, 1800000, 2500000, 2750000,
2800000, 3000000, 3100000, 3300000,
};
static int tps65217_vsel_to_uv1(unsigned int vsel)
{
int uV = 0;
if (vsel > 63)
return -EINVAL;
if (vsel <= 24)
uV = vsel * 25000 + 900000;
else if (vsel <= 52)
uV = (vsel - 24) * 50000 + 1500000;
else if (vsel < 56)
uV = (vsel - 52) * 100000 + 2900000;
else
uV = 3300000;
return uV;
}
static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel)
{
if (uV < 0 || uV > 3300000)
return -EINVAL;
if (uV <= 1500000)
*vsel = DIV_ROUND_UP(uV - 900000, 25000);
else if (uV <= 2900000)
*vsel = 24 + DIV_ROUND_UP(uV - 1500000, 50000);
else if (uV < 3300000)
*vsel = 52 + DIV_ROUND_UP(uV - 2900000, 100000);
else
*vsel = 56;
return 0;
}
static int tps65217_vsel_to_uv2(unsigned int vsel)
{
int uV = 0;
if (vsel > 31)
return -EINVAL;
if (vsel <= 8)
uV = vsel * 50000 + 1500000;
else if (vsel <= 13)
uV = (vsel - 8) * 100000 + 1900000;
else
uV = (vsel - 13) * 50000 + 2400000;
return uV;
}
static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel)
{
if (uV < 0 || uV > 3300000)
return -EINVAL;
if (uV <= 1900000)
*vsel = DIV_ROUND_UP(uV - 1500000, 50000);
else if (uV <= 2400000)
*vsel = 8 + DIV_ROUND_UP(uV - 1900000, 100000);
else
*vsel = 13 + DIV_ROUND_UP(uV - 2400000, 50000);
return 0;
}
static struct tps_info tps65217_pmic_regs[] = {
TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1,
tps65217_uv_to_vsel1),
TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1,
tps65217_uv_to_vsel1),
TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1,
tps65217_uv_to_vsel1),
TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL),
TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1,
tps65217_uv_to_vsel1),
TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2,
tps65217_uv_to_vsel2),
TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2,
tps65217_uv_to_vsel2),
};
static int tps65217_pmic_enable(struct regulator_dev *dev)
{
struct tps65217 *tps = rdev_get_drvdata(dev);
unsigned int rid = rdev_get_id(dev);
if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
return -EINVAL;
/* Enable the regulator and password protection is level 1 */
return tps65217_set_bits(tps, TPS65217_REG_ENABLE,
dev->desc->enable_mask, dev->desc->enable_mask,
TPS65217_PROTECT_L1);
}
static int tps65217_pmic_disable(struct regulator_dev *dev)
{
struct tps65217 *tps = rdev_get_drvdata(dev);
unsigned int rid = rdev_get_id(dev);
if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
return -EINVAL;
/* Disable the regulator and password protection is level 1 */
return tps65217_clear_bits(tps, TPS65217_REG_ENABLE,
dev->desc->enable_mask, TPS65217_PROTECT_L1);
}
static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev,
unsigned selector)
{
int ret;
struct tps65217 *tps = rdev_get_drvdata(dev);
unsigned int rid = rdev_get_id(dev);
/* Set the voltage based on vsel value and write protect level is 2 */
ret = tps65217_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask,
selector, TPS65217_PROTECT_L2);
/* Set GO bit for DCDCx to initiate voltage transistion */
switch (rid) {
case TPS65217_DCDC_1 ... TPS65217_DCDC_3:
ret = tps65217_set_bits(tps, TPS65217_REG_DEFSLEW,
TPS65217_DEFSLEW_GO, TPS65217_DEFSLEW_GO,
TPS65217_PROTECT_L2);
break;
}
return ret;
}
static int tps65217_pmic_map_voltage(struct regulator_dev *dev,
int min_uV, int max_uV)
{
struct tps65217 *tps = rdev_get_drvdata(dev);
unsigned int sel, rid = rdev_get_id(dev);
int ret;
/* LDO1 uses regulator_map_voltage_iterate() */
if (rid == TPS65217_LDO_1)
return -EINVAL;
if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
return -EINVAL;
if (min_uV < tps->info[rid]->min_uV)
min_uV = tps->info[rid]->min_uV;
if (max_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV)
return -EINVAL;
ret = tps->info[rid]->uv_to_vsel(min_uV, &sel);
if (ret)
return ret;
return sel;
}
static int tps65217_pmic_list_voltage(struct regulator_dev *dev,
unsigned selector)
{
struct tps65217 *tps = rdev_get_drvdata(dev);
unsigned int rid = rdev_get_id(dev);
if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
return -EINVAL;
if (selector >= dev->desc->n_voltages)
return -EINVAL;
return tps->info[rid]->vsel_to_uv(selector);
}
/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */
static struct regulator_ops tps65217_pmic_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = tps65217_pmic_enable,
.disable = tps65217_pmic_disable,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = tps65217_pmic_set_voltage_sel,
.list_voltage = tps65217_pmic_list_voltage,
.map_voltage = tps65217_pmic_map_voltage,
};
/* Operations permitted on LDO1 */
static struct regulator_ops tps65217_pmic_ldo1_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = tps65217_pmic_enable,
.disable = tps65217_pmic_disable,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = tps65217_pmic_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
};
static const struct regulator_desc regulators[] = {
TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK,
TPS65217_ENABLE_DC1_EN, NULL),
TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK,
TPS65217_ENABLE_DC2_EN, NULL),
TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK,
TPS65217_ENABLE_DC3_EN, NULL),
TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16,
TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK,
TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table),
TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64,
TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK,
TPS65217_ENABLE_LDO2_EN, NULL),
TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32,
TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK,
TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN,
NULL),
TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32,
TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK,
TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN,
NULL),
};
#ifdef CONFIG_OF
static struct of_regulator_match reg_matches[] = {
{ .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 },
{ .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 },
{ .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 },
{ .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 },
{ .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 },
{ .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 },
{ .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 },
};
static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
{
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
struct device_node *node = tps->dev->of_node;
struct tps65217_board *pdata;
struct device_node *regs;
int i, count;
regs = of_find_node_by_name(node, "regulators");
if (!regs)
return NULL;
count = of_regulator_match(pdev->dev.parent, regs,
reg_matches, TPS65217_NUM_REGULATOR);
of_node_put(regs);
if ((count < 0) || (count > TPS65217_NUM_REGULATOR))
return NULL;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
for (i = 0; i < count; i++) {
if (!reg_matches[i].init_data || !reg_matches[i].of_node)
continue;
pdata->tps65217_init_data[i] = reg_matches[i].init_data;
pdata->of_node[i] = reg_matches[i].of_node;
}
return pdata;
}
#else
static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
{
return NULL;
}
#endif
static int __devinit tps65217_regulator_probe(struct platform_device *pdev)
{
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
struct tps65217_board *pdata = dev_get_platdata(tps->dev);
struct regulator_init_data *reg_data;
struct regulator_dev *rdev;
struct regulator_config config = { };
int i, ret;
if (tps->dev->of_node)
pdata = tps65217_parse_dt(pdev);
if (!pdata) {
dev_err(&pdev->dev, "Platform data not found\n");
return -EINVAL;
}
if (tps65217_chip_id(tps) != TPS65217) {
dev_err(&pdev->dev, "Invalid tps chip version\n");
return -ENODEV;
}
platform_set_drvdata(pdev, tps);
for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
reg_data = pdata->tps65217_init_data[i];
/*
* Regulator API handles empty constraints but not NULL
* constraints
*/
if (!reg_data)
continue;
/* Register the regulators */
tps->info[i] = &tps65217_pmic_regs[i];
config.dev = tps->dev;
config.init_data = reg_data;
config.driver_data = tps;
config.regmap = tps->regmap;
if (tps->dev->of_node)
config.of_node = pdata->of_node[i];
rdev = regulator_register(&regulators[i], &config);
if (IS_ERR(rdev)) {
dev_err(tps->dev, "failed to register %s regulator\n",
pdev->name);
ret = PTR_ERR(rdev);
goto err_unregister_regulator;
}
/* Save regulator for cleanup */
tps->rdev[i] = rdev;
}
return 0;
err_unregister_regulator:
while (--i >= 0)
regulator_unregister(tps->rdev[i]);
return ret;
}
static int __devexit tps65217_regulator_remove(struct platform_device *pdev)
{
struct tps65217 *tps = platform_get_drvdata(pdev);
unsigned int i;
for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
regulator_unregister(tps->rdev[i]);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver tps65217_regulator_driver = {
.driver = {
.name = "tps65217-pmic",
},
.probe = tps65217_regulator_probe,
.remove = __devexit_p(tps65217_regulator_remove),
};
static int __init tps65217_regulator_init(void)
{
return platform_driver_register(&tps65217_regulator_driver);
}
subsys_initcall(tps65217_regulator_init);
static void __exit tps65217_regulator_exit(void)
{
platform_driver_unregister(&tps65217_regulator_driver);
}
module_exit(tps65217_regulator_exit);
MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>");
MODULE_DESCRIPTION("TPS65217 voltage regulator driver");
MODULE_ALIAS("platform:tps65217-pmic");
MODULE_LICENSE("GPL v2");