summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaveh Jalali <caveh@chromium.org>2018-07-02 23:52:32 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2018-07-03 07:43:48 +0000
commita0c4eb28f611838c2c9287e9aa6c2fc3e8ac8a5a (patch)
tree4650159d9977ce4cf7b8d51b45c0cdee06dbc8d4
parente9d86819fbdd4d6d9b234403fb22ef156312f8d4 (diff)
downloadchrome-ec-a0c4eb28f611838c2c9287e9aa6c2fc3e8ac8a5a.tar.gz
atlas: tune MP2949 PMIC on boot
we need to tune some of the MP2949 PMIC parameters for stability. we can do this from the EC when we boot the AP. at this point we always check to see if the desired values are programmed and apply them if necessary. eventually, the PMIC will be pre-programmed and we won't have to do this any more. BUG=b:110890675 BRANCH=none TEST=verified settings are being applied Change-Id: Ia814a362934f9cb9eb5c28dc0588f7425b3ab79e Signed-off-by: Caveh Jalali <caveh@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1124219 Commit-Queue: Caveh Jalali <caveh@google.com> Tested-by: Caveh Jalali <caveh@google.com> Reviewed-by: Caveh Jalali <caveh@google.com> Trybot-Ready: Caveh Jalali <caveh@google.com>
-rw-r--r--board/atlas/build.mk1
-rw-r--r--board/atlas/cpu_pmic.c243
2 files changed, 244 insertions, 0 deletions
diff --git a/board/atlas/build.mk b/board/atlas/build.mk
index 77001ecfa1..ee47554e2f 100644
--- a/board/atlas/build.mk
+++ b/board/atlas/build.mk
@@ -11,6 +11,7 @@ CHIP_FAMILY:=npcx7
CHIP_VARIANT:=npcx7m6f
board-y=board.o
+board-y+=cpu_pmic.o
board-$(CONFIG_BATTERY_SMART)+=battery.o
board-$(CONFIG_LED_COMMON)+=led.o
board-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o
diff --git a/board/atlas/cpu_pmic.c b/board/atlas/cpu_pmic.c
new file mode 100644
index 0000000000..d550c1c3a8
--- /dev/null
+++ b/board/atlas/cpu_pmic.c
@@ -0,0 +1,243 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Tune the MP2949A IMVP8 parameters for atlas */
+
+#include "console.h"
+#include "hooks.h"
+#include "i2c.h"
+#include "timer.h"
+#include "util.h"
+
+#define MP2949_PAGE 0x00
+#define MP2949_STORE_USER_ALL 0x15
+#define MP2949_RESTORE_USER_ALL 0x16
+#define MP2949_MFR_VOUT_TRIM 0x22
+#define MP2949_STATUS_CML 0x7e
+
+#define MP2949_PRODUCT_REV_DATA 0xc0
+#define MP2949_CODE_REV_PROTOCOL_RB_RA 0xc1
+#define MP2949_MINOFF_BLANK_TIME 0xce
+
+#define MP2949_MFR_SLOPE_CNT_1P 0xdb
+#define MP2949_MFR_TRIM_2_1_DCM 0xde
+
+#define MP2949_MFR_FS_VBOOT 0xe5
+#define MP2949_VFB_TRIM_DCLL 0xe7
+#define MP2949_MFR_OCP_SET_LEVEL 0xee
+#define MP2949_OC_LIMIT_ICC_MAX 0xef
+
+static int mp2949_write8(uint8_t reg, uint8_t value)
+{
+ uint8_t buf[2] = { reg, value };
+
+ return i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ buf, sizeof(buf), NULL, 0, I2C_XFER_SINGLE);
+}
+
+static int mp2949_read8(uint8_t reg, uint8_t *value)
+{
+ uint8_t buf[2] = { reg };
+ int status;
+
+ status = i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ buf, 1, buf + 1, 1, I2C_XFER_SINGLE);
+ if (status != EC_SUCCESS)
+ return status;
+ *value = buf[1];
+ return EC_SUCCESS;
+}
+
+static void mp2949_read16(uint8_t reg, uint16_t *value)
+{
+ uint8_t buf[3] = { reg };
+
+ i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ buf, 1, buf + 1, 2, I2C_XFER_SINGLE);
+ *value = (buf[2] << 8) | buf[1];
+}
+
+static void mp2949_write16(uint8_t reg, uint16_t value)
+{
+ uint8_t buf[3] = { reg, value & 0xff, value >> 8 };
+
+ i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ buf, sizeof(buf), NULL, 0, I2C_XFER_SINGLE);
+}
+
+struct reg_val16 {
+ uint8_t reg;
+ uint16_t val;
+};
+
+static int mp2949_select_page(uint8_t page)
+{
+ int status;
+
+ if (page > 2)
+ return EC_ERROR_INVAL;
+
+ status = mp2949_write8(MP2949_PAGE, page);
+ if (status != EC_SUCCESS) {
+ ccprintf("%s: could not select page 0x%02x, error %d\n",
+ __func__, page, status);
+ }
+ return status;
+}
+
+static void mp2949_write_vec16(const struct reg_val16 *init_list, int count,
+ int *delta)
+{
+ const struct reg_val16 *reg_val;
+ uint16_t oval;
+ int i;
+
+ reg_val = init_list;
+ for (i = 0; i < count; ++i, ++reg_val) {
+ mp2949_read16(reg_val->reg, &oval);
+ if (oval == reg_val->val) {
+ ccprintf("mp2949: reg 0x%02x already 0x%04x\n",
+ reg_val->reg, oval);
+ continue;
+ }
+ ccprintf("mp2949: tuning reg 0x%02x from 0x%04x to %04x\n",
+ reg_val->reg, oval, reg_val->val);
+ mp2949_write16(reg_val->reg, reg_val->val);
+ *delta += 1;
+ }
+}
+
+static int mp2949_store_user_all(void)
+{
+ const uint8_t wr = MP2949_STORE_USER_ALL;
+ const uint8_t rd = MP2949_RESTORE_USER_ALL;
+ int status;
+ uint8_t val;
+
+ ccprintf("%s: updating persistent settings\n", __func__);
+
+ status = i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ &wr, sizeof(wr), NULL, 0, I2C_XFER_SINGLE);
+ if (status != EC_SUCCESS)
+ return status;
+ usleep(1000 * MSEC);
+
+ status = i2c_xfer(I2C_PORT_POWER, I2C_ADDR_MP2949,
+ &rd, sizeof(rd), NULL, 0, I2C_XFER_SINGLE);
+ if (status != EC_SUCCESS)
+ return status;
+ usleep(2 * MSEC);
+
+ mp2949_select_page(0);
+ status = mp2949_read8(MP2949_STATUS_CML, &val);
+ if ((val & 0x09) != 0) {
+ ccprintf("%s: store seems to have failed with status %02x\n",
+ __func__, val);
+ return status;
+ }
+ return EC_SUCCESS;
+}
+
+static void board_patch_rail1(int *delta)
+{
+ const static struct reg_val16 rail1[] = {
+ { MP2949_MFR_VOUT_TRIM, 0x0012 },
+ { MP2949_PRODUCT_REV_DATA, 0x0053 },
+ { MP2949_CODE_REV_PROTOCOL_RB_RA, 0x0155 },
+ { MP2949_MINOFF_BLANK_TIME, 0xc28a },
+ { MP2949_MFR_SLOPE_CNT_1P, 0x008c },
+ { MP2949_MFR_TRIM_2_1_DCM, 0x20c7 },
+ { MP2949_MFR_FS_VBOOT, 0x1400 },
+ };
+
+ if (mp2949_select_page(0x00) != EC_SUCCESS)
+ return;
+ mp2949_write_vec16(rail1, ARRAY_SIZE(rail1), delta);
+}
+
+static void board_patch_rail2(int *delta)
+{
+ const static struct reg_val16 rail2[] = {
+ { MP2949_MFR_VOUT_TRIM, 0x00ff },
+ { MP2949_MFR_TRIM_2_1_DCM, 0x20c7 },
+ { MP2949_VFB_TRIM_DCLL, 0x0028 },
+ { MP2949_MFR_OCP_SET_LEVEL, 0x0024 },
+ { MP2949_OC_LIMIT_ICC_MAX, 0xb11c },
+ };
+
+ if (mp2949_select_page(0x01) != EC_SUCCESS)
+ return;
+ mp2949_write_vec16(rail2, ARRAY_SIZE(rail2), delta);
+}
+
+static void board_patch_rail3(int *delta)
+{
+ const static struct reg_val16 rail3[] = {
+ { MP2949_MFR_VOUT_TRIM, 0x00ff },
+ { MP2949_MFR_TRIM_2_1_DCM, 0x00c7 },
+ };
+
+ if (mp2949_select_page(0x02) != EC_SUCCESS)
+ return;
+ mp2949_write_vec16(rail3, ARRAY_SIZE(rail3), delta);
+}
+
+/*
+ * 1 ms - page0 fails
+ * 10 ms - OK
+ * 50 ms - OK
+ * 100 ms - OK
+ * 500 ms - OK
+ * 1000 ms - OK
+ * 2000 ms - watchdog
+ */
+
+static void mp2949_on_startup(void)
+{
+ int delta = 0;
+ int status;
+
+ ccprintf("%s: attempting to tune PMIC\n", __func__);
+ udelay(50 * MSEC);
+ i2c_lock(I2C_PORT_POWER, 1);
+ board_patch_rail1(&delta);
+ board_patch_rail2(&delta);
+ board_patch_rail3(&delta);
+ if (delta > 0) {
+ status = mp2949_store_user_all();
+ if (status != EC_SUCCESS)
+ ccprintf("%s: could not store settings\n", __func__);
+ }
+ i2c_lock(I2C_PORT_POWER, 0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, mp2949_on_startup,
+ HOOK_PRIO_FIRST);
+
+/*
+ * these are the before/after tuning parameters
+ *
+ * mp2949_on_startup: attempting to tune PMIC
+ *
+ * mp2949_write8: returning on count 0
+ * mp2949: tuning reg 0x22 from 0x0000 to 0012
+ * mp2949: tuning reg 0xc0 from 0x0050 to 0053
+ * mp2949: tuning reg 0xc1 from 0x0855 to 0155
+ * mp2949: tuning reg 0xce from 0xc64a to c28a
+ * mp2949: tuning reg 0xdb from 0x00aa to 008c
+ * mp2949: tuning reg 0xde from 0x2128 to 20c7
+ * mp2949: tuning reg 0xe5 from 0x1000 to 1400
+ *
+ * mp2949_write8: returning on count 0
+ * mp2949: tuning reg 0x22 from 0x0011 to 00ff
+ * mp2949: tuning reg 0xde from 0x2107 to 20c7
+ * mp2949: tuning reg 0xe7 from 0x2230 to 0028
+ * mp2949: tuning reg 0xee from 0x001d to 0024
+ * mp2949: tuning reg 0xef from 0xab18 to b11c
+ *
+ * mp2949_write8: returning on count 0
+ * mp2949: tuning reg 0x22 from 0x00ee to 00ff
+ * mp2949: tuning reg 0xde from 0x0128 to 00c7
+ *
+ */