mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 00:21:32 +00:00
259b93b21a
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>
1280 lines
33 KiB
C
1280 lines
33 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* tps65910.c -- TI tps65910
|
|
*
|
|
* Copyright 2010 Texas Instruments Inc.
|
|
*
|
|
* Author: Graeme Gregory <gg@slimlogic.co.uk>
|
|
* Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/mfd/tps65910.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
|
|
#define TPS65910_SUPPLY_STATE_ENABLED 0x1
|
|
#define EXT_SLEEP_CONTROL (TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1 | \
|
|
TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2 | \
|
|
TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 | \
|
|
TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
|
|
|
|
/* supported VIO voltages in microvolts */
|
|
static const unsigned int VIO_VSEL_table[] = {
|
|
1500000, 1800000, 2500000, 3300000,
|
|
};
|
|
|
|
/* VSEL tables for TPS65910 specific LDOs and dcdc's */
|
|
|
|
/* supported VRTC voltages in microvolts */
|
|
static const unsigned int VRTC_VSEL_table[] = {
|
|
1800000,
|
|
};
|
|
|
|
/* supported VDD3 voltages in microvolts */
|
|
static const unsigned int VDD3_VSEL_table[] = {
|
|
5000000,
|
|
};
|
|
|
|
/* supported VDIG1 voltages in microvolts */
|
|
static const unsigned int VDIG1_VSEL_table[] = {
|
|
1200000, 1500000, 1800000, 2700000,
|
|
};
|
|
|
|
/* supported VDIG2 voltages in microvolts */
|
|
static const unsigned int VDIG2_VSEL_table[] = {
|
|
1000000, 1100000, 1200000, 1800000,
|
|
};
|
|
|
|
/* supported VPLL voltages in microvolts */
|
|
static const unsigned int VPLL_VSEL_table[] = {
|
|
1000000, 1100000, 1800000, 2500000,
|
|
};
|
|
|
|
/* supported VDAC voltages in microvolts */
|
|
static const unsigned int VDAC_VSEL_table[] = {
|
|
1800000, 2600000, 2800000, 2850000,
|
|
};
|
|
|
|
/* supported VAUX1 voltages in microvolts */
|
|
static const unsigned int VAUX1_VSEL_table[] = {
|
|
1800000, 2500000, 2800000, 2850000,
|
|
};
|
|
|
|
/* supported VAUX2 voltages in microvolts */
|
|
static const unsigned int VAUX2_VSEL_table[] = {
|
|
1800000, 2800000, 2900000, 3300000,
|
|
};
|
|
|
|
/* supported VAUX33 voltages in microvolts */
|
|
static const unsigned int VAUX33_VSEL_table[] = {
|
|
1800000, 2000000, 2800000, 3300000,
|
|
};
|
|
|
|
/* supported VMMC voltages in microvolts */
|
|
static const unsigned int VMMC_VSEL_table[] = {
|
|
1800000, 2800000, 3000000, 3300000,
|
|
};
|
|
|
|
/* supported BBCH voltages in microvolts */
|
|
static const unsigned int VBB_VSEL_table[] = {
|
|
3000000, 2520000, 3150000, 5000000,
|
|
};
|
|
|
|
struct tps_info {
|
|
const char *name;
|
|
const char *vin_name;
|
|
u8 n_voltages;
|
|
const unsigned int *voltage_table;
|
|
int enable_time_us;
|
|
};
|
|
|
|
static struct tps_info tps65910_regs[] = {
|
|
{
|
|
.name = "vrtc",
|
|
.vin_name = "vcc7",
|
|
.n_voltages = ARRAY_SIZE(VRTC_VSEL_table),
|
|
.voltage_table = VRTC_VSEL_table,
|
|
.enable_time_us = 2200,
|
|
},
|
|
{
|
|
.name = "vio",
|
|
.vin_name = "vccio",
|
|
.n_voltages = ARRAY_SIZE(VIO_VSEL_table),
|
|
.voltage_table = VIO_VSEL_table,
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vdd1",
|
|
.vin_name = "vcc1",
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vdd2",
|
|
.vin_name = "vcc2",
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vdd3",
|
|
.n_voltages = ARRAY_SIZE(VDD3_VSEL_table),
|
|
.voltage_table = VDD3_VSEL_table,
|
|
.enable_time_us = 200,
|
|
},
|
|
{
|
|
.name = "vdig1",
|
|
.vin_name = "vcc6",
|
|
.n_voltages = ARRAY_SIZE(VDIG1_VSEL_table),
|
|
.voltage_table = VDIG1_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vdig2",
|
|
.vin_name = "vcc6",
|
|
.n_voltages = ARRAY_SIZE(VDIG2_VSEL_table),
|
|
.voltage_table = VDIG2_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vpll",
|
|
.vin_name = "vcc5",
|
|
.n_voltages = ARRAY_SIZE(VPLL_VSEL_table),
|
|
.voltage_table = VPLL_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vdac",
|
|
.vin_name = "vcc5",
|
|
.n_voltages = ARRAY_SIZE(VDAC_VSEL_table),
|
|
.voltage_table = VDAC_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vaux1",
|
|
.vin_name = "vcc4",
|
|
.n_voltages = ARRAY_SIZE(VAUX1_VSEL_table),
|
|
.voltage_table = VAUX1_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vaux2",
|
|
.vin_name = "vcc4",
|
|
.n_voltages = ARRAY_SIZE(VAUX2_VSEL_table),
|
|
.voltage_table = VAUX2_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vaux33",
|
|
.vin_name = "vcc3",
|
|
.n_voltages = ARRAY_SIZE(VAUX33_VSEL_table),
|
|
.voltage_table = VAUX33_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vmmc",
|
|
.vin_name = "vcc3",
|
|
.n_voltages = ARRAY_SIZE(VMMC_VSEL_table),
|
|
.voltage_table = VMMC_VSEL_table,
|
|
.enable_time_us = 100,
|
|
},
|
|
{
|
|
.name = "vbb",
|
|
.vin_name = "vcc7",
|
|
.n_voltages = ARRAY_SIZE(VBB_VSEL_table),
|
|
.voltage_table = VBB_VSEL_table,
|
|
},
|
|
};
|
|
|
|
static struct tps_info tps65911_regs[] = {
|
|
{
|
|
.name = "vrtc",
|
|
.vin_name = "vcc7",
|
|
.enable_time_us = 2200,
|
|
},
|
|
{
|
|
.name = "vio",
|
|
.vin_name = "vccio",
|
|
.n_voltages = ARRAY_SIZE(VIO_VSEL_table),
|
|
.voltage_table = VIO_VSEL_table,
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vdd1",
|
|
.vin_name = "vcc1",
|
|
.n_voltages = 0x4C,
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vdd2",
|
|
.vin_name = "vcc2",
|
|
.n_voltages = 0x4C,
|
|
.enable_time_us = 350,
|
|
},
|
|
{
|
|
.name = "vddctrl",
|
|
.n_voltages = 0x44,
|
|
.enable_time_us = 900,
|
|
},
|
|
{
|
|
.name = "ldo1",
|
|
.vin_name = "vcc6",
|
|
.n_voltages = 0x33,
|
|
.enable_time_us = 420,
|
|
},
|
|
{
|
|
.name = "ldo2",
|
|
.vin_name = "vcc6",
|
|
.n_voltages = 0x33,
|
|
.enable_time_us = 420,
|
|
},
|
|
{
|
|
.name = "ldo3",
|
|
.vin_name = "vcc5",
|
|
.n_voltages = 0x1A,
|
|
.enable_time_us = 230,
|
|
},
|
|
{
|
|
.name = "ldo4",
|
|
.vin_name = "vcc5",
|
|
.n_voltages = 0x33,
|
|
.enable_time_us = 230,
|
|
},
|
|
{
|
|
.name = "ldo5",
|
|
.vin_name = "vcc4",
|
|
.n_voltages = 0x1A,
|
|
.enable_time_us = 230,
|
|
},
|
|
{
|
|
.name = "ldo6",
|
|
.vin_name = "vcc3",
|
|
.n_voltages = 0x1A,
|
|
.enable_time_us = 230,
|
|
},
|
|
{
|
|
.name = "ldo7",
|
|
.vin_name = "vcc3",
|
|
.n_voltages = 0x1A,
|
|
.enable_time_us = 230,
|
|
},
|
|
{
|
|
.name = "ldo8",
|
|
.vin_name = "vcc3",
|
|
.n_voltages = 0x1A,
|
|
.enable_time_us = 230,
|
|
},
|
|
};
|
|
|
|
#define EXT_CONTROL_REG_BITS(id, regs_offs, bits) (((regs_offs) << 8) | (bits))
|
|
static unsigned int tps65910_ext_sleep_control[] = {
|
|
0,
|
|
EXT_CONTROL_REG_BITS(VIO, 1, 0),
|
|
EXT_CONTROL_REG_BITS(VDD1, 1, 1),
|
|
EXT_CONTROL_REG_BITS(VDD2, 1, 2),
|
|
EXT_CONTROL_REG_BITS(VDD3, 1, 3),
|
|
EXT_CONTROL_REG_BITS(VDIG1, 0, 1),
|
|
EXT_CONTROL_REG_BITS(VDIG2, 0, 2),
|
|
EXT_CONTROL_REG_BITS(VPLL, 0, 6),
|
|
EXT_CONTROL_REG_BITS(VDAC, 0, 7),
|
|
EXT_CONTROL_REG_BITS(VAUX1, 0, 3),
|
|
EXT_CONTROL_REG_BITS(VAUX2, 0, 4),
|
|
EXT_CONTROL_REG_BITS(VAUX33, 0, 5),
|
|
EXT_CONTROL_REG_BITS(VMMC, 0, 0),
|
|
};
|
|
|
|
static unsigned int tps65911_ext_sleep_control[] = {
|
|
0,
|
|
EXT_CONTROL_REG_BITS(VIO, 1, 0),
|
|
EXT_CONTROL_REG_BITS(VDD1, 1, 1),
|
|
EXT_CONTROL_REG_BITS(VDD2, 1, 2),
|
|
EXT_CONTROL_REG_BITS(VDDCTRL, 1, 3),
|
|
EXT_CONTROL_REG_BITS(LDO1, 0, 1),
|
|
EXT_CONTROL_REG_BITS(LDO2, 0, 2),
|
|
EXT_CONTROL_REG_BITS(LDO3, 0, 7),
|
|
EXT_CONTROL_REG_BITS(LDO4, 0, 6),
|
|
EXT_CONTROL_REG_BITS(LDO5, 0, 3),
|
|
EXT_CONTROL_REG_BITS(LDO6, 0, 0),
|
|
EXT_CONTROL_REG_BITS(LDO7, 0, 5),
|
|
EXT_CONTROL_REG_BITS(LDO8, 0, 4),
|
|
};
|
|
|
|
struct tps65910_reg {
|
|
struct regulator_desc *desc;
|
|
struct tps65910 *mfd;
|
|
struct regulator_dev **rdev;
|
|
struct tps_info **info;
|
|
int num_regulators;
|
|
int mode;
|
|
int (*get_ctrl_reg)(int);
|
|
unsigned int *ext_sleep_control;
|
|
unsigned int board_ext_control[TPS65910_NUM_REGS];
|
|
};
|
|
|
|
static int tps65910_get_ctrl_register(int id)
|
|
{
|
|
switch (id) {
|
|
case TPS65910_REG_VRTC:
|
|
return TPS65910_VRTC;
|
|
case TPS65910_REG_VIO:
|
|
return TPS65910_VIO;
|
|
case TPS65910_REG_VDD1:
|
|
return TPS65910_VDD1;
|
|
case TPS65910_REG_VDD2:
|
|
return TPS65910_VDD2;
|
|
case TPS65910_REG_VDD3:
|
|
return TPS65910_VDD3;
|
|
case TPS65910_REG_VDIG1:
|
|
return TPS65910_VDIG1;
|
|
case TPS65910_REG_VDIG2:
|
|
return TPS65910_VDIG2;
|
|
case TPS65910_REG_VPLL:
|
|
return TPS65910_VPLL;
|
|
case TPS65910_REG_VDAC:
|
|
return TPS65910_VDAC;
|
|
case TPS65910_REG_VAUX1:
|
|
return TPS65910_VAUX1;
|
|
case TPS65910_REG_VAUX2:
|
|
return TPS65910_VAUX2;
|
|
case TPS65910_REG_VAUX33:
|
|
return TPS65910_VAUX33;
|
|
case TPS65910_REG_VMMC:
|
|
return TPS65910_VMMC;
|
|
case TPS65910_REG_VBB:
|
|
return TPS65910_BBCH;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int tps65911_get_ctrl_register(int id)
|
|
{
|
|
switch (id) {
|
|
case TPS65910_REG_VRTC:
|
|
return TPS65910_VRTC;
|
|
case TPS65910_REG_VIO:
|
|
return TPS65910_VIO;
|
|
case TPS65910_REG_VDD1:
|
|
return TPS65910_VDD1;
|
|
case TPS65910_REG_VDD2:
|
|
return TPS65910_VDD2;
|
|
case TPS65911_REG_VDDCTRL:
|
|
return TPS65911_VDDCTRL;
|
|
case TPS65911_REG_LDO1:
|
|
return TPS65911_LDO1;
|
|
case TPS65911_REG_LDO2:
|
|
return TPS65911_LDO2;
|
|
case TPS65911_REG_LDO3:
|
|
return TPS65911_LDO3;
|
|
case TPS65911_REG_LDO4:
|
|
return TPS65911_LDO4;
|
|
case TPS65911_REG_LDO5:
|
|
return TPS65911_LDO5;
|
|
case TPS65911_REG_LDO6:
|
|
return TPS65911_LDO6;
|
|
case TPS65911_REG_LDO7:
|
|
return TPS65911_LDO7;
|
|
case TPS65911_REG_LDO8:
|
|
return TPS65911_LDO8;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int reg, id = rdev_get_id(dev);
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
switch (mode) {
|
|
case REGULATOR_MODE_NORMAL:
|
|
return regmap_update_bits(regmap, reg,
|
|
LDO_ST_MODE_BIT | LDO_ST_ON_BIT,
|
|
LDO_ST_ON_BIT);
|
|
case REGULATOR_MODE_IDLE:
|
|
return regmap_set_bits(regmap, reg,
|
|
LDO_ST_ON_BIT | LDO_ST_MODE_BIT);
|
|
case REGULATOR_MODE_STANDBY:
|
|
return regmap_clear_bits(regmap, reg, LDO_ST_ON_BIT);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static unsigned int tps65910_get_mode(struct regulator_dev *dev)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int ret, reg, value, id = rdev_get_id(dev);
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
ret = regmap_read(regmap, reg, &value);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (!(value & LDO_ST_ON_BIT))
|
|
return REGULATOR_MODE_STANDBY;
|
|
else if (value & LDO_ST_MODE_BIT)
|
|
return REGULATOR_MODE_IDLE;
|
|
else
|
|
return REGULATOR_MODE_NORMAL;
|
|
}
|
|
|
|
static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev)
|
|
{
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int ret, id = rdev_get_id(dev);
|
|
int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0;
|
|
|
|
switch (id) {
|
|
case TPS65910_REG_VDD1:
|
|
ret = regmap_read(regmap, TPS65910_VDD1_OP, &opvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = regmap_read(regmap, TPS65910_VDD1, &mult);
|
|
if (ret < 0)
|
|
return ret;
|
|
mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT;
|
|
ret = regmap_read(regmap, TPS65910_VDD1_SR, &srvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
sr = opvsel & VDD1_OP_CMD_MASK;
|
|
opvsel &= VDD1_OP_SEL_MASK;
|
|
srvsel &= VDD1_SR_SEL_MASK;
|
|
vselmax = 75;
|
|
break;
|
|
case TPS65910_REG_VDD2:
|
|
ret = regmap_read(regmap, TPS65910_VDD2_OP, &opvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = regmap_read(regmap, TPS65910_VDD2, &mult);
|
|
if (ret < 0)
|
|
return ret;
|
|
mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT;
|
|
ret = regmap_read(regmap, TPS65910_VDD2_SR, &srvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
sr = opvsel & VDD2_OP_CMD_MASK;
|
|
opvsel &= VDD2_OP_SEL_MASK;
|
|
srvsel &= VDD2_SR_SEL_MASK;
|
|
vselmax = 75;
|
|
break;
|
|
case TPS65911_REG_VDDCTRL:
|
|
ret = regmap_read(regmap, TPS65911_VDDCTRL_OP, &opvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = regmap_read(regmap, TPS65911_VDDCTRL_SR, &srvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
sr = opvsel & VDDCTRL_OP_CMD_MASK;
|
|
opvsel &= VDDCTRL_OP_SEL_MASK;
|
|
srvsel &= VDDCTRL_SR_SEL_MASK;
|
|
vselmax = 64;
|
|
break;
|
|
}
|
|
|
|
/* multiplier 0 == 1 but 2,3 normal */
|
|
if (!mult)
|
|
mult = 1;
|
|
|
|
if (sr) {
|
|
/* normalise to valid range */
|
|
if (srvsel < 3)
|
|
srvsel = 3;
|
|
if (srvsel > vselmax)
|
|
srvsel = vselmax;
|
|
return srvsel - 3;
|
|
} else {
|
|
|
|
/* normalise to valid range*/
|
|
if (opvsel < 3)
|
|
opvsel = 3;
|
|
if (opvsel > vselmax)
|
|
opvsel = vselmax;
|
|
return opvsel - 3;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int tps65910_get_voltage_sel(struct regulator_dev *dev)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int ret, reg, value, id = rdev_get_id(dev);
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
ret = regmap_read(regmap, reg, &value);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
switch (id) {
|
|
case TPS65910_REG_VIO:
|
|
case TPS65910_REG_VDIG1:
|
|
case TPS65910_REG_VDIG2:
|
|
case TPS65910_REG_VPLL:
|
|
case TPS65910_REG_VDAC:
|
|
case TPS65910_REG_VAUX1:
|
|
case TPS65910_REG_VAUX2:
|
|
case TPS65910_REG_VAUX33:
|
|
case TPS65910_REG_VMMC:
|
|
value &= LDO_SEL_MASK;
|
|
value >>= LDO_SEL_SHIFT;
|
|
break;
|
|
case TPS65910_REG_VBB:
|
|
value &= BBCH_BBSEL_MASK;
|
|
value >>= BBCH_BBSEL_SHIFT;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int tps65910_get_voltage_vdd3(struct regulator_dev *dev)
|
|
{
|
|
return dev->desc->volt_table[0];
|
|
}
|
|
|
|
static int tps65911_get_voltage_sel(struct regulator_dev *dev)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int ret, id = rdev_get_id(dev);
|
|
unsigned int value, reg;
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
|
|
ret = regmap_read(regmap, reg, &value);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
switch (id) {
|
|
case TPS65911_REG_LDO1:
|
|
case TPS65911_REG_LDO2:
|
|
case TPS65911_REG_LDO4:
|
|
value &= LDO1_SEL_MASK;
|
|
value >>= LDO_SEL_SHIFT;
|
|
break;
|
|
case TPS65911_REG_LDO3:
|
|
case TPS65911_REG_LDO5:
|
|
case TPS65911_REG_LDO6:
|
|
case TPS65911_REG_LDO7:
|
|
case TPS65911_REG_LDO8:
|
|
value &= LDO3_SEL_MASK;
|
|
value >>= LDO_SEL_SHIFT;
|
|
break;
|
|
case TPS65910_REG_VIO:
|
|
value &= LDO_SEL_MASK;
|
|
value >>= LDO_SEL_SHIFT;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
|
|
unsigned selector)
|
|
{
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int id = rdev_get_id(dev), vsel;
|
|
int dcdc_mult = 0;
|
|
|
|
switch (id) {
|
|
case TPS65910_REG_VDD1:
|
|
dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
|
|
if (dcdc_mult == 1)
|
|
dcdc_mult--;
|
|
vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3;
|
|
|
|
regmap_update_bits(regmap, TPS65910_VDD1, VDD1_VGAIN_SEL_MASK,
|
|
dcdc_mult << VDD1_VGAIN_SEL_SHIFT);
|
|
regmap_write(regmap, TPS65910_VDD1_OP, vsel);
|
|
break;
|
|
case TPS65910_REG_VDD2:
|
|
dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
|
|
if (dcdc_mult == 1)
|
|
dcdc_mult--;
|
|
vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3;
|
|
|
|
regmap_update_bits(regmap, TPS65910_VDD2, VDD1_VGAIN_SEL_MASK,
|
|
dcdc_mult << VDD2_VGAIN_SEL_SHIFT);
|
|
regmap_write(regmap, TPS65910_VDD2_OP, vsel);
|
|
break;
|
|
case TPS65911_REG_VDDCTRL:
|
|
vsel = selector + 3;
|
|
regmap_write(regmap, TPS65911_VDDCTRL_OP, vsel);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tps65910_set_voltage_sel(struct regulator_dev *dev,
|
|
unsigned selector)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int reg, id = rdev_get_id(dev);
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
switch (id) {
|
|
case TPS65910_REG_VIO:
|
|
case TPS65910_REG_VDIG1:
|
|
case TPS65910_REG_VDIG2:
|
|
case TPS65910_REG_VPLL:
|
|
case TPS65910_REG_VDAC:
|
|
case TPS65910_REG_VAUX1:
|
|
case TPS65910_REG_VAUX2:
|
|
case TPS65910_REG_VAUX33:
|
|
case TPS65910_REG_VMMC:
|
|
return regmap_update_bits(regmap, reg, LDO_SEL_MASK,
|
|
selector << LDO_SEL_SHIFT);
|
|
case TPS65910_REG_VBB:
|
|
return regmap_update_bits(regmap, reg, BBCH_BBSEL_MASK,
|
|
selector << BBCH_BBSEL_SHIFT);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int tps65911_set_voltage_sel(struct regulator_dev *dev,
|
|
unsigned selector)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
struct regmap *regmap = rdev_get_regmap(dev);
|
|
int reg, id = rdev_get_id(dev);
|
|
|
|
reg = pmic->get_ctrl_reg(id);
|
|
if (reg < 0)
|
|
return reg;
|
|
|
|
switch (id) {
|
|
case TPS65911_REG_LDO1:
|
|
case TPS65911_REG_LDO2:
|
|
case TPS65911_REG_LDO4:
|
|
return regmap_update_bits(regmap, reg, LDO1_SEL_MASK,
|
|
selector << LDO_SEL_SHIFT);
|
|
case TPS65911_REG_LDO3:
|
|
case TPS65911_REG_LDO5:
|
|
case TPS65911_REG_LDO6:
|
|
case TPS65911_REG_LDO7:
|
|
case TPS65911_REG_LDO8:
|
|
return regmap_update_bits(regmap, reg, LDO3_SEL_MASK,
|
|
selector << LDO_SEL_SHIFT);
|
|
case TPS65910_REG_VIO:
|
|
return regmap_update_bits(regmap, reg, LDO_SEL_MASK,
|
|
selector << LDO_SEL_SHIFT);
|
|
case TPS65910_REG_VBB:
|
|
return regmap_update_bits(regmap, reg, BBCH_BBSEL_MASK,
|
|
selector << BBCH_BBSEL_SHIFT);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static int tps65910_list_voltage_dcdc(struct regulator_dev *dev,
|
|
unsigned selector)
|
|
{
|
|
int volt, mult = 1, id = rdev_get_id(dev);
|
|
|
|
switch (id) {
|
|
case TPS65910_REG_VDD1:
|
|
case TPS65910_REG_VDD2:
|
|
mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
|
|
volt = VDD1_2_MIN_VOLT +
|
|
(selector % VDD1_2_NUM_VOLT_FINE) * VDD1_2_OFFSET;
|
|
break;
|
|
case TPS65911_REG_VDDCTRL:
|
|
volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
|
|
break;
|
|
default:
|
|
BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
return volt * 100 * mult;
|
|
}
|
|
|
|
static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector)
|
|
{
|
|
struct tps65910_reg *pmic = rdev_get_drvdata(dev);
|
|
int step_mv = 0, id = rdev_get_id(dev);
|
|
|
|
switch (id) {
|
|
case TPS65911_REG_LDO1:
|
|
case TPS65911_REG_LDO2:
|
|
case TPS65911_REG_LDO4:
|
|
/* The first 5 values of the selector correspond to 1V */
|
|
if (selector < 5)
|
|
selector = 0;
|
|
else
|
|
selector -= 4;
|
|
|
|
step_mv = 50;
|
|
break;
|
|
case TPS65911_REG_LDO3:
|
|
case TPS65911_REG_LDO5:
|
|
case TPS65911_REG_LDO6:
|
|
case TPS65911_REG_LDO7:
|
|
case TPS65911_REG_LDO8:
|
|
/* The first 3 values of the selector correspond to 1V */
|
|
if (selector < 3)
|
|
selector = 0;
|
|
else
|
|
selector -= 2;
|
|
|
|
step_mv = 100;
|
|
break;
|
|
case TPS65910_REG_VIO:
|
|
return pmic->info[id]->voltage_table[selector];
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return (LDO_MIN_VOLT + selector * step_mv) * 1000;
|
|
}
|
|
|
|
/* Regulator ops (except VRTC) */
|
|
static const struct regulator_ops tps65910_ops_dcdc = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = tps65910_set_mode,
|
|
.get_mode = tps65910_get_mode,
|
|
.get_voltage_sel = tps65910_get_voltage_dcdc_sel,
|
|
.set_voltage_sel = tps65910_set_voltage_dcdc_sel,
|
|
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
|
.list_voltage = tps65910_list_voltage_dcdc,
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
};
|
|
|
|
static const struct regulator_ops tps65910_ops_vdd3 = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = tps65910_set_mode,
|
|
.get_mode = tps65910_get_mode,
|
|
.get_voltage = tps65910_get_voltage_vdd3,
|
|
.list_voltage = regulator_list_voltage_table,
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
};
|
|
|
|
static const struct regulator_ops tps65910_ops_vbb = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = tps65910_set_mode,
|
|
.get_mode = tps65910_get_mode,
|
|
.get_voltage_sel = tps65910_get_voltage_sel,
|
|
.set_voltage_sel = tps65910_set_voltage_sel,
|
|
.list_voltage = regulator_list_voltage_table,
|
|
.map_voltage = regulator_map_voltage_iterate,
|
|
};
|
|
|
|
static const struct regulator_ops tps65910_ops = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = tps65910_set_mode,
|
|
.get_mode = tps65910_get_mode,
|
|
.get_voltage_sel = tps65910_get_voltage_sel,
|
|
.set_voltage_sel = tps65910_set_voltage_sel,
|
|
.list_voltage = regulator_list_voltage_table,
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
};
|
|
|
|
static const struct regulator_ops tps65911_ops = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = tps65910_set_mode,
|
|
.get_mode = tps65910_get_mode,
|
|
.get_voltage_sel = tps65911_get_voltage_sel,
|
|
.set_voltage_sel = tps65911_set_voltage_sel,
|
|
.list_voltage = tps65911_list_voltage,
|
|
.map_voltage = regulator_map_voltage_ascend,
|
|
};
|
|
|
|
static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
|
int id, int ext_sleep_config)
|
|
{
|
|
struct tps65910 *mfd = pmic->mfd;
|
|
u8 regoffs = (pmic->ext_sleep_control[id] >> 8) & 0xFF;
|
|
u8 bit_pos = (1 << pmic->ext_sleep_control[id] & 0xFF);
|
|
int ret;
|
|
|
|
/*
|
|
* Regulator can not be control from multiple external input EN1, EN2
|
|
* and EN3 together.
|
|
*/
|
|
if (ext_sleep_config & EXT_SLEEP_CONTROL) {
|
|
int en_count;
|
|
en_count = ((ext_sleep_config &
|
|
TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1) != 0);
|
|
en_count += ((ext_sleep_config &
|
|
TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2) != 0);
|
|
en_count += ((ext_sleep_config &
|
|
TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3) != 0);
|
|
en_count += ((ext_sleep_config &
|
|
TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) != 0);
|
|
if (en_count > 1) {
|
|
dev_err(mfd->dev,
|
|
"External sleep control flag is not proper\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
pmic->board_ext_control[id] = ext_sleep_config;
|
|
|
|
/* External EN1 control */
|
|
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1)
|
|
ret = regmap_set_bits(mfd->regmap,
|
|
TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
|
|
else
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
|
|
if (ret < 0) {
|
|
dev_err(mfd->dev,
|
|
"Error in configuring external control EN1\n");
|
|
return ret;
|
|
}
|
|
|
|
/* External EN2 control */
|
|
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2)
|
|
ret = regmap_set_bits(mfd->regmap,
|
|
TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
|
|
else
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
|
|
if (ret < 0) {
|
|
dev_err(mfd->dev,
|
|
"Error in configuring external control EN2\n");
|
|
return ret;
|
|
}
|
|
|
|
/* External EN3 control for TPS65910 LDO only */
|
|
if ((tps65910_chip_id(mfd) == TPS65910) &&
|
|
(id >= TPS65910_REG_VDIG1)) {
|
|
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3)
|
|
ret = regmap_set_bits(mfd->regmap,
|
|
TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
|
|
else
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
|
|
if (ret < 0) {
|
|
dev_err(mfd->dev,
|
|
"Error in configuring external control EN3\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Return if no external control is selected */
|
|
if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) {
|
|
/* Clear all sleep controls */
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
|
|
if (!ret)
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
|
if (ret < 0)
|
|
dev_err(mfd->dev,
|
|
"Error in configuring SLEEP register\n");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* For regulator that has separate operational and sleep register make
|
|
* sure that operational is used and clear sleep register to turn
|
|
* regulator off when external control is inactive
|
|
*/
|
|
if ((id == TPS65910_REG_VDD1) ||
|
|
(id == TPS65910_REG_VDD2) ||
|
|
((id == TPS65911_REG_VDDCTRL) &&
|
|
(tps65910_chip_id(mfd) == TPS65911))) {
|
|
int op_reg_add = pmic->get_ctrl_reg(id) + 1;
|
|
int sr_reg_add = pmic->get_ctrl_reg(id) + 2;
|
|
int opvsel, srvsel;
|
|
|
|
ret = regmap_read(mfd->regmap, op_reg_add, &opvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = regmap_read(mfd->regmap, sr_reg_add, &srvsel);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (opvsel & VDD1_OP_CMD_MASK) {
|
|
u8 reg_val = srvsel & VDD1_OP_SEL_MASK;
|
|
|
|
ret = regmap_write(mfd->regmap, op_reg_add, reg_val);
|
|
if (ret < 0) {
|
|
dev_err(mfd->dev,
|
|
"Error in configuring op register\n");
|
|
return ret;
|
|
}
|
|
}
|
|
ret = regmap_write(mfd->regmap, sr_reg_add, 0);
|
|
if (ret < 0) {
|
|
dev_err(mfd->dev, "Error in setting sr register\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
|
|
if (!ret) {
|
|
if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
|
|
ret = regmap_set_bits(mfd->regmap,
|
|
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
|
else
|
|
ret = regmap_clear_bits(mfd->regmap,
|
|
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
|
}
|
|
if (ret < 0)
|
|
dev_err(mfd->dev,
|
|
"Error in configuring SLEEP register\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static struct of_regulator_match tps65910_matches[] = {
|
|
{ .name = "vrtc", .driver_data = (void *) &tps65910_regs[0] },
|
|
{ .name = "vio", .driver_data = (void *) &tps65910_regs[1] },
|
|
{ .name = "vdd1", .driver_data = (void *) &tps65910_regs[2] },
|
|
{ .name = "vdd2", .driver_data = (void *) &tps65910_regs[3] },
|
|
{ .name = "vdd3", .driver_data = (void *) &tps65910_regs[4] },
|
|
{ .name = "vdig1", .driver_data = (void *) &tps65910_regs[5] },
|
|
{ .name = "vdig2", .driver_data = (void *) &tps65910_regs[6] },
|
|
{ .name = "vpll", .driver_data = (void *) &tps65910_regs[7] },
|
|
{ .name = "vdac", .driver_data = (void *) &tps65910_regs[8] },
|
|
{ .name = "vaux1", .driver_data = (void *) &tps65910_regs[9] },
|
|
{ .name = "vaux2", .driver_data = (void *) &tps65910_regs[10] },
|
|
{ .name = "vaux33", .driver_data = (void *) &tps65910_regs[11] },
|
|
{ .name = "vmmc", .driver_data = (void *) &tps65910_regs[12] },
|
|
{ .name = "vbb", .driver_data = (void *) &tps65910_regs[13] },
|
|
};
|
|
|
|
static struct of_regulator_match tps65911_matches[] = {
|
|
{ .name = "vrtc", .driver_data = (void *) &tps65911_regs[0] },
|
|
{ .name = "vio", .driver_data = (void *) &tps65911_regs[1] },
|
|
{ .name = "vdd1", .driver_data = (void *) &tps65911_regs[2] },
|
|
{ .name = "vdd2", .driver_data = (void *) &tps65911_regs[3] },
|
|
{ .name = "vddctrl", .driver_data = (void *) &tps65911_regs[4] },
|
|
{ .name = "ldo1", .driver_data = (void *) &tps65911_regs[5] },
|
|
{ .name = "ldo2", .driver_data = (void *) &tps65911_regs[6] },
|
|
{ .name = "ldo3", .driver_data = (void *) &tps65911_regs[7] },
|
|
{ .name = "ldo4", .driver_data = (void *) &tps65911_regs[8] },
|
|
{ .name = "ldo5", .driver_data = (void *) &tps65911_regs[9] },
|
|
{ .name = "ldo6", .driver_data = (void *) &tps65911_regs[10] },
|
|
{ .name = "ldo7", .driver_data = (void *) &tps65911_regs[11] },
|
|
{ .name = "ldo8", .driver_data = (void *) &tps65911_regs[12] },
|
|
};
|
|
|
|
static struct tps65910_board *tps65910_parse_dt_reg_data(
|
|
struct platform_device *pdev,
|
|
struct of_regulator_match **tps65910_reg_matches)
|
|
{
|
|
struct tps65910_board *pmic_plat_data;
|
|
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
|
|
struct device_node *np, *regulators;
|
|
struct of_regulator_match *matches;
|
|
unsigned int prop;
|
|
int idx = 0, ret, count;
|
|
|
|
pmic_plat_data = devm_kzalloc(&pdev->dev, sizeof(*pmic_plat_data),
|
|
GFP_KERNEL);
|
|
if (!pmic_plat_data)
|
|
return NULL;
|
|
|
|
np = pdev->dev.parent->of_node;
|
|
regulators = of_get_child_by_name(np, "regulators");
|
|
if (!regulators) {
|
|
dev_err(&pdev->dev, "regulator node not found\n");
|
|
return NULL;
|
|
}
|
|
|
|
switch (tps65910_chip_id(tps65910)) {
|
|
case TPS65910:
|
|
count = ARRAY_SIZE(tps65910_matches);
|
|
matches = tps65910_matches;
|
|
break;
|
|
case TPS65911:
|
|
count = ARRAY_SIZE(tps65911_matches);
|
|
matches = tps65911_matches;
|
|
break;
|
|
default:
|
|
of_node_put(regulators);
|
|
dev_err(&pdev->dev, "Invalid tps chip version\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = of_regulator_match(&pdev->dev, regulators, matches, count);
|
|
of_node_put(regulators);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
|
|
ret);
|
|
return NULL;
|
|
}
|
|
|
|
*tps65910_reg_matches = matches;
|
|
|
|
for (idx = 0; idx < count; idx++) {
|
|
if (!matches[idx].of_node)
|
|
continue;
|
|
|
|
pmic_plat_data->tps65910_pmic_init_data[idx] =
|
|
matches[idx].init_data;
|
|
|
|
ret = of_property_read_u32(matches[idx].of_node,
|
|
"ti,regulator-ext-sleep-control", &prop);
|
|
if (!ret)
|
|
pmic_plat_data->regulator_ext_sleep_control[idx] = prop;
|
|
|
|
}
|
|
|
|
return pmic_plat_data;
|
|
}
|
|
#else
|
|
static inline struct tps65910_board *tps65910_parse_dt_reg_data(
|
|
struct platform_device *pdev,
|
|
struct of_regulator_match **tps65910_reg_matches)
|
|
{
|
|
*tps65910_reg_matches = NULL;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static int tps65910_probe(struct platform_device *pdev)
|
|
{
|
|
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
|
|
struct regulator_config config = { };
|
|
struct tps_info *info;
|
|
struct regulator_dev *rdev;
|
|
struct tps65910_reg *pmic;
|
|
struct tps65910_board *pmic_plat_data;
|
|
struct of_regulator_match *tps65910_reg_matches = NULL;
|
|
int i, err;
|
|
|
|
pmic_plat_data = dev_get_platdata(tps65910->dev);
|
|
if (!pmic_plat_data && tps65910->dev->of_node)
|
|
pmic_plat_data = tps65910_parse_dt_reg_data(pdev,
|
|
&tps65910_reg_matches);
|
|
|
|
if (!pmic_plat_data) {
|
|
dev_err(&pdev->dev, "Platform data not found\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
|
|
if (!pmic)
|
|
return -ENOMEM;
|
|
|
|
pmic->mfd = tps65910;
|
|
platform_set_drvdata(pdev, pmic);
|
|
|
|
/* Give control of all register to control port */
|
|
err = regmap_set_bits(pmic->mfd->regmap, TPS65910_DEVCTRL,
|
|
DEVCTRL_SR_CTL_I2C_SEL_MASK);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
switch (tps65910_chip_id(tps65910)) {
|
|
case TPS65910:
|
|
BUILD_BUG_ON(TPS65910_NUM_REGS < ARRAY_SIZE(tps65910_regs));
|
|
pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
|
|
pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
|
|
pmic->ext_sleep_control = tps65910_ext_sleep_control;
|
|
info = tps65910_regs;
|
|
/* Work around silicon erratum SWCZ010: output programmed
|
|
* voltage level can go higher than expected or crash
|
|
* Workaround: use no synchronization of DCDC clocks
|
|
*/
|
|
regmap_clear_bits(pmic->mfd->regmap, TPS65910_DCDCCTRL,
|
|
DCDCCTRL_DCDCCKSYNC_MASK);
|
|
break;
|
|
case TPS65911:
|
|
BUILD_BUG_ON(TPS65910_NUM_REGS < ARRAY_SIZE(tps65911_regs));
|
|
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
|
|
pmic->num_regulators = ARRAY_SIZE(tps65911_regs);
|
|
pmic->ext_sleep_control = tps65911_ext_sleep_control;
|
|
info = tps65911_regs;
|
|
break;
|
|
default:
|
|
dev_err(&pdev->dev, "Invalid tps chip version\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pmic->desc = devm_kcalloc(&pdev->dev,
|
|
pmic->num_regulators,
|
|
sizeof(struct regulator_desc),
|
|
GFP_KERNEL);
|
|
if (!pmic->desc)
|
|
return -ENOMEM;
|
|
|
|
pmic->info = devm_kcalloc(&pdev->dev,
|
|
pmic->num_regulators,
|
|
sizeof(struct tps_info *),
|
|
GFP_KERNEL);
|
|
if (!pmic->info)
|
|
return -ENOMEM;
|
|
|
|
pmic->rdev = devm_kcalloc(&pdev->dev,
|
|
pmic->num_regulators,
|
|
sizeof(struct regulator_dev *),
|
|
GFP_KERNEL);
|
|
if (!pmic->rdev)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < pmic->num_regulators; i++, info++) {
|
|
/* Register the regulators */
|
|
pmic->info[i] = info;
|
|
|
|
pmic->desc[i].name = info->name;
|
|
pmic->desc[i].supply_name = info->vin_name;
|
|
pmic->desc[i].id = i;
|
|
pmic->desc[i].n_voltages = info->n_voltages;
|
|
pmic->desc[i].enable_time = info->enable_time_us;
|
|
|
|
if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) {
|
|
pmic->desc[i].ops = &tps65910_ops_dcdc;
|
|
pmic->desc[i].n_voltages = VDD1_2_NUM_VOLT_FINE *
|
|
VDD1_2_NUM_VOLT_COARSE;
|
|
pmic->desc[i].ramp_delay = 12500;
|
|
} else if (i == TPS65910_REG_VDD3) {
|
|
if (tps65910_chip_id(tps65910) == TPS65910) {
|
|
pmic->desc[i].ops = &tps65910_ops_vdd3;
|
|
pmic->desc[i].volt_table = info->voltage_table;
|
|
} else {
|
|
pmic->desc[i].ops = &tps65910_ops_dcdc;
|
|
pmic->desc[i].ramp_delay = 5000;
|
|
}
|
|
} else if (i == TPS65910_REG_VBB &&
|
|
tps65910_chip_id(tps65910) == TPS65910) {
|
|
pmic->desc[i].ops = &tps65910_ops_vbb;
|
|
pmic->desc[i].volt_table = info->voltage_table;
|
|
} else {
|
|
if (tps65910_chip_id(tps65910) == TPS65910) {
|
|
pmic->desc[i].ops = &tps65910_ops;
|
|
pmic->desc[i].volt_table = info->voltage_table;
|
|
} else {
|
|
pmic->desc[i].ops = &tps65911_ops;
|
|
}
|
|
}
|
|
|
|
err = tps65910_set_ext_sleep_config(pmic, i,
|
|
pmic_plat_data->regulator_ext_sleep_control[i]);
|
|
/*
|
|
* Failing on regulator for configuring externally control
|
|
* is not a serious issue, just throw warning.
|
|
*/
|
|
if (err < 0)
|
|
dev_warn(tps65910->dev,
|
|
"Failed to initialise ext control config\n");
|
|
|
|
pmic->desc[i].type = REGULATOR_VOLTAGE;
|
|
pmic->desc[i].owner = THIS_MODULE;
|
|
pmic->desc[i].enable_reg = pmic->get_ctrl_reg(i);
|
|
pmic->desc[i].enable_mask = TPS65910_SUPPLY_STATE_ENABLED;
|
|
|
|
config.dev = tps65910->dev;
|
|
config.init_data = pmic_plat_data->tps65910_pmic_init_data[i];
|
|
config.driver_data = pmic;
|
|
config.regmap = tps65910->regmap;
|
|
|
|
if (tps65910_reg_matches)
|
|
config.of_node = tps65910_reg_matches[i].of_node;
|
|
|
|
rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i],
|
|
&config);
|
|
if (IS_ERR(rdev))
|
|
return dev_err_probe(tps65910->dev, PTR_ERR(rdev),
|
|
"failed to register %s regulator\n",
|
|
pdev->name);
|
|
|
|
/* Save regulator for cleanup */
|
|
pmic->rdev[i] = rdev;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void tps65910_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct tps65910_reg *pmic = platform_get_drvdata(pdev);
|
|
int i;
|
|
|
|
/*
|
|
* Before bootloader jumps to kernel, it makes sure that required
|
|
* external control signals are in desired state so that given rails
|
|
* can be configure accordingly.
|
|
* If rails are configured to be controlled from external control
|
|
* then before shutting down/rebooting the system, the external
|
|
* control configuration need to be remove from the rails so that
|
|
* its output will be available as per register programming even
|
|
* if external controls are removed. This is require when the POR
|
|
* value of the control signals are not in active state and before
|
|
* bootloader initializes it, the system requires the rail output
|
|
* to be active for booting.
|
|
*/
|
|
for (i = 0; i < pmic->num_regulators; i++) {
|
|
int err;
|
|
if (!pmic->rdev[i])
|
|
continue;
|
|
|
|
err = tps65910_set_ext_sleep_config(pmic, i, 0);
|
|
if (err < 0)
|
|
dev_err(&pdev->dev,
|
|
"Error in clearing external control\n");
|
|
}
|
|
}
|
|
|
|
static struct platform_driver tps65910_driver = {
|
|
.driver = {
|
|
.name = "tps65910-pmic",
|
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
|
},
|
|
.probe = tps65910_probe,
|
|
.shutdown = tps65910_shutdown,
|
|
};
|
|
|
|
static int __init tps65910_init(void)
|
|
{
|
|
return platform_driver_register(&tps65910_driver);
|
|
}
|
|
subsys_initcall(tps65910_init);
|
|
|
|
static void __exit tps65910_cleanup(void)
|
|
{
|
|
platform_driver_unregister(&tps65910_driver);
|
|
}
|
|
module_exit(tps65910_cleanup);
|
|
|
|
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
|
|
MODULE_DESCRIPTION("TPS65910/TPS65911 voltage regulator driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:tps65910-pmic");
|