blob: fa68c217c50de7082dcec368cd29576091153462 [file] [log] [blame]
/*
* Regulators driver for ASR PM802
*
* Copyright 2018 ASR Microelectronics (Shanghai) Co., Ltd.
*
* 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/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/88pm80x.h>
#include <linux/mfd/pm802.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/regulator/of_regulator.h>
/* LDO1 with DVC[0..3] */
#define PM802_LDO1_VOUT (0x09) /* VOUT1 */
#define PM802_LDO2_VOUT (0x0C)
#define PM802_LDO3_VOUT (0x0F)
#define PM802_LDO4_VOUT (0x12)
#define PM802_LDO5_VOUT (0x15)
#define PM802_LDO6_VOUT (0x18)
/* BUCK1 with DVC[0..3] */
#define PM802_BUCK1 (0x29)
#define PM802_BUCK2 (0x30)
#define PM802_BUCK3 (0x40)
#define PM802_BUCK4 (0x50)
#define PM802_BUCK5 (0x60)
#define PM802_BUCK1_ENA (0x20)
#define PM802_BUCK2_ENA (0x30)
#define PM802_BUCK3_ENA (0x40)
#define PM802_BUCK4_ENA (0x50)
#define PM802_BUCK5_ENA (0x60)
#define PM802_LDO1_ENA (0x09) /* VOUT1 */
#define PM802_LDO2_ENA (0x0C)
#define PM802_LDO3_ENA (0x0F)
#define PM802_LDO4_ENA (0x12)
#define PM802_LDO5_ENA (0x15)
#define PM802_LDO6_ENA (0x18)
#define MAX_SLEEP_CURRENT 5000
#define REGULATOR_SLEEP_MODE_DIS 0
#define REGULATOR_SLEEP_MODE_EN 1
/*
* Convert voltage value to reg value for BUCK1
*/
#define BUCK1_VOL2REG(vol) \
(((vol) > 1587500) \
? ((((vol)-1600000)/50000) + 0x50) \
: (((vol)-600000)/12500))
#define BUCK1_REG2VOL(reg) \
(((reg) < 0x50) \
? (reg * 12500 + 600000) \
: ((reg - 0x50) * 50000 + 1600000))
struct pm802_regulator_volt_range {
int min_uv;
int max_uv;
int step_uv;
/* the register value for min_uv */
int min_val;
};
struct pm802_regulator_info {
struct regulator_desc desc;
int max_ua;
unsigned int sleep_enable_bit;
unsigned int sleep_enable_reg;
struct pm802_regulator_volt_range *volt;
unsigned int ranges;
};
struct pm802_regulators {
struct regulator_dev *regulators[PM802_ID_RG_MAX];
struct pm80x_chip *chip;
struct regmap *map;
};
/*
* vreg - the buck regs string.
* ereg - the string for the enable register.
* ebit - the bit number in the enable register.
* amax - the current
* Buck has 2 kinds of voltage steps. It is easy to find voltage by ranges,
* not the constant voltage table.
*/
#define PM802_BUCK(vreg, ereg, ebit, amax, sebit, sereg, volt_ranges) \
{ \
.desc = { \
.name = #vreg, \
.ops = &pm802_volt_range_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PM802_ID_##vreg, \
.owner = THIS_MODULE, \
.vsel_reg = PM802_##vreg, \
.vsel_mask = 0x7f, \
.enable_reg = PM802_##ereg, \
.enable_mask = 1 << (ebit), \
}, \
.max_ua = (amax), \
.volt = (volt_ranges), \
.ranges = ARRAY_SIZE(volt_ranges), \
.sleep_enable_bit = sebit, \
.sleep_enable_reg = PM802_##sereg, \
}
/*
* vreg - the LDO regs string
* ereg - the string for the enable register.
* ebit - the bit number in the enable register.
* amax - the current
* volt_table - the LDO voltage table
* For all the LDOes, there are too many ranges. Using volt_table will be
* simpler and faster.
*/
#define PM802_LDO(vreg, ereg, ebit, amax, sebit, sereg, ldo_volt_table) \
{ \
.desc = { \
.name = #vreg, \
.ops = &pm802_volt_table_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PM802_ID_##vreg, \
.owner = THIS_MODULE, \
.n_voltages = ARRAY_SIZE(ldo_volt_table), \
.vsel_reg = PM802_##vreg##_VOUT, \
.vsel_mask = 0x3c, \
.enable_reg = PM802_##ereg, \
.enable_mask = 1 << (ebit), \
.volt_table = ldo_volt_table, \
}, \
.max_ua = (amax), \
.sleep_enable_bit = sebit, \
.sleep_enable_reg = PM802_##sereg, \
}
#define PM802S_LDO(vreg, ereg, ebit, amax, sebit, sereg, ldo_volt_table) \
{ \
.desc = { \
.name = #vreg, \
.ops = &pm802_volt_table_ops, \
.type = REGULATOR_VOLTAGE, \
.id = PM802_ID_##vreg, \
.owner = THIS_MODULE, \
.n_voltages = ARRAY_SIZE(ldo_volt_table), \
.vsel_reg = PM802_##vreg##_VOUT, \
.vsel_mask = 0xf, \
.enable_reg = PM802_##ereg, \
.enable_mask = 1 << (ebit), \
.volt_table = ldo_volt_table, \
}, \
.max_ua = (amax), \
.sleep_enable_bit = sebit, \
.sleep_enable_reg = PM802_##sereg, \
}
/* Ranges are sorted in ascending order. */
static struct pm802_regulator_volt_range buck1_5_volt_range[] = {
{600000, 1587500, 12500, 0x0},
{1600000, 3950000, 50000, 0x50},
};
/* LDO 1~6 have same voltage table. */
static const unsigned int ldo1_4_volt_table[] = {
1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000,
2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000,
};
static const unsigned int ldo5_6_volt_table[] = {
1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000,
};
static const unsigned int pm802s_ldo_volt_table[] = {
1200000, 1250000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000,
1900000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000,
};
int pm802_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct pm802_regulator_info *info = rdev_get_drvdata(rdev);
unsigned int val, sleep_bit, sleep_enable_mask, reg;
sleep_bit = info->sleep_enable_bit;
sleep_enable_mask = (0x3 << sleep_bit);
/*
* if mode is REGULATOR_SLEEP_MODE_EN, enable related LDO sleep mode
* else disable related LDO sleep mode.
*/
switch (mode) {
case REGULATOR_MODE_IDLE:
val = (0x2 << sleep_bit);
break;
case REGULATOR_MODE_NORMAL:
val = (0x3 << sleep_bit);
break;
default:
dev_err(rdev_get_dev(rdev), "No right mode to set!\n");
return -EINVAL;
}
reg = info->sleep_enable_reg;
return regmap_update_bits(rdev->regmap, reg, sleep_enable_mask, val);
}
static unsigned int pm802_get_optimum_mode(struct regulator_dev *rdev,
int input_uV, int output_uV,
int output_uA)
{
struct pm802_regulator_info *info = rdev_get_drvdata(rdev);
if (info == NULL) {
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
return REGULATOR_MODE_IDLE;
}
if (output_uA < 0) {
dev_err(rdev_get_dev(rdev), "wrong current input, current < 0!!!\n");
return REGULATOR_MODE_IDLE;
}
/*
* get_optimum_mode be called at enbale/disable_regulator function.
* If output_uA is not set it will be 0,
* set its defult value to be REGULATOR_MODE_IDLE.
*/
return (MAX_SLEEP_CURRENT > output_uA) ? REGULATOR_MODE_IDLE :
REGULATOR_MODE_NORMAL;
}
static int pm802_get_current_limit(struct regulator_dev *rdev)
{
struct pm802_regulator_info *info = rdev_get_drvdata(rdev);
return info->max_ua;
}
static int pm802_set_voltage(struct regulator_dev *rdev,
int min_uv, int max_uv, unsigned *selector)
{
struct pm802_regulator_info *info = rdev_get_drvdata(rdev);
struct pm802_regulator_volt_range *range;
int i, best_index = -1;
if (info->volt == NULL)
return -EINVAL;
if (info->desc.id == PM802_ID_VOUTSW)
return 0;
/*
* Ranges are sorted in ascending order. So if we found a best_uv
* in this range, we can break out.
*/
for (i = 0; i < info->ranges; i++) {
range = &info->volt[i];
if (min_uv <= range->max_uv && max_uv >= range->min_uv) {
if (min_uv <= range->min_uv)
best_index = 0;
else
best_index = (min_uv - range->min_uv +
range->step_uv - 1) / range->step_uv;
break;
}
}
if (best_index == -1)
return -EINVAL;
*selector = best_index + range->min_val;
return regulator_set_voltage_sel_regmap(rdev, *selector);
}
static int pm802_get_voltage(struct regulator_dev *rdev)
{
struct pm802_regulator_info *info = rdev_get_drvdata(rdev);
struct pm802_regulator_volt_range *range;
int i, val, max_val, volt = -EINVAL;
if (info->volt == NULL)
return -EINVAL;
val = regulator_get_voltage_sel_regmap(rdev);
if (val < 0)
return val;
if (info->desc.id == PM802_ID_VOUTSW)
return 0;
/* get the voltage via the register value */
for (i = 0; i < info->ranges; i++) {
range = &info->volt[i];
max_val = (range->max_uv - range->min_uv) / range->step_uv
+ range->min_val;
if (val >= range->min_val && val <= max_val) {
volt = (val - range->min_val) * range->step_uv
+ range->min_uv;
break;
}
}
return volt;
}
static struct regulator_ops pm802_volt_range_ops = {
.set_voltage = pm802_set_voltage,
.get_voltage = pm802_get_voltage,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_current_limit = pm802_get_current_limit,
.get_optimum_mode = pm802_get_optimum_mode,
.set_mode = pm802_set_mode,
};
static struct regulator_ops pm802_volt_table_ops = {
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_iterate,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.get_current_limit = pm802_get_current_limit,
.get_optimum_mode = pm802_get_optimum_mode,
.set_mode = pm802_set_mode,
};
/* The array is indexed by id(PM802_ID_XXX) */
static struct pm802_regulator_info pm802_regulator_info[] = {
PM802_BUCK(BUCK1, BUCK1_ENA, 7, 1600000, 3, BUCK_SLP1,
buck1_5_volt_range),
PM802_BUCK(BUCK2, BUCK2_ENA, 7, 800000, 3, BUCK_SLP2,
buck1_5_volt_range),
PM802_BUCK(BUCK3, BUCK3_ENA, 7, 800000, 3, BUCK_SLP3,
buck1_5_volt_range),
PM802_BUCK(BUCK4, BUCK4_ENA, 7, 800000, 3, BUCK_SLP4,
buck1_5_volt_range),
PM802_BUCK(BUCK5, BUCK5_ENA, 7, 1200000, 3, BUCK_SLP5,
buck1_5_volt_range),
PM802_LDO(LDO1, LDO1_ENA, 6, 200000, 4, LDO_SLP1,
ldo1_4_volt_table),
PM802_LDO(LDO2, LDO2_ENA, 6, 200000, 4, LDO_SLP2,
ldo1_4_volt_table),
PM802_LDO(LDO3, LDO3_ENA, 6, 400000, 4, LDO_SLP3,
ldo1_4_volt_table),
PM802_LDO(LDO4, LDO4_ENA, 6, 400000, 4, LDO_SLP4,
ldo1_4_volt_table),
PM802_LDO(LDO5, LDO5_ENA, 6, 200000, 4, LDO_SLP5,
ldo5_6_volt_table),
PM802_LDO(LDO6, LDO6_ENA, 6, 200000, 4, LDO_SLP6,
ldo5_6_volt_table),
};
static struct pm802_regulator_info pm802s_regulator_info[] = {
PM802_BUCK(BUCK1, BUCK1_ENA, 7, 1600000, 3, BUCK_SLP1,
buck1_5_volt_range),
PM802_BUCK(BUCK2, BUCK2_ENA, 7, 800000, 3, BUCK_SLP2,
buck1_5_volt_range),
PM802_BUCK(BUCK3, BUCK3_ENA, 7, 800000, 3, BUCK_SLP3,
buck1_5_volt_range),
PM802_BUCK(BUCK4, BUCK4_ENA, 7, 800000, 3, BUCK_SLP4,
buck1_5_volt_range),
PM802_BUCK(BUCK5, BUCK5_ENA, 7, 1200000, 3, BUCK_SLP5,
buck1_5_volt_range),
PM802S_LDO(LDO1, LDO1_ENA, 7, 200000, 4, LDO_SLP1,
pm802s_ldo_volt_table),
PM802S_LDO(LDO2, LDO2_ENA, 7, 200000, 4, LDO_SLP2,
pm802s_ldo_volt_table),
PM802S_LDO(LDO3, LDO3_ENA, 7, 400000, 4, LDO_SLP3,
pm802s_ldo_volt_table),
PM802S_LDO(LDO4, LDO4_ENA, 7, 400000, 4, LDO_SLP4,
pm802s_ldo_volt_table),
PM802S_LDO(LDO5, LDO5_ENA, 7, 200000, 4, LDO_SLP5,
pm802s_ldo_volt_table),
PM802S_LDO(LDO6, LDO6_ENA, 7, 200000, 4, LDO_SLP6,
pm802s_ldo_volt_table),
};
#define PM802_REGULATOR_OF_MATCH(id) \
{ \
.name = "PM802-" #id, \
.driver_data = &pm802_regulator_info[PM802_ID_##id], \
}
static struct of_regulator_match pm802_regulator_matches[] = {
PM802_REGULATOR_OF_MATCH(BUCK1),
PM802_REGULATOR_OF_MATCH(BUCK2),
PM802_REGULATOR_OF_MATCH(BUCK3),
PM802_REGULATOR_OF_MATCH(BUCK4),
PM802_REGULATOR_OF_MATCH(BUCK5),
PM802_REGULATOR_OF_MATCH(LDO1),
PM802_REGULATOR_OF_MATCH(LDO2),
/* PM802_REGULATOR_OF_MATCH(LDO3), */
PM802_REGULATOR_OF_MATCH(LDO4),
PM802_REGULATOR_OF_MATCH(LDO5),
PM802_REGULATOR_OF_MATCH(LDO6),
};
#define PM802S_REGULATOR_OF_MATCH(id) \
{ \
.name = "PM802-" #id, \
.driver_data = &pm802s_regulator_info[PM802_ID_##id], \
}
static struct of_regulator_match pm802s_regulator_matches[] = {
PM802S_REGULATOR_OF_MATCH(BUCK1),
PM802S_REGULATOR_OF_MATCH(BUCK2),
PM802S_REGULATOR_OF_MATCH(BUCK3),
PM802S_REGULATOR_OF_MATCH(BUCK4),
PM802S_REGULATOR_OF_MATCH(BUCK5),
PM802S_REGULATOR_OF_MATCH(LDO1),
PM802S_REGULATOR_OF_MATCH(LDO2),
/* PM802S_REGULATOR_OF_MATCH(LDO3), */
PM802S_REGULATOR_OF_MATCH(LDO4),
PM802S_REGULATOR_OF_MATCH(LDO5),
PM802S_REGULATOR_OF_MATCH(LDO6),
};
static int pm802_regulator_dt_init(struct platform_device *pdev,
struct of_regulator_match **regulator_matches, int *range)
{
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct device_node *np = pdev->dev.of_node;
if (chip->type == CHIP_PM802) {
if (chip->chip_id == CHIP_PM802S_ID_A0
|| chip->chip_id == CHIP_PM802S_ID_A1
|| chip->chip_id == CHIP_PM802S_ID_A2) {
*regulator_matches = pm802s_regulator_matches;
*range = ARRAY_SIZE(pm802s_regulator_matches);
} else {
*regulator_matches = pm802_regulator_matches;
*range = ARRAY_SIZE(pm802_regulator_matches);
}
} else
return -ENODEV;
return of_regulator_match(&pdev->dev, np, *regulator_matches, *range);
}
static int pm802_regulator_probe(struct platform_device *pdev)
{
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm80x_platform_data *pdata = pdev->dev.parent->platform_data;
struct pm802_regulators *pm802_data;
struct pm802_regulator_info *info;
struct regulator_config config = { };
struct regulator_init_data *init_data;
int i, ret, range = 0;
struct of_regulator_match *regulator_matches = NULL;
if (!pdata || pdata->num_regulators == 0) {
if (IS_ENABLED(CONFIG_OF)) {
ret = pm802_regulator_dt_init(pdev, &regulator_matches,
&range);
if (ret < 0)
return ret;
} else {
return -ENODEV;
}
} else if (pdata->num_regulators) {
/* Check whether num_regulator is valid. */
unsigned int count = 0;
for (i = 0; pdata->regulators[i]; i++)
count++;
if (count != pdata->num_regulators)
return -EINVAL;
} else {
return -EINVAL;
}
pm802_data = devm_kzalloc(&pdev->dev, sizeof(*pm802_data),
GFP_KERNEL);
if (!pm802_data) {
dev_err(&pdev->dev, "Failed to allocate PM802_regualtors");
return -ENOMEM;
}
pm802_data->map = chip->subchip->regmap_power;
pm802_data->chip = chip;
platform_set_drvdata(pdev, pm802_data);
for (i = 0; i < range; i++) {
if (!pdata || pdata->num_regulators == 0)
init_data = regulator_matches->init_data;
else
init_data = pdata->regulators[i];
if (!init_data) {
dev_err(&pdev->dev, "%s not matched!\n",
regulator_matches->name);
regulator_matches++;
continue;
}
info = regulator_matches->driver_data;
config.dev = &pdev->dev;
config.init_data = init_data;
config.driver_data = info;
config.regmap = pm802_data->map;
config.of_node = regulator_matches->of_node;
pm802_data->regulators[i] =
regulator_register(&info->desc, &config);
if (IS_ERR(pm802_data->regulators[i])) {
ret = PTR_ERR(pm802_data->regulators[i]);
dev_err(&pdev->dev, "Failed to register %s\n",
info->desc.name);
while (--i >= 0 && pm802_data->regulators[i])
regulator_unregister(pm802_data->regulators[i]);
return ret;
}
pm802_data->regulators[i]->constraints->valid_ops_mask |=
(REGULATOR_CHANGE_DRMS | REGULATOR_CHANGE_MODE);
pm802_data->regulators[i]->constraints->valid_modes_mask |=
(REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE);
pm802_data->regulators[i]->constraints->input_uV = 1000;
regulator_matches++;
}
return 0;
}
static int pm802_regulator_remove(struct platform_device *pdev)
{
struct pm802_regulators *pm802_data = platform_get_drvdata(pdev);
int i;
for (i = 0; pm802_data->regulators[i] && i < PM802_ID_RG_MAX; i++)
regulator_unregister(pm802_data->regulators[i]);
return 0;
}
static struct platform_driver pm802_regulator_driver = {
.driver = {
.name = "pm802-regulator",
.owner = THIS_MODULE,
},
.probe = pm802_regulator_probe,
.remove = pm802_regulator_remove,
};
static int __init pm802_regulator_init(void)
{
return platform_driver_register(&pm802_regulator_driver);
}
subsys_initcall(pm802_regulator_init);
static void __exit pm802_regulator_exit(void)
{
platform_driver_unregister(&pm802_regulator_driver);
}
module_exit(pm802_regulator_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Regulator Driver for ASR PM802 PMIC");
MODULE_ALIAS("platform:PM802-regulator");