mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 08:31:03 +00:00
caa4daa2ae
We use 'priv' for private data but often use 'platdata' for platform data. We can't really use 'pdata' since that is ambiguous (it could mean private or platform data). Rename some of the latter variables to end with 'plat' for consistency. Signed-off-by: Simon Glass <sjg@chromium.org>
409 lines
7.7 KiB
C
409 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2019
|
|
* Texas Instruments Incorporated, <www.ti.com>
|
|
*
|
|
* Keerthy <j-keerthy@ti.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <fdtdec.h>
|
|
#include <errno.h>
|
|
#include <dm.h>
|
|
#include <i2c.h>
|
|
#include <log.h>
|
|
#include <linux/delay.h>
|
|
#include <power/pmic.h>
|
|
#include <power/regulator.h>
|
|
#include <power/tps65941.h>
|
|
|
|
static const char tps65941_buck_ctrl[TPS65941_BUCK_NUM] = {0x4, 0x6, 0x8, 0xA,
|
|
0xC};
|
|
static const char tps65941_buck_vout[TPS65941_BUCK_NUM] = {0xE, 0x10, 0x12,
|
|
0x14, 0x16};
|
|
static const char tps65941_ldo_ctrl[TPS65941_BUCK_NUM] = {0x1D, 0x1E, 0x1F,
|
|
0x20};
|
|
static const char tps65941_ldo_vout[TPS65941_BUCK_NUM] = {0x23, 0x24, 0x25,
|
|
0x26};
|
|
|
|
static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable)
|
|
{
|
|
int ret;
|
|
unsigned int adr;
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
adr = uc_pdata->ctrl_reg;
|
|
|
|
ret = pmic_reg_read(dev->parent, adr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
ret &= TPS65941_BUCK_MODE_MASK;
|
|
|
|
if (ret)
|
|
*enable = true;
|
|
else
|
|
*enable = false;
|
|
|
|
return 0;
|
|
} else if (op == PMIC_OP_SET) {
|
|
if (*enable)
|
|
ret |= TPS65941_BUCK_MODE_MASK;
|
|
else
|
|
ret &= ~TPS65941_BUCK_MODE_MASK;
|
|
ret = pmic_reg_write(dev->parent, adr, ret);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tps65941_buck_volt2val(int uV)
|
|
{
|
|
if (uV > TPS65941_BUCK_VOLT_MAX)
|
|
return -EINVAL;
|
|
else if (uV > 1650000)
|
|
return (uV - 1660000) / 20000 + 0xAB;
|
|
else if (uV > 1110000)
|
|
return (uV - 1110000) / 10000 + 0x73;
|
|
else if (uV > 600000)
|
|
return (uV - 600000) / 5000 + 0x0F;
|
|
else if (uV >= 300000)
|
|
return (uV - 300000) / 20000 + 0x00;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int tps65941_buck_val2volt(int val)
|
|
{
|
|
if (val > TPS65941_BUCK_VOLT_MAX_HEX)
|
|
return -EINVAL;
|
|
else if (val > 0xAB)
|
|
return 1660000 + (val - 0xAB) * 20000;
|
|
else if (val > 0x73)
|
|
return 1100000 + (val - 0x73) * 10000;
|
|
else if (val > 0xF)
|
|
return 600000 + (val - 0xF) * 5000;
|
|
else if (val >= 0x0)
|
|
return 300000 + val * 5000;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
int tps65941_lookup_slew(int id)
|
|
{
|
|
switch (id) {
|
|
case 0:
|
|
return 33000;
|
|
case 1:
|
|
return 20000;
|
|
case 2:
|
|
return 10000;
|
|
case 3:
|
|
return 5000;
|
|
case 4:
|
|
return 2500;
|
|
case 5:
|
|
return 1300;
|
|
case 6:
|
|
return 630;
|
|
case 7:
|
|
return 310;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int tps65941_buck_val(struct udevice *dev, int op, int *uV)
|
|
{
|
|
unsigned int hex, adr;
|
|
int ret, delta, uwait, slew;
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
|
|
if (op == PMIC_OP_GET)
|
|
*uV = 0;
|
|
|
|
adr = uc_pdata->volt_reg;
|
|
|
|
ret = pmic_reg_read(dev->parent, adr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret &= TPS65941_BUCK_VOLT_MASK;
|
|
ret = tps65941_buck_val2volt(ret);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
*uV = ret;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Compute the delta voltage, find the slew rate and wait
|
|
* for the appropriate amount of time after voltage switch
|
|
*/
|
|
if (*uV > ret)
|
|
delta = *uV - ret;
|
|
else
|
|
delta = ret - *uV;
|
|
|
|
slew = pmic_reg_read(dev->parent, uc_pdata->ctrl_reg + 1);
|
|
if (slew < 0)
|
|
return ret;
|
|
|
|
slew &= TP65941_BUCK_CONF_SLEW_MASK;
|
|
slew = tps65941_lookup_slew(slew);
|
|
if (slew <= 0)
|
|
return ret;
|
|
|
|
uwait = delta / slew;
|
|
|
|
hex = tps65941_buck_volt2val(*uV);
|
|
if (hex < 0)
|
|
return hex;
|
|
|
|
ret &= 0x0;
|
|
ret = hex;
|
|
|
|
ret = pmic_reg_write(dev->parent, adr, ret);
|
|
|
|
udelay(uwait);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tps65941_ldo_enable(struct udevice *dev, int op, bool *enable)
|
|
{
|
|
int ret;
|
|
unsigned int adr;
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
adr = uc_pdata->ctrl_reg;
|
|
|
|
ret = pmic_reg_read(dev->parent, adr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
ret &= TPS65941_LDO_MODE_MASK;
|
|
|
|
if (ret)
|
|
*enable = true;
|
|
else
|
|
*enable = false;
|
|
|
|
return 0;
|
|
} else if (op == PMIC_OP_SET) {
|
|
if (*enable)
|
|
ret |= TPS65941_LDO_MODE_MASK;
|
|
else
|
|
ret &= ~TPS65941_LDO_MODE_MASK;
|
|
ret = pmic_reg_write(dev->parent, adr, ret);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tps65941_ldo_val2volt(int val)
|
|
{
|
|
if (val > TPS65941_LDO_VOLT_MAX_HEX || val < TPS65941_LDO_VOLT_MIN_HEX)
|
|
return -EINVAL;
|
|
else if (val >= TPS65941_LDO_VOLT_MIN_HEX)
|
|
return 600000 + (val - TPS65941_LDO_VOLT_MIN_HEX) * 50000;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int tps65941_ldo_val(struct udevice *dev, int op, int *uV)
|
|
{
|
|
unsigned int hex, adr;
|
|
int ret;
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
|
|
if (op == PMIC_OP_GET)
|
|
*uV = 0;
|
|
|
|
adr = uc_pdata->volt_reg;
|
|
|
|
ret = pmic_reg_read(dev->parent, adr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret &= TPS65941_LDO_VOLT_MASK;
|
|
ret = tps65941_ldo_val2volt(ret);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
*uV = ret;
|
|
return 0;
|
|
}
|
|
|
|
hex = tps65941_buck_volt2val(*uV);
|
|
if (hex < 0)
|
|
return hex;
|
|
|
|
ret &= 0x0;
|
|
ret = hex;
|
|
|
|
ret = pmic_reg_write(dev->parent, adr, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tps65941_ldo_probe(struct udevice *dev)
|
|
{
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
int idx;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
uc_pdata->type = REGULATOR_TYPE_LDO;
|
|
|
|
idx = dev->driver_data;
|
|
if (idx == 1 || idx == 2 || idx == 3 || idx == 4) {
|
|
debug("Single phase regulator\n");
|
|
} else {
|
|
printf("Wrong ID for regulator\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
uc_pdata->ctrl_reg = tps65941_ldo_ctrl[idx - 1];
|
|
uc_pdata->volt_reg = tps65941_ldo_vout[idx - 1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tps65941_buck_probe(struct udevice *dev)
|
|
{
|
|
struct dm_regulator_uclass_plat *uc_pdata;
|
|
int idx;
|
|
|
|
uc_pdata = dev_get_uclass_plat(dev);
|
|
uc_pdata->type = REGULATOR_TYPE_BUCK;
|
|
|
|
idx = dev->driver_data;
|
|
if (idx == 1 || idx == 2 || idx == 3 || idx == 4 || idx == 5) {
|
|
debug("Single phase regulator\n");
|
|
} else if (idx == 12) {
|
|
idx = 1;
|
|
} else if (idx == 34) {
|
|
idx = 3;
|
|
} else if (idx == 1234) {
|
|
idx = 1;
|
|
} else {
|
|
printf("Wrong ID for regulator\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
uc_pdata->ctrl_reg = tps65941_buck_ctrl[idx - 1];
|
|
uc_pdata->volt_reg = tps65941_buck_vout[idx - 1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ldo_get_value(struct udevice *dev)
|
|
{
|
|
int uV;
|
|
int ret;
|
|
|
|
ret = tps65941_ldo_val(dev, PMIC_OP_GET, &uV);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return uV;
|
|
}
|
|
|
|
static int ldo_set_value(struct udevice *dev, int uV)
|
|
{
|
|
return tps65941_ldo_val(dev, PMIC_OP_SET, &uV);
|
|
}
|
|
|
|
static int ldo_get_enable(struct udevice *dev)
|
|
{
|
|
bool enable = false;
|
|
int ret;
|
|
|
|
ret = tps65941_ldo_enable(dev, PMIC_OP_GET, &enable);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return enable;
|
|
}
|
|
|
|
static int ldo_set_enable(struct udevice *dev, bool enable)
|
|
{
|
|
return tps65941_ldo_enable(dev, PMIC_OP_SET, &enable);
|
|
}
|
|
|
|
static int buck_get_value(struct udevice *dev)
|
|
{
|
|
int uV;
|
|
int ret;
|
|
|
|
ret = tps65941_buck_val(dev, PMIC_OP_GET, &uV);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return uV;
|
|
}
|
|
|
|
static int buck_set_value(struct udevice *dev, int uV)
|
|
{
|
|
return tps65941_buck_val(dev, PMIC_OP_SET, &uV);
|
|
}
|
|
|
|
static int buck_get_enable(struct udevice *dev)
|
|
{
|
|
bool enable = false;
|
|
int ret;
|
|
|
|
ret = tps65941_buck_enable(dev, PMIC_OP_GET, &enable);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return enable;
|
|
}
|
|
|
|
static int buck_set_enable(struct udevice *dev, bool enable)
|
|
{
|
|
return tps65941_buck_enable(dev, PMIC_OP_SET, &enable);
|
|
}
|
|
|
|
static const struct dm_regulator_ops tps65941_ldo_ops = {
|
|
.get_value = ldo_get_value,
|
|
.set_value = ldo_set_value,
|
|
.get_enable = ldo_get_enable,
|
|
.set_enable = ldo_set_enable,
|
|
};
|
|
|
|
U_BOOT_DRIVER(tps65941_ldo) = {
|
|
.name = TPS65941_LDO_DRIVER,
|
|
.id = UCLASS_REGULATOR,
|
|
.ops = &tps65941_ldo_ops,
|
|
.probe = tps65941_ldo_probe,
|
|
};
|
|
|
|
static const struct dm_regulator_ops tps65941_buck_ops = {
|
|
.get_value = buck_get_value,
|
|
.set_value = buck_set_value,
|
|
.get_enable = buck_get_enable,
|
|
.set_enable = buck_set_enable,
|
|
};
|
|
|
|
U_BOOT_DRIVER(tps65941_buck) = {
|
|
.name = TPS65941_BUCK_DRIVER,
|
|
.id = UCLASS_REGULATOR,
|
|
.ops = &tps65941_buck_ops,
|
|
.probe = tps65941_buck_probe,
|
|
};
|