diff options
Diffstat (limited to 'baseboard/honeybuns')
-rw-r--r-- | baseboard/honeybuns/baseboard.c | 377 | ||||
-rw-r--r-- | baseboard/honeybuns/baseboard.h | 4 |
2 files changed, 364 insertions, 17 deletions
diff --git a/baseboard/honeybuns/baseboard.c b/baseboard/honeybuns/baseboard.c index 3b6d4ba626..3425417b81 100644 --- a/baseboard/honeybuns/baseboard.c +++ b/baseboard/honeybuns/baseboard.c @@ -11,24 +11,81 @@ #include "i2c.h" #include "usb_pd.h" #include "system.h" +#include "task.h" #include "timer.h" +#include "usbc_ppc.h" +#include "driver/tcpm/tcpm.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) +#define POWER_BUTTON_SHORT_USEC (300 * MSEC) +#define POWER_BUTTON_LONG_USEC (5000 * MSEC) +#define POWER_BUTTON_DEBOUNCE_USEC (30) + +#define BUTTON_PRESSED_LEVEL 1 +#define BUTTON_RELEASED_LEVEL 0 + +#define BUTTON_EVT_CHANGE BIT(0) +#define BUTTON_EVT_INFO BIT(1) + +enum power { + POWER_OFF, + POWER_ON +}; + +enum button { + BUTTON_RELEASE, + BUTTON_PRESS, + BUTTON_PRESS_POWER_ON, + BUTTON_PRESS_SHORT, + BUTTON_PRESS_LONG, +}; + +#define LED_ON_OFF_BIT BIT(0) +#define LED_COLOR_BIT BIT(2) +#define LED_FLASH_SEQ_LENGTH 8 + +enum led_color { + GREEN, + YELLOW, + OFF, +}; + +static enum power dock_state; +#ifdef SECTION_IS_RW +static int button_level; +static int button_level_pending; +static int dock_mf; +static int led_count; +#endif + /******************************************************************************/ -static void board_power_sequence(void) +__maybe_unused static void board_power_sequence(int enable) { int i; - for(i = 0; i < board_power_seq_count; i++) { - gpio_set_level(board_power_seq[i].signal, - board_power_seq[i].level); - if (board_power_seq[i].delay_ms) - msleep(board_power_seq[i].delay_ms); + if (enable) { + for(i = 0; i < board_power_seq_count; i++) { + gpio_set_level(board_power_seq[i].signal, + board_power_seq[i].level); + CPRINTS("power seq: rail = %d", i); + if (board_power_seq[i].delay_ms) + msleep(board_power_seq[i].delay_ms); + } + } else { + for(i = board_power_seq_count - 1; i >= 0; i--) { + gpio_set_level(board_power_seq[i].signal, + !board_power_seq[i].level); + CPRINTS("sequence[%d]: level = %d", i, + !board_power_seq[i].level); + } } + + dock_state = enable; + CPRINTS("board: Power rails %s", dock_state ? "on" : "off"); } /******************************************************************************/ @@ -39,31 +96,92 @@ const struct i2c_port_t i2c_ports[] = { }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); +#ifdef SECTION_IS_RW +static void baseboard_set_led(enum led_color color) +{ + /* + * TODO(b/164157329): The power button feature should be connected to a + * 2 color LED which is part of the button. Currently, the power button + * LED is a single color LED which is controlled by on the of the power + * rails. Using the status LED now to demonstrate the LED behavior + * associated with a power button press. + */ + CPRINTS("led: color = %d", color); + if (color == OFF) { + gpio_set_level(GPIO_EC_STATUS_LED1, 1); + gpio_set_level(GPIO_EC_STATUS_LED2, 1); + } else if (color == GREEN) { + gpio_set_level(GPIO_EC_STATUS_LED1, 1); + gpio_set_level(GPIO_EC_STATUS_LED2, 0); + } else if (color == YELLOW) { + gpio_set_level(GPIO_EC_STATUS_LED1, 0); + gpio_set_level(GPIO_EC_STATUS_LED2, 0); + } +} + +static void baseboard_led_callback(void); +DECLARE_DEFERRED(baseboard_led_callback); -void baseboard_set_mst_lane_control(int dock_mf) +static void baseboard_led_callback(void) { - /* Check if lane swich level is changing */ - if (dock_mf != gpio_get_level(GPIO_MST_HUB_LANE_SWITCH)) { + /* + * Flash LED on transition using a simple 3 bit counter. Bit 0 controls + * LED on/off and bit 2 controls which color to set during the on phase. + */ + int color = led_count & LED_COLOR_BIT ? dock_mf : dock_mf ^ 1; + + /* + * TODO(b/164157329): This function implements a simple flashing + * transition when the MF preference bit is changed via a long power + * button press sequence. This might need to move to the board function + * if not required/desired on all variants. + */ + + if (led_count & LED_ON_OFF_BIT) + baseboard_set_led(color); + else + baseboard_set_led(OFF); + + /* Flash sequence is 8 steps */ + if (++led_count < LED_FLASH_SEQ_LENGTH) + hook_call_deferred(&baseboard_led_callback_data, 150 * MSEC); +} + +static void baseboard_change_mf_led(void) +{ + led_count = 0; + baseboard_led_callback(); +} + +void baseboard_set_mst_lane_control(int mf) +{ + /* + * The parameter mf reflects the desired lane control value. If the + * current value does not match the desired, then the MST hub must first + * be put into reset, so the MST hub will latch in the correct value + * when it's taken out of reset. + */ + if (mf != gpio_get_level(GPIO_MST_HUB_LANE_SWITCH)) { /* put MST into reset */ gpio_set_level(GPIO_MST_RST_L, 0); msleep(1); - gpio_set_level(GPIO_MST_HUB_LANE_SWITCH, dock_mf); - CPRINTS("MST: lane control = %s", dock_mf ? "high" : "low"); + gpio_set_level(GPIO_MST_HUB_LANE_SWITCH, mf); + CPRINTS("MST: lane control = %s", mf ? "high" : "low"); msleep(1); /* lane control is set, take MST out of reset */ gpio_set_level(GPIO_MST_RST_L, 1); } } +#endif /* SECTION_IS_RW */ static void baseboard_init(void) { #ifdef SECTION_IS_RW - int rv; uint32_t fw_config; #endif /* Turn on power rails */ - board_power_sequence(); + board_power_sequence(1); CPRINTS("board: Power rails enabled"); #ifdef SECTION_IS_RW @@ -77,9 +195,16 @@ static void baseboard_init(void) * field of the CBI. If this value is programmed, then make sure the * MST_LANE_CONTROL gpio matches the mf bit. */ - rv = cbi_get_fw_config(&fw_config); - if (!rv) - baseboard_set_mst_lane_control(fw_config & 1); + if (cbi_get_fw_config(&fw_config)) { + dock_mf = CBI_FW_MF_PREFERENCE(fw_config); + baseboard_set_mst_lane_control(dock_mf); + } + + /* Enable power button interrupt */ + gpio_enable_interrupt(GPIO_PWR_BTN); + /* Set dock mf preference LED */ + baseboard_set_led(dock_mf); + #else /* Set up host port usbc to present Rd on CC lines */ if(baseboard_usbc_init(USB_PD_PORT_HOST)) @@ -91,3 +216,223 @@ static void baseboard_init(void) * power sequencing as soon as I2C bus is initialized. */ DECLARE_HOOK(HOOK_INIT, baseboard_init, HOOK_PRIO_INIT_I2C + 1); + +#ifdef SECTION_IS_RW +static void baseboard_power_on(void) +{ + int port_max = board_get_usb_pd_port_count(); + int port; + + /* Adjust system flags to full PPC init occurs */ + system_clear_reset_flags(EC_RESET_FLAG_POWER_ON); + system_set_reset_flags(EC_RESET_FLAG_EFS); + /* Enable power rails and release reset signals */ + board_power_sequence(1); + /* + * Lane control (realtek MST) must be set prior to releasing MST + * reset. + */ + baseboard_set_mst_lane_control(dock_mf); + /* + * When the power to the PPC is turned off, then back on, the PPC will + * default into dead battery mode. Dead battery resistors are disabled + * as part of the full PPC intializaiton sequence. This is required to + * force a detach event with port parter which can be attached as usbc + * source when honeybuns power rails are off. + */ + for (port = 0; port < port_max; port++) { + ppc_init(port); + msleep(1000); + /* Inform TC state machine that it can resume */ + pd_set_suspend(port, 0); + } + /* Enable usbc interrupts */ + board_enable_usbc_interrupts(); +} + +static void baseboard_power_off(void) +{ + int port_max = board_get_usb_pd_port_count(); + int port; + + /* Put ports in TC suspend state */ + for (port = 0; port < port_max; port++) + pd_set_suspend(port, 1); + + /* Disable ucpd peripheral (prevents interrupts) */ + tcpm_release(USB_PD_PORT_HOST); + /* Disable PPC/TCPC interrupts */ + board_disable_usbc_interrupts(); + /* Go into power off state */ + board_power_sequence(0); +} + +static void baseboard_toggle_mf(void) +{ + uint32_t fw_config; + + if (!cbi_get_fw_config(&fw_config)) { + /* Update the user MF preference stored in CBI */ + fw_config ^= CBI_FW_MF_MASK; + cbi_set_fw_config(fw_config); + /* Update variable used to track user MF preference */ + dock_mf = CBI_FW_MF_PREFERENCE(fw_config); + /* Flash led for visual indication of user MF change */ + baseboard_change_mf_led(); + /* Power the dock off, then on, to apply user preference */ + baseboard_power_off(); + baseboard_power_on(); + } +} + +/* + * Main task entry point for UCPD task + */ +void power_button_task(void *u) +{ + int timer_us = POWER_BUTTON_DEBOUNCE_USEC * 4; + enum button state = BUTTON_RELEASE; + uint32_t evt; + + /* + * Capture current button level in case it's being pressed when the dock + * is powered on. Note timer_us is initialized for debounce time to + * double check. + */ + button_level = gpio_get_level(GPIO_PWR_BTN); + + while (1) { + evt = task_wait_event(timer_us); + timer_us = -1; + + if (evt == BUTTON_EVT_INFO) { + /* Only used for console command for debug */ + CPRINTS("pwrbtn: pwr = %d, state = %d, level = %d", + dock_state, state, button_level); + continue; + } + + switch (state) { + case BUTTON_RELEASE: + /* + * Default wait state: Only need to check if the button + * is pressed and start the short press timer. + */ + if (evt & BUTTON_EVT_CHANGE && button_level == + BUTTON_PRESSED_LEVEL) { + state = BUTTON_PRESS; + timer_us = (POWER_BUTTON_SHORT_USEC - + POWER_BUTTON_DEBOUNCE_USEC); + } + break; + case BUTTON_PRESS: + /* + * Validate short press by ensuring that button is still + * pressed after short press timer expires. + */ + if (evt & BUTTON_EVT_CHANGE && + button_level == BUTTON_RELEASED_LEVEL) { + state = BUTTON_RELEASE; + } else { + /* Start long press timer */ + timer_us = POWER_BUTTON_LONG_USEC - + POWER_BUTTON_SHORT_USEC; + /* + * If dock is currently off, then change to the + * power on state. If dock is already on, then + * advance to short press state. + */ + if (dock_state == POWER_OFF) { + baseboard_power_on(); + state = BUTTON_PRESS_POWER_ON; + } else { + state = BUTTON_PRESS_SHORT; + } + } + break; + case BUTTON_PRESS_POWER_ON: + /* + * Short press recognized and dock was just powered + * on. If button is no longer pressed, then just return + * to the default state. Else, button is still pressed + * after long press timer has expired. + */ + if (evt & BUTTON_EVT_CHANGE && + button_level == BUTTON_RELEASED_LEVEL) { + state = BUTTON_RELEASE; + } else { + state = BUTTON_PRESS_LONG; + baseboard_toggle_mf(); + } + break; + case BUTTON_PRESS_SHORT: + /* + * Short press was recognized and dock power state was + * already on. If button is now released, then turn dock + * off. + */ + if (evt & BUTTON_EVT_CHANGE && + button_level == BUTTON_RELEASED_LEVEL) { + state = BUTTON_RELEASE; + baseboard_power_off(); + } else { + state = BUTTON_PRESS_LONG; + baseboard_toggle_mf(); + } + break; + case BUTTON_PRESS_LONG: + if (evt & BUTTON_EVT_CHANGE && + button_level == BUTTON_RELEASED_LEVEL) { + state = BUTTON_RELEASE; + } + break; + } + } +} + +static void baseboard_power_button_debounce(void) +{ + int level = gpio_get_level(GPIO_PWR_BTN); + + /* Sanity check, level should be same after debounce interval */ + if (level != button_level_pending) + return; + + button_level = level; + task_set_event(TASK_ID_POWER_BUTTON, BUTTON_EVT_CHANGE); +} +DECLARE_DEFERRED(baseboard_power_button_debounce); + +void baseboard_power_button_evt(int level) +{ + button_level_pending = level; + + hook_call_deferred(&baseboard_power_button_debounce_data, + POWER_BUTTON_DEBOUNCE_USEC); +} + +static int command_pwr_btn(int argc, char **argv) +{ + + if (argc == 1) { + task_set_event(TASK_ID_POWER_BUTTON, BUTTON_EVT_INFO); + return EC_SUCCESS; + } + + if (!strcasecmp(argv[1], "on")) { + baseboard_power_on(); + } else if (!strcasecmp(argv[1], "off")) { + baseboard_power_off(); + } else if (!strcasecmp(argv[1], "mf")) { + baseboard_toggle_mf(); + } else { + return EC_ERROR_PARAM1; + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(pwr_btn, command_pwr_btn, + "<on|off|mf>", + "Simulate Power Button Press"); + +#endif diff --git a/baseboard/honeybuns/baseboard.h b/baseboard/honeybuns/baseboard.h index 0faf727ab8..76bcaf490d 100644 --- a/baseboard/honeybuns/baseboard.h +++ b/baseboard/honeybuns/baseboard.h @@ -87,7 +87,8 @@ #define CONFIG_CROS_BOARD_INFO #define CONFIG_BOARD_VERSION_CBI #define CONFIG_CMD_CBI -#define CONFIG_CMD_CBI_SET +#define CBI_FW_MF_MASK BIT(0) +#define CBI_FW_MF_PREFERENCE(val) (val & (CBI_FW_MF_MASK)) /* USB Configuration */ #define CONFIG_USB @@ -228,6 +229,7 @@ enum adc_channel { extern const struct power_seq board_power_seq[]; extern const size_t board_power_seq_count; +void baseboard_power_button_evt(int level); /* * Configure the host port to present Rd on both CC lines. This function is |