This commit adds emulation of sandbox PMIC device, which includes: - PMIC I2C emulation driver - PMIC I/O driver (UCLASS_PMIC) - PMIC regulator driver (UCLASS_REGULATOR) The sandbox PMIC has 12 significant registers and 4 as padding to 16 bytes, which allows using 'i2c md' command with the default count (16). The sandbox PMIC provides regulators: - 2x BUCK - 2x LDO Each, with adjustable output: - Enable state - Voltage - Current limit (LDO1/BUCK1 only) - Operation mode (different for BUCK and LDO) Each attribute has it's own register, beside the enable state, which depends on operation mode. The header file: sandbox_pmic.h includes PMIC's default register values, which are set on i2c pmic emul driver's probe() method. Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com> Acked-by: Simon Glass <sjg@chromium.org> Tested on sandbox: Tested-by: Simon Glass <sjg@chromium.org>master
parent
a989ec8dd4
commit
5d387d0df9
@ -0,0 +1,35 @@ |
||||
Sandbox pmic |
||||
|
||||
This device uses two drivers: |
||||
- drivers/power/pmic/sandbox.c (for parent device) |
||||
- drivers/power/regulator/sandbox.c (for child regulators) |
||||
|
||||
This file describes the binding info for the PMIC driver. |
||||
|
||||
To bind the regulators, please read the regulator binding info: |
||||
- doc/device-tree-bindings/regulator/sandbox.txt |
||||
|
||||
Required PMIC node properties: |
||||
- compatible: "sandbox,pmic" |
||||
- reg = 0x40 |
||||
|
||||
Required PMIC's "emul" subnode, with property: |
||||
- compatible: "sandbox,i2c-pmic" |
||||
|
||||
With the above properties, the pmic device can be used for read/write only. |
||||
To bind each regulator, the optional regulator subnodes should exists. |
||||
|
||||
Optional subnodes: |
||||
- ldo/buck subnodes of each device's regulator (see regulator binding info) |
||||
|
||||
Example: |
||||
|
||||
sandbox_pmic { |
||||
compatible = "sandbox,pmic"; |
||||
reg = <0x40>; |
||||
|
||||
/* Mandatory for I/O */ |
||||
emul { |
||||
compatible = "sandbox,i2c-pmic"; |
||||
}; |
||||
}; |
@ -0,0 +1,45 @@ |
||||
Sandbox, PMIC regulators |
||||
|
||||
This device uses two drivers: |
||||
- drivers/power/pmic/sandbox.c (as parent I/O device) |
||||
- drivers/power/regulator/sandbox.c (for child regulators) |
||||
|
||||
This file describes the binding info for the REGULATOR driver. |
||||
|
||||
First, please read the binding info for the PMIC: |
||||
- doc/device-tree-bindings/pmic/sandbox.txt |
||||
|
||||
Required subnodes: |
||||
- ldoN { }; |
||||
- buckN { }; |
||||
|
||||
The sandbox PMIC can support: ldo1, ldo2, buck1, buck2. |
||||
|
||||
For each PMIC's regulator subnode, there is one required property: |
||||
- regulator-name: used for regulator uclass platform data '.name' |
||||
|
||||
Optional: |
||||
- regulator-min-microvolt: minimum allowed Voltage to set |
||||
- regulator-max-microvolt: minimum allowed Voltage to set |
||||
- regulator-min-microamps: minimum allowed Current limit to set (LDO1/BUCK1) |
||||
- regulator-max-microamps: minimum allowed Current limit to set (LDO1/BUCK1) |
||||
- regulator-always-on: regulator should be never disabled |
||||
- regulator-boot-on: regulator should be enabled by the bootloader |
||||
|
||||
Example PMIC's regulator subnodes: |
||||
|
||||
ldo1 { |
||||
regulator-name = "VDD_1.0V"; |
||||
regulator-min-microvolt = <1000000>; |
||||
regulator-max-microvolt = <1200000>; |
||||
regulator-min-microamps = <100000>; |
||||
regulator-max-microamps = <400000>; |
||||
regulator-always-on; |
||||
}; |
||||
|
||||
buck2 { |
||||
regulator-name = "VDD_1.8V"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-boot-on; |
||||
}; |
@ -0,0 +1,142 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics |
||||
* Przemyslaw Marczak <p.marczak@samsung.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <errno.h> |
||||
#include <dm.h> |
||||
#include <i2c.h> |
||||
#include <power/pmic.h> |
||||
#include <power/sandbox_pmic.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
/**
|
||||
* struct sandbox_i2c_pmic_plat_data - platform data for the PMIC |
||||
* |
||||
* @rw_reg: PMICs register of the chip I/O transaction |
||||
* @reg: PMICs registers array |
||||
*/ |
||||
struct sandbox_i2c_pmic_plat_data { |
||||
u8 rw_reg; |
||||
u8 reg[SANDBOX_PMIC_REG_COUNT]; |
||||
}; |
||||
|
||||
static int sandbox_i2c_pmic_read_data(struct udevice *emul, uchar chip, |
||||
uchar *buffer, int len) |
||||
{ |
||||
struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); |
||||
|
||||
if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) { |
||||
error("Request exceeds PMIC register range! Max register: %#x", |
||||
SANDBOX_PMIC_REG_COUNT); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
debug("Read PMIC: %#x at register: %#x count: %d\n", |
||||
(unsigned)chip & 0xff, plat->rw_reg, len); |
||||
|
||||
memcpy(buffer, &plat->reg[plat->rw_reg], len); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sandbox_i2c_pmic_write_data(struct udevice *emul, uchar chip, |
||||
uchar *buffer, int len, |
||||
bool next_is_read) |
||||
{ |
||||
struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); |
||||
|
||||
/* Probe only */ |
||||
if (!len) |
||||
return 0; |
||||
|
||||
/* Set PMIC register for I/O */ |
||||
plat->rw_reg = *buffer; |
||||
|
||||
debug("Write PMIC: %#x at register: %#x count: %d\n", |
||||
(unsigned)chip & 0xff, plat->rw_reg, len); |
||||
|
||||
/* For read operation, set (write) only chip reg */ |
||||
if (next_is_read) |
||||
return 0; |
||||
|
||||
buffer++; |
||||
len--; |
||||
|
||||
if (plat->rw_reg + len > SANDBOX_PMIC_REG_COUNT) { |
||||
error("Request exceeds PMIC register range! Max register: %#x", |
||||
SANDBOX_PMIC_REG_COUNT); |
||||
} |
||||
|
||||
memcpy(&plat->reg[plat->rw_reg], buffer, len); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sandbox_i2c_pmic_xfer(struct udevice *emul, struct i2c_msg *msg, |
||||
int nmsgs) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
for (; nmsgs > 0; nmsgs--, msg++) { |
||||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); |
||||
if (msg->flags & I2C_M_RD) { |
||||
ret = sandbox_i2c_pmic_read_data(emul, msg->addr, |
||||
msg->buf, msg->len); |
||||
} else { |
||||
ret = sandbox_i2c_pmic_write_data(emul, msg->addr, |
||||
msg->buf, msg->len, |
||||
next_is_read); |
||||
} |
||||
|
||||
if (ret) |
||||
break; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int sandbox_i2c_pmic_ofdata_to_platdata(struct udevice *emul) |
||||
{ |
||||
struct sandbox_i2c_pmic_plat_data *plat = dev_get_platdata(emul); |
||||
const u8 *reg_defaults; |
||||
|
||||
debug("%s:%d Setting PMIC default registers\n", __func__, __LINE__); |
||||
|
||||
reg_defaults = fdtdec_locate_byte_array(gd->fdt_blob, emul->of_offset, |
||||
"reg-defaults", |
||||
SANDBOX_PMIC_REG_COUNT); |
||||
|
||||
if (!reg_defaults) { |
||||
error("Property \"reg-defaults\" not found for device: %s!", |
||||
emul->name); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
memcpy(&plat->reg, reg_defaults, SANDBOX_PMIC_REG_COUNT); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = { |
||||
.xfer = sandbox_i2c_pmic_xfer, |
||||
}; |
||||
|
||||
static const struct udevice_id sandbox_i2c_pmic_ids[] = { |
||||
{ .compatible = "sandbox,i2c-pmic" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(sandbox_i2c_pmic_emul) = { |
||||
.name = "sandbox_i2c_pmic_emul", |
||||
.id = UCLASS_I2C_EMUL, |
||||
.of_match = sandbox_i2c_pmic_ids, |
||||
.ofdata_to_platdata = sandbox_i2c_pmic_ofdata_to_platdata, |
||||
.platdata_auto_alloc_size = sizeof(struct sandbox_i2c_pmic_plat_data), |
||||
.ops = &sandbox_i2c_pmic_emul_ops, |
||||
}; |
@ -0,0 +1,79 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics |
||||
* Przemyslaw Marczak <p.marczak@samsung.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#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/sandbox_pmic.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
static const struct pmic_child_info pmic_children_info[] = { |
||||
{ .prefix = SANDBOX_OF_LDO_PREFIX, .driver = SANDBOX_LDO_DRIVER }, |
||||
{ .prefix = SANDBOX_OF_BUCK_PREFIX, .driver = SANDBOX_BUCK_DRIVER }, |
||||
{ }, |
||||
}; |
||||
|
||||
static int sandbox_pmic_reg_count(struct udevice *dev) |
||||
{ |
||||
return SANDBOX_PMIC_REG_COUNT; |
||||
} |
||||
|
||||
static int sandbox_pmic_write(struct udevice *dev, uint reg, |
||||
const uint8_t *buff, int len) |
||||
{ |
||||
if (dm_i2c_write(dev, reg, buff, len)) { |
||||
error("write error to device: %p register: %#x!", dev, reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sandbox_pmic_read(struct udevice *dev, uint reg, |
||||
uint8_t *buff, int len) |
||||
{ |
||||
if (dm_i2c_read(dev, reg, buff, len)) { |
||||
error("read error from device: %p register: %#x!", dev, reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int sandbox_pmic_bind(struct udevice *dev) |
||||
{ |
||||
if (!pmic_bind_children(dev, dev->of_offset, pmic_children_info)) |
||||
error("%s:%d PMIC: %s - no child found!", __func__, __LINE__, |
||||
dev->name); |
||||
|
||||
/* Always return success for this device - allows for PMIC I/O */ |
||||
return 0; |
||||
} |
||||
|
||||
static struct dm_pmic_ops sandbox_pmic_ops = { |
||||
.reg_count = sandbox_pmic_reg_count, |
||||
.read = sandbox_pmic_read, |
||||
.write = sandbox_pmic_write, |
||||
}; |
||||
|
||||
static const struct udevice_id sandbox_pmic_ids[] = { |
||||
{ .compatible = "sandbox,pmic" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(sandbox_pmic) = { |
||||
.name = "sandbox_pmic", |
||||
.id = UCLASS_PMIC, |
||||
.of_match = sandbox_pmic_ids, |
||||
.bind = sandbox_pmic_bind, |
||||
.ops = &sandbox_pmic_ops, |
||||
}; |
@ -0,0 +1,355 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics |
||||
* Przemyslaw Marczak <p.marczak@samsung.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#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/sandbox_pmic.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
#define MODE(_id, _val, _name) [_id] = { \ |
||||
.id = _id, \
|
||||
.register_value = _val, \
|
||||
.name = _name, \
|
||||
} |
||||
|
||||
#define RANGE(_min, _max, _step) { \ |
||||
.min = _min, \
|
||||
.max = _max, \
|
||||
.step = _step, \
|
||||
} |
||||
|
||||
/*
|
||||
* struct output_range - helper structure type to define the range of output |
||||
* operating values (current/voltage), limited by the PMIC IC design. |
||||
* |
||||
* @min - minimum value |
||||
* @max - maximum value |
||||
* @step - step value |
||||
*/ |
||||
struct output_range { |
||||
int min; |
||||
int max; |
||||
int step; |
||||
}; |
||||
|
||||
/* BUCK: 1,2 - voltage range */ |
||||
static struct output_range buck_voltage_range[] = { |
||||
RANGE(OUT_BUCK1_UV_MIN, OUT_BUCK1_UV_MAX, OUT_BUCK1_UV_STEP), |
||||
RANGE(OUT_BUCK2_UV_MIN, OUT_BUCK2_UV_MAX, OUT_BUCK2_UV_STEP), |
||||
}; |
||||
|
||||
/* BUCK: 1 - current range */ |
||||
static struct output_range buck_current_range[] = { |
||||
RANGE(OUT_BUCK1_UA_MIN, OUT_BUCK1_UA_MAX, OUT_BUCK1_UA_STEP), |
||||
}; |
||||
|
||||
/* BUCK operating modes */ |
||||
static struct dm_regulator_mode sandbox_buck_modes[] = { |
||||
MODE(BUCK_OM_OFF, OM2REG(BUCK_OM_OFF), "OFF"), |
||||
MODE(BUCK_OM_ON, OM2REG(BUCK_OM_ON), "ON"), |
||||
MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"), |
||||
}; |
||||
|
||||
/* LDO: 1,2 - voltage range */ |
||||
static struct output_range ldo_voltage_range[] = { |
||||
RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP), |
||||
RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP), |
||||
}; |
||||
|
||||
/* LDO: 1 - current range */ |
||||
static struct output_range ldo_current_range[] = { |
||||
RANGE(OUT_LDO1_UA_MIN, OUT_LDO1_UA_MAX, OUT_LDO1_UA_STEP), |
||||
}; |
||||
|
||||
/* LDO operating modes */ |
||||
static struct dm_regulator_mode sandbox_ldo_modes[] = { |
||||
MODE(LDO_OM_OFF, OM2REG(LDO_OM_OFF), "OFF"), |
||||
MODE(LDO_OM_ON, OM2REG(LDO_OM_ON), "ON"), |
||||
MODE(LDO_OM_SLEEP, OM2REG(LDO_OM_SLEEP), "SLEEP"), |
||||
MODE(LDO_OM_STANDBY, OM2REG(LDO_OM_STANDBY), "STANDBY"), |
||||
}; |
||||
|
||||
int out_get_value(struct udevice *dev, int output_count, int reg_type, |
||||
struct output_range *range) |
||||
{ |
||||
uint8_t reg_val; |
||||
uint reg; |
||||
int ret; |
||||
|
||||
if (dev->driver_data > output_count) { |
||||
error("Unknown regulator number: %lu for PMIC %s!", |
||||
dev->driver_data, dev->name); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; |
||||
ret = pmic_read(dev->parent, reg, ®_val, 1); |
||||
if (ret) { |
||||
error("PMIC read failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
ret = REG2VAL(range[dev->driver_data - 1].min, |
||||
range[dev->driver_data - 1].step, |
||||
reg_val); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int out_set_value(struct udevice *dev, int output_count, int reg_type, |
||||
struct output_range *range, int value) |
||||
{ |
||||
uint8_t reg_val; |
||||
uint reg; |
||||
int ret; |
||||
int max_value; |
||||
|
||||
if (dev->driver_data > output_count) { |
||||
error("Unknown regulator number: %lu for PMIC %s!", |
||||
dev->driver_data, dev->name); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
max_value = range[dev->driver_data - 1].max; |
||||
if (value > max_value) { |
||||
error("Wrong value for %s: %lu. Max is: %d.", |
||||
dev->name, dev->driver_data, max_value); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
reg_val = VAL2REG(range[dev->driver_data - 1].min, |
||||
range[dev->driver_data - 1].step, |
||||
value); |
||||
|
||||
reg = (dev->driver_data - 1) * OUT_REG_COUNT + reg_type; |
||||
ret = pmic_write(dev->parent, reg, ®_val, 1); |
||||
if (ret) { |
||||
error("PMIC write failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int out_get_mode(struct udevice *dev) |
||||
{ |
||||
struct dm_regulator_uclass_platdata *uc_pdata; |
||||
uint8_t reg_val; |
||||
uint reg; |
||||
int ret; |
||||
int i; |
||||
|
||||
uc_pdata = dev_get_uclass_platdata(dev); |
||||
|
||||
reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; |
||||
ret = pmic_read(dev->parent, reg, ®_val, 1); |
||||
if (ret) { |
||||
error("PMIC read failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
for (i = 0; i < uc_pdata->mode_count; i++) { |
||||
if (reg_val == uc_pdata->mode[i].register_value) |
||||
return uc_pdata->mode[i].id; |
||||
} |
||||
|
||||
error("Unknown operation mode for %s!", dev->name); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
static int out_set_mode(struct udevice *dev, int mode) |
||||
{ |
||||
struct dm_regulator_uclass_platdata *uc_pdata; |
||||
int reg_val = -1; |
||||
uint reg; |
||||
int ret; |
||||
int i; |
||||
|
||||
uc_pdata = dev_get_uclass_platdata(dev); |
||||
|
||||
if (mode >= uc_pdata->mode_count) |
||||
return -EINVAL; |
||||
|
||||
for (i = 0; i < uc_pdata->mode_count; i++) { |
||||
if (mode == uc_pdata->mode[i].id) { |
||||
reg_val = uc_pdata->mode[i].register_value; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (reg_val == -1) { |
||||
error("Unknown operation mode for %s!", dev->name); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
reg = (dev->driver_data - 1) * OUT_REG_COUNT + OUT_REG_OM; |
||||
ret = pmic_write(dev->parent, reg, (uint8_t *)®_val, 1); |
||||
if (ret) { |
||||
error("PMIC write failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int buck_get_voltage(struct udevice *dev) |
||||
{ |
||||
return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, |
||||
buck_voltage_range); |
||||
} |
||||
|
||||
static int buck_set_voltage(struct udevice *dev, int uV) |
||||
{ |
||||
return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UV, |
||||
buck_voltage_range, uV); |
||||
} |
||||
|
||||
static int buck_get_current(struct udevice *dev) |
||||
{ |
||||
/* BUCK2 - unsupported */ |
||||
if (dev->driver_data == 2) |
||||
return -ENOSYS; |
||||
|
||||
return out_get_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, |
||||
buck_current_range); |
||||
} |
||||
|
||||
static int buck_set_current(struct udevice *dev, int uA) |
||||
{ |
||||
/* BUCK2 - unsupported */ |
||||
if (dev->driver_data == 2) |
||||
return -ENOSYS; |
||||
|
||||
return out_set_value(dev, SANDBOX_BUCK_COUNT, OUT_REG_UA, |
||||
buck_current_range, uA); |
||||
} |
||||
|
||||
static bool buck_get_enable(struct udevice *dev) |
||||
{ |
||||
if (out_get_mode(dev) == BUCK_OM_OFF) |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static int buck_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
return out_set_mode(dev, enable ? BUCK_OM_ON : BUCK_OM_OFF); |
||||
} |
||||
|
||||
static int sandbox_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 = sandbox_buck_modes; |
||||
uc_pdata->mode_count = ARRAY_SIZE(sandbox_buck_modes); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct dm_regulator_ops sandbox_buck_ops = { |
||||
.get_value = buck_get_voltage, |
||||
.set_value = buck_set_voltage, |
||||
.get_current = buck_get_current, |
||||
.set_current = buck_set_current, |
||||
.get_enable = buck_get_enable, |
||||
.set_enable = buck_set_enable, |
||||
.get_mode = out_get_mode, |
||||
.set_mode = out_set_mode, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(sandbox_buck) = { |
||||
.name = SANDBOX_BUCK_DRIVER, |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &sandbox_buck_ops, |
||||
.probe = sandbox_buck_probe, |
||||
}; |
||||
|
||||
static int ldo_get_voltage(struct udevice *dev) |
||||
{ |
||||
return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, |
||||
ldo_voltage_range); |
||||
} |
||||
|
||||
static int ldo_set_voltage(struct udevice *dev, int uV) |
||||
{ |
||||
return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UV, |
||||
ldo_voltage_range, uV); |
||||
} |
||||
|
||||
static int ldo_get_current(struct udevice *dev) |
||||
{ |
||||
/* LDO2 - unsupported */ |
||||
if (dev->driver_data == 2) |
||||
return -ENOSYS; |
||||
|
||||
return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, |
||||
ldo_current_range); |
||||
} |
||||
|
||||
static int ldo_set_current(struct udevice *dev, int uA) |
||||
{ |
||||
/* LDO2 - unsupported */ |
||||
if (dev->driver_data == 2) |
||||
return -ENOSYS; |
||||
|
||||
return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA, |
||||
ldo_current_range, uA); |
||||
} |
||||
|
||||
static bool ldo_get_enable(struct udevice *dev) |
||||
{ |
||||
if (out_get_mode(dev) == LDO_OM_OFF) |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static int ldo_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
return out_set_mode(dev, enable ? LDO_OM_ON : LDO_OM_OFF); |
||||
} |
||||
|
||||
static int sandbox_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 = sandbox_ldo_modes; |
||||
uc_pdata->mode_count = ARRAY_SIZE(sandbox_ldo_modes); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct dm_regulator_ops sandbox_ldo_ops = { |
||||
.get_value = ldo_get_voltage, |
||||
.set_value = ldo_set_voltage, |
||||
.get_current = ldo_get_current, |
||||
.set_current = ldo_set_current, |
||||
.get_enable = ldo_get_enable, |
||||
.set_enable = ldo_set_enable, |
||||
.get_mode = out_get_mode, |
||||
.set_mode = out_set_mode, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(sandbox_ldo) = { |
||||
.name = SANDBOX_LDO_DRIVER, |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &sandbox_ldo_ops, |
||||
.probe = sandbox_ldo_probe, |
||||
}; |
@ -0,0 +1,105 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics |
||||
* Przemyslaw Marczak <p.marczak@samsung.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef _SANDBOX_PMIC_H_ |
||||
#define _SANDBOX_PMIC_H_ |
||||
|
||||
#define SANDBOX_LDO_DRIVER "sandbox_ldo" |
||||
#define SANDBOX_OF_LDO_PREFIX "ldo" |
||||
#define SANDBOX_BUCK_DRIVER "sandbox_buck" |
||||
#define SANDBOX_OF_BUCK_PREFIX "buck" |
||||
|
||||
#define SANDBOX_BUCK_COUNT 2 |
||||
#define SANDBOX_LDO_COUNT 2 |
||||
/*
|
||||
* Sandbox PMIC registers: |
||||
* We have only 12 significant registers, but we alloc 16 for padding. |
||||
*/ |
||||
enum { |
||||
SANDBOX_PMIC_REG_BUCK1_UV = 0, |
||||
SANDBOX_PMIC_REG_BUCK1_UA, |
||||
SANDBOX_PMIC_REG_BUCK1_OM, |
||||
|
||||
SANDBOX_PMIC_REG_BUCK2_UV, |
||||
SANDBOX_PMIC_REG_BUCK2_UA, |
||||
SANDBOX_PMIC_REG_BUCK2_OM, |
||||
|
||||
SANDBOX_PMIC_REG_LDO_OFFSET, |
||||
SANDBOX_PMIC_REG_LDO1_UV = SANDBOX_PMIC_REG_LDO_OFFSET, |
||||
SANDBOX_PMIC_REG_LDO1_UA, |
||||
SANDBOX_PMIC_REG_LDO1_OM, |
||||
|
||||
SANDBOX_PMIC_REG_LDO2_UV, |
||||
SANDBOX_PMIC_REG_LDO2_UA, |
||||
SANDBOX_PMIC_REG_LDO2_OM, |
||||
|
||||
SANDBOX_PMIC_REG_COUNT = 16, |
||||
}; |
||||
|
||||
/* Register offset for output: micro Volts, micro Amps, Operation Mode */ |
||||
enum { |
||||
OUT_REG_UV = 0, |
||||
OUT_REG_UA, |
||||
OUT_REG_OM, |
||||
OUT_REG_COUNT, |
||||
}; |
||||
|
||||
/* Buck operation modes */ |
||||
enum { |
||||
BUCK_OM_OFF = 0, |
||||
BUCK_OM_ON, |
||||
BUCK_OM_PWM, |
||||
BUCK_OM_COUNT, |
||||
}; |
||||
|
||||
/* Ldo operation modes */ |
||||
enum { |
||||
LDO_OM_OFF = 0, |
||||
LDO_OM_ON, |
||||
LDO_OM_SLEEP, |
||||
LDO_OM_STANDBY, |
||||
LDO_OM_COUNT, |
||||
}; |
||||
|
||||
/* BUCK1 Voltage: min: 0.8V, step: 25mV, max 2.4V */ |
||||
#define OUT_BUCK1_UV_MIN 800000 |
||||
#define OUT_BUCK1_UV_MAX 2400000 |
||||
#define OUT_BUCK1_UV_STEP 25000 |
||||
|
||||
/* BUCK1 Amperage: min: 150mA, step: 25mA, max: 250mA */ |
||||
#define OUT_BUCK1_UA_MIN 150000 |
||||
#define OUT_BUCK1_UA_MAX 250000 |
||||
#define OUT_BUCK1_UA_STEP 25000 |
||||
|
||||
/* BUCK2 Voltage: min: 0.75V, step: 50mV, max 3.95V */ |
||||
#define OUT_BUCK2_UV_MIN 750000 |
||||
#define OUT_BUCK2_UV_MAX 3950000 |
||||
#define OUT_BUCK2_UV_STEP 50000 |
||||
|
||||
/* LDO1 Voltage: min: 0.8V, step: 25mV, max 2.4V */ |
||||
#define OUT_LDO1_UV_MIN 800000 |
||||
#define OUT_LDO1_UV_MAX 2400000 |
||||
#define OUT_LDO1_UV_STEP 25000 |
||||
|
||||
/* LDO1 Amperage: min: 100mA, step: 50mA, max: 200mA */ |
||||
#define OUT_LDO1_UA_MIN 100000 |
||||
#define OUT_LDO1_UA_MAX 200000 |
||||
#define OUT_LDO1_UA_STEP 50000 |
||||
|
||||
/* LDO2 Voltage: min: 0.75V, step: 50mV, max 3.95V */ |
||||
#define OUT_LDO2_UV_MIN 750000 |
||||
#define OUT_LDO2_UV_MAX 3950000 |
||||
#define OUT_LDO2_UV_STEP 50000 |
||||
|
||||
/* register <-> value conversion */ |
||||
#define REG2VAL(min, step, reg) ((min) + ((step) * (reg))) |
||||
#define VAL2REG(min, step, val) (((val) - (min)) / (step)) |
||||
|
||||
/* Operation mode id -> register value conversion */ |
||||
#define OM2REG(x) (x) |
||||
|
||||
#endif |
Loading…
Reference in new issue