summaryrefslogtreecommitdiff
path: root/board/grunt/board.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/grunt/board.c')
-rw-r--r--board/grunt/board.c221
1 files changed, 218 insertions, 3 deletions
diff --git a/board/grunt/board.c b/board/grunt/board.c
index 68d765dab4..23e8e0dad6 100644
--- a/board/grunt/board.c
+++ b/board/grunt/board.c
@@ -11,8 +11,11 @@
#include "charge_state.h"
#include "charge_state_v2.h"
#include "common.h"
-#include "console.h"
#include "compile_time_macros.h"
+#include "console.h"
+#include "driver/ppc/sn5s330.h"
+#include "driver/tcpm/anx74xx.h"
+#include "driver/tcpm/ps8xxx.h"
#include "ec_commands.h"
#include "extpower.h"
#include "gpio.h"
@@ -23,8 +26,9 @@
#include "power.h"
#include "power_button.h"
#include "registers.h"
-#include "system.h"
#include "switch.h"
+#include "system.h"
+#include "task.h"
#include "tcpci.h"
#include "usb_mux.h"
#include "usb_pd_tcpm.h"
@@ -32,8 +36,46 @@
static void tcpc_alert_event(enum gpio_signal signal)
{
- /* TODO(ecgh) */
+ if ((signal == GPIO_USB_C0_PD_INT_ODL) &&
+ !gpio_get_level(GPIO_USB_C0_PD_RST_L))
+ return;
+
+ if ((signal == GPIO_USB_C1_PD_INT_ODL) &&
+ !gpio_get_level(GPIO_USB_C1_PD_RST_L))
+ return;
+
+#ifdef HAS_TASK_PDCMD
+ /* Exchange status with TCPCs */
+ host_command_pd_send_status(PD_CHARGE_NO_CHANGE);
+#endif
+}
+
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+static void anx74xx_cable_det_handler(void)
+{
+ int cable_det = gpio_get_level(GPIO_USB_C0_CABLE_DET);
+ int reset_n = gpio_get_level(GPIO_USB_C0_PD_RST_L);
+
+ /*
+ * A cable_det low->high transition was detected. If following the
+ * debounce time, cable_det is high, and reset_n is low, then ANX3429 is
+ * currently in standby mode and needs to be woken up. Set the
+ * TCPC_RESET event which will bring the ANX3429 out of standby
+ * mode. Setting this event is gated on reset_n being low because the
+ * ANX3429 will always set cable_det when transitioning to normal mode
+ * and if in normal mode, then there is no need to trigger a tcpc reset.
+ */
+ if (cable_det && !reset_n)
+ task_set_event(TASK_ID_PD_C0, PD_EVENT_TCPC_RESET, 0);
+}
+DECLARE_DEFERRED(anx74xx_cable_det_handler);
+
+void anx74xx_cable_det_interrupt(enum gpio_signal signal)
+{
+ /* debounce for 2 msec */
+ hook_call_deferred(&anx74xx_cable_det_handler_data, (2 * MSEC));
}
+#endif
#include "gpio_list.h"
@@ -72,6 +114,179 @@ const struct i2c_port_t i2c_ports[] = {
};
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
+#define USB_PD_PORT_ANX74XX 0
+#define USB_PD_PORT_PS8751 1
+
+const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
+ [USB_PD_PORT_ANX74XX] = {
+ .i2c_host_port = I2C_PORT_TCPC0,
+ .i2c_slave_addr = 0x50,
+ .drv = &anx74xx_tcpm_drv,
+ .pol = TCPC_ALERT_ACTIVE_LOW,
+ },
+ [USB_PD_PORT_PS8751] = {
+ .i2c_host_port = I2C_PORT_TCPC1,
+ .i2c_slave_addr = 0x16,
+ .drv = &ps8xxx_tcpm_drv,
+ .pol = TCPC_ALERT_ACTIVE_LOW,
+ },
+};
+
+uint16_t tcpc_get_alert_status(void)
+{
+ uint16_t status = 0;
+
+ if (!gpio_get_level(GPIO_USB_C0_PD_INT_ODL)) {
+ if (gpio_get_level(GPIO_USB_C0_PD_RST_L))
+ status |= PD_STATUS_TCPC_ALERT_0;
+ }
+
+ if (!gpio_get_level(GPIO_USB_C1_PD_INT_ODL)) {
+ if (gpio_get_level(GPIO_USB_C1_PD_RST_L))
+ status |= PD_STATUS_TCPC_ALERT_1;
+ }
+
+ return status;
+}
+
+struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
+ {
+ .port_addr = USB_PD_PORT_ANX74XX, /* don't care / unused */
+ .driver = &anx74xx_tcpm_usb_mux_driver,
+ .hpd_update = &anx74xx_tcpc_update_hpd_status,
+ },
+ {
+ .port_addr = USB_PD_PORT_PS8751,
+ .driver = &tcpci_tcpm_usb_mux_driver,
+ .hpd_update = &ps8xxx_tcpc_update_hpd_status,
+ /* TODO(ecgh): ps8751_tune_mux needed? */
+ }
+};
+
+const struct sn5s330_config sn5s330_chips[] = {
+ {I2C_PORT_TCPC0, SN5S330_ADDR0},
+ {I2C_PORT_TCPC1, SN5S330_ADDR0},
+};
+const unsigned int sn5s330_cnt = ARRAY_SIZE(sn5s330_chips);
+
+const int usb_port_enable[CONFIG_USB_PORT_POWER_SMART_PORT_COUNT] = {
+ GPIO_EN_USB_A0_5V,
+ GPIO_EN_USB_A1_5V,
+};
+
+/**
+ * Power on (or off) a single TCPC.
+ * minimum on/off delays are included.
+ *
+ * @param port Port number of TCPC.
+ * @param mode 0: power off, 1: power on.
+ */
+void board_set_tcpc_power_mode(int port, int mode)
+{
+ if (port != USB_PD_PORT_ANX74XX)
+ return;
+
+ switch (mode) {
+ case ANX74XX_NORMAL_MODE:
+ gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 1);
+ msleep(ANX74XX_PWR_H_RST_H_DELAY_MS);
+ gpio_set_level(GPIO_USB_C0_PD_RST_L, 1);
+ break;
+ case ANX74XX_STANDBY_MODE:
+ gpio_set_level(GPIO_USB_C0_PD_RST_L, 0);
+ msleep(ANX74XX_RST_L_PWR_L_DELAY_MS);
+ gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 0);
+ msleep(ANX74XX_PWR_L_PWR_H_DELAY_MS);
+ break;
+ default:
+ break;
+ }
+}
+
+void board_reset_pd_mcu(void)
+{
+ /* Assert reset to TCPC1 (ps8751) */
+ gpio_set_level(GPIO_USB_C1_PD_RST_L, 0);
+
+ /* Assert reset to TCPC0 (anx3429) */
+ gpio_set_level(GPIO_USB_C0_PD_RST_L, 0);
+
+ /* TCPC1 (ps8751) requires 1ms reset down assertion */
+ msleep(MAX(1, ANX74XX_RST_L_PWR_L_DELAY_MS));
+
+ /* Deassert reset to TCPC1 */
+ gpio_set_level(GPIO_USB_C1_PD_RST_L, 1);
+ /* Disable TCPC0 power */
+ gpio_set_level(GPIO_EN_USB_C0_TCPC_PWR, 0);
+
+ /*
+ * anx3429 requires 10ms reset/power down assertion
+ */
+ msleep(ANX74XX_PWR_L_PWR_H_DELAY_MS);
+ board_set_tcpc_power_mode(USB_PD_PORT_ANX74XX, 1);
+}
+
+void board_tcpc_init(void)
+{
+ int port;
+
+ /* TODO(ecgh): need to wait for disconnected battery? */
+
+ /* Only reset TCPC if not sysjump */
+ if (!system_jumped_to_this_image())
+ board_reset_pd_mcu();
+
+ /* Enable TCPC0 interrupt */
+ gpio_enable_interrupt(GPIO_USB_C0_PD_INT_ODL);
+
+ /* Enable TCPC1 interrupt */
+ gpio_enable_interrupt(GPIO_USB_C1_PD_INT_ODL);
+
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ /* Enable CABLE_DET interrupt for ANX3429 wake from standby */
+ gpio_enable_interrupt(GPIO_USB_C0_CABLE_DET);
+#endif
+ /*
+ * Initialize HPD to low; after sysjump SOC needs to see
+ * HPD pulse to enable video path
+ */
+ for (port = 0; port < CONFIG_USB_PD_PORT_COUNT; port++) {
+ const struct usb_mux *mux = &usb_muxes[port];
+
+ mux->hpd_update(port, 0, 0);
+ }
+}
+
+int pd_snk_is_vbus_provided(int port)
+{
+ /* TODO(ecgh): Detect VBUS via SWCTL_INT and I2C to SN5S330 */
+ return 0;
+}
+
+int board_set_active_charge_port(int port)
+{
+ /* TODO(ecgh): how to disable/enable charge ports? */
+ /* TODO(ecgh): how to check if port sourcing VBUS? */
+ /* TODO(ecgh): disable/enable SN5S330 FETs? */
+ return EC_SUCCESS;
+}
+
+void board_set_charge_limit(int port, int supplier, int charge_ma,
+ int max_ma, int charge_mv)
+{
+ /* TODO(ecgh): check limits */
+ /*
+ * To protect the charge inductor, at voltages above 18V we should
+ * set the current limit to 2.7A.
+ */
+ if (charge_mv > 18000)
+ charge_ma = MIN(2700, charge_ma);
+
+ charge_set_input_current_limit(MAX(charge_ma,
+ CONFIG_CHARGER_INPUT_CURRENT),
+ charge_mv);
+}
+
/* Keyboard scan setting */
struct keyboard_scan_config keyscan_config = {
/* Extra delay when KSO2 is tied to Cr50. */