diff options
author | Pi-Hsun Shih <pihsun@chromium.org> | 2020-06-16 13:47:36 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-08-04 09:31:12 +0000 |
commit | 7403ebaee46fa96c47b5ff112f345fb06440c355 (patch) | |
tree | 640d39a77f7508722bfd2bb4307edaa211f8d193 /driver | |
parent | 031bb67e864ba2c5d681afab5ca517606eca7203 (diff) | |
download | chrome-ec-7403ebaee46fa96c47b5ff112f345fb06440c355.tar.gz |
driver/bc12: mt6360: Add ldo regulator controls.
BUG=b:149274957
TEST=build
BRANCH=none
Change-Id: I557d189ab8389f539aa967be3aaea32dd604a784
Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2247432
Reviewed-by: Eric Yilun Lin <yllin@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r-- | driver/bc12/mt6360.c | 221 | ||||
-rw-r--r-- | driver/bc12/mt6360.h | 39 |
2 files changed, 260 insertions, 0 deletions
diff --git a/driver/bc12/mt6360.c b/driver/bc12/mt6360.c index 418d3f9b61..ed4029cb63 100644 --- a/driver/bc12/mt6360.c +++ b/driver/bc12/mt6360.c @@ -6,7 +6,9 @@ #include "charger.h" #include "charge_manager.h" #include "console.h" +#include "crc8.h" #include "driver/bc12/mt6360.h" +#include "ec_commands.h" #include "hooks.h" #include "i2c.h" #include "task.h" @@ -161,6 +163,225 @@ static void mt6360_usb_charger_task(const int port) } } +/* LDO */ +static int mt6360_ldo_write8(int reg, int val) +{ + /* + * Note: The checksum from I2C_FLAG_PEC happens to be correct because + * length == 1 -> the high 3 bits of the offset byte is 0. + */ + return i2c_write8(mt6360_config.i2c_port, + MT6360_LDO_SLAVE_ADDR_FLAGS | I2C_FLAG_PEC, reg, val); +} + +static int mt6360_ldo_read8(int reg, int *val) +{ + int rv; + uint8_t crc = 0, real_crc; + uint8_t addr = MT6360_LDO_SLAVE_ADDR_FLAGS; + uint8_t out[3] = {(addr << 1) | 1, reg}; + + rv = i2c_read16(mt6360_config.i2c_port, addr, reg, val); + if (rv) + return rv; + + real_crc = (*val >> 8) & 0xFF; + *val &= 0xFF; + out[2] = *val; + crc = crc8(out, ARRAY_SIZE(out)); + + if (crc != real_crc) + return EC_ERROR_CRC; + + return EC_SUCCESS; +} + +static int mt6360_ldo_update_bits(int reg, int mask, int val) +{ + int rv; + int reg_val = 0; + + rv = mt6360_ldo_read8(reg, ®_val); + if (rv) + return rv; + reg_val &= ~mask; + reg_val |= (mask & val); + rv = mt6360_ldo_write8(reg, reg_val); + return rv; +} + +struct mt6360_ldo_data { + const char *name; + const uint16_t *vosel_table; + uint16_t vosel_table_len; + uint8_t reg_en_ctrl2; + uint8_t reg_ctrl3; + uint8_t mask_vosel; + uint8_t shift_vosel; + uint8_t mask_vocal; +}; + +static const uint16_t MT6360_LDO3_VOSEL_TABLE[16] = { + [0x4] = 1800, + [0xA] = 2900, + [0xB] = 3000, + [0xD] = 3300, +}; + +static const uint16_t MT6360_LDO5_VOSEL_TABLE[8] = { + [0x2] = 2900, + [0x3] = 3000, + [0x5] = 3300, +}; + +static const struct mt6360_ldo_data ldo_data[MT6360_LDO_COUNT] = { + [MT6360_LDO3] = { + .name = "mt6360_ldo3", + .vosel_table = MT6360_LDO3_VOSEL_TABLE, + .vosel_table_len = ARRAY_SIZE(MT6360_LDO3_VOSEL_TABLE), + .reg_en_ctrl2 = MT6360_REG_LDO3_EN_CTRL2, + .reg_ctrl3 = MT6360_REG_LDO3_CTRL3, + .mask_vosel = MT6360_MASK_LDO3_VOSEL, + .shift_vosel = MT6360_MASK_LDO3_VOSEL_SHIFT, + .mask_vocal = MT6360_MASK_LDO3_VOCAL, + }, + [MT6360_LDO5] = { + .name = "mt6360_ldo5", + .vosel_table = MT6360_LDO5_VOSEL_TABLE, + .vosel_table_len = ARRAY_SIZE(MT6360_LDO5_VOSEL_TABLE), + .reg_en_ctrl2 = MT6360_REG_LDO5_EN_CTRL2, + .reg_ctrl3 = MT6360_REG_LDO5_CTRL3, + .mask_vosel = MT6360_MASK_LDO5_VOSEL, + .shift_vosel = MT6360_MASK_LDO5_VOSEL_SHIFT, + .mask_vocal = MT6360_MASK_LDO5_VOCAL, + }, +}; + +int mt6360_ldo_get_info(enum mt6360_ldo_id ldo_id, char *name, + uint16_t *num_voltages, uint16_t *voltages_mv) +{ + int i; + int cnt = 0; + const struct mt6360_ldo_data *data; + + if (ldo_id >= MT6360_LDO_COUNT) + return EC_ERROR_INVAL; + data = &ldo_data[ldo_id]; + + strzcpy(name, data->name, EC_REGULATOR_NAME_MAX_LEN); + for (i = 0; i < data->vosel_table_len; i++) { + int mv = data->vosel_table[i]; + + if (!mv) + continue; + if (cnt < EC_REGULATOR_VOLTAGE_MAX_COUNT) + voltages_mv[cnt++] = mv; + else + CPRINTS("LDO3 Voltage info overflow: %d", mv); + } + *num_voltages = cnt; + return EC_SUCCESS; +} + +int mt6360_ldo_enable(enum mt6360_ldo_id ldo_id, uint8_t enable) +{ + const struct mt6360_ldo_data *data; + + if (ldo_id >= MT6360_LDO_COUNT) + return EC_ERROR_INVAL; + data = &ldo_data[ldo_id]; + + if (enable) + return mt6360_ldo_update_bits( + data->reg_en_ctrl2, + MT6360_MASK_LDO_SW_OP_EN | MT6360_MASK_LDO_SW_EN, + MT6360_MASK_LDO_SW_OP_EN | MT6360_MASK_LDO_SW_EN); + else + return mt6360_ldo_update_bits( + data->reg_en_ctrl2, + MT6360_MASK_LDO_SW_OP_EN | MT6360_MASK_LDO_SW_EN, + MT6360_MASK_LDO_SW_OP_EN); +} + +int mt6360_ldo_is_enabled(enum mt6360_ldo_id ldo_id, uint8_t *enabled) +{ + int rv; + int value; + const struct mt6360_ldo_data *data; + + if (ldo_id >= MT6360_LDO_COUNT) + return EC_ERROR_INVAL; + data = &ldo_data[ldo_id]; + + rv = mt6360_ldo_read8(data->reg_en_ctrl2, &value); + if (rv) { + CPRINTS("Error reading LDO3 enabled: %d", rv); + return rv; + } + *enabled = !!(value & MT6360_MASK_LDO_SW_EN); + return EC_SUCCESS; +} + +int mt6360_ldo_set_voltage(enum mt6360_ldo_id ldo_id, int min_mv, int max_mv) +{ + int i; + const struct mt6360_ldo_data *data; + + if (ldo_id >= MT6360_LDO_COUNT) + return EC_ERROR_INVAL; + data = &ldo_data[ldo_id]; + + for (i = 0; i < data->vosel_table_len; i++) { + int mv = data->vosel_table[i]; + int step; + + if (!mv) + continue; + if (mv + MT6360_LDO_VOCAL_STEP_MV * MT6360_LDO_VOCAL_MAX_STEP < + min_mv) + continue; + mv = DIV_ROUND_UP(mv, MT6360_LDO_VOCAL_STEP_MV) * + MT6360_LDO_VOCAL_STEP_MV; + if (mv > max_mv) + continue; + step = (mv - data->vosel_table[i]) / MT6360_LDO_VOCAL_STEP_MV; + + return mt6360_ldo_update_bits( + data->reg_ctrl3, + data->mask_vosel | data->mask_vocal, + (i << data->shift_vosel) | step); + } + CPRINTS("LDO3 voltage %d - %d out of range", min_mv, max_mv); + return EC_ERROR_INVAL; +} + +int mt6360_ldo_get_voltage(enum mt6360_ldo_id ldo_id, int *voltage_mv) +{ + int value; + int rv; + const struct mt6360_ldo_data *data; + + if (ldo_id >= MT6360_LDO_COUNT) + return EC_ERROR_INVAL; + data = &ldo_data[ldo_id]; + + rv = mt6360_ldo_read8(data->reg_ctrl3, &value); + if (rv) { + CPRINTS("Error reading LDO3 ctrl3: %d", rv); + return rv; + } + *voltage_mv = data->vosel_table[(value & data->mask_vosel) >> + data->shift_vosel]; + if (*voltage_mv == 0) { + CPRINTS("Unknown LDO3 voltage value: %d", value); + return EC_ERROR_INVAL; + } + *voltage_mv += + MIN(MT6360_LDO_VOCAL_MAX_STEP, value & data->mask_vocal) * + MT6360_LDO_VOCAL_STEP_MV; + return EC_SUCCESS; +} + /* RGB LED */ int mt6360_led_enable(enum mt6360_led_id led_id, int enable) { diff --git a/driver/bc12/mt6360.h b/driver/bc12/mt6360.h index 330b85337f..aa370b4e48 100644 --- a/driver/bc12/mt6360.h +++ b/driver/bc12/mt6360.h @@ -34,6 +34,45 @@ #define MT6360_REG_DPDM_MASK1 0xF6 #define MT6360_REG_DPDM_MASK1_CHGDET_DONEI_M BIT(0) +#define MT6360_REG_LDO3_EN_CTRL2 0x05 + +#define MT6360_REG_LDO3_CTRL3 0x09 +#define MT6360_MASK_LDO3_VOSEL 0xF0 +#define MT6360_MASK_LDO3_VOSEL_SHIFT 4 +#define MT6360_MASK_LDO3_VOCAL 0x0F + +#define MT6360_REG_LDO5_EN_CTRL2 0x0B + +#define MT6360_REG_LDO5_CTRL3 0x0F +#define MT6360_MASK_LDO5_VOSEL 0x70 +#define MT6360_MASK_LDO5_VOSEL_SHIFT 4 +#define MT6360_MASK_LDO5_VOCAL 0x0F + +/* This is same for LDO{3,5,2,1}_EN_CTRL2 */ +#define MT6360_MASK_LDO_SW_OP_EN BIT(7) +#define MT6360_MASK_LDO_SW_EN BIT(6) + +#define MT6360_LDO_VOCAL_STEP_MV 10 +#define MT6360_LDO_VOCAL_MAX_STEP 10 + +enum mt6360_ldo_id { + MT6360_LDO3, + MT6360_LDO5, + + MT6360_LDO_COUNT, +}; + +int mt6360_ldo_get_info(enum mt6360_ldo_id ldo_id, char *name, + uint16_t *voltage_count, uint16_t *voltages_mv); + +int mt6360_ldo_enable(enum mt6360_ldo_id ldo_id, uint8_t enable); + +int mt6360_ldo_is_enabled(enum mt6360_ldo_id ldo_id, uint8_t *enabled); + +int mt6360_ldo_set_voltage(enum mt6360_ldo_id ldo_id, int min_mv, int max_mv); + +int mt6360_ldo_get_voltage(enum mt6360_ldo_id ldo_id, int *voltage_mv); + enum mt6360_led_id { MT6360_LED_RGB1, MT6360_LED_RGB2, |