summaryrefslogtreecommitdiff
path: root/driver/wpc/cps8100.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/wpc/cps8100.c')
-rw-r--r--driver/wpc/cps8100.c278
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");