summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorPi-Hsun Shih <pihsun@chromium.org>2020-06-16 13:47:36 +0800
committerCommit Bot <commit-bot@chromium.org>2020-08-04 09:31:12 +0000
commit7403ebaee46fa96c47b5ff112f345fb06440c355 (patch)
tree640d39a77f7508722bfd2bb4307edaa211f8d193 /driver
parent031bb67e864ba2c5d681afab5ca517606eca7203 (diff)
downloadchrome-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.c221
-rw-r--r--driver/bc12/mt6360.h39
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, &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,