/* 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. */ /* Volteer family-specific configuration */ #include "adc_chip.h" #include "bb_retimer.h" #include "charge_manager.h" #include "charge_state.h" #include "cros_board_info.h" #include "driver/bc12/pi3usb9201.h" #include "driver/ppc/sn5s330.h" #include "driver/ppc/syv682x.h" #include "driver/tcpm/tusb422.h" #include "driver/temp_sensor/thermistor.h" #include "fan.h" #include "fan_chip.h" #include "gpio.h" #include "hooks.h" #include "i2c.h" #include "keyboard_scan.h" #include "pwm.h" #include "pwm_chip.h" #include "task.h" #include "temp_sensor.h" #include "usbc_ppc.h" #include "usb_mux.h" #include "usb_pd.h" #include "usb_pd_tcpm.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) #define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) #define CPRINTSUSB(format, args...) cprints(CC_USBCHARGE, format, ## args) #define CPRINTFUSB(format, args...) cprintf(CC_USBCHARGE, format, ## args) /******************************************************************************/ /* ADC configuration */ const struct adc_t adc_channels[] = { [ADC_TEMP_SENSOR_1_CHARGER] = { .name = "TEMP_CHARGER", .input_ch = NPCX_ADC_CH0, .factor_mul = ADC_MAX_VOLT, .factor_div = ADC_READ_MAX + 1, .shift = 0, }, [ADC_TEMP_SENSOR_2_PP3300_REGULATOR] = { .name = "TEMP_PP3300_REGULATOR", .input_ch = NPCX_ADC_CH1, .factor_mul = ADC_MAX_VOLT, .factor_div = ADC_READ_MAX + 1, .shift = 0, }, [ADC_TEMP_SENSOR_3_DDR_SOC] = { .name = "TEMP_DDR_SOC", .input_ch = NPCX_ADC_CH8, .factor_mul = ADC_MAX_VOLT, .factor_div = ADC_READ_MAX + 1, .shift = 0, }, [ADC_TEMP_SENSOR_4_FAN] = { .name = "TEMP_FAN", .input_ch = NPCX_ADC_CH3, .factor_mul = ADC_MAX_VOLT, .factor_div = ADC_READ_MAX + 1, .shift = 0, }, }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); /******************************************************************************/ /* BC1.2 charger detect configuration */ const struct pi3usb9201_config_t pi3usb9201_bc12_chips[] = { [USBC_PORT_C0] = { .i2c_port = I2C_PORT_USB_C0, .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS, }, [USBC_PORT_C1] = { .i2c_port = I2C_PORT_USB_C1, .i2c_addr_flags = PI3USB9201_I2C_ADDR_3_FLAGS, }, }; BUILD_ASSERT(ARRAY_SIZE(pi3usb9201_bc12_chips) == USBC_PORT_COUNT); /******************************************************************************/ /* Wake up pins */ const enum gpio_signal hibernate_wake_pins[] = { GPIO_LID_OPEN, GPIO_ACOK_OD, GPIO_POWER_BUTTON_L, GPIO_EC_RST_ODL, }; const int hibernate_wake_pins_used = ARRAY_SIZE(hibernate_wake_pins); /******************************************************************************/ /* Keyboard scan setting */ struct keyboard_scan_config keyscan_config = { /* Increase from 50 us, because KSO_02 passes through the H1. */ .output_settle_us = 80, /* Other values should be the same as the default configuration. */ .debounce_down_us = 9 * MSEC, .debounce_up_us = 30 * MSEC, .scan_period_us = 3 * MSEC, .min_post_scan_delay_us = 1000, .poll_timeout_us = 100 * MSEC, .actual_key_mask = { 0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* full set */ }, }; /******************************************************************************/ /* I2C port map configuration */ const struct i2c_port_t i2c_ports[] = { { .name = "sensor", .port = I2C_PORT_SENSOR, .kbps = 400, .scl = GPIO_EC_I2C0_SENSOR_SCL, .sda = GPIO_EC_I2C0_SENSOR_SDA, }, { .name = "usb_c0", .port = I2C_PORT_USB_C0, /* TODO: design supports 1 MHz, set to 100 KHz for bringup */ .kbps = 100, .scl = GPIO_EC_I2C1_USB_C0_SCL, .sda = GPIO_EC_I2C1_USB_C0_SDA, }, { .name = "usb_c1", .port = I2C_PORT_USB_C1, /* TODO: design supports 1 MHz, set to 100 KHz for bringup */ .kbps = 100, .scl = GPIO_EC_I2C2_USB_C1_SCL, .sda = GPIO_EC_I2C2_USB_C1_SDA, }, { .name = "usb_1_mix", .port = I2C_PORT_USB_1_MIX, .kbps = 100, .scl = GPIO_EC_I2C3_USB_1_MIX_SCL, .sda = GPIO_EC_I2C3_USB_1_MIX_SDA, }, { .name = "power", .port = I2C_PORT_POWER, .kbps = 100, .scl = GPIO_EC_I2C5_POWER_SCL, .sda = GPIO_EC_I2C5_POWER_SDA, }, { .name = "eeprom", .port = I2C_PORT_EEPROM, .kbps = 400, .scl = GPIO_EC_I2C7_EEPROM_SCL, .sda = GPIO_EC_I2C7_EEPROM_SDA, }, }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); /******************************************************************************/ /* PWM configuration */ const struct pwm_t pwm_channels[] = { [PWM_CH_LED1_BLUE] = { .channel = 2, .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, .freq = 2400, }, [PWM_CH_LED2_GREEN] = { .channel = 0, .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, .freq = 2400, }, [PWM_CH_LED3_RED] = { .channel = 1, .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, .freq = 2400, }, [PWM_CH_LED4_SIDESEL] = { .channel = 7, .flags = PWM_CONFIG_ACTIVE_LOW | PWM_CONFIG_DSLEEP, /* Run at a higher frequency than the color PWM signals to avoid * timing-based color shifts. */ .freq = 4800, }, [PWM_CH_FAN] = { .channel = 5, .flags = PWM_CONFIG_OPEN_DRAIN, .freq = 25000 }, [PWM_CH_KBLIGHT] = { .channel = 3, .flags = 0, /* * Set PWM frequency to multiple of 50 Hz and 60 Hz to prevent * flicker. Higher frequencies consume similar average power to * lower PWM frequencies, but higher frequencies record a much * lower maximum power. */ .freq = 2400, }, }; BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); /******************************************************************************/ /* Physical fans. These are logically separate from pwm_channels. */ const struct fan_conf fan_conf_0 = { .flags = FAN_USE_RPM_MODE, .ch = MFT_CH_0, /* Use MFT id to control fan */ .pgood_gpio = -1, .enable_gpio = GPIO_EN_PP5000_FAN, }; /* * Fan specs from datasheet: * Max speed 5900 rpm (+/- 7%), minimum duty cycle 30%. * Minimum speed not specified by RPM. Set minimum RPM to max speed (with * margin) x 30%. * 5900 x 1.07 x 0.30 = 1894, round up to 1900 */ const struct fan_rpm fan_rpm_0 = { .rpm_min = 1900, .rpm_start = 1900, .rpm_max = 5900, }; const struct fan_t fans[FAN_CH_COUNT] = { [FAN_CH_0] = { .conf = &fan_conf_0, .rpm = &fan_rpm_0, }, }; /******************************************************************************/ /* MFT channels. These are logically separate from pwm_channels. */ const struct mft_t mft_channels[] = { [MFT_CH_0] = { .module = NPCX_MFT_MODULE_1, .clk_src = TCKC_LFCLK, .pwm_id = PWM_CH_FAN, }, }; BUILD_ASSERT(ARRAY_SIZE(mft_channels) == MFT_CH_COUNT); /******************************************************************************/ /* Temperature sensor configuration */ const struct temp_sensor_t temp_sensors[] = { [TEMP_SENSOR_1_CHARGER] = {.name = "Charger", .type = TEMP_SENSOR_TYPE_BOARD, .read = get_temp_3v3_30k9_47k_4050b, .idx = ADC_TEMP_SENSOR_1_CHARGER, .action_delay_sec = 1}, [TEMP_SENSOR_2_PP3300_REGULATOR] = {.name = "PP3300 Regulator", .type = TEMP_SENSOR_TYPE_BOARD, .read = get_temp_3v3_30k9_47k_4050b, .idx = ADC_TEMP_SENSOR_2_PP3300_REGULATOR, .action_delay_sec = 1}, [TEMP_SENSOR_3_DDR_SOC] = {.name = "DDR and SOC", .type = TEMP_SENSOR_TYPE_BOARD, .read = get_temp_3v3_30k9_47k_4050b, .idx = ADC_TEMP_SENSOR_3_DDR_SOC, .action_delay_sec = 1}, [TEMP_SENSOR_4_FAN] = {.name = "Fan", .type = TEMP_SENSOR_TYPE_BOARD, .read = get_temp_3v3_30k9_47k_4050b, .idx = ADC_TEMP_SENSOR_4_FAN, .action_delay_sec = 1}, }; BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); /******************************************************************************/ /* EC thermal management configuration */ /* * TODO: b/144941023 - revisit thermal limits with each board spin. */ /* * Tiger Lake specifies 100 C as maximum TDP temperature. THRMTRIP# occurs at * 130 C. However, sensor is located next to DDR, so we need to use the lower * DDR temperature limit (85 C) */ const static struct ec_thermal_config thermal_cpu = { .temp_host = { [EC_TEMP_THRESH_HIGH] = C_TO_K(70), [EC_TEMP_THRESH_HALT] = C_TO_K(80), }, .temp_host_release = { [EC_TEMP_THRESH_HIGH] = C_TO_K(65), }, .temp_fan_off = C_TO_K(35), .temp_fan_max = C_TO_K(50), }; /* * Inductor limits - used for both charger and PP3300 regulator * * Need to use the lower of the charger IC, PP3300 regulator, and the inductors * * Charger max recommended temperature 100C, max absolute temperature 125C * PP3300 regulator: operating range -40 C to 145 C * * Inductors: waiting on info from hardware team, assuming 80 C for now */ const static struct ec_thermal_config thermal_inductor = { .temp_host = { [EC_TEMP_THRESH_HIGH] = C_TO_K(75), [EC_TEMP_THRESH_HALT] = C_TO_K(80), }, .temp_host_release = { [EC_TEMP_THRESH_HIGH] = C_TO_K(65), }, .temp_fan_off = C_TO_K(40), .temp_fan_max = C_TO_K(55), }; struct ec_thermal_config thermal_params[] = { [TEMP_SENSOR_1_CHARGER] = thermal_inductor, [TEMP_SENSOR_2_PP3300_REGULATOR] = thermal_inductor, [TEMP_SENSOR_3_DDR_SOC] = thermal_cpu, [TEMP_SENSOR_4_FAN] = thermal_cpu, }; BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); /******************************************************************************/ /* 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, .addr_flags = TUSB422_I2C_ADDR_FLAGS, }, .drv = &tusb422_tcpm_drv, .usb23 = USBC_PORT_0_USB2_NUM | (USBC_PORT_0_USB3_NUM << 4), }, [USBC_PORT_C1] = { .bus_type = EC_BUS_TYPE_I2C, .i2c_info = { .port = I2C_PORT_USB_C1, .addr_flags = TUSB422_I2C_ADDR_FLAGS, }, .drv = &tusb422_tcpm_drv, .usb23 = USBC_PORT_1_USB2_NUM | (USBC_PORT_1_USB3_NUM << 4), }, }; 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, .i2c_addr_flags = SN5S330_ADDR0_FLAGS, .drv = &sn5s330_drv, }, [USBC_PORT_C1] = { .i2c_port = I2C_PORT_USB_C1, .i2c_addr_flags = SYV682X_ADDR0_FLAGS, .drv = &syv682x_drv, }, }; BUILD_ASSERT(ARRAY_SIZE(ppc_chips) == USBC_PORT_COUNT); unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips); /******************************************************************************/ /* USBC mux configuration - Tiger Lake includes internal mux */ struct usb_mux usb_muxes[] = { [USBC_PORT_C0] = { .driver = &virtual_usb_mux_driver, .hpd_update = &virtual_hpd_update, }, [USBC_PORT_C1] = { .driver = &virtual_usb_mux_driver, .hpd_update = &virtual_hpd_update, }, }; BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT); const struct bb_usb_control bb_controls[] = { [USBC_PORT_C0] = { /* USB-C port 0 doesn't have a retimer */ }, [USBC_PORT_C1] = { .shared_nvm = false, .usb_ls_en_gpio = GPIO_USB_C1_LS_EN, .retimer_rst_gpio = GPIO_USB_C1_RT_RST_ODL, .force_power_gpio = GPIO_USB_C1_RT_FORCE_PWR, }, }; BUILD_ASSERT(ARRAY_SIZE(bb_controls) == USBC_PORT_COUNT); struct usb_retimer usb_retimers[] = { [USBC_PORT_C0] = { /* USB-C port 0 doesn't have a retimer */ }, [USBC_PORT_C1] = { .driver = &bb_usb_retimer, .i2c_port = I2C_PORT_USB_1_MIX, .i2c_addr_flags = USBC_PORT_C1_BB_RETIMER_I2C_ADDR, }, }; BUILD_ASSERT(ARRAY_SIZE(usb_retimers) == USBC_PORT_COUNT); static void baseboard_tcpc_init(void) { /* 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, baseboard_tcpc_init, HOOK_PRIO_INIT_I2C + 1); /******************************************************************************/ /* PPC support routines */ void ppc_interrupt(enum gpio_signal signal) { switch (signal) { case GPIO_USB_C0_PPC_INT_ODL: sn5s330_interrupt(USBC_PORT_C0); break; default: break; } } /******************************************************************************/ /* TCPC support routines */ void board_reset_pd_mcu(void) { /* No reset available for TCPC on port 0 */ } uint16_t tcpc_get_alert_status(void) { uint16_t status = 0; /* * Check which port has the ALERT line set */ if (!gpio_get_level(GPIO_USB_C0_TCPC_INT_ODL)) status |= PD_STATUS_TCPC_ALERT_0; if (!gpio_get_level(GPIO_USB_C1_TCPC_INT_ODL)) status |= PD_STATUS_TCPC_ALERT_1; return status; } void tcpc_alert_event(enum gpio_signal signal) { /* TODO: b/140572591 - check correct operation for Volteer */ int port = -1; switch (signal) { case GPIO_USB_C0_TCPC_INT_ODL: port = USBC_PORT_C0; break; case GPIO_USB_C1_TCPC_INT_ODL: port = USBC_PORT_C1; break; default: return; } schedule_deferred_pd_interrupt(port); } 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, 0); break; case GPIO_USB_C1_BC12_INT_ODL: task_set_event(TASK_ID_USB_CHG_P1, USB_CHG_EVENT_BC12, 0); break; default: break; } } int board_set_active_charge_port(int port) { /* TODO: b/140561826 - check correct operation for Volteer */ int is_valid_port = (port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); int i; if (port == CHARGE_PORT_NONE) { CPRINTSUSB("Disabling all charger ports"); /* Disable all ports. */ for (i = 0; i < ppc_cnt; i++) { /* * Do not return early if one fails otherwise we can * get into a boot loop assertion failure. */ if (ppc_vbus_sink_enable(i, 0)) CPRINTSUSB("Disabling C%d as sink failed.", i); } return EC_SUCCESS; } else if (!is_valid_port) { return EC_ERROR_INVAL; } /* Check if the port is sourcing VBUS. */ if (ppc_is_sourcing_vbus(port)) { CPRINTFUSB("Skip enable C%d", port); return EC_ERROR_INVAL; } CPRINTSUSB("New charge port: C%d", port); /* * Turn off the other ports' sink path FETs, before enabling the * requested charge port. */ for (i = 0; i < ppc_cnt; i++) { if (i == port) continue; if (ppc_vbus_sink_enable(i, 0)) CPRINTSUSB("C%d: sink path disable failed.", i); } /* Enable requested charge port. */ if (ppc_vbus_sink_enable(port, 1)) { CPRINTSUSB("C%d: sink path enable failed.", port); return EC_ERROR_UNKNOWN; } return EC_SUCCESS; } void board_set_charge_limit(int port, int supplier, int charge_ma, int max_ma, int charge_mv) { charge_set_input_current_limit(MAX(charge_ma, CONFIG_CHARGER_INPUT_CURRENT), charge_mv); } void board_overcurrent_event(int port, int is_overcurrented) { /* TODO: b/140561826 - check correct operation for Volteer */ } /* * Delay assertion of PCH_SYS_PWROK from assertion of the PG_EC_ALL_SYS_PWRGD * input. This ensures PCH_SYS_PWROK is asserted only after all rails have * stabilized. See b/144478941 for full discussion. */ __override void board_icl_tgl_all_sys_pwrgood(void) { msleep(50); } static void baseboard_init(void) { /* Illuminate motherboard and daughter board LEDs equally. * TODO(b/139554899): Illuminate only the LED next to the active * charging port. */ pwm_enable(PWM_CH_LED4_SIDESEL, 1); pwm_set_duty(PWM_CH_LED4_SIDESEL, 50); } DECLARE_HOOK(HOOK_INIT, baseboard_init, HOOK_PRIO_DEFAULT); static uint8_t board_id; static enum usb_db_id usb_db_type = USB_DB_NONE; uint8_t get_board_id(void) { return board_id; } /* * Read CBI from i2c eeprom and initialize variables for board variants * * Example for configuring for a USB3 DB: * ectool cbi set 6 2 4 10 */ static void cbi_init(void) { uint32_t cbi_val; uint32_t usb_db_val; /* Board ID */ if (cbi_get_board_version(&cbi_val) != EC_SUCCESS || cbi_val > UINT8_MAX) CPRINTS("CBI: Read Board ID failed"); board_id = cbi_val; CPRINTS("Board ID: %d", board_id); /* FW config */ if (cbi_get_fw_config(&cbi_val) != EC_SUCCESS) { CPRINTS("CBI: Read FW config failed, assuming USB4"); usb_db_val = USB_DB_USB4; } else { usb_db_val = CBI_FW_CONFIG_USB_DB_TYPE(cbi_val); } switch (usb_db_val) { case USB_DB_NONE: CPRINTS("Daughterboard type: None"); break; case USB_DB_USB4: CPRINTS("Daughterboard type: USB4"); break; default: CPRINTS("Daughterboard ID %d not supported", usb_db_val); usb_db_val = USB_DB_NONE; } usb_db_type = usb_db_val; } DECLARE_HOOK(HOOK_INIT, cbi_init, HOOK_PRIO_INIT_I2C + 1);