2019-05-01 01:11:31 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//
|
|
|
|
// Device driver for regulators in Hi6421 IC
|
|
|
|
//
|
|
|
|
// Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd.
|
|
|
|
// http://www.hisilicon.com
|
|
|
|
// Copyright (c) <2013-2014> Linaro Ltd.
|
2020-07-19 20:06:23 +00:00
|
|
|
// https://www.linaro.org
|
2019-05-01 01:11:31 +00:00
|
|
|
//
|
|
|
|
// Author: Guodong Xu <guodong.xu@linaro.org>
|
2014-08-13 11:33:42 +00:00
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/regmap.h>
|
|
|
|
#include <linux/regulator/driver.h>
|
|
|
|
#include <linux/regulator/machine.h>
|
|
|
|
#include <linux/regulator/of_regulator.h>
|
|
|
|
#include <linux/mfd/hi6421-pmic.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* struct hi6421_regulator_pdata - Hi6421 regulator data of platform device
|
|
|
|
* @lock: mutex to serialize regulator enable
|
|
|
|
*/
|
|
|
|
struct hi6421_regulator_pdata {
|
|
|
|
struct mutex lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* struct hi6421_regulator_info - hi6421 regulator information
|
|
|
|
* @desc: regulator description
|
|
|
|
* @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
|
2014-09-05 01:14:26 +00:00
|
|
|
* @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
|
2014-08-13 11:33:42 +00:00
|
|
|
*/
|
|
|
|
struct hi6421_regulator_info {
|
|
|
|
struct regulator_desc desc;
|
|
|
|
u8 mode_mask;
|
|
|
|
u32 eco_microamp;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* HI6421 regulators */
|
|
|
|
enum hi6421_regulator_id {
|
|
|
|
HI6421_LDO0,
|
|
|
|
HI6421_LDO1,
|
|
|
|
HI6421_LDO2,
|
|
|
|
HI6421_LDO3,
|
|
|
|
HI6421_LDO4,
|
|
|
|
HI6421_LDO5,
|
|
|
|
HI6421_LDO6,
|
|
|
|
HI6421_LDO7,
|
|
|
|
HI6421_LDO8,
|
|
|
|
HI6421_LDO9,
|
|
|
|
HI6421_LDO10,
|
|
|
|
HI6421_LDO11,
|
|
|
|
HI6421_LDO12,
|
|
|
|
HI6421_LDO13,
|
|
|
|
HI6421_LDO14,
|
|
|
|
HI6421_LDO15,
|
|
|
|
HI6421_LDO16,
|
|
|
|
HI6421_LDO17,
|
|
|
|
HI6421_LDO18,
|
|
|
|
HI6421_LDO19,
|
|
|
|
HI6421_LDO20,
|
|
|
|
HI6421_LDOAUDIO,
|
|
|
|
HI6421_BUCK0,
|
|
|
|
HI6421_BUCK1,
|
|
|
|
HI6421_BUCK2,
|
|
|
|
HI6421_BUCK3,
|
|
|
|
HI6421_BUCK4,
|
|
|
|
HI6421_BUCK5,
|
|
|
|
HI6421_NUM_REGULATORS,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* LDO 0, 4~7, 9~14, 16~20 have same voltage table. */
|
|
|
|
static const unsigned int ldo_0_voltages[] = {
|
|
|
|
1500000, 1800000, 2400000, 2500000,
|
|
|
|
2600000, 2700000, 2850000, 3000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* LDO 8, 15 have same voltage table. */
|
|
|
|
static const unsigned int ldo_8_voltages[] = {
|
|
|
|
1500000, 1800000, 2400000, 2600000,
|
|
|
|
2700000, 2850000, 3000000, 3300000,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Ranges are sorted in ascending order. */
|
2020-05-08 15:43:36 +00:00
|
|
|
static const struct linear_range ldo_audio_volt_range[] = {
|
2014-08-13 11:33:42 +00:00
|
|
|
REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000),
|
|
|
|
REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000),
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned int buck_3_voltages[] = {
|
|
|
|
950000, 1050000, 1100000, 1117000,
|
|
|
|
1134000, 1150000, 1167000, 1200000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned int buck_4_voltages[] = {
|
|
|
|
1150000, 1200000, 1250000, 1350000,
|
|
|
|
1700000, 1800000, 1900000, 2000000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned int buck_5_voltages[] = {
|
|
|
|
1150000, 1200000, 1250000, 1350000,
|
|
|
|
1600000, 1700000, 1800000, 1900000,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_ldo_ops;
|
|
|
|
static const struct regulator_ops hi6421_ldo_linear_ops;
|
|
|
|
static const struct regulator_ops hi6421_ldo_linear_range_ops;
|
|
|
|
static const struct regulator_ops hi6421_buck012_ops;
|
|
|
|
static const struct regulator_ops hi6421_buck345_ops;
|
|
|
|
|
|
|
|
#define HI6421_LDO_ENABLE_TIME (350)
|
|
|
|
/*
|
|
|
|
* _id - LDO id name string
|
2019-04-10 16:14:27 +00:00
|
|
|
* _match - of match name string
|
2014-08-13 11:33:42 +00:00
|
|
|
* v_table - voltage table
|
|
|
|
* vreg - voltage select register
|
|
|
|
* vmask - voltage select mask
|
|
|
|
* ereg - enable register
|
|
|
|
* emask - enable mask
|
|
|
|
* odelay - off/on delay time in uS
|
|
|
|
* ecomask - eco mode mask
|
2014-09-06 12:59:36 +00:00
|
|
|
* ecoamp - eco mode load uppler limit in uA
|
2014-08-13 11:33:42 +00:00
|
|
|
*/
|
2019-04-10 16:14:27 +00:00
|
|
|
#define HI6421_LDO(_id, _match, v_table, vreg, vmask, ereg, emask, \
|
2014-08-13 11:33:42 +00:00
|
|
|
odelay, ecomask, ecoamp) \
|
|
|
|
[HI6421_##_id] = { \
|
|
|
|
.desc = { \
|
|
|
|
.name = #_id, \
|
2019-04-10 16:14:27 +00:00
|
|
|
.of_match = of_match_ptr(#_match), \
|
|
|
|
.regulators_node = of_match_ptr("regulators"), \
|
2014-08-13 11:33:42 +00:00
|
|
|
.ops = &hi6421_ldo_ops, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.id = HI6421_##_id, \
|
|
|
|
.owner = THIS_MODULE, \
|
|
|
|
.n_voltages = ARRAY_SIZE(v_table), \
|
|
|
|
.volt_table = v_table, \
|
|
|
|
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
|
|
|
.vsel_mask = vmask, \
|
|
|
|
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
|
|
|
.enable_mask = emask, \
|
|
|
|
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
|
|
|
.off_on_delay = odelay, \
|
|
|
|
}, \
|
|
|
|
.mode_mask = ecomask, \
|
|
|
|
.eco_microamp = ecoamp, \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HI6421 LDO1~3 are linear voltage regulators at fixed uV_step
|
|
|
|
*
|
|
|
|
* _id - LDO id name string
|
2019-04-10 16:14:27 +00:00
|
|
|
* _match - of match name string
|
2014-08-13 11:33:42 +00:00
|
|
|
* _min_uV - minimum voltage supported in uV
|
|
|
|
* n_volt - number of votages available
|
|
|
|
* vstep - voltage increase in each linear step in uV
|
|
|
|
* vreg - voltage select register
|
|
|
|
* vmask - voltage select mask
|
|
|
|
* ereg - enable register
|
|
|
|
* emask - enable mask
|
|
|
|
* odelay - off/on delay time in uS
|
|
|
|
* ecomask - eco mode mask
|
2014-09-06 12:59:36 +00:00
|
|
|
* ecoamp - eco mode load uppler limit in uA
|
2014-08-13 11:33:42 +00:00
|
|
|
*/
|
2019-04-10 16:14:27 +00:00
|
|
|
#define HI6421_LDO_LINEAR(_id, _match, _min_uV, n_volt, vstep, vreg, vmask,\
|
2014-08-13 11:33:42 +00:00
|
|
|
ereg, emask, odelay, ecomask, ecoamp) \
|
|
|
|
[HI6421_##_id] = { \
|
|
|
|
.desc = { \
|
|
|
|
.name = #_id, \
|
2019-04-10 16:14:27 +00:00
|
|
|
.of_match = of_match_ptr(#_match), \
|
|
|
|
.regulators_node = of_match_ptr("regulators"), \
|
2014-08-13 11:33:42 +00:00
|
|
|
.ops = &hi6421_ldo_linear_ops, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.id = HI6421_##_id, \
|
|
|
|
.owner = THIS_MODULE, \
|
|
|
|
.min_uV = _min_uV, \
|
|
|
|
.n_voltages = n_volt, \
|
|
|
|
.uV_step = vstep, \
|
|
|
|
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
|
|
|
.vsel_mask = vmask, \
|
|
|
|
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
|
|
|
.enable_mask = emask, \
|
|
|
|
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
|
|
|
.off_on_delay = odelay, \
|
|
|
|
}, \
|
|
|
|
.mode_mask = ecomask, \
|
|
|
|
.eco_microamp = ecoamp, \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HI6421 LDOAUDIO is a linear voltage regulator with two 4-step ranges
|
|
|
|
*
|
|
|
|
* _id - LDO id name string
|
2019-04-10 16:14:27 +00:00
|
|
|
* _match - of match name string
|
2014-08-13 11:33:42 +00:00
|
|
|
* n_volt - number of votages available
|
2020-05-08 15:43:36 +00:00
|
|
|
* volt_ranges - array of linear_range
|
2014-08-13 11:33:42 +00:00
|
|
|
* vstep - voltage increase in each linear step in uV
|
|
|
|
* vreg - voltage select register
|
|
|
|
* vmask - voltage select mask
|
|
|
|
* ereg - enable register
|
|
|
|
* emask - enable mask
|
|
|
|
* odelay - off/on delay time in uS
|
|
|
|
* ecomask - eco mode mask
|
2014-09-06 12:59:36 +00:00
|
|
|
* ecoamp - eco mode load uppler limit in uA
|
2014-08-13 11:33:42 +00:00
|
|
|
*/
|
2019-04-10 16:14:27 +00:00
|
|
|
#define HI6421_LDO_LINEAR_RANGE(_id, _match, n_volt, volt_ranges, vreg, vmask,\
|
2014-08-13 11:33:42 +00:00
|
|
|
ereg, emask, odelay, ecomask, ecoamp) \
|
|
|
|
[HI6421_##_id] = { \
|
|
|
|
.desc = { \
|
|
|
|
.name = #_id, \
|
2019-04-10 16:14:27 +00:00
|
|
|
.of_match = of_match_ptr(#_match), \
|
|
|
|
.regulators_node = of_match_ptr("regulators"), \
|
2014-08-13 11:33:42 +00:00
|
|
|
.ops = &hi6421_ldo_linear_range_ops, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.id = HI6421_##_id, \
|
|
|
|
.owner = THIS_MODULE, \
|
|
|
|
.n_voltages = n_volt, \
|
|
|
|
.linear_ranges = volt_ranges, \
|
|
|
|
.n_linear_ranges = ARRAY_SIZE(volt_ranges), \
|
|
|
|
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
|
|
|
.vsel_mask = vmask, \
|
|
|
|
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
|
|
|
.enable_mask = emask, \
|
|
|
|
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
|
|
|
.off_on_delay = odelay, \
|
|
|
|
}, \
|
|
|
|
.mode_mask = ecomask, \
|
|
|
|
.eco_microamp = ecoamp, \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HI6421 BUCK0/1/2 are linear voltage regulators at fixed uV_step
|
|
|
|
*
|
|
|
|
* _id - BUCK0/1/2 id name string
|
2019-04-10 16:14:27 +00:00
|
|
|
* _match - of match name string
|
2014-08-13 11:33:42 +00:00
|
|
|
* vreg - voltage select register
|
|
|
|
* vmask - voltage select mask
|
|
|
|
* ereg - enable register
|
|
|
|
* emask - enable mask
|
|
|
|
* sleepmask - mask of sleep mode
|
|
|
|
* etime - enable time
|
|
|
|
* odelay - off/on delay time in uS
|
|
|
|
*/
|
2019-04-10 16:14:27 +00:00
|
|
|
#define HI6421_BUCK012(_id, _match, vreg, vmask, ereg, emask, sleepmask,\
|
2014-08-13 11:33:42 +00:00
|
|
|
etime, odelay) \
|
|
|
|
[HI6421_##_id] = { \
|
|
|
|
.desc = { \
|
|
|
|
.name = #_id, \
|
2019-04-10 16:14:27 +00:00
|
|
|
.of_match = of_match_ptr(#_match), \
|
|
|
|
.regulators_node = of_match_ptr("regulators"), \
|
2014-08-13 11:33:42 +00:00
|
|
|
.ops = &hi6421_buck012_ops, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.id = HI6421_##_id, \
|
|
|
|
.owner = THIS_MODULE, \
|
|
|
|
.min_uV = 700000, \
|
|
|
|
.n_voltages = 128, \
|
|
|
|
.uV_step = 7086, \
|
|
|
|
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
|
|
|
.vsel_mask = vmask, \
|
|
|
|
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
|
|
|
.enable_mask = emask, \
|
|
|
|
.enable_time = etime, \
|
|
|
|
.off_on_delay = odelay, \
|
|
|
|
}, \
|
|
|
|
.mode_mask = sleepmask, \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HI6421 BUCK3/4/5 share similar configurations as LDOs, with exception
|
|
|
|
* that it supports SLEEP mode, so has different .ops.
|
|
|
|
*
|
|
|
|
* _id - LDO id name string
|
2019-04-10 16:14:27 +00:00
|
|
|
* _match - of match name string
|
2014-08-13 11:33:42 +00:00
|
|
|
* v_table - voltage table
|
|
|
|
* vreg - voltage select register
|
|
|
|
* vmask - voltage select mask
|
|
|
|
* ereg - enable register
|
|
|
|
* emask - enable mask
|
|
|
|
* odelay - off/on delay time in uS
|
|
|
|
* sleepmask - mask of sleep mode
|
|
|
|
*/
|
2019-04-10 16:14:27 +00:00
|
|
|
#define HI6421_BUCK345(_id, _match, v_table, vreg, vmask, ereg, emask, \
|
2014-08-13 11:33:42 +00:00
|
|
|
odelay, sleepmask) \
|
|
|
|
[HI6421_##_id] = { \
|
|
|
|
.desc = { \
|
|
|
|
.name = #_id, \
|
2019-04-10 16:14:27 +00:00
|
|
|
.of_match = of_match_ptr(#_match), \
|
|
|
|
.regulators_node = of_match_ptr("regulators"), \
|
2014-08-13 11:33:42 +00:00
|
|
|
.ops = &hi6421_buck345_ops, \
|
|
|
|
.type = REGULATOR_VOLTAGE, \
|
|
|
|
.id = HI6421_##_id, \
|
|
|
|
.owner = THIS_MODULE, \
|
|
|
|
.n_voltages = ARRAY_SIZE(v_table), \
|
|
|
|
.volt_table = v_table, \
|
|
|
|
.vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \
|
|
|
|
.vsel_mask = vmask, \
|
|
|
|
.enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \
|
|
|
|
.enable_mask = emask, \
|
|
|
|
.enable_time = HI6421_LDO_ENABLE_TIME, \
|
|
|
|
.off_on_delay = odelay, \
|
|
|
|
}, \
|
|
|
|
.mode_mask = sleepmask, \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* HI6421 regulator information */
|
|
|
|
static struct hi6421_regulator_info
|
|
|
|
hi6421_regulator_info[HI6421_NUM_REGULATORS] = {
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO0, hi6421_vout0, ldo_0_voltages, 0x20, 0x07, 0x20, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
10000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO_LINEAR(LDO1, hi6421_vout1, 1700000, 4, 100000, 0x21, 0x03,
|
|
|
|
0x21, 0x10, 10000, 0x20, 5000),
|
|
|
|
HI6421_LDO_LINEAR(LDO2, hi6421_vout2, 1050000, 8, 50000, 0x22, 0x07,
|
|
|
|
0x22, 0x10, 20000, 0x20, 8000),
|
|
|
|
HI6421_LDO_LINEAR(LDO3, hi6421_vout3, 1050000, 8, 50000, 0x23, 0x07,
|
|
|
|
0x23, 0x10, 20000, 0x20, 8000),
|
|
|
|
HI6421_LDO(LDO4, hi6421_vout4, ldo_0_voltages, 0x24, 0x07, 0x24, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
20000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO5, hi6421_vout5, ldo_0_voltages, 0x25, 0x07, 0x25, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
20000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO6, hi6421_vout6, ldo_0_voltages, 0x26, 0x07, 0x26, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
20000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO7, hi6421_vout7, ldo_0_voltages, 0x27, 0x07, 0x27, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
20000, 0x20, 5000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO8, hi6421_vout8, ldo_8_voltages, 0x28, 0x07, 0x28, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
20000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO9, hi6421_vout9, ldo_0_voltages, 0x29, 0x07, 0x29, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO10, hi6421_vout10, ldo_0_voltages, 0x2a, 0x07, 0x2a, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO11, hi6421_vout11, ldo_0_voltages, 0x2b, 0x07, 0x2b, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO12, hi6421_vout12, ldo_0_voltages, 0x2c, 0x07, 0x2c, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO13, hi6421_vout13, ldo_0_voltages, 0x2d, 0x07, 0x2d, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO14, hi6421_vout14, ldo_0_voltages, 0x2e, 0x07, 0x2e, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO15, hi6421_vout15, ldo_8_voltages, 0x2f, 0x07, 0x2f, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO16, hi6421_vout16, ldo_0_voltages, 0x30, 0x07, 0x30, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO17, hi6421_vout17, ldo_0_voltages, 0x31, 0x07, 0x31, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO18, hi6421_vout18, ldo_0_voltages, 0x32, 0x07, 0x32, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO19, hi6421_vout19, ldo_0_voltages, 0x33, 0x07, 0x33, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO(LDO20, hi6421_vout20, ldo_0_voltages, 0x34, 0x07, 0x34, 0x10,
|
2014-08-13 11:33:42 +00:00
|
|
|
40000, 0x20, 8000),
|
2019-04-10 16:14:27 +00:00
|
|
|
HI6421_LDO_LINEAR_RANGE(LDOAUDIO, hi6421_vout_audio, 8,
|
|
|
|
ldo_audio_volt_range, 0x36, 0x70, 0x36, 0x01,
|
|
|
|
40000, 0x02, 5000),
|
|
|
|
HI6421_BUCK012(BUCK0, hi6421_buck0, 0x0d, 0x7f, 0x0c, 0x01, 0x10, 400,
|
|
|
|
20000),
|
|
|
|
HI6421_BUCK012(BUCK1, hi6421_buck1, 0x0f, 0x7f, 0x0e, 0x01, 0x10, 400,
|
|
|
|
20000),
|
|
|
|
HI6421_BUCK012(BUCK2, hi6421_buck2, 0x11, 0x7f, 0x10, 0x01, 0x10, 350,
|
|
|
|
100),
|
|
|
|
HI6421_BUCK345(BUCK3, hi6421_buck3, buck_3_voltages, 0x13, 0x07, 0x12,
|
|
|
|
0x01, 20000, 0x10),
|
|
|
|
HI6421_BUCK345(BUCK4, hi6421_buck4, buck_4_voltages, 0x15, 0x07, 0x14,
|
|
|
|
0x01, 20000, 0x10),
|
|
|
|
HI6421_BUCK345(BUCK5, hi6421_buck5, buck_5_voltages, 0x17, 0x07, 0x16,
|
|
|
|
0x01, 20000, 0x10),
|
2014-08-13 11:33:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int hi6421_regulator_enable(struct regulator_dev *rdev)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_pdata *pdata = rdev_get_drvdata(rdev);
|
2014-08-13 11:33:42 +00:00
|
|
|
|
|
|
|
/* hi6421 spec requires regulator enablement must be serialized:
|
|
|
|
* - Because when BUCK, LDO switching from off to on, it will have
|
|
|
|
* a huge instantaneous current; so you can not turn on two or
|
|
|
|
* more LDO or BUCKs simultaneously, or it may burn the chip.
|
|
|
|
*/
|
|
|
|
mutex_lock(&pdata->lock);
|
|
|
|
|
|
|
|
/* call regulator regmap helper */
|
|
|
|
regulator_enable_regmap(rdev);
|
|
|
|
|
|
|
|
mutex_unlock(&pdata->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
2021-06-19 12:41:33 +00:00
|
|
|
unsigned int reg_val;
|
2014-08-13 11:33:42 +00:00
|
|
|
|
2021-06-30 09:59:59 +00:00
|
|
|
info = container_of(rdev->desc, struct hi6421_regulator_info, desc);
|
2014-08-13 11:33:42 +00:00
|
|
|
regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val);
|
|
|
|
if (reg_val & info->mode_mask)
|
|
|
|
return REGULATOR_MODE_IDLE;
|
2014-08-19 10:07:42 +00:00
|
|
|
|
|
|
|
return REGULATOR_MODE_NORMAL;
|
2014-08-13 11:33:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
2021-06-19 12:41:33 +00:00
|
|
|
unsigned int reg_val;
|
2014-08-13 11:33:42 +00:00
|
|
|
|
2021-06-30 09:59:59 +00:00
|
|
|
info = container_of(rdev->desc, struct hi6421_regulator_info, desc);
|
2014-08-13 11:33:42 +00:00
|
|
|
regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val);
|
|
|
|
if (reg_val & info->mode_mask)
|
|
|
|
return REGULATOR_MODE_STANDBY;
|
2014-08-19 10:07:42 +00:00
|
|
|
|
|
|
|
return REGULATOR_MODE_NORMAL;
|
2014-08-13 11:33:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev,
|
|
|
|
unsigned int mode)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
2021-06-19 12:41:33 +00:00
|
|
|
unsigned int new_mode;
|
2014-08-13 11:33:42 +00:00
|
|
|
|
2021-06-30 09:59:59 +00:00
|
|
|
info = container_of(rdev->desc, struct hi6421_regulator_info, desc);
|
2014-08-13 11:33:42 +00:00
|
|
|
switch (mode) {
|
|
|
|
case REGULATOR_MODE_NORMAL:
|
|
|
|
new_mode = 0;
|
|
|
|
break;
|
|
|
|
case REGULATOR_MODE_IDLE:
|
|
|
|
new_mode = info->mode_mask;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set mode */
|
|
|
|
regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
|
|
|
|
info->mode_mask, new_mode);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
|
|
|
|
unsigned int mode)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
2021-06-19 12:41:33 +00:00
|
|
|
unsigned int new_mode;
|
2014-08-13 11:33:42 +00:00
|
|
|
|
2021-06-30 09:59:59 +00:00
|
|
|
info = container_of(rdev->desc, struct hi6421_regulator_info, desc);
|
2014-08-13 11:33:42 +00:00
|
|
|
switch (mode) {
|
|
|
|
case REGULATOR_MODE_NORMAL:
|
|
|
|
new_mode = 0;
|
|
|
|
break;
|
|
|
|
case REGULATOR_MODE_STANDBY:
|
|
|
|
new_mode = info->mode_mask;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set mode */
|
|
|
|
regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
|
|
|
|
info->mode_mask, new_mode);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-31 09:30:31 +00:00
|
|
|
static unsigned int
|
|
|
|
hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
|
2014-08-13 11:33:42 +00:00
|
|
|
int input_uV, int output_uV, int load_uA)
|
|
|
|
{
|
2021-06-30 09:59:59 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
|
|
|
|
|
|
|
info = container_of(rdev->desc, struct hi6421_regulator_info, desc);
|
2014-08-13 11:33:42 +00:00
|
|
|
|
|
|
|
if (load_uA > info->eco_microamp)
|
|
|
|
return REGULATOR_MODE_NORMAL;
|
2014-08-19 10:07:42 +00:00
|
|
|
|
|
|
|
return REGULATOR_MODE_IDLE;
|
2014-08-13 11:33:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_ldo_ops = {
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
|
|
.enable = hi6421_regulator_enable,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.list_voltage = regulator_list_voltage_table,
|
|
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_mode = hi6421_regulator_ldo_get_mode,
|
|
|
|
.set_mode = hi6421_regulator_ldo_set_mode,
|
|
|
|
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_ldo_linear_ops = {
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
|
|
.enable = hi6421_regulator_enable,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_mode = hi6421_regulator_ldo_get_mode,
|
|
|
|
.set_mode = hi6421_regulator_ldo_set_mode,
|
|
|
|
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_ldo_linear_range_ops = {
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
|
|
.enable = hi6421_regulator_enable,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.list_voltage = regulator_list_voltage_linear_range,
|
|
|
|
.map_voltage = regulator_map_voltage_linear_range,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_mode = hi6421_regulator_ldo_get_mode,
|
|
|
|
.set_mode = hi6421_regulator_ldo_set_mode,
|
|
|
|
.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_buck012_ops = {
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
|
|
.enable = hi6421_regulator_enable,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_mode = hi6421_regulator_buck_get_mode,
|
|
|
|
.set_mode = hi6421_regulator_buck_set_mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regulator_ops hi6421_buck345_ops = {
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
|
|
.enable = hi6421_regulator_enable,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.list_voltage = regulator_list_voltage_table,
|
|
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_mode = hi6421_regulator_buck_get_mode,
|
|
|
|
.set_mode = hi6421_regulator_buck_set_mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int hi6421_regulator_probe(struct platform_device *pdev)
|
|
|
|
{
|
2019-04-10 16:14:27 +00:00
|
|
|
struct hi6421_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
|
2014-08-13 11:33:42 +00:00
|
|
|
struct hi6421_regulator_pdata *pdata;
|
2019-04-10 16:14:27 +00:00
|
|
|
struct hi6421_regulator_info *info;
|
|
|
|
struct regulator_config config = { };
|
|
|
|
struct regulator_dev *rdev;
|
|
|
|
int i;
|
2014-08-13 11:33:42 +00:00
|
|
|
|
|
|
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
|
|
|
if (!pdata)
|
|
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&pdata->lock);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hi6421_regulator_info); i++) {
|
2019-04-10 16:14:27 +00:00
|
|
|
/* assign per-regulator data */
|
|
|
|
info = &hi6421_regulator_info[i];
|
|
|
|
|
|
|
|
config.dev = pdev->dev.parent;
|
2021-06-30 09:59:59 +00:00
|
|
|
config.driver_data = pdata;
|
2019-04-10 16:14:27 +00:00
|
|
|
config.regmap = pmic->regmap;
|
|
|
|
|
|
|
|
rdev = devm_regulator_register(&pdev->dev, &info->desc,
|
|
|
|
&config);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
dev_err(&pdev->dev, "failed to register regulator %s\n",
|
|
|
|
info->desc.name);
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
}
|
2014-08-13 11:33:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-07 07:06:04 +00:00
|
|
|
static const struct platform_device_id hi6421_regulator_table[] = {
|
|
|
|
{ .name = "hi6421-regulator" },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(platform, hi6421_regulator_table);
|
|
|
|
|
2014-08-13 11:33:42 +00:00
|
|
|
static struct platform_driver hi6421_regulator_driver = {
|
2017-06-07 07:06:04 +00:00
|
|
|
.id_table = hi6421_regulator_table,
|
2014-08-13 11:33:42 +00:00
|
|
|
.driver = {
|
|
|
|
.name = "hi6421-regulator",
|
regulator: Set PROBE_PREFER_ASYNCHRONOUS for drivers that existed in 4.14
Probing of regulators can be a slow operation and can contribute to
slower boot times. This is especially true if a regulator is turned on
at probe time (with regulator-boot-on or regulator-always-on) and the
regulator requires delays (off-on-time, ramp time, etc).
While the overall kernel is not ready to switch to async probe by
default, as per the discussion on the mailing lists [1] it is believed
that the regulator subsystem is in good shape and we can move
regulator drivers over wholesale. There is no way to just magically
opt in all regulators (regulators are just normal drivers like
platform_driver), so we set PROBE_PREFER_ASYNCHRONOUS for all
regulators found in 'drivers/regulator' individually.
Given the number of drivers touched and the impossibility to test this
ahead of time, it wouldn't be shocking at all if this caused a
regression for someone. If there is a regression caused by this patch,
it's likely to be one of the cases talked about in [1]. As a "quick
fix", drivers involved in the regression could be fixed by changing
them to PROBE_FORCE_SYNCHRONOUS. That being said, the correct fix
would be to directly fix the problem that caused the issue with async
probe.
The approach here follows a similar approach that was used for the mmc
subsystem several years ago [2]. In fact, I ran nearly the same python
script to auto-generate the changes. The only thing I changed was to
search for "i2c_driver", "spmi_driver", and "spi_driver" in addition
to "platform_driver".
[1] https://lore.kernel.org/r/06db017f-e985-4434-8d1d-02ca2100cca0@sirena.org.uk
[2] https://lore.kernel.org/r/20200903232441.2694866-1-dianders@chromium.org/
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20230316125351.1.I2a4677392a38db5758dee0788b2cea5872562a82@changeid
Signed-off-by: Mark Brown <broonie@kernel.org>
2023-03-16 19:54:38 +00:00
|
|
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
2014-08-13 11:33:42 +00:00
|
|
|
},
|
|
|
|
.probe = hi6421_regulator_probe,
|
|
|
|
};
|
|
|
|
module_platform_driver(hi6421_regulator_driver);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
|
|
|
|
MODULE_DESCRIPTION("Hi6421 regulator driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|