text: /* 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.
   */
  
  
  #include <stdint.h>
  #include <stdbool.h>
  
  #include "common.h"
  #include "compile_time_macros.h"
  #include "console.h"
  #include "driver/bc12/pi3usb9201_public.h"
  #include "driver/ppc/nx20p348x.h"
  #include "driver/ppc/syv682x_public.h"
  #include "driver/retimer/bb_retimer_public.h"
  #include "driver/tcpm/rt1715.h"
  #include "driver/tcpm/tcpci.h"
  #include "ec_commands.h"
  #include "fw_config.h"
  #include "gpio.h"
  #include "gpio_signal.h"
  #include "hooks.h"
  #include "system.h"
  #include "task.h"
  #include "task_id.h"
  #include "timer.h"
  #include "usbc_config.h"
  #include "usbc_ppc.h"
  #include "usb_charge.h"
  #include "usb_mux.h"
  #include "usb_pd.h"
  #include "usb_pd_tcpm.h"
  
  #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
  #define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
  
  /* USBC TCPC configuration */
  const struct tcpc_config_t tcpc_config[] = {
  	[USBC_PORT_C0] = {
  		.bus_type = EC_BUS_TYPE_I2C,
  		.i2c_info = {
  			.port = I2C_PORT_USB_C0_TCPC,
  			.addr_flags = RT1715_I2C_ADDR_FLAGS,
  		},
  		.drv = &rt1715_tcpm_drv,
  	},
  	[USBC_PORT_C1] = {
  		.bus_type = EC_BUS_TYPE_I2C,
  		.i2c_info = {
  			.port = I2C_PORT_USB_C1_TCPC,
  			.addr_flags = RT1715_I2C_ADDR_FLAGS,
  		},
  		.drv = &rt1715_tcpm_drv,
  	},
  };
  BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == USBC_PORT_COUNT);
  BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT == USBC_PORT_COUNT);
  
  /* USBC PPC configuration */
  struct ppc_config_t ppc_chips[] = {
  	[USBC_PORT_C0] = {
  		.i2c_port = I2C_PORT_USB_C0_C1_PPC_BC,
  		.i2c_addr_flags = SYV682X_ADDR0_FLAGS,
  		.drv = &syv682x_drv,
  	},
  	[USBC_PORT_C1] = {
  		.i2c_port = I2C_PORT_USB_C0_C1_PPC_BC,
  		.i2c_addr_flags = SYV682X_ADDR2_FLAGS,
  		.drv = &syv682x_drv,
  	},
  };
  BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT);
  
  unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
  
  /* USBC mux configuration - Alder Lake includes internal mux */
  static const struct usb_mux usbc0_tcss_usb_mux = {
  	.usb_port = USBC_PORT_C0,
  	.driver = &virtual_usb_mux_driver,
  	.hpd_update = &virtual_hpd_update,
  };
  static const struct usb_mux usbc1_tcss_usb_mux = {
  	.usb_port = USBC_PORT_C1,
  	.driver = &virtual_usb_mux_driver,
  	.hpd_update = &virtual_hpd_update,
  };
  
  const struct usb_mux usb_muxes[] = {
  	[USBC_PORT_C0] = {
  		.usb_port = USBC_PORT_C0,
  		.driver = &bb_usb_retimer,
  		.hpd_update = bb_retimer_hpd_update,
  		.i2c_port = I2C_PORT_USB_C0_C1_RT,
  		.i2c_addr_flags = USBC_PORT_C0_BB_RETIMER_I2C_ADDR,
  		.next_mux = &usbc0_tcss_usb_mux,
  	},
  	[USBC_PORT_C1] = {
  		.usb_port = USBC_PORT_C1,
  		.driver = &bb_usb_retimer,
  		.hpd_update = bb_retimer_hpd_update,
  		.i2c_port = I2C_PORT_USB_C0_C1_RT,
  		.i2c_addr_flags = USBC_PORT_C1_BB_RETIMER_I2C_ADDR,
  		.next_mux = &usbc1_tcss_usb_mux,
  	},
  };
  BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT);
  
  /* BC1.2 charger detect configuration */
  const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = {
  	[USBC_PORT_C0] = {
  		.i2c_port = I2C_PORT_USB_C0_C1_PPC_BC,
  		.i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS,
  	},
  	[USBC_PORT_C1] = {
  		.i2c_port = I2C_PORT_USB_C0_C1_PPC_BC,
  		.i2c_addr_flags = PI3USB9201_I2C_ADDR_1_FLAGS,
  	},
  };
  BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT);
  
  void config_usb_db_type(void)
  {
  	enum ec_cfg_usb_db_type db_type = ec_cfg_usb_db_type();
  
  	/*
  	 * TODO(b/180434685): implement multiple DB types
  	 */
  
  	CPRINTS("Configured USB DB type number is %d", db_type);
  }
  
  __override int bb_retimer_power_enable(const struct usb_mux *me, bool enable)
  {
  	enum gpio_signal rst_signal;
  
  	if (me->usb_port == USBC_PORT_C0) {
  		rst_signal = GPIO_USB_C0_RT_RST_ODL;
  	} else if (me->usb_port == USBC_PORT_C1) {
  		rst_signal = GPIO_USB_C1_RT_RST_ODL;
  	} else {
  		return EC_ERROR_INVAL;
  	}
  
  	/*
  	 * We do not have a load switch for the burnside bridge chips,
  	 * so we only need to sequence reset.
  	 */
  
  	if (enable) {
  		/*
  		 * Tpw, minimum time from VCC to RESET_N de-assertion is 100us.
  		 * For boards that don't provide a load switch control, the
  		 * retimer_init() function ensures power is up before calling
  		 * this function.
  		 */
  		gpio_set_level(rst_signal, 1);
  		/*
  		 * Allow 1ms time for the retimer to power up lc_domain
  		 * which powers I2C controller within retimer
  		 */
  		msleep(1);
  
  		/* This will allow power saving on BB retimer */
  		gpio_set_level(GPIO_TBT_PWR_EN, 1);
  	} else {
  		gpio_set_level(rst_signal, 0);
  		msleep(1);
  		gpio_set_level(GPIO_TBT_PWR_EN, 0);
  	}
  	return EC_SUCCESS;
  }
  
  void board_reset_pd_mcu(void)
  {
  	/* Using RT1716, no reset available for TCPC on port 0/ port 2 */
  }
  
  static void board_tcpc_init(void)
  {
  
  	/* Don't reset TCPCs after initial reset */
  	if (!system_jumped_late()) {
  		board_reset_pd_mcu();
  	}
  
  	/* Enable PPC interrupts. */
  	gpio_enable_interrupt(GPIO_USB_C0_PPC_INT_ODL);
  	gpio_enable_interrupt(GPIO_USB_C1_PPC_INT_ODL);
  
  	/* Enable TCPC interrupts. */
  	gpio_enable_interrupt(GPIO_USB_C0_TCPC_INT_ODL);
  	gpio_enable_interrupt(GPIO_USB_C1_TCPC_INT_ODL);
  
  	/* Enable BC1.2 interrupts. */
  	gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_ODL);
  	gpio_enable_interrupt(GPIO_USB_C1_BC12_INT_ODL);
  
  }
  DECLARE_HOOK(HOOK_INIT, board_tcpc_init, HOOK_PRIO_INIT_CHIPSET);
  
  uint16_t tcpc_get_alert_status(void)
  {
  	uint16_t status = 0;
  
  	if (gpio_get_level(GPIO_USB_C0_TCPC_INT_ODL) == 0)
  		status |= PD_STATUS_TCPC_ALERT_0;
  
  	if (gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL) == 0)
  		status |= PD_STATUS_TCPC_ALERT_1;
  
  	return status;
  }
  
  int ppc_get_alert_status(int port)
  {
  	if (port == USBC_PORT_C0)
  		return gpio_get_level(GPIO_USB_C0_PPC_INT_ODL) == 0;
  	else if (port == USBC_PORT_C1)
  		return gpio_get_level(GPIO_USB_C1_PPC_INT_ODL) == 0;
  	return 0;
  }
  
  void tcpc_alert_event(enum gpio_signal signal)
  {
  	switch (signal) {
  	case GPIO_USB_C0_TCPC_INT_ODL:
  		schedule_deferred_pd_interrupt(USBC_PORT_C0);
  		break;
  	case GPIO_USB_C1_TCPC_INT_ODL:
  		schedule_deferred_pd_interrupt(USBC_PORT_C1);
  		break;
  	default:
  		break;
  	}
  }
  
  void bc12_interrupt(enum gpio_signal signal)
  {
  	switch (signal) {
  	case GPIO_USB_C0_BC12_INT_ODL:
  		task_set_event(TASK_ID_USB_CHG_P0, USB_CHG_EVENT_BC12);
  		break;
  	case GPIO_USB_C1_BC12_INT_ODL:
  		task_set_event(TASK_ID_USB_CHG_P1, USB_CHG_EVENT_BC12);
  		break;
  	default:
  		break;
  	}
  }
  
  void ppc_interrupt(enum gpio_signal signal)
  {
  	switch (signal) {
  	case GPIO_USB_C0_PPC_INT_ODL:
  		syv682x_interrupt(USBC_PORT_C0);
  		break;
  	case GPIO_USB_C1_PPC_INT_ODL:
  		syv682x_interrupt(USBC_PORT_C1);
  		break;
  	default:
  		break;
  	}
  }
  
  void retimer_interrupt(enum gpio_signal signal)
  {
  	/*
  	 * TODO(b/179513527): add USB-C support
  	 */
  }
  
  __override bool board_is_dts_port(int port)
  {
  	return port == USBC_PORT_C0;
  }
  
  __override enum tbt_compat_cable_speed board_get_max_tbt_speed(int port)
  {
  	if (ec_cfg_mlb_usb())
  		return TBT_SS_U32_GEN1_GEN2;
  
  	return TBT_SS_TBT_GEN3;
  }
  