mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-16 09:48:16 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
594 lines
11 KiB
C
594 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2018 Samsung Electronics
|
|
* Jaehoon Chung <jh80.chung@samsung.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <fdtdec.h>
|
|
#include <errno.h>
|
|
#include <dm.h>
|
|
#include <i2c.h>
|
|
#include <power/pmic.h>
|
|
#include <power/regulator.h>
|
|
#include <power/s2mps11.h>
|
|
|
|
#define MODE(_id, _val, _name) { \
|
|
.id = _id, \
|
|
.register_value = _val, \
|
|
.name = _name, \
|
|
}
|
|
|
|
/* BUCK : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */
|
|
static struct dm_regulator_mode s2mps11_buck_modes[] = {
|
|
MODE(OP_OFF, S2MPS11_BUCK_MODE_OFF, "OFF"),
|
|
MODE(OP_STANDBY, S2MPS11_BUCK_MODE_STANDBY, "ON/OFF"),
|
|
MODE(OP_ON, S2MPS11_BUCK_MODE_STANDBY, "ON"),
|
|
};
|
|
|
|
static struct dm_regulator_mode s2mps11_ldo_modes[] = {
|
|
MODE(OP_OFF, S2MPS11_LDO_MODE_OFF, "OFF"),
|
|
MODE(OP_STANDBY, S2MPS11_LDO_MODE_STANDBY, "ON/OFF"),
|
|
MODE(OP_STANDBY_LPM, S2MPS11_LDO_MODE_STANDBY_LPM, "ON/LPM"),
|
|
MODE(OP_ON, S2MPS11_LDO_MODE_ON, "ON"),
|
|
};
|
|
|
|
static const char s2mps11_buck_ctrl[] = {
|
|
0xff, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x37, 0x39, 0x3b
|
|
};
|
|
|
|
static const char s2mps11_buck_out[] = {
|
|
0xff, 0x26, 0x28, 0x2a, 0x2c, 0x2f, 0x34, 0x36, 0x38, 0x3a, 0x3c
|
|
};
|
|
|
|
static int s2mps11_buck_hex2volt(int buck, int hex)
|
|
{
|
|
unsigned int uV = 0;
|
|
|
|
if (hex < 0)
|
|
goto bad;
|
|
|
|
switch (buck) {
|
|
case 7:
|
|
case 8:
|
|
case 10:
|
|
if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX)
|
|
goto bad;
|
|
|
|
uV = hex * S2MPS11_BUCK_HSTEP + S2MPS11_BUCK_UV_HMIN;
|
|
break;
|
|
case 9:
|
|
if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX)
|
|
goto bad;
|
|
uV = hex * S2MPS11_BUCK9_STEP * 2 + S2MPS11_BUCK9_UV_MIN;
|
|
break;
|
|
default:
|
|
if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX)
|
|
goto bad;
|
|
else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX)
|
|
goto bad;
|
|
|
|
uV = hex * S2MPS11_BUCK_LSTEP + S2MPS11_BUCK_UV_MIN;
|
|
break;
|
|
}
|
|
|
|
return uV;
|
|
bad:
|
|
pr_err("Value: %#x is wrong for BUCK%d", hex, buck);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int s2mps11_buck_volt2hex(int buck, int uV)
|
|
{
|
|
int hex;
|
|
|
|
switch (buck) {
|
|
case 7:
|
|
case 8:
|
|
case 10:
|
|
hex = (uV - S2MPS11_BUCK_UV_HMIN) / S2MPS11_BUCK_HSTEP;
|
|
if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX)
|
|
goto bad;
|
|
|
|
break;
|
|
case 9:
|
|
hex = (uV - S2MPS11_BUCK9_UV_MIN) / S2MPS11_BUCK9_STEP;
|
|
if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX)
|
|
goto bad;
|
|
break;
|
|
default:
|
|
hex = (uV - S2MPS11_BUCK_UV_MIN) / S2MPS11_BUCK_LSTEP;
|
|
if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX)
|
|
goto bad;
|
|
else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX)
|
|
goto bad;
|
|
break;
|
|
};
|
|
|
|
if (hex >= 0)
|
|
return hex;
|
|
|
|
bad:
|
|
pr_err("Value: %d uV is wrong for BUCK%d", uV, buck);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int s2mps11_buck_val(struct udevice *dev, int op, int *uV)
|
|
{
|
|
int hex, buck, ret;
|
|
u32 mask, addr;
|
|
u8 val;
|
|
|
|
buck = dev->driver_data;
|
|
if (buck < 1 || buck > S2MPS11_BUCK_NUM) {
|
|
pr_err("Wrong buck number: %d\n", buck);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (op == PMIC_OP_GET)
|
|
*uV = 0;
|
|
|
|
addr = s2mps11_buck_out[buck];
|
|
|
|
switch (buck) {
|
|
case 9:
|
|
mask = S2MPS11_BUCK9_VOLT_MASK;
|
|
break;
|
|
default:
|
|
mask = S2MPS11_BUCK_VOLT_MASK;
|
|
break;
|
|
}
|
|
|
|
ret = pmic_read(dev->parent, addr, &val, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
val &= mask;
|
|
ret = s2mps11_buck_hex2volt(buck, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
*uV = ret;
|
|
return 0;
|
|
}
|
|
|
|
hex = s2mps11_buck_volt2hex(buck, *uV);
|
|
if (hex < 0)
|
|
return hex;
|
|
|
|
val &= ~mask;
|
|
val |= hex;
|
|
ret = pmic_write(dev->parent, addr, &val, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int s2mps11_buck_mode(struct udevice *dev, int op, int *opmode)
|
|
{
|
|
unsigned int addr, mode;
|
|
unsigned char val;
|
|
int buck, ret;
|
|
|
|
buck = dev->driver_data;
|
|
if (buck < 1 || buck > S2MPS11_BUCK_NUM) {
|
|
pr_err("Wrong buck number: %d\n", buck);
|
|
return -EINVAL;
|
|
}
|
|
|
|
addr = s2mps11_buck_ctrl[buck];
|
|
|
|
ret = pmic_read(dev->parent, addr, &val, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
val &= (S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT);
|
|
switch (val) {
|
|
case S2MPS11_BUCK_MODE_OFF:
|
|
*opmode = OP_OFF;
|
|
break;
|
|
case S2MPS11_BUCK_MODE_STANDBY:
|
|
*opmode = OP_STANDBY;
|
|
break;
|
|
case S2MPS11_BUCK_MODE_ON:
|
|
*opmode = OP_ON;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
switch (*opmode) {
|
|
case OP_OFF:
|
|
mode = S2MPS11_BUCK_MODE_OFF;
|
|
break;
|
|
case OP_STANDBY:
|
|
mode = S2MPS11_BUCK_MODE_STANDBY;
|
|
break;
|
|
case OP_ON:
|
|
mode = S2MPS11_BUCK_MODE_ON;
|
|
break;
|
|
default:
|
|
pr_err("Wrong mode: %d for buck: %d\n", *opmode, buck);
|
|
return -EINVAL;
|
|
}
|
|
|
|
val &= ~(S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT);
|
|
val |= mode;
|
|
ret = pmic_write(dev->parent, addr, &val, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int s2mps11_buck_enable(struct udevice *dev, int op, bool *enable)
|
|
{
|
|
int ret, on_off;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
ret = s2mps11_buck_mode(dev, op, &on_off);
|
|
if (ret)
|
|
return ret;
|
|
switch (on_off) {
|
|
case OP_OFF:
|
|
*enable = false;
|
|
break;
|
|
case OP_ON:
|
|
*enable = true;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
} else if (op == PMIC_OP_SET) {
|
|
if (*enable)
|
|
on_off = OP_ON;
|
|
else
|
|
on_off = OP_OFF;
|
|
|
|
ret = s2mps11_buck_mode(dev, op, &on_off);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int buck_get_value(struct udevice *dev)
|
|
{
|
|
int uV;
|
|
int ret;
|
|
|
|
ret = s2mps11_buck_val(dev, PMIC_OP_GET, &uV);
|
|
if (ret)
|
|
return ret;
|
|
return uV;
|
|
}
|
|
|
|
static int buck_set_value(struct udevice *dev, int uV)
|
|
{
|
|
return s2mps11_buck_val(dev, PMIC_OP_SET, &uV);
|
|
}
|
|
|
|
static int buck_get_enable(struct udevice *dev)
|
|
{
|
|
bool enable = false;
|
|
int ret;
|
|
|
|
ret = s2mps11_buck_enable(dev, PMIC_OP_GET, &enable);
|
|
if (ret)
|
|
return ret;
|
|
return enable;
|
|
}
|
|
|
|
static int buck_set_enable(struct udevice *dev, bool enable)
|
|
{
|
|
return s2mps11_buck_enable(dev, PMIC_OP_SET, &enable);
|
|
}
|
|
|
|
static int buck_get_mode(struct udevice *dev)
|
|
{
|
|
int mode;
|
|
int ret;
|
|
|
|
ret = s2mps11_buck_mode(dev, PMIC_OP_GET, &mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return mode;
|
|
}
|
|
|
|
static int buck_set_mode(struct udevice *dev, int mode)
|
|
{
|
|
return s2mps11_buck_mode(dev, PMIC_OP_SET, &mode);
|
|
}
|
|
|
|
static int s2mps11_buck_probe(struct udevice *dev)
|
|
{
|
|
struct dm_regulator_uclass_platdata *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_platdata(dev);
|
|
|
|
uc_pdata->type = REGULATOR_TYPE_BUCK;
|
|
uc_pdata->mode = s2mps11_buck_modes;
|
|
uc_pdata->mode_count = ARRAY_SIZE(s2mps11_buck_modes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_regulator_ops s2mps11_buck_ops = {
|
|
.get_value = buck_get_value,
|
|
.set_value = buck_set_value,
|
|
.get_enable = buck_get_enable,
|
|
.set_enable = buck_set_enable,
|
|
.get_mode = buck_get_mode,
|
|
.set_mode = buck_set_mode,
|
|
};
|
|
|
|
U_BOOT_DRIVER(s2mps11_buck) = {
|
|
.name = S2MPS11_BUCK_DRIVER,
|
|
.id = UCLASS_REGULATOR,
|
|
.ops = &s2mps11_buck_ops,
|
|
.probe = s2mps11_buck_probe,
|
|
};
|
|
|
|
static int s2mps11_ldo_hex2volt(int ldo, int hex)
|
|
{
|
|
unsigned int uV = 0;
|
|
|
|
if (hex > S2MPS11_LDO_VOLT_MAX_HEX) {
|
|
pr_err("Value: %#x is wrong for LDO%d", hex, ldo);
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (ldo) {
|
|
case 1:
|
|
case 6:
|
|
case 11:
|
|
case 22:
|
|
case 23:
|
|
uV = hex * S2MPS11_LDO_STEP + S2MPS11_LDO_UV_MIN;
|
|
break;
|
|
default:
|
|
uV = hex * S2MPS11_LDO_STEP * 2 + S2MPS11_LDO_UV_MIN;
|
|
break;
|
|
}
|
|
|
|
return uV;
|
|
}
|
|
|
|
static int s2mps11_ldo_volt2hex(int ldo, int uV)
|
|
{
|
|
int hex = 0;
|
|
|
|
switch (ldo) {
|
|
case 1:
|
|
case 6:
|
|
case 11:
|
|
case 22:
|
|
case 23:
|
|
hex = (uV - S2MPS11_LDO_UV_MIN) / S2MPS11_LDO_STEP;
|
|
break;
|
|
default:
|
|
hex = (uV - S2MPS11_LDO_UV_MIN) / (S2MPS11_LDO_STEP * 2);
|
|
break;
|
|
}
|
|
|
|
if (hex >= 0 && hex <= S2MPS11_LDO_VOLT_MAX_HEX)
|
|
return hex;
|
|
|
|
pr_err("Value: %d uV is wrong for LDO%d", uV, ldo);
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s2mps11_ldo_val(struct udevice *dev, int op, int *uV)
|
|
{
|
|
unsigned int addr;
|
|
unsigned char val;
|
|
int hex, ldo, ret;
|
|
|
|
ldo = dev->driver_data;
|
|
if (ldo < 1 || ldo > S2MPS11_LDO_NUM) {
|
|
pr_err("Wrong ldo number: %d\n", ldo);
|
|
return -EINVAL;
|
|
}
|
|
|
|
addr = S2MPS11_REG_L1CTRL + ldo - 1;
|
|
|
|
ret = pmic_read(dev->parent, addr, &val, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
*uV = 0;
|
|
val &= S2MPS11_LDO_VOLT_MASK;
|
|
ret = s2mps11_ldo_hex2volt(ldo, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*uV = ret;
|
|
return 0;
|
|
}
|
|
|
|
hex = s2mps11_ldo_volt2hex(ldo, *uV);
|
|
if (hex < 0)
|
|
return hex;
|
|
|
|
val &= ~S2MPS11_LDO_VOLT_MASK;
|
|
val |= hex;
|
|
ret = pmic_write(dev->parent, addr, &val, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int s2mps11_ldo_mode(struct udevice *dev, int op, int *opmode)
|
|
{
|
|
unsigned int addr, mode;
|
|
unsigned char val;
|
|
int ldo, ret;
|
|
|
|
ldo = dev->driver_data;
|
|
if (ldo < 1 || ldo > S2MPS11_LDO_NUM) {
|
|
pr_err("Wrong ldo number: %d\n", ldo);
|
|
return -EINVAL;
|
|
}
|
|
addr = S2MPS11_REG_L1CTRL + ldo - 1;
|
|
|
|
ret = pmic_read(dev->parent, addr, &val, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
val &= (S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT);
|
|
switch (val) {
|
|
case S2MPS11_LDO_MODE_OFF:
|
|
*opmode = OP_OFF;
|
|
break;
|
|
case S2MPS11_LDO_MODE_STANDBY:
|
|
*opmode = OP_STANDBY;
|
|
break;
|
|
case S2MPS11_LDO_MODE_STANDBY_LPM:
|
|
*opmode = OP_STANDBY_LPM;
|
|
break;
|
|
case S2MPS11_LDO_MODE_ON:
|
|
*opmode = OP_ON;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
switch (*opmode) {
|
|
case OP_OFF:
|
|
mode = S2MPS11_LDO_MODE_OFF;
|
|
break;
|
|
case OP_STANDBY:
|
|
mode = S2MPS11_LDO_MODE_STANDBY;
|
|
break;
|
|
case OP_STANDBY_LPM:
|
|
mode = S2MPS11_LDO_MODE_STANDBY_LPM;
|
|
break;
|
|
case OP_ON:
|
|
mode = S2MPS11_LDO_MODE_ON;
|
|
break;
|
|
default:
|
|
pr_err("Wrong mode: %d for ldo: %d\n", *opmode, ldo);
|
|
return -EINVAL;
|
|
}
|
|
|
|
val &= ~(S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT);
|
|
val |= mode;
|
|
ret = pmic_write(dev->parent, addr, &val, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int s2mps11_ldo_enable(struct udevice *dev, int op, bool *enable)
|
|
{
|
|
int ret, on_off;
|
|
|
|
if (op == PMIC_OP_GET) {
|
|
ret = s2mps11_ldo_mode(dev, op, &on_off);
|
|
if (ret)
|
|
return ret;
|
|
switch (on_off) {
|
|
case OP_OFF:
|
|
*enable = false;
|
|
break;
|
|
case OP_ON:
|
|
*enable = true;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
} else if (op == PMIC_OP_SET) {
|
|
if (*enable)
|
|
on_off = OP_ON;
|
|
else
|
|
on_off = OP_OFF;
|
|
|
|
ret = s2mps11_ldo_mode(dev, op, &on_off);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ldo_get_value(struct udevice *dev)
|
|
{
|
|
int uV;
|
|
int ret;
|
|
|
|
ret = s2mps11_ldo_val(dev, PMIC_OP_GET, &uV);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return uV;
|
|
}
|
|
|
|
static int ldo_set_value(struct udevice *dev, int uV)
|
|
{
|
|
return s2mps11_ldo_val(dev, PMIC_OP_SET, &uV);
|
|
}
|
|
|
|
static int ldo_get_enable(struct udevice *dev)
|
|
{
|
|
bool enable = false;
|
|
int ret;
|
|
|
|
ret = s2mps11_ldo_enable(dev, PMIC_OP_GET, &enable);
|
|
if (ret)
|
|
return ret;
|
|
return enable;
|
|
}
|
|
|
|
static int ldo_set_enable(struct udevice *dev, bool enable)
|
|
{
|
|
return s2mps11_ldo_enable(dev, PMIC_OP_SET, &enable);
|
|
}
|
|
|
|
static int ldo_get_mode(struct udevice *dev)
|
|
{
|
|
int mode, ret;
|
|
|
|
ret = s2mps11_ldo_mode(dev, PMIC_OP_GET, &mode);
|
|
if (ret)
|
|
return ret;
|
|
return mode;
|
|
}
|
|
|
|
static int ldo_set_mode(struct udevice *dev, int mode)
|
|
{
|
|
return s2mps11_ldo_mode(dev, PMIC_OP_SET, &mode);
|
|
}
|
|
|
|
static int s2mps11_ldo_probe(struct udevice *dev)
|
|
{
|
|
struct dm_regulator_uclass_platdata *uc_pdata;
|
|
|
|
uc_pdata = dev_get_uclass_platdata(dev);
|
|
uc_pdata->type = REGULATOR_TYPE_LDO;
|
|
uc_pdata->mode = s2mps11_ldo_modes;
|
|
uc_pdata->mode_count = ARRAY_SIZE(s2mps11_ldo_modes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_regulator_ops s2mps11_ldo_ops = {
|
|
.get_value = ldo_get_value,
|
|
.set_value = ldo_set_value,
|
|
.get_enable = ldo_get_enable,
|
|
.set_enable = ldo_set_enable,
|
|
.get_mode = ldo_get_mode,
|
|
.set_mode = ldo_set_mode,
|
|
};
|
|
|
|
U_BOOT_DRIVER(s2mps11_ldo) = {
|
|
.name = S2MPS11_LDO_DRIVER,
|
|
.id = UCLASS_REGULATOR,
|
|
.ops = &s2mps11_ldo_ops,
|
|
.probe = s2mps11_ldo_probe,
|
|
};
|