summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/flapjack/board.c35
-rw-r--r--board/flapjack/board.h7
-rw-r--r--board/flapjack/ec.tasklist3
-rw-r--r--board/flapjack/gpio.inc5
-rw-r--r--common/charge_manager.c23
-rw-r--r--common/usb_charger.c9
-rw-r--r--driver/build.mk3
-rw-r--r--driver/charger/rt946x.c29
-rw-r--r--driver/wpc/p9221.c802
-rw-r--r--driver/wpc/p9221.h323
-rw-r--r--include/charge_manager.h5
11 files changed, 1227 insertions, 17 deletions
diff --git a/board/flapjack/board.c b/board/flapjack/board.c
index f05616f853..dd0f1bebe3 100644
--- a/board/flapjack/board.c
+++ b/board/flapjack/board.c
@@ -46,6 +46,8 @@
#include "usb_mux.h"
#include "usb_pd_tcpm.h"
#include "util.h"
+#include "driver/wpc/p9221.h"
+
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
@@ -273,8 +275,13 @@ int board_set_active_charge_port(int charge_port)
switch (charge_port) {
case 0:
- /* Don't charge from a source port */
+ /* Don't charge from a source port except wireless charging*/
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ if (board_vbus_source_enabled(charge_port)
+ && !wpc_chip_is_online())
+#else
if (board_vbus_source_enabled(charge_port))
+#endif
return -1;
break;
case CHARGE_PORT_NONE:
@@ -302,14 +309,7 @@ void board_set_charge_limit(int port, int supplier, int charge_ma,
int extpower_is_present(void)
{
- /*
- * The charger will indicate VBUS presence if we're sourcing 5V,
- * so exclude such ports.
- */
- if (board_vbus_source_enabled(0))
- return 0;
- else
- return tcpm_get_vbus_level(0);
+ return tcpm_get_vbus_level(0);
}
int pd_snk_is_vbus_provided(int port)
@@ -355,6 +355,10 @@ static void board_init(void)
gpio_enable_interrupt(GPIO_CHARGER_INT_ODL);
#ifdef SECTION_IS_RW
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ /* Enable Wireless charger interrupts */
+ gpio_enable_interrupt(GPIO_P9221_INT_ODL);
+#endif
/* Enable interrupts from BMI160 sensor. */
gpio_enable_interrupt(GPIO_ACCEL_INT_ODL);
@@ -495,3 +499,16 @@ int board_allow_i2c_passthru(int port)
void usb_charger_set_switches(int port, enum usb_switch setting)
{
}
+
+int board_get_fod(uint8_t **fod)
+{
+ *fod = NULL;
+ return 0;
+}
+
+int board_get_epp_fod(uint8_t **fod)
+{
+ *fod = NULL;
+ return 0;
+}
+
diff --git a/board/flapjack/board.h b/board/flapjack/board.h
index 6884f309a2..e31c8d3a77 100644
--- a/board/flapjack/board.h
+++ b/board/flapjack/board.h
@@ -110,6 +110,12 @@
#define CONFIG_ACCEL_FIFO 256
#define CONFIG_ACCEL_FIFO_THRES (CONFIG_ACCEL_FIFO / 3)
+/* Wireless Power Charger Config */
+#ifdef SECTION_IS_RW
+#define CONFIG_WIRELESS_CHARGER_P9221_R7
+#endif
+
+
/* USB PD config */
#define CONFIG_CHARGE_MANAGER
#define CONFIG_USB_POWER_DELIVERY
@@ -203,6 +209,7 @@
/* I2C ports */
#define I2C_PORT_CHARGER 0
#define I2C_PORT_TCPC0 0
+#define I2C_PORT_WPC 0
#define I2C_PORT_BATTERY 1
#define I2C_PORT_VIRTUAL_BATTERY I2C_PORT_BATTERY
#define I2C_PORT_ACCEL 1
diff --git a/board/flapjack/ec.tasklist b/board/flapjack/ec.tasklist
index f265649f3e..6000ece4b5 100644
--- a/board/flapjack/ec.tasklist
+++ b/board/flapjack/ec.tasklist
@@ -17,5 +17,6 @@
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
TASK_ALWAYS(PD_C0, pd_task, NULL, VENTI_TASK_STACK_SIZE) \
TASK_ALWAYS(PD_INT_C0, pd_interrupt_handler_task, 0, TASK_STACK_SIZE) \
- TASK_ALWAYS_RO(EMMC, emmc_task, NULL, LARGER_TASK_STACK_SIZE)
+ TASK_ALWAYS_RO(EMMC, emmc_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS_RW(WPC,wireless_power_charger_task,NULL,LARGER_TASK_STACK_SIZE)
diff --git a/board/flapjack/gpio.inc b/board/flapjack/gpio.inc
index 3ff868d09f..309c36c714 100644
--- a/board/flapjack/gpio.inc
+++ b/board/flapjack/gpio.inc
@@ -43,9 +43,8 @@ GPIO_INT(LID_OPEN, PIN(C, 5), GPIO_INT_BOTH,
lid_interrupt) /* HALL_INT_L */
GPIO_INT(GAUGE_INT_ODL, PIN(C, 9), GPIO_INT_FALLING | GPIO_PULL_UP,
gauge_interrupt)
-
-/* Interrupts not implemented yet */
-GPIO(P9221_INT_ODL, PIN(A, 6), GPIO_INPUT)
+GPIO_INT_RW(P9221_INT_ODL, PIN(A, 6), GPIO_INT_FALLING,
+ p9221_interrupt)
/* Voltage rails control pins */
diff --git a/common/charge_manager.c b/common/charge_manager.c
index 804376f528..cdc3bc228b 100644
--- a/common/charge_manager.c
+++ b/common/charge_manager.c
@@ -42,8 +42,14 @@ test_mockable const int supplier_priority[] = {
[CHARGE_SUPPLIER_BC12_CDP] = 4,
[CHARGE_SUPPLIER_BC12_SDP] = 5,
[CHARGE_SUPPLIER_OTHER] = 6,
- [CHARGE_SUPPLIER_VBUS] = 7
+ [CHARGE_SUPPLIER_VBUS] = 7,
#endif
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ [CHARGE_SUPPLIER_WPC_BPP] = 6,
+ [CHARGE_SUPPLIER_WPC_EPP] = 6,
+ [CHARGE_SUPPLIER_WPC_GPP] = 6,
+#endif
+
};
BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT);
@@ -333,13 +339,28 @@ static void charge_manager_fill_power_info(int port,
r->type = USB_CHG_TYPE_VBUS;
break;
#endif
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ /*
+ * Todo:need kernel add wpc device node in power_supply
+ * before that use USB_CHG_TYPE_PROPRIETARY to present WPC.
+ */
+ case CHARGE_SUPPLIER_WPC_BPP:
+ case CHARGE_SUPPLIER_WPC_EPP:
+ case CHARGE_SUPPLIER_WPC_GPP:
+ r->type = USB_CHG_TYPE_PROPRIETARY;
+ break;
+#endif
#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
case CHARGE_SUPPLIER_DEDICATED:
r->type = USB_CHG_TYPE_DEDICATED;
break;
#endif
default:
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ r->type = USB_CHG_TYPE_VBUS;
+#else
r->type = USB_CHG_TYPE_OTHER;
+#endif
}
r->meas.voltage_max = available_charge[sup][port].voltage;
diff --git a/common/usb_charger.c b/common/usb_charger.c
index 8b1c39647f..4d0d615a9b 100644
--- a/common/usb_charger.c
+++ b/common/usb_charger.c
@@ -97,6 +97,15 @@ void usb_charger_reset_charge(int port)
charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED,
port, NULL);
#endif
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ charge_manager_update_charge(CHARGE_SUPPLIER_WPC_BPP,
+ port, NULL);
+ charge_manager_update_charge(CHARGE_SUPPLIER_WPC_EPP,
+ port, NULL);
+ charge_manager_update_charge(CHARGE_SUPPLIER_WPC_GPP,
+ port, NULL);
+#endif
+
}
static void usb_charger_init(void)
diff --git a/driver/build.mk b/driver/build.mk
index 714fae0449..40ff37fc13 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -140,3 +140,6 @@ driver-$(CONFIG_USBC_PPC_NX20P3483)+=ppc/nx20p348x.o
# video converters
driver-$(CONFIG_MCDP28X0)+=mcdp28x0.o
+
+# Wireless Power Chargers
+driver-$(HAS_TASK_WPC) += wpc/p9221.o
diff --git a/driver/charger/rt946x.c b/driver/charger/rt946x.c
index 9a69f3847a..57dc8707a8 100644
--- a/driver/charger/rt946x.c
+++ b/driver/charger/rt946x.c
@@ -16,6 +16,7 @@
#include "hooks.h"
#include "i2c.h"
#include "printf.h"
+#include "driver/wpc/p9221.h"
#include "rt946x.h"
#include "task.h"
#include "timer.h"
@@ -24,6 +25,8 @@
/* Console output macros */
#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_CHARGER, "CHG " format, ## args)
+
/* Charger parameters */
static const struct charger_info rt946x_charger_info = {
@@ -926,17 +929,37 @@ void usb_charger_task(void *u)
/* VBUS attach event */
if (reg & RT946X_MASK_DPDMIRQ_ATTACH) {
+ CPRINTS("VBUS attached: %dmV",
+ charger_get_vbus_voltage(0));
bc12_type = rt946x_get_bc12_device_type();
+
+ CPRINTS("BC12 type %d", bc12_type);
if (bc12_type != CHARGE_SUPPLIER_NONE) {
- chg.current = rt946x_get_bc12_ilim(bc12_type);
- charge_manager_update_charge(bc12_type,
- 0, &chg);
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ if ((bc12_type == CHARGE_SUPPLIER_BC12_SDP) &&
+ wpc_chip_is_online()) {
+ p9221_notify_vbus_change(1);
+ CPRINTS("WPC ON");
+ } else {
+
+#endif
+ chg.current = rt946x_get_bc12_ilim(
+ bc12_type);
+ charge_manager_update_charge(bc12_type,
+ 0, &chg);
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ }
+#endif
rt946x_enable_bc12_detection(0);
}
}
/* VBUS detach event */
if (reg & RT946X_MASK_DPDMIRQ_DETACH) {
+ CPRINTS("VBUS detached");
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ p9221_notify_vbus_change(0);
+#endif
charge_manager_update_charge(bc12_type, 0, NULL);
rt946x_enable_bc12_detection(1);
}
diff --git a/driver/wpc/p9221.c b/driver/wpc/p9221.c
new file mode 100644
index 0000000000..2678173ea6
--- /dev/null
+++ b/driver/wpc/p9221.c
@@ -0,0 +1,802 @@
+/* Copyright 2019 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.
+ */
+
+/*
+ * IDT P9221-R7 Wireless Power Receiver driver.
+ */
+
+#include "p9221.h"
+#include "charge_manager.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "power.h"
+#include "tcpm.h"
+#include "timer.h"
+#include "usb_charge.h"
+#include "usb_pd.h"
+#include "util.h"
+#include <stdbool.h>
+#include "printf.h"
+
+#define CPRINTS(format, args...) cprints(CC_USBPD, "WPC " format, ## args)
+
+#define P9221_TX_TIMEOUT_MS (20 * 1000*1000)
+#define P9221_DCIN_TIMEOUT_MS (2 * 1000*1000)
+#define P9221_VRECT_TIMEOUT_MS (2 * 1000*1000)
+#define P9221_NOTIFIER_DELAY_MS (80*1000)
+#define P9221R7_ILIM_MAX_UA (1600 * 1000)
+#define P9221R7_OVER_CHECK_NUM 3
+
+#define OVC_LIMIT 1
+#define OVC_THRESHOLD 1400000
+#define OVC_BACKOFF_LIMIT 900000
+#define OVC_BACKOFF_AMOUNT 100000
+
+/* P9221 parameters */
+static struct wpc_charger_info p9221_charger_info = {
+ .online = false,
+ .i2c_port = I2C_PORT_WPC,
+ .pp_buf_valid = false,
+};
+
+static struct wpc_charger_info *wpc = &p9221_charger_info;
+
+static void p9221_set_offline(void);
+
+static const uint32_t p9221_ov_set_lut[] = {
+ 17000000, 20000000, 15000000, 13000000,
+ 11000000, 11000000, 11000000, 11000000
+};
+
+static int p9221_reg_is_8_bit(uint16_t reg)
+{
+ switch (reg) {
+ case P9221_CHIP_REVISION_REG:
+ case P9221R7_VOUT_SET_REG:
+ case P9221R7_ILIM_SET_REG:
+ case P9221R7_CHARGE_STAT_REG:
+ case P9221R7_EPT_REG:
+ case P9221R7_SYSTEM_MODE_REG:
+ case P9221R7_COM_CHAN_RESET_REG:
+ case P9221R7_COM_CHAN_SEND_SIZE_REG:
+ case P9221R7_COM_CHAN_SEND_IDX_REG:
+ case P9221R7_COM_CHAN_RECV_SIZE_REG:
+ case P9221R7_COM_CHAN_RECV_IDX_REG:
+ case P9221R7_DEBUG_REG:
+ case P9221R7_EPP_Q_FACTOR_REG:
+ case P9221R7_EPP_TX_GUARANTEED_POWER_REG:
+ case P9221R7_EPP_TX_POTENTIAL_POWER_REG:
+ case P9221R7_EPP_TX_CAPABILITY_FLAGS_REG:
+ case P9221R7_EPP_RENEGOTIATION_REG:
+ case P9221R7_EPP_CUR_RPP_HEADER_REG:
+ case P9221R7_EPP_CUR_NEGOTIATED_POWER_REG:
+ case P9221R7_EPP_CUR_MAXIMUM_POWER_REG:
+ case P9221R7_EPP_CUR_FSK_MODULATION_REG:
+ case P9221R7_EPP_REQ_RPP_HEADER_REG:
+ case P9221R7_EPP_REQ_NEGOTIATED_POWER_REG:
+ case P9221R7_EPP_REQ_MAXIMUM_POWER_REG:
+ case P9221R7_EPP_REQ_FSK_MODULATION_REG:
+ case P9221R7_VRECT_TARGET_REG:
+ case P9221R7_VRECT_KNEE_REG:
+ case P9221R7_FOD_SECTION_REG:
+ case P9221R7_VRECT_ADJ_REG:
+ case P9221R7_ALIGN_X_ADC_REG:
+ case P9221R7_ALIGN_Y_ADC_REG:
+ case P9221R7_ASK_MODULATION_DEPTH_REG:
+ case P9221R7_OVSET_REG:
+ case P9221R7_EPP_TX_SPEC_REV_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int p9221_read8(uint16_t reg, int *val)
+{
+ return i2c_read_offset16(wpc->i2c_port, P9221_R7_ADDR, reg, val, 1);
+}
+
+static int p9221_write8(uint16_t reg, int val)
+{
+ return i2c_write_offset16(wpc->i2c_port, P9221_R7_ADDR, reg, val, 1);
+}
+
+static int p9221_read16(uint16_t reg, int *val)
+{
+ return i2c_read_offset16(wpc->i2c_port, P9221_R7_ADDR, reg, val, 2);
+}
+
+static int p9221_write16(uint16_t reg, int val)
+{
+ return i2c_write_offset16(wpc->i2c_port, P9221_R7_ADDR, reg, val, 2);
+}
+
+static int p9221_block_read(uint16_t reg, uint8_t *data, int len)
+{
+ return i2c_read_offset16_block(wpc->i2c_port, P9221_R7_ADDR, reg, data,
+ len);
+}
+
+static int p9221_block_write(uint16_t reg, uint8_t *data, int len)
+{
+ return i2c_write_offset16_block(wpc->i2c_port, P9221_R7_ADDR, reg, data,
+ len);
+}
+
+static int p9221_set_cmd_reg(uint8_t cmd)
+{
+ int cur_cmd;
+ int retry;
+ int ret;
+
+ for (retry = 0; retry < P9221_COM_CHAN_RETRIES; retry++) {
+ ret = p9221_read8(P9221_COM_REG, &cur_cmd);
+ if (ret == EC_SUCCESS && cur_cmd == 0)
+ break;
+ msleep(25);
+ }
+
+ if (retry >= P9221_COM_CHAN_RETRIES) {
+ CPRINTS("Failed to wait for cmd free %02x", cur_cmd);
+ return EC_ERROR_TIMEOUT;
+ }
+
+ ret = p9221_write8(P9221_COM_REG, cmd);
+ if (ret)
+ CPRINTS("Failed to set cmd reg %02x: %d", cmd, ret);
+
+ return ret;
+}
+
+/* Convert a register value to uV, Hz, or uA */
+static int p9221_convert_reg_r7(uint16_t reg, uint16_t raw_data, uint32_t *val)
+{
+ switch (reg) {
+ case P9221R7_ALIGN_X_ADC_REG: /* raw */
+ case P9221R7_ALIGN_Y_ADC_REG: /* raw */
+ *val = raw_data;
+ break;
+ case P9221R7_VOUT_ADC_REG: /* 12-bit ADC raw */
+ case P9221R7_IOUT_ADC_REG: /* 12-bit ADC raw */
+ case P9221R7_DIE_TEMP_ADC_REG: /* 12-bit ADC raw */
+ case P9221R7_EXT_TEMP_REG:
+ *val = raw_data & 0xFFF;
+ break;
+ case P9221R7_VOUT_SET_REG: /* 0.1V -> uV */
+ *val = raw_data * 100 * 1000;
+ break;
+ case P9221R7_IOUT_REG: /* mA -> uA */
+ case P9221R7_VRECT_REG: /* mV -> uV */
+ case P9221R7_VOUT_REG: /* mV -> uV */
+ case P9221R7_OP_FREQ_REG: /* kHz -> Hz */
+ case P9221R7_TX_PINGFREQ_REG: /* kHz -> Hz */
+ *val = raw_data * 1000;
+ break;
+ case P9221R7_ILIM_SET_REG: /* 100mA -> uA, 200mA offset */
+ *val = ((raw_data * 100) + 200) * 1000;
+ break;
+ case P9221R7_OVSET_REG: /* uV */
+ raw_data &= P9221R7_OVSET_MASK;
+ *val = p9221_ov_set_lut[raw_data];
+ break;
+ default:
+ return -2;
+ }
+
+ return 0;
+}
+
+static int p9221_reg_read_converted(uint16_t reg, uint32_t *val)
+{
+ int ret;
+ int data;
+
+ if (p9221_reg_is_8_bit(reg))
+ ret = p9221_read8(reg, &data);
+ else
+ ret = p9221_read16(reg, &data);
+
+ if (ret)
+ return ret;
+
+ return p9221_convert_reg_r7(reg, data, val);
+}
+
+static int p9221_is_online(void)
+{
+ int chip_id;
+
+ if (p9221_read16(P9221_CHIP_ID_REG, &chip_id)
+ || chip_id != P9221_CHIP_ID)
+ return false;
+ else
+ return true;
+}
+
+int wpc_chip_is_online(void)
+{
+ return p9221_is_online();
+}
+
+
+void p9221_interrupt(enum gpio_signal signal)
+{
+ task_wake(TASK_ID_WPC);
+}
+
+static int p9221r7_clear_interrupts(uint16_t mask)
+{
+ int ret;
+
+ ret = p9221_write16(P9221R7_INT_CLEAR_REG, mask);
+ if (ret) {
+ CPRINTS("Failed to clear INT reg: %d", ret);
+ return ret;
+ }
+
+ ret = p9221_set_cmd_reg(P9221_COM_CLEAR_INT_MASK);
+ if (ret)
+ CPRINTS("Failed to reset INT: %d", ret);
+
+ return ret;
+}
+
+/*
+ * Enable interrupts on the P9221 R7, note we don't really need to disable
+ * interrupts since when the device goes out of field, the P9221 is reset.
+ */
+static int p9221_enable_interrupts_r7(void)
+{
+ uint16_t mask = 0;
+ int ret;
+
+ CPRINTS("Enable interrupts");
+
+ mask = P9221R7_STAT_LIMIT_MASK | P9221R7_STAT_CC_MASK
+ | P9221_STAT_VRECT;
+
+ p9221r7_clear_interrupts(mask);
+
+ ret = p9221_write8(P9221_INT_ENABLE_REG, mask);
+ if (ret)
+ CPRINTS("Failed to enable INTs: %d", ret);
+ return ret;
+}
+
+static int p9221_send_csp(uint8_t status)
+{
+ int ret;
+
+ CPRINTS("Send CSP=%d", status);
+ mutex_lock(&wpc->cmd_lock);
+
+ ret = p9221_write8(P9221R7_CHARGE_STAT_REG, status);
+ if (ret == EC_SUCCESS)
+ ret = p9221_set_cmd_reg(P9221R7_COM_SENDCSP);
+
+ mutex_unlock(&wpc->cmd_lock);
+ return ret;
+}
+
+static int p9221_send_eop(uint8_t reason)
+{
+ int rv;
+
+ CPRINTS("Send EOP reason=%d", reason);
+ mutex_lock(&wpc->cmd_lock);
+
+ rv = p9221_write8(P9221R7_EPT_REG, reason);
+ if (rv == EC_SUCCESS)
+ rv = p9221_set_cmd_reg(P9221R7_COM_SENDEPT);
+
+ mutex_unlock(&wpc->cmd_lock);
+ return rv;
+}
+
+static void print_current_samples(uint32_t *iout_val, int count)
+{
+ int i;
+ char temp[P9221R7_OVER_CHECK_NUM * 9 + 1] = { 0 };
+
+ for (i = 0; i < count ; i++)
+ snprintf(temp + i * 9, sizeof(temp) - i * 9,
+ "%08x ", iout_val[i]);
+ CPRINTS("OVER IOUT_SAMPLES: %s", temp);
+}
+
+
+/*
+ * Number of times to poll the status to see if the current limit condition
+ * was transient or not.
+ */
+static void p9221_limit_handler_r7(uint16_t orign_irq_src)
+{
+ uint8_t reason;
+ int i;
+ int ret;
+ int ovc_count = 0;
+ uint32_t iout_val[P9221R7_OVER_CHECK_NUM] = { 0 };
+ int irq_src = (int)orign_irq_src;
+
+ CPRINTS("OVER INT: %02x", irq_src);
+
+ if (irq_src & P9221R7_STAT_OVV) {
+ reason = P9221_EOP_OVER_VOLT;
+ goto send_eop;
+ }
+
+ if (irq_src & P9221R7_STAT_OVT) {
+ reason = P9221_EOP_OVER_TEMP;
+ goto send_eop;
+ }
+
+ if ((irq_src & P9221R7_STAT_UV) && !(irq_src & P9221R7_STAT_OVC))
+ return;
+
+ reason = P9221_EOP_OVER_CURRENT;
+ for (i = 0; i < P9221R7_OVER_CHECK_NUM; i++) {
+ ret = p9221r7_clear_interrupts(
+ irq_src & P9221R7_STAT_LIMIT_MASK);
+ msleep(50);
+ if (ret)
+ continue;
+
+ ret = p9221_reg_read_converted(P9221R7_IOUT_REG, &iout_val[i]);
+ if (ret) {
+ CPRINTS("Failed to read IOUT[%d]: %d", i, ret);
+ continue;
+ } else if (iout_val[i] > OVC_THRESHOLD) {
+ ovc_count++;
+ }
+
+ ret = p9221_read16(P9221_STATUS_REG, &irq_src);
+ if (ret) {
+ CPRINTS("Failed to read status: %d", ret);
+ continue;
+ }
+
+ if ((irq_src & P9221R7_STAT_OVC) == 0) {
+ print_current_samples(iout_val, i + 1);
+ CPRINTS("OVER condition %04x cleared after %d tries",
+ irq_src, i);
+ return;
+ }
+
+ CPRINTS("OVER status is still %04x, retry", irq_src);
+ }
+
+ if (ovc_count < OVC_LIMIT) {
+ print_current_samples(iout_val, P9221R7_OVER_CHECK_NUM);
+ CPRINTS("ovc_threshold=%d, ovc_count=%d, ovc_limit=%d",
+ OVC_THRESHOLD, ovc_count, OVC_LIMIT);
+ return;
+ }
+
+send_eop:
+ CPRINTS("OVER is %04x, sending EOP %d", irq_src, reason);
+
+ ret = p9221_send_eop(reason);
+ if (ret)
+ CPRINTS("Failed to send EOP %d: %d", reason, ret);
+}
+
+static void p9221_abort_transfers(void)
+{
+ wpc->tx_busy = false;
+ wpc->tx_done = true;
+ wpc->rx_done = true;
+ wpc->rx_len = 0;
+}
+
+/* Handler for r7 and R7 chips */
+static void p9221r7_irq_handler(uint16_t irq_src)
+{
+ int res;
+
+ if (irq_src & P9221R7_STAT_LIMIT_MASK)
+ p9221_limit_handler_r7(irq_src);
+
+ /* Receive complete */
+ if (irq_src & P9221R7_STAT_CCDATARCVD) {
+ int rxlen = 0;
+
+ res = p9221_read8(P9221R7_COM_CHAN_RECV_SIZE_REG, &rxlen);
+ if (res)
+ CPRINTS("Failed to read len: %d", res);
+
+ if (rxlen) {
+ res = p9221_block_read(P9221R7_DATA_RECV_BUF_START,
+ wpc->rx_buf, rxlen);
+ if (res) {
+ CPRINTS("Failed to read CC data: %d", res);
+ rxlen = 0;
+ }
+
+ wpc->rx_len = rxlen;
+ wpc->rx_done = true;
+ }
+ }
+
+ /* Send complete */
+ if (irq_src & P9221R7_STAT_CCSENDBUSY) {
+ wpc->tx_busy = false;
+ wpc->tx_done = true;
+ }
+
+ /* Proprietary packet */
+ if (irq_src & P9221R7_STAT_PPRCVD) {
+ res = p9221_block_read(P9221R7_DATA_RECV_BUF_START,
+ wpc->pp_buf, sizeof(wpc->pp_buf));
+ if (res) {
+ CPRINTS("Failed to read PP: %d", res);
+ wpc->pp_buf_valid = false;
+ return;
+ }
+
+ /* We only care about PP which come with 0x4F header */
+ wpc->pp_buf_valid = (wpc->pp_buf[0] == 0x4F);
+
+ hexdump(wpc->pp_buf, sizeof(wpc->pp_buf));
+ }
+
+ /* CC Reset complete */
+ if (irq_src & P9221R7_STAT_CCRESET)
+ p9221_abort_transfers();
+}
+
+static int p9221_is_epp(void)
+{
+ int ret, reg;
+ uint32_t vout_uv;
+
+ if (p9221_read8(P9221R7_SYSTEM_MODE_REG, &reg) == EC_SUCCESS)
+ return reg & P9221R7_SYSTEM_MODE_EXTENDED_MASK;
+
+ /* Check based on power supply voltage */
+ ret = p9221_reg_read_converted(P9221R7_VOUT_ADC_REG, &vout_uv);
+ if (ret) {
+ CPRINTS("Failed to read VOUT_ADC: %d", ret);
+ return false;
+ }
+
+ CPRINTS("Voltage is %duV", vout_uv);
+ if (vout_uv > P9221_EPP_THRESHOLD_UV)
+ return true;
+
+ return false;
+}
+
+static void p9221_config_fod(void)
+{
+
+ int epp;
+ uint8_t *fod;
+ int fod_len;
+ int ret;
+ int retries = 3;
+
+ CPRINTS("Config FOD");
+
+ epp = p9221_is_epp();
+ fod_len = epp ? board_get_epp_fod(&fod) : board_get_fod(&fod);
+ if (!fod_len || !fod) {
+ CPRINTS("FOD data not found");
+ return;
+ }
+
+ while (retries) {
+ uint8_t fod_read[fod_len];
+
+ CPRINTS("Writing %s FOD (n=%d try=%d)",
+ epp ? "EPP" : "BPP", fod_len, retries);
+
+ ret = p9221_block_write(P9221R7_FOD_REG, fod, fod_len);
+ if (ret)
+ goto no_fod;
+
+ /* Verify the FOD has been written properly */
+ ret = p9221_block_read(P9221R7_FOD_REG, fod_read, fod_len);
+ if (ret)
+ goto no_fod;
+
+ if (memcmp(fod, fod_read, fod_len) == 0)
+ return;
+
+ hexdump(fod_read, fod_len);
+
+ retries--;
+ msleep(100);
+ }
+
+no_fod:
+ CPRINTS("Failed to set FOD. retries:%d ret:%d", retries, ret);
+}
+
+static void p9221_set_online(void)
+{
+ int ret;
+
+ CPRINTS("Set online");
+
+ wpc->online = true;
+
+ wpc->tx_busy = false;
+ wpc->tx_done = true;
+ wpc->rx_done = false;
+ wpc->charge_supplier = CHARGE_SUPPLIER_WPC_BPP;
+
+ ret = p9221_enable_interrupts_r7();
+ if (ret)
+ CPRINTS("Failed to enable INT: %d", ret);
+
+ /* NOTE: depends on _is_epp() which is not valid until DC_IN */
+ p9221_config_fod();
+}
+
+static void p9221_vbus_check_timeout(void)
+{
+ CPRINTS("Timeout VBUS, online=%d", wpc->online);
+ if (wpc->online)
+ p9221_set_offline();
+
+}
+DECLARE_DEFERRED(p9221_vbus_check_timeout);
+
+static void p9221_set_offline(void)
+{
+ CPRINTS("Set offline");
+
+ wpc->online = false;
+ /* Reset PP buf so we can get a new serial number next time around */
+ wpc->pp_buf_valid = false;
+
+ p9221_abort_transfers();
+
+ hook_call_deferred(&p9221_vbus_check_timeout_data, -1);
+}
+
+/* P9221_NOTIFIER_DELAY_MS from VRECTON */
+static int p9221_notifier_check_det(void)
+{
+ if (wpc->online)
+ goto done;
+
+ /* send out a FOD but is_epp() is still invalid */
+ p9221_set_online();
+
+ /* Give the vbus 2 seconds to come up. */
+ CPRINTS("Waiting VBUS");
+ hook_call_deferred(&p9221_vbus_check_timeout_data, -1);
+ hook_call_deferred(&p9221_vbus_check_timeout_data,
+ P9221_DCIN_TIMEOUT_MS);
+
+done:
+ wpc->p9221_check_det = false;
+ return 0;
+}
+
+static int p9221_get_charge_supplier(void)
+{
+ uint32_t tx_id;
+ int txmf_id;
+
+ if (!wpc->online)
+ return EC_ERROR_UNKNOWN;
+
+ if (p9221_is_epp()) {
+ int ret;
+ wpc->charge_supplier = CHARGE_SUPPLIER_WPC_EPP;
+
+ ret = p9221_read16(P9221R7_EPP_TX_MFG_CODE_REG, &txmf_id);
+ if (ret || txmf_id != P9221_GPP_TX_MF_ID)
+ return ret;
+
+ ret = p9221_block_read(P9221R7_PROP_TX_ID_REG,
+ (uint8_t *) &tx_id,
+ P9221R7_PROP_TX_ID_SIZE);
+ if (ret)
+ return ret;
+
+ if (tx_id & P9221R7_PROP_TX_ID_GPP_MASK)
+ wpc->charge_supplier = CHARGE_SUPPLIER_WPC_GPP;
+ } else {
+ wpc->charge_supplier = CHARGE_SUPPLIER_WPC_BPP;
+ }
+
+ CPRINTS("txmf_id=0x%04x tx_id=0x%08x supplier=%d",
+ txmf_id, tx_id, wpc->charge_supplier);
+ return EC_SUCCESS;
+}
+
+static int p9221_get_icl(int charge_supplier)
+{
+ switch (charge_supplier) {
+ case CHARGE_SUPPLIER_WPC_EPP:
+ case CHARGE_SUPPLIER_WPC_GPP:
+ return P9221_DC_ICL_EPP_MA;
+ case CHARGE_SUPPLIER_WPC_BPP:
+ default:
+ return P9221_DC_ICL_BPP_MA;
+ }
+}
+
+static int p9221_get_ivl(int charge_supplier)
+{
+ switch (charge_supplier) {
+ case CHARGE_SUPPLIER_WPC_EPP:
+ case CHARGE_SUPPLIER_WPC_GPP:
+ return P9221_DC_IVL_EPP_MV;
+ case CHARGE_SUPPLIER_WPC_BPP:
+ default:
+ return P9221_DC_IVL_BPP_MV;
+ }
+}
+
+static void p9221_update_charger(int type, struct charge_port_info *chg)
+{
+ if (!chg)
+ charge_manager_update_dualrole(0, CAP_UNKNOWN);
+ else
+ charge_manager_update_dualrole(0, CAP_DEDICATED);
+
+ charge_manager_update_charge(type, 0, chg);
+}
+
+static int p9221_reg_write_converted_r7(uint16_t reg, uint32_t val)
+{
+ int ret = 0;
+ uint16_t data;
+ int i;
+ /* Do the appropriate conversion */
+ switch (reg) {
+ case P9221R7_ILIM_SET_REG:
+ /* uA -> 0.1A, offset 0.2A */
+ if ((val < 200000) || (val > 1600000))
+ return -EC_ERROR_INVAL;
+ data = (val / (100 * 1000)) - 2;
+ break;
+ case P9221R7_VOUT_SET_REG:
+ /* uV -> 0.1V */
+ val /= 1000;
+ if (val < 3500 || val > 9000)
+ return -EC_ERROR_INVAL;
+ data = val / 100;
+ break;
+ case P9221R7_OVSET_REG:
+ /* uV */
+ for (i = 0; i < ARRAY_SIZE(p9221_ov_set_lut); i++) {
+ if (val == p9221_ov_set_lut[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(p9221_ov_set_lut))
+ return -EC_ERROR_INVAL;
+ data = i;
+ break;
+ default:
+ return -EC_ERROR_INVAL;
+ }
+ if (p9221_reg_is_8_bit(reg))
+ ret = p9221_write8(reg, data);
+ else
+ ret = p9221_write16(reg, data);
+ return ret;
+}
+
+static int p9221_set_dc_icl(void)
+{
+ /* Increase the IOUT limit */
+ if (p9221_reg_write_converted_r7(P9221R7_ILIM_SET_REG,
+ P9221R7_ILIM_MAX_UA))
+ CPRINTS("%s set rx_iout limit fail.", __func__);
+
+ return EC_SUCCESS;
+}
+
+
+static void p9221_notifier_check_vbus(void)
+{
+ struct charge_port_info chg;
+
+ wpc->p9221_check_vbus = false;
+
+ CPRINTS("%s online:%d vbus:%d is_online:%d", __func__, wpc->online,
+ wpc->vbus_status);
+
+ /*
+ * We now have confirmation from DC_IN, kill the timer, p9221_online
+ * will be set by this function.
+ */
+ hook_call_deferred(&p9221_vbus_check_timeout_data, -1);
+
+ if (wpc->vbus_status) {
+ /* WPC VBUS on ,Always write FOD, check dc_icl, send CSP */
+ p9221_set_dc_icl();
+ p9221_config_fod();
+
+ p9221_send_csp(1);
+
+ /* when wpc vbus attached after 2s, set wpc online */
+ if (!wpc->online)
+ p9221_set_online();
+
+ /* WPC VBUS on , update charge voltage and current */
+ p9221_get_charge_supplier();
+ chg.voltage = p9221_get_ivl(wpc->charge_supplier);
+ chg.current = p9221_get_icl(wpc->charge_supplier);
+
+ p9221_update_charger(wpc->charge_supplier, &chg);
+ } else {
+ /*
+ * Vbus detached, set wpc offline and update wpc charge voltage
+ * and current to zero.
+ */
+ if (wpc->online) {
+ p9221_set_offline();
+ p9221_update_charger(wpc->charge_supplier, NULL);
+ }
+ }
+
+ CPRINTS("check_vbus changed on:%d vbus:%d", wpc->online,
+ wpc->vbus_status);
+
+}
+
+static void p9221_detect_work(void)
+{
+
+ CPRINTS("%s online:%d check_vbus:%d check_det:%d vbus:%d", __func__,
+ wpc->online, wpc->p9221_check_vbus, wpc->p9221_check_det,
+ wpc->vbus_status);
+
+ /* Step 1 */
+ if (wpc->p9221_check_det)
+ p9221_notifier_check_det();
+
+ /* Step 2 */
+ if (wpc->p9221_check_vbus)
+ p9221_notifier_check_vbus();
+
+}
+DECLARE_DEFERRED(p9221_detect_work);
+
+void p9221_notify_vbus_change(int vbus)
+{
+ wpc->p9221_check_vbus = true;
+ wpc->vbus_status = vbus;
+ hook_call_deferred(&p9221_detect_work_data, P9221_NOTIFIER_DELAY_MS);
+}
+
+void wireless_power_charger_task(void *u)
+{
+ while (1) {
+ int ret, irq_src;
+ task_wait_event(-1);
+
+ ret = p9221_read16(P9221_INT_REG, &irq_src);
+ if (ret) {
+ CPRINTS("Failed to read INT REG");
+ continue;
+ }
+
+ CPRINTS("INT SRC 0x%04x", irq_src);
+
+ if (p9221r7_clear_interrupts(irq_src))
+ continue;
+
+ if (irq_src & P9221_STAT_VRECT) {
+ CPRINTS("VRECTON, online=%d", wpc->online);
+ if (!wpc->online) {
+ wpc->p9221_check_det = true;
+ hook_call_deferred(&p9221_detect_work_data,
+ P9221_NOTIFIER_DELAY_MS);
+ }
+ }
+
+ p9221r7_irq_handler(irq_src);
+ }
+}
diff --git a/driver/wpc/p9221.h b/driver/wpc/p9221.h
new file mode 100644
index 0000000000..7c5156c833
--- /dev/null
+++ b/driver/wpc/p9221.h
@@ -0,0 +1,323 @@
+/* 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.
+ */
+
+
+/*
+ * IDT P9221-R7 Wireless Power Receiver driver definitions.
+ */
+
+#ifndef __P9221_R7_H
+#define __P9221_R7_H
+
+#include "common.h"
+#include "gpio.h"
+#include "charge_manager.h"
+#include "task.h"
+
+
+/* ========== Variant-specific configuration ============ */
+
+#define P9221_R7_ADDR (0x61 << 1)
+
+/*
+ * P9221 common registers
+ */
+#define P9221_CHIP_ID_REG 0x00
+#define P9221_CHIP_ID 0x9220
+#define P9221_CHIP_REVISION_REG 0x02
+#define P9221_CUSTOMER_ID_REG 0x03
+#define P9221R7_CUSTOMER_ID_VAL 0x05
+#define P9221_OTP_FW_MAJOR_REV_REG 0x04
+#define P9221_OTP_FW_MINOR_REV_REG 0x06
+#define P9221_OTP_FW_DATE_REG 0x08
+#define P9221_OTP_FW_DATE_SIZE 12
+#define P9221_OTP_FW_TIME_REG 0x14
+#define P9221_OTP_FW_TIME_SIZE 8
+#define P9221_SRAM_FW_MAJOR_REV_REG 0x1C
+#define P9221_SRAM_FW_MINOR_REV_REG 0x1E
+#define P9221_SRAM_FW_DATE_REG 0x20
+#define P9221_SRAM_FW_DATE_SIZE 12
+#define P9221_SRAM_FW_TIME_REG 0x2C
+#define P9221_SRAM_FW_TIME_SIZE 8
+#define P9221_STATUS_REG 0x34
+#define P9221_INT_REG 0x36
+#define P9221_INT_MASK 0xF7
+#define P9221_INT_ENABLE_REG 0x38
+#define P9221_GPP_TX_MF_ID 0x0072
+
+/*
+ * P9221 Rx registers (x != 5)
+ */
+#define P9221_CHARGE_STAT_REG 0x3A
+#define P9221_EPT_REG 0x3B
+#define P9221_VOUT_ADC_REG 0x3C
+#define P9221_VOUT_ADC_MASK 0x0FFF
+#define P9221_VOUT_SET_REG 0x3E
+#define P9221_MAX_VOUT_SET_MV_DEFAULT 9000
+#define P9221_VRECT_ADC_REG 0x40
+#define P9221_VRECT_ADC_MASK 0x0FFF
+#define P9221_OVSET_REG 0x42
+#define P9221_OVSET_MASK 0x70
+#define P9221_OVSET_SHIFT 4
+#define P9221_RX_IOUT_REG 0x44
+#define P9221_DIE_TEMP_ADC_REG 0x46
+#define P9221_DIE_TEMP_ADC_MASK 0x0FFF
+#define P9221_OP_FREQ_REG 0x48
+#define P9221_ILIM_SET_REG 0x4A
+#define P9221_ALIGN_X_ADC_REG 0x4B
+#define P9221_ALIGN_Y_ADC_REG 0x4C
+#define P9221_OP_MODE_REG 0x4D
+#define P9221_COM_REG 0x4E
+#define P9221_FW_SWITCH_KEY_REG 0x4F
+#define P9221_INT_CLEAR_REG 0x56
+#define P9221_RXID_REG 0x5C
+#define P9221_RXID_LEN 6
+#define P9221_MPREQ_REG 0x5C
+#define P9221_MPREQ_LEN 6
+#define P9221_FOD_REG 0x68
+#define P9221_NUM_FOD 16
+#define P9221_RX_RAWIOUT_REG 0x7A
+#define P9221_RX_RAWIOUT_MASK 0xFFF
+#define P9221_PMA_AD_REG 0x7C
+#define P9221_RX_PINGFREQ_REG 0xFC
+#define P9221_RX_PINGFREQ_MASK 0xFFF
+#define P9221_LAST_REG 0xFF
+
+/*
+ * P9221R7 unique registers
+ */
+#define P9221R7_INT_CLEAR_REG 0x3A
+#define P9221R7_VOUT_SET_REG 0x3C
+#define P9221R7_ILIM_SET_REG 0x3D
+#define P9221R7_ILIM_SET_MAX 0x0E /* 0x0E = 1.6A */
+#define P9221R7_CHARGE_STAT_REG 0x3E
+#define P9221R7_EPT_REG 0x3F
+#define P9221R7_VRECT_REG 0x40
+#define P9221R7_VOUT_REG 0x42
+#define P9221R7_IOUT_REG 0x44
+#define P9221R7_OP_FREQ_REG 0x48
+#define P9221R7_SYSTEM_MODE_REG 0x4C
+#define P9221R7_COM_CHAN_RESET_REG 0x50
+#define P9221R7_COM_CHAN_SEND_SIZE_REG 0x58
+#define P9221R7_COM_CHAN_SEND_IDX_REG 0x59
+#define P9221R7_COM_CHAN_RECV_SIZE_REG 0x5A
+#define P9221R7_COM_CHAN_RECV_IDX_REG 0x5B
+#define P9221R7_VRECT_ADC_REG 0x60
+#define P9221R7_VOUT_ADC_REG 0x62
+#define P9221R7_VOUT_ADC_MASK 0xFFF
+#define P9221R7_IOUT_ADC_REG 0x64
+#define P9221R7_IOUT_ADC_MASK 0xFFF
+#define P9221R7_DIE_TEMP_ADC_REG 0x66
+#define P9221R7_DIE_TEMP_ADC_MASK 0xFFF
+#define P9221R7_AC_PERIOD_REG 0x68
+#define P9221R7_TX_PINGFREQ_REG 0x6A
+#define P9221R7_EXT_TEMP_REG 0x6C
+#define P9221R7_EXT_TEMP_MASK 0xFFF
+#define P9221R7_FOD_REG 0x70
+#define P9221R7_NUM_FOD 16
+#define P9221R7_DEBUG_REG 0x80
+#define P9221R7_EPP_Q_FACTOR_REG 0x83
+#define P9221R7_EPP_TX_GUARANTEED_POWER_REG 0x84
+#define P9221R7_EPP_TX_POTENTIAL_POWER_REG 0x85
+#define P9221R7_EPP_TX_CAPABILITY_FLAGS_REG 0x86
+#define P9221R7_EPP_RENEGOTIATION_REG 0x87
+#define P9221R7_EPP_CUR_RPP_HEADER_REG 0x88
+#define P9221R7_EPP_CUR_NEGOTIATED_POWER_REG 0x89
+#define P9221R7_EPP_CUR_MAXIMUM_POWER_REG 0x8A
+#define P9221R7_EPP_CUR_FSK_MODULATION_REG 0x8B
+#define P9221R7_EPP_REQ_RPP_HEADER_REG 0x8C
+#define P9221R7_EPP_REQ_NEGOTIATED_POWER_REG 0x8D
+#define P9221R7_EPP_REQ_MAXIMUM_POWER_REG 0x8E
+#define P9221R7_EPP_REQ_FSK_MODULATION_REG 0x8F
+#define P9221R7_VRECT_TARGET_REG 0x90
+#define P9221R7_VRECT_KNEE_REG 0x92
+#define P9221R7_VRECT_CORRECTION_FACTOR_REG 0x93
+#define P9221R7_VRECT_MAX_CORRECTION_FACTOR_REG 0x94
+#define P9221R7_VRECT_MIN_CORRECTION_FACTOR_REG 0x96
+#define P9221R7_FOD_SECTION_REG 0x99
+#define P9221R7_VRECT_ADJ_REG 0x9E
+#define P9221R7_ALIGN_X_ADC_REG 0xA0
+#define P9221R7_ALIGN_Y_ADC_REG 0xA1
+#define P9221R7_ASK_MODULATION_DEPTH_REG 0xA2
+#define P9221R7_OVSET_REG 0xA3
+#define P9221R7_OVSET_MASK 0x7
+#define P9221R7_EPP_TX_SPEC_REV_REG 0xA9
+#define P9221R7_EPP_TX_MFG_CODE_REG 0xAA
+#define P9221R7_GP0_RESET_VOLT_REG 0xAC
+#define P9221R7_GP1_RESET_VOLT_REG 0xAE
+#define P9221R7_GP2_RESET_VOLT_REG 0xB0
+#define P9221R7_GP3_RESET_VOLT_REG 0xB2
+#define P9221R7_PROP_TX_ID_REG 0xB4
+#define P9221R7_PROP_TX_ID_SIZE 4
+#define P9221R7_DATA_SEND_BUF_START 0x100
+#define P9221R7_DATA_SEND_BUF_SIZE 0x80
+#define P9221R7_DATA_RECV_BUF_START 0x180
+#define P9221R7_DATA_RECV_BUF_SIZE 0x80
+#define P9221R7_MAX_PP_BUF_SIZE 16
+#define P9221R7_LAST_REG 0x1FF
+
+/*
+ * System Mode Mask (r7+/0x4C)
+ */
+#define P9221R7_SYSTEM_MODE_EXTENDED_MASK (1 << 3)
+
+/*
+ * TX ID GPP Mask (r7+/0xB4->0xB7)
+ */
+#define P9221R7_PROP_TX_ID_GPP_MASK (1 << 29)
+
+/*
+ * Com Channel Commands
+ */
+#define P9221R7_COM_CHAN_CCRESET BIT(7)
+#define P9221_COM_CHAN_RETRIES 5
+
+/*
+ * End of Power packet types
+ */
+#define P9221_EOP_UNKNOWN 0x00
+#define P9221_EOP_EOC 0x01
+#define P9221_EOP_INTERNAL_FAULT 0x02
+#define P9221_EOP_OVER_TEMP 0x03
+#define P9221_EOP_OVER_VOLT 0x04
+#define P9221_EOP_OVER_CURRENT 0x05
+#define P9221_EOP_BATT_FAIL 0x06
+#define P9221_EOP_RECONFIG 0x07
+#define P9221_EOP_NO_RESPONSE 0x08
+#define P9221_EOP_NEGOTIATION_FAIL 0x0A
+#define P9221_EOP_RESTART_POWER 0x0B
+
+/*
+ * Command flags
+ */
+#define P9221R7_COM_RENEGOTIATE P9221_COM_RENEGOTIATE
+#define P9221R7_COM_SWITCH2RAM P9221_COM_SWITCH_TO_RAM_MASK
+#define P9221R7_COM_CLRINT P9221_COM_CLEAR_INT_MASK
+#define P9221R7_COM_SENDCSP P9221_COM_SEND_CHG_STAT_MASK
+#define P9221R7_COM_SENDEPT P9221_COM_SEND_EOP_MASK
+#define P9221R7_COM_LDOTGL P9221_COM_LDO_TOGGLE
+#define P9221R7_COM_CCACTIVATE BIT(0)
+
+#define P9221_COM_RENEGOTIATE BIT(7)
+#define P9221_COM_SWITCH_TO_RAM_MASK BIT(6)
+#define P9221_COM_CLEAR_INT_MASK BIT(5)
+#define P9221_COM_SEND_CHG_STAT_MASK BIT(4)
+#define P9221_COM_SEND_EOP_MASK BIT(3)
+#define P9221_COM_LDO_TOGGLE BIT(1)
+
+/*
+ * Interrupt/Status flags for P9221
+ */
+#define P9221_STAT_VOUT BIT(7)
+#define P9221_STAT_VRECT BIT(6)
+#define P9221_STAT_ACMISSING BIT(5)
+#define P9221_STAT_OV_TEMP BIT(2)
+#define P9221_STAT_OV_VOLT BIT(1)
+#define P9221_STAT_OV_CURRENT BIT(0)
+#define P9221_STAT_LIMIT_MASK (P9221_STAT_OV_TEMP | \
+ P9221_STAT_OV_VOLT | \
+ P9221_STAT_OV_CURRENT)
+/*
+ * Interrupt/Status flags for P9221R7
+ */
+#define P9221R7_STAT_CCRESET BIT(12)
+#define P9221R7_STAT_CCERROR BIT(11)
+#define P9221R7_STAT_PPRCVD BIT(10)
+#define P9221R7_STAT_CCDATARCVD BIT(9)
+#define P9221R7_STAT_CCSENDBUSY BIT(8)
+#define P9221R7_STAT_VOUTCHANGED BIT(7)
+#define P9221R7_STAT_VRECTON BIT(6)
+#define P9221R7_STAT_MODECHANGED BIT(5)
+#define P9221R7_STAT_UV BIT(3)
+#define P9221R7_STAT_OVT BIT(2)
+#define P9221R7_STAT_OVV BIT(1)
+#define P9221R7_STAT_OVC BIT(0)
+#define P9221R7_STAT_MASK 0x1FFF
+#define P9221R7_STAT_CC_MASK (P9221R7_STAT_CCRESET | \
+ P9221R7_STAT_PPRCVD | \
+ P9221R7_STAT_CCERROR | \
+ P9221R7_STAT_CCDATARCVD | \
+ P9221R7_STAT_CCSENDBUSY)
+#define P9221R7_STAT_LIMIT_MASK (P9221R7_STAT_UV | \
+ P9221R7_STAT_OVV | \
+ P9221R7_STAT_OVT | \
+ P9221R7_STAT_OVC)
+
+#define P9221_DC_ICL_BPP_MA 1000
+#define P9221_DC_ICL_EPP_MA 1100
+#define P9221_DC_IVL_BPP_MV 5000
+#define P9221_DC_IVL_EPP_MV 9000
+#define P9221_EPP_THRESHOLD_UV 7000000
+
+#define true 1
+#define false 0
+
+struct wpc_charger_info {
+ uint8_t online; /* wpc is online */
+ uint8_t cust_id; /* customer id */
+ uint8_t i2c_port; /* i2c port */
+ /* Proprietary Packets receive buffer, to get Proprietary data from TX*/
+ uint8_t pp_buf[P9221R7_MAX_PP_BUF_SIZE];
+ uint8_t pp_buf_valid;
+ /* Common message Packets receive buffer, for get data from TX */
+ uint8_t rx_buf[P9221R7_DATA_RECV_BUF_SIZE];
+ uint8_t rx_len;
+ uint8_t rx_done;
+ /* Message packets send buffer, used when send messages from RX to TX*/
+ uint8_t tx_buf[P9221R7_DATA_SEND_BUF_SIZE];
+ uint8_t tx_id; /* TX device id */
+ uint8_t tx_len; /* The data size need send to TX */
+ uint8_t tx_done; /* TX data send has done */
+ uint8_t tx_busy; /* when tx_busy=1, can't transfer data from RX to TX */
+ /* p9221_check_vbus=1 when VBUS has changed, need update charge state */
+ uint8_t p9221_check_vbus;
+ /* p9221_check_det=1 when TX device has detected */
+ uint8_t p9221_check_det;
+ /* vbus_status is 1 when VBUS attached and is 0 when VBUS detached*/
+ uint8_t vbus_status;
+ /* supplier type of wireless charger */
+ uint8_t charge_supplier;
+ /* lock of send command to p9221 */
+ struct mutex cmd_lock;
+};
+
+/* Interrupt handler for p9221 */
+void p9221_interrupt(enum gpio_signal signal);
+
+/**
+ * notify p9221 detect update charger status when VBUS changed
+ *
+ * @param vbus: new status of VBUS, 1 if VBUS on, 0 if VBUS off.
+ */
+void p9221_notify_vbus_change(int vbus);
+
+/**
+ * get the fod (foreign-object detection) parameters for bpp charger type
+ *
+ * @param fod: return the real value of fod paramerters,
+ * return NULL if fod paramerters not set.
+ *
+ * @return the count bytes of fod paramerters.
+ */
+int board_get_fod(uint8_t **fod);
+
+/**
+ * get the fod (foreign-object detection) parameters for epp chager type
+ *
+ * @param fod: return the real value of fod paramerters,
+ * return NULL if fod paramerters not set.
+ *
+ * @return the count bytes of fod paramerters.
+ */
+int board_get_epp_fod(uint8_t **fod);
+
+/**
+ * return the wireless charge online status
+ *
+ * @return true if online, false if offline.
+ */
+int wpc_chip_is_online(void);
+
+#endif
diff --git a/include/charge_manager.h b/include/charge_manager.h
index 41ba9bcb59..6177408acb 100644
--- a/include/charge_manager.h
+++ b/include/charge_manager.h
@@ -45,6 +45,11 @@ enum charge_supplier {
#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0
CHARGE_SUPPLIER_DEDICATED,
#endif
+#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
+ CHARGE_SUPPLIER_WPC_BPP,
+ CHARGE_SUPPLIER_WPC_EPP,
+ CHARGE_SUPPLIER_WPC_GPP,
+#endif
CHARGE_SUPPLIER_COUNT
};