diff options
Diffstat (limited to 'driver/wpc/cps8100.c')
-rw-r--r-- | driver/wpc/cps8100.c | 278 |
1 files changed, 202 insertions, 76 deletions
diff --git a/driver/wpc/cps8100.c b/driver/wpc/cps8100.c index 78aa73fbfd..acfdc06a32 100644 --- a/driver/wpc/cps8100.c +++ b/driver/wpc/cps8100.c @@ -1,4 +1,4 @@ -/* Copyright 2022 The Chromium OS Authors. All rights reserved. +/* Copyright 2022 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ @@ -18,49 +18,61 @@ /* Print additional data */ #define CPS8100_DEBUG -#define CPUTS(outstr) cputs(CC_PCHG, outstr) -#define CPRINTS(fmt, args...) cprints(CC_PCHG, "CPS8100: " fmt, ##args) -#define CPRINTFP(fmt, args...) cprintf(CC_PCHG, "CPS8100: " fmt, ##args) -#define CPRINTF(fmt, args...) cprintf(CC_PCHG, fmt, ##args) +#define CPUTS(outstr) cputs(CC_PCHG, outstr) +#define CPRINTS(fmt, args...) cprints(CC_PCHG, "CPS8100: " fmt, ##args) +#define CPRINTFP(fmt, args...) cprintf(CC_PCHG, "CPS8100: " fmt, ##args) +#define CPRINTF(fmt, args...) cprintf(CC_PCHG, fmt, ##args) /* * Configuration */ -#define CPS8100_I2C_ADDR_H 0x31 -#define CPS8100_I2C_ADDR_L 0x30 +#define CPS8100_I2C_ADDR_H 0x31 +#define CPS8100_I2C_ADDR_L 0x30 +#define CPS8200_I2C_ADDR 0x30 /* High address registers (commands?) */ -#define CPS8100_REGH_PASSWORD 0xf500 -#define CPS8100_REGH_ACCESS_MODE 0xf505 -#define CPS8100_REGH_ADDRESS 0xf503 +#define CPS8100_REGH_PASSWORD 0xf500 +#define CPS8100_REGH_ACCESS_MODE 0xf505 +#define CPS8100_REGH_ADDRESS 0xf503 -#define CPS8100_ACCESS_MODE_8 0x00 -#define CPS8100_ACCESS_MODE_16 0x01 -#define CPS8100_ACCESS_MODE_32 0x02 +#define CPS8100_ACCESS_MODE_8 0x00 +#define CPS8100_ACCESS_MODE_16 0x01 +#define CPS8100_ACCESS_MODE_32 0x02 +#define CPS8100_PASSWORD 0x19e5 +#define CPS8100_CHIPID 0x8100 +#define CPS8200_CHIPID 0x8200 + +#define CPS8200_I2C_ENABLE 0x0000000E +#define CPS8200_PASSWORD 0x00001250 /* Registers */ -#define CPS8100_REG_IC_INFO 0x20000000 -#define CPS8100_REG_FW_INFO 0x20000004 -#define CPS8100_REG_FUNC_EN 0x2000003c -#define CPS8100_REG_ALERT_INFO 0x20000158 -#define CPS8100_REG_INT_ENABLE 0x20000160 -#define CPS8100_REG_INT_FLAG 0x20000164 - -#define CPS8100_STATUS_PROFILE(r) (((r) & GENMASK(5, 4)) >> 4) -#define CPS8100_STATUS_CHARGE(r) ((r) & BIT(6)) -#define CPS8100_STATUS_DEVICE(r) ((r) & BIT(7)) -#define CPS8100_STATUS_BATTERY(r) (((r) & GENMASK(15, 8)) >> 8) -#define CPS8100_IRQ_TYPE(r) (((r) & GENMASK(23, 20)) >> 20) +#define CPS8100_REG_IC_INFO 0x20000000 +#define CPS8100_REG_FW_INFO 0x20000004 +#define CPS8100_REG_FUNC_EN 0x2000003c +#define CPS8100_REG_ALERT_INFO 0x20000158 +#define CPS8100_REG_INT_ENABLE 0x20000160 +#define CPS8100_REG_INT_FLAG 0x20000164 + +#define CPS8200_REG_I2C_ENABLE 0xFFFFFF00 +#define CPS8200_REG_PASSWORD 0x400140FC + +#define CPS8100_STATUS_PROFILE(r) (((r)&GENMASK(5, 4)) >> 4) +#define CPS8100_STATUS_CHARGE(r) ((r)&BIT(6)) +#define CPS8100_STATUS_DEVICE(r) ((r)&BIT(7)) +#define CPS8100_STATUS_BATTERY(r) (((r)&GENMASK(15, 8)) >> 8) +#define CPS8100_IRQ_TYPE(r) (((r)&GENMASK(23, 20)) >> 20) /* Status flags in ALERT_INFO register */ -#define CPS8100_STATUS_FOD BIT(0) -#define CPS8100_STATUS_OCP BIT(1) -#define CPS8100_STATUS_OVP BIT(2) -#define CPS8100_STATUS_OTP BIT(3) -#define CPS8100_STATUS_UVP BIT(16) +#define CPS8100_STATUS_FOD BIT(0) +#define CPS8100_STATUS_OCP BIT(1) +#define CPS8100_STATUS_OVP BIT(2) +#define CPS8100_STATUS_OTP BIT(3) +#define CPS8100_STATUS_UVP BIT(16) /* Buffer size for i2c read & write */ -#define CPS8100_MESSAGE_BUFFER_SIZE 0x20 +#define CPS8100_MESSAGE_BUFFER_SIZE 0x20 + +static uint32_t chip_id; /* TODO: Check datasheet how to wake up and how long it takes to wake up. */ static const int cps8100_wake_up_delay_ms = 10; @@ -81,22 +93,15 @@ struct cps8100_msg { uint8_t data[2]; } __packed; +static int (*cps8x00_read32)(int port, uint32_t reg, uint32_t *val); + /* This driver isn't compatible with big endian. */ -BUILD_ASSERT(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); - -static const char * const cps8100_func_names[] = { - [0] = "DPL", - [1] = "OPP", - [2] = "OTP", - [3] = "OVPK", - [4] = "OCP", - [5] = "UVP", - [6] = "OVP", - [7] = "FOD", - [8] = "SAMSUNG", - [9] = "APPLE", - [10] = "EPP", - [11] = "HUAWEI", +BUILD_ASSERT(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); + +static const char *const cps8100_func_names[] = { + [0] = "DPL", [1] = "OPP", [2] = "OTP", [3] = "OVPK", + [4] = "OCP", [5] = "UVP", [6] = "OVP", [7] = "FOD", + [8] = "SAMSUNG", [9] = "APPLE", [10] = "EPP", [11] = "HUAWEI", [12] = "CPS", }; @@ -114,7 +119,7 @@ enum cps8100_irq_type { CPS8100_IRQ_TYPE_COUNT }; -static const char * const cps8100_irq_type_names[] = { +static const char *const cps8100_irq_type_names[] = { [CPS8100_IRQ_TYPE_FOD] = "FOD", [CPS8100_IRQ_TYPE_OCP] = "OCP", [CPS8100_IRQ_TYPE_OVP] = "OVP", @@ -127,7 +132,7 @@ static const char * const cps8100_irq_type_names[] = { [CPS8100_IRQ_TYPE_RESET] = "RESET", }; -static const char * const cps8100_profile_names[] = { +static const char *const cps8100_profile_names[] = { [0] = "NONE", [1] = "BPP", [2] = "EPP", @@ -211,14 +216,54 @@ static int cps8100_i2c_write(int port, int addr, const uint8_t *buf, size_t len) static int cps8100_set_unlock(int port) { - uint8_t buf[4]; + const uint8_t cps8100_unlock_cmd[] = { + /* Password register address */ + (CPS8100_REGH_PASSWORD >> 8) & 0xff, + (CPS8100_REGH_PASSWORD >> 0) & 0xff, + /* Password */ + (CPS8100_PASSWORD >> 0) & 0xff, + (CPS8100_PASSWORD >> 8) & 0xff, + }; + + return cps8100_i2c_write(port, CPS8100_I2C_ADDR_H, cps8100_unlock_cmd, + 4); +} - buf[0] = 0xf5; - buf[1] = 0x00; /* Password register address */ - buf[2] = 0xe5; - buf[3] = 0x19; /* Password */ +static int cps8200_set_unlock(int port) +{ + const uint8_t cps8200_unlock_cmd[] = { + /* Password register addr */ + (CPS8200_REG_PASSWORD >> 24) & 0xff, + (CPS8200_REG_PASSWORD >> 16) & 0xff, + (CPS8200_REG_PASSWORD >> 8) & 0xff, + (CPS8200_REG_PASSWORD >> 0) & 0xff, + /* Password */ + (CPS8200_PASSWORD >> 0) & 0xff, + (CPS8200_PASSWORD >> 8) & 0xff, + (CPS8200_PASSWORD >> 16) & 0xff, + (CPS8200_PASSWORD >> 24) & 0xff, + }; + + return cps8100_i2c_write(port, CPS8200_I2C_ADDR, cps8200_unlock_cmd, 8); +} - return cps8100_i2c_write(port, CPS8100_I2C_ADDR_H, buf, 4); +static int cps8200_i2c_enable(int port) +{ + const uint8_t cps8200_i2c_enable_cmd[] = { + /* addr */ + (CPS8200_REG_I2C_ENABLE >> 24) & 0xff, + (CPS8200_REG_I2C_ENABLE >> 16) & 0xff, + (CPS8200_REG_I2C_ENABLE >> 8) & 0xff, + (CPS8200_REG_I2C_ENABLE >> 0) & 0xff, + /* data */ + (CPS8200_I2C_ENABLE >> 0) & 0xff, + (CPS8200_I2C_ENABLE >> 8) & 0xff, + (CPS8200_I2C_ENABLE >> 16) & 0xff, + (CPS8200_I2C_ENABLE >> 24) & 0xff, + }; + + return cps8100_i2c_write(port, CPS8200_I2C_ADDR, cps8200_i2c_enable_cmd, + 8); } static int cps8100_set_write_mode(int port, uint8_t mode) @@ -249,17 +294,44 @@ static int cps8100_read32(int port, uint32_t reg, uint32_t *val) { uint8_t buf[CPS8100_MESSAGE_BUFFER_SIZE]; - if (cps8100_set_unlock(port) || - cps8100_set_write_mode(port, CPS8100_ACCESS_MODE_32) || - cps8100_set_high_address(port, reg)) + if (cps8100_set_high_address(port, reg)) return EC_ERROR_UNKNOWN; /* Set low 16 bits of register address and read a byte. */ buf[0] = (reg >> 8) & 0xff; buf[1] = (reg >> 0) & 0xff; - return i2c_xfer(port, CPS8100_I2C_ADDR_L, buf, 2, - (void *)val, sizeof(*val)); + return i2c_xfer(port, CPS8100_I2C_ADDR_L, buf, 2, (void *)val, + sizeof(*val)); +} + +static int cps8200_read32(int port, uint32_t reg, uint32_t *val) +{ + uint8_t buf[4]; + + buf[0] = (reg >> 24) & 0xff; + buf[1] = (reg >> 16) & 0xff; + buf[2] = (reg >> 8) & 0xff; + buf[3] = (reg >> 0) & 0xff; + + return i2c_xfer(port, CPS8200_I2C_ADDR, buf, 4, (void *)val, + sizeof(*val)); +} + +static int cps8100_unlock(int port) +{ + int rv; + + rv = cps8100_set_unlock(port); + return rv ? rv : cps8100_set_write_mode(port, CPS8100_ACCESS_MODE_32); +} + +static int cps8200_unlock(int port) +{ + int rv; + + rv = cps8200_i2c_enable(port); + return rv ? rv : cps8200_set_unlock(port); } static int cps8100_reset(struct pchg *ctx) @@ -274,19 +346,15 @@ static int cps8100_reset(struct pchg *ctx) static int cps8100_init(struct pchg *ctx) { - uint32_t u32; int port = ctx->cfg->i2c_port; - int rv; - rv = cps8100_read32(port, CPS8100_REG_IC_INFO, &u32); - if (!rv) - CPRINTS("IC=0x%08x", u32); - - rv = cps8100_read32(port, CPS8100_REG_FW_INFO, &u32); - if (!rv) - CPRINTS("FW=0x%08x", u32); - - return EC_SUCCESS; + /* Enable I2C, unlock and set mode */; + if (chip_id == CPS8100_CHIPID) + return cps8100_unlock(port); + else if (chip_id == CPS8200_CHIPID) + return cps8200_unlock(port); + else + return EC_ERROR_UNKNOWN; } static int cps8100_enable(struct pchg *ctx, bool enable) @@ -298,7 +366,7 @@ static int cps8100_get_alert_info(struct pchg *ctx, uint32_t *reg) { int rv; - rv = cps8100_read32(ctx->cfg->i2c_port, CPS8100_REG_ALERT_INFO, reg); + rv = cps8x00_read32(ctx->cfg->i2c_port, CPS8100_REG_ALERT_INFO, reg); if (rv) { CPRINTS("Failed to get alert info (%d)", rv); return rv; @@ -307,6 +375,63 @@ static int cps8100_get_alert_info(struct pchg *ctx, uint32_t *reg) return EC_SUCCESS; } +static int cps8100_get_chip_info(struct pchg *ctx) +{ + uint32_t u32; + int port = ctx->cfg->i2c_port; + int rv = EC_ERROR_UNKNOWN; + + /* + * CPS8100 needs 100~120ms delay, CPS8200 needs 40~50ms delay + * between reset and the first access to I2C register. + */ + if (chip_id == CPS8100_CHIPID) { + /* + * already probed but unlock again in case it's turned + * off. + */ + msleep(120); + return cps8100_unlock(port); + } else if (chip_id == CPS8200_CHIPID) { + msleep(50); + return cps8200_unlock(port); + } + + /* not probed yet, need to unlock blindly first. */ + msleep(120); + if (!cps8100_unlock(port)) + rv = cps8100_read32(port, CPS8100_REG_IC_INFO, &u32); + else if (!cps8200_unlock(port)) + rv = cps8200_read32(port, CPS8100_REG_IC_INFO, &u32); + + if (rv) { + CPRINTS("Failed to read IC info!"); + return rv; + } + + /* Probe */; + CPRINTS("IC=0x%08x", u32); + if ((u32 & 0xffff) == CPS8100_CHIPID) { + cps8x00_read32 = cps8100_read32; + chip_id = CPS8100_CHIPID; + } else if ((u32 & 0xffff) == CPS8200_CHIPID) { + cps8x00_read32 = cps8200_read32; + chip_id = CPS8200_CHIPID; + } else { + CPRINTS("Unknown chip!"); + return EC_ERROR_UNKNOWN; + } + + if (!cps8x00_read32(port, CPS8100_REG_FW_INFO, &u32)) { + CPRINTS("FW=0x%08x", u32); + } else { + CPRINTS("Failed to read FW info!"); + return EC_ERROR_UNKNOWN; + } + + return EC_SUCCESS; +} + static void cps8100_print_alert_info(uint32_t reg) { cps8100_print_irq_type_names("IRQ_TYPE: ", reg); @@ -315,7 +440,8 @@ static void cps8100_print_alert_info(uint32_t reg) CPRINTFP("Profile: %s\n", cps8100_profile_names[CPS8100_STATUS_PROFILE(reg)]); CPRINTFP("%sCharging\n", CPS8100_STATUS_CHARGE(reg) ? "" : "Not "); - CPRINTFP("Device %sPresent\n", CPS8100_STATUS_DEVICE(reg) ? "":"Not "); + CPRINTFP("Device %sPresent\n", + CPS8100_STATUS_DEVICE(reg) ? "" : "Not "); CPRINTFP("Battery: %d%%\n", CPS8100_STATUS_BATTERY(reg)); } @@ -386,6 +512,7 @@ const struct pchg_drv cps8100_drv = { .reset = cps8100_reset, .init = cps8100_init, .enable = cps8100_enable, + .get_chip_info = cps8100_get_chip_info, .get_event = cps8100_get_event, .get_soc = cps8100_get_soc, .update_open = cps8100_update_open, @@ -398,7 +525,7 @@ static void cps8100_dump(struct pchg *ctx) uint32_t val; int rv; - rv = cps8100_read32(ctx->cfg->i2c_port, CPS8100_REG_FUNC_EN, &val); + rv = cps8x00_read32(ctx->cfg->i2c_port, CPS8100_REG_FUNC_EN, &val); if (rv == EC_SUCCESS) cps8100_print_func_names("FEATURES: ", val); @@ -407,7 +534,7 @@ static void cps8100_dump(struct pchg *ctx) cps8100_print_alert_info(val); } -static int cc_cps8100(int argc, char **argv) +static int cc_cps8100(int argc, const char **argv) { struct pchg *ctx; char *end; @@ -436,6 +563,5 @@ static int cc_cps8100(int argc, char **argv) return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(cps8100, cc_cps8100, - "<port> [reset]", +DECLARE_CONSOLE_COMMAND(cps8100, cc_cps8100, "<port> [reset]", "Print status of or reset CPS8100"); |