diff options
author | Caveh Jalali <caveh@chromium.org> | 2021-04-14 23:06:59 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-04-16 00:12:36 +0000 |
commit | 93c4ac5f985709cec95545e73c52267e2a90a731 (patch) | |
tree | 703249ba65352a3f68a2e96ac8657ed592277849 /driver/mp2964.c | |
parent | bb5ea6072edc23b4e258bc19a90881a4c88290fc (diff) | |
download | chrome-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.c | 152 |
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; +} |