From 5d387d0df9816cdf05d7bce3b7379057068a7e58 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 13 May 2015 13:38:32 +0200 Subject: [PATCH] sandbox: add: sandbox PMIC device drivers: I2C emul, pmic, regulator 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 Acked-by: Simon Glass Tested on sandbox: Tested-by: Simon Glass --- doc/device-tree-bindings/pmic/sandbox.txt | 35 ++ .../regulator/sandbox.txt | 45 +++ drivers/power/pmic/Kconfig | 25 ++ drivers/power/pmic/Makefile | 3 +- drivers/power/pmic/i2c_pmic_emul.c | 142 +++++++ drivers/power/pmic/sandbox.c | 79 ++++ drivers/power/regulator/Kconfig | 30 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/sandbox.c | 355 ++++++++++++++++++ include/power/sandbox_pmic.h | 105 ++++++ 10 files changed, 819 insertions(+), 1 deletion(-) create mode 100644 doc/device-tree-bindings/pmic/sandbox.txt create mode 100644 doc/device-tree-bindings/regulator/sandbox.txt create mode 100644 drivers/power/pmic/i2c_pmic_emul.c create mode 100644 drivers/power/pmic/sandbox.c create mode 100644 drivers/power/regulator/sandbox.c create mode 100644 include/power/sandbox_pmic.h diff --git a/doc/device-tree-bindings/pmic/sandbox.txt b/doc/device-tree-bindings/pmic/sandbox.txt new file mode 100644 index 0000000000..d84c97717b --- /dev/null +++ b/doc/device-tree-bindings/pmic/sandbox.txt @@ -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"; + }; +}; diff --git a/doc/device-tree-bindings/regulator/sandbox.txt b/doc/device-tree-bindings/regulator/sandbox.txt new file mode 100644 index 0000000000..d70494c450 --- /dev/null +++ b/doc/device-tree-bindings/regulator/sandbox.txt @@ -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; +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index d99d9e3a06..164f42143f 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -16,3 +16,28 @@ config DM_PMIC_MAX77686 ---help--- This config enables implementation of driver-model pmic uclass features for PMIC MAX77686. The driver implements read/write operations. + +config DM_PMIC_SANDBOX + bool "Enable Driver Model for emulated Sandbox PMIC " + depends on DM_PMIC + ---help--- + Enable the driver for Sandbox PMIC emulation. The emulated PMIC device + depends on two drivers: + - sandbox PMIC I/O driver - implements dm pmic operations + - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission + + A detailed information can be found in header: '' + + The Sandbox PMIC info: + * I/O interface: + - I2C chip address: 0x40 + - first register address: 0x0 + - register count: 0x10 + * Adjustable outputs: + - 2x LDO + - 2x BUCK + - Each, with a different operating conditions (header). + * Reset values: + - set by i2c emul driver's probe() (defaults in header) + + Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 8cb993d0a7..ae86f041f3 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -6,12 +6,13 @@ # obj-$(CONFIG_DM_PMIC) += pmic-uclass.o +obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o +obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o -obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o obj-$(CONFIG_POWER_TPS65090_I2C) += pmic_tps65090.o obj-$(CONFIG_POWER_TPS65090_EC) += pmic_tps65090_ec.o diff --git a/drivers/power/pmic/i2c_pmic_emul.c b/drivers/power/pmic/i2c_pmic_emul.c new file mode 100644 index 0000000000..aeab5c949a --- /dev/null +++ b/drivers/power/pmic/i2c_pmic_emul.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +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, +}; diff --git a/drivers/power/pmic/sandbox.c b/drivers/power/pmic/sandbox.c new file mode 100644 index 0000000000..3e56acd5e2 --- /dev/null +++ b/drivers/power/pmic/sandbox.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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, +}; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index fd3cf351b1..6289b83910 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -31,3 +31,33 @@ config DM_REGULATOR_FIXED This config enables implementation of driver-model regulator uclass features for fixed value regulators. The driver implements get/set api for enable and get only for voltage value. + +config DM_REGULATOR_SANDBOX + bool "Enable Driver Model for Sandbox PMIC regulator" + depends on DM_REGULATOR && DM_PMIC_SANDBOX + ---help--- + Enable the regulator driver for emulated Sandbox PMIC. + The emulated PMIC device depends on two drivers: + - sandbox PMIC I/O driver - implements dm pmic operations + - sandbox PMIC regulator driver - implements dm regulator operations + - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission + + The regulator driver provides uclass operations for sandbox PMIC's + regulators. The driver implements get/set api for: voltage, current, + operation mode and enable state. + The driver supports LDO and BUCK regulators. + + The Sandbox PMIC info: + * I/O interface: + - I2C chip address: 0x40 + - first register address: 0x0 + - register count: 0x10 + * Adjustable outputs: + - 2x LDO + - 2x BUCK + - Each, with a different operating conditions (header). + * Reset values: + - set by i2c emul driver's probe() (defaults in header) + + A detailed information can be found in header: '' + Binding info: 'doc/device-tree-bindings/pmic/max77686.txt' diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index cc8326d78d..96aa624961 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o +obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o diff --git a/drivers/power/regulator/sandbox.c b/drivers/power/regulator/sandbox.c new file mode 100644 index 0000000000..2cca579a57 --- /dev/null +++ b/drivers/power/regulator/sandbox.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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, +}; diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h new file mode 100644 index 0000000000..f317c3ae90 --- /dev/null +++ b/include/power/sandbox_pmic.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 Samsung Electronics + * Przemyslaw Marczak + * + * 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 -- 2.30.2