summaryrefslogtreecommitdiff
path: root/driver/mp2964.c
diff options
context:
space:
mode:
authorCaveh Jalali <caveh@chromium.org>2021-04-14 23:06:59 -0700
committerCommit Bot <commit-bot@chromium.org>2021-04-16 00:12:36 +0000
commit93c4ac5f985709cec95545e73c52267e2a90a731 (patch)
tree703249ba65352a3f68a2e96ac8657ed592277849 /driver/mp2964.c
parentbb5ea6072edc23b4e258bc19a90881a4c88290fc (diff)
downloadchrome-ec-93c4ac5f985709cec95545e73c52267e2a90a731.tar.gz
mp2964: Add PMIC driver
This adds a driver for the mp2964 IMVP9.1 PMIC. This driver enables programming control registers within the PMIC. BRANCH=none BUG=b:185424011 TEST=buildall passes. functional testing was done with the follow-on patch. Change-Id: I9b2d89007b5f6933ceeb9a1bcd3bbefb819888a3 Signed-off-by: Caveh Jalali <caveh@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2827950 Reviewed-by: Keith Short <keithshort@chromium.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Commit-Queue: Keith Short <keithshort@chromium.org>
Diffstat (limited to 'driver/mp2964.c')
-rw-r--r--driver/mp2964.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/driver/mp2964.c b/driver/mp2964.c
new file mode 100644
index 0000000000..21a23a8f4c
--- /dev/null
+++ b/driver/mp2964.c
@@ -0,0 +1,152 @@
+/* Copyright 2021 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.
+ */
+
+/* Driver for tuning the MP2964 IMVP8 - IMVP9.1 parameters */
+
+#include "console.h"
+#include "i2c.h"
+#include "mp2964.h"
+#include "timer.h"
+#include "util.h"
+
+#define MP2964_STARTUP_WAIT_US (50 * MSEC)
+#define MP2964_STORE_WAIT_US (300 * MSEC)
+#define MP2964_RESTORE_WAIT_US (2 * MSEC)
+
+enum reg_page {
+ REG_PAGE_0,
+ REG_PAGE_1,
+ REG_PAGE_COUNT
+};
+
+static int mp2964_write8(uint8_t reg, uint8_t value)
+{
+ const uint8_t tx[2] = { reg, value };
+
+ return i2c_xfer_unlocked(I2C_PORT_MP2964, I2C_ADDR_MP2964_FLAGS,
+ tx, sizeof(tx), NULL, 0, I2C_XFER_SINGLE);
+}
+
+static void mp2964_read16(uint8_t reg, uint16_t *value)
+{
+ const uint8_t tx[1] = { reg };
+ uint8_t rx[2];
+
+ i2c_xfer_unlocked(I2C_PORT_MP2964, I2C_ADDR_MP2964_FLAGS,
+ tx, sizeof(tx), rx, sizeof(rx), I2C_XFER_SINGLE);
+ *value = (rx[1] << 8) | rx[0];
+}
+
+static void mp2964_write16(uint8_t reg, uint16_t value)
+{
+ const uint8_t tx[3] = { reg, value & 0xff, value >> 8 };
+
+ i2c_xfer_unlocked(I2C_PORT_MP2964, I2C_ADDR_MP2964_FLAGS,
+ tx, sizeof(tx), NULL, 0, I2C_XFER_SINGLE);
+}
+
+static int mp2964_select_page(enum reg_page page)
+{
+ int status;
+
+ if (page >= REG_PAGE_COUNT)
+ return EC_ERROR_INVAL;
+
+ status = mp2964_write8(MP2964_PAGE, page);
+ if (status != EC_SUCCESS) {
+ ccprintf("%s: could not select page 0x%02x, error %d\n",
+ __func__, page, status);
+ }
+ return status;
+}
+
+static void mp2964_write_vec16(const struct mp2964_reg_val *init_list,
+ int count, int *delta)
+{
+ const struct mp2964_reg_val *reg_val;
+ uint16_t outval;
+ int i;
+
+ reg_val = init_list;
+ for (i = 0; i < count; ++i, ++reg_val) {
+ mp2964_read16(reg_val->reg, &outval);
+ if (outval == reg_val->val) {
+ ccprintf("mp2964: reg 0x%02x already 0x%04x\n",
+ reg_val->reg, outval);
+ continue;
+ }
+ ccprintf("mp2964: tuning reg 0x%02x from 0x%04x to 0x%04x\n",
+ reg_val->reg, outval, reg_val->val);
+ mp2964_write16(reg_val->reg, reg_val->val);
+ *delta += 1;
+ }
+}
+
+static int mp2964_store_user_all(void)
+{
+ const uint8_t wr = MP2964_STORE_USER_ALL;
+ const uint8_t rd = MP2964_RESTORE_USER_ALL;
+ int status;
+
+ ccprintf("%s: updating persistent settings\n", __func__);
+
+ status = i2c_xfer_unlocked(I2C_PORT_MP2964, I2C_ADDR_MP2964_FLAGS,
+ &wr, sizeof(wr), NULL, 0, I2C_XFER_SINGLE);
+ if (status != EC_SUCCESS)
+ return status;
+
+ usleep(MP2964_STORE_WAIT_US);
+
+ status = i2c_xfer_unlocked(I2C_PORT_MP2964, I2C_ADDR_MP2964_FLAGS,
+ &rd, sizeof(rd), NULL, 0, I2C_XFER_SINGLE);
+ if (status != EC_SUCCESS)
+ return status;
+
+ usleep(MP2964_RESTORE_WAIT_US);
+
+ return EC_SUCCESS;
+}
+
+static void mp2964_patch_rail(enum reg_page page,
+ const struct mp2964_reg_val *page_vals,
+ int count,
+ int *delta)
+{
+ if (mp2964_select_page(page) != EC_SUCCESS)
+ return;
+ mp2964_write_vec16(page_vals, count, delta);
+}
+
+int mp2964_tune(const struct mp2964_reg_val *rail_a, int count_a,
+ const struct mp2964_reg_val *rail_b, int count_b)
+{
+ int tries = 2;
+ int delta;
+
+ udelay(MP2964_STARTUP_WAIT_US);
+
+ i2c_lock(I2C_PORT_MP2964, 1);
+
+ do {
+ int status;
+
+ delta = 0;
+ mp2964_patch_rail(REG_PAGE_0, rail_a, count_a, &delta);
+ mp2964_patch_rail(REG_PAGE_1, rail_b, count_b, &delta);
+ if (delta == 0)
+ break;
+
+ status = mp2964_store_user_all();
+ if (status != EC_SUCCESS)
+ ccprintf("%s: STORE_USER_ALL failed\n", __func__);
+ } while (--tries > 0);
+
+ i2c_lock(I2C_PORT_MP2964, 0);
+
+ if (delta)
+ return EC_ERROR_UNKNOWN;
+ else
+ return EC_SUCCESS;
+}