diff options
Diffstat (limited to 'common')
188 files changed, 0 insertions, 78826 deletions
diff --git a/common/accel_cal.c b/common/accel_cal.c deleted file mode 100644 index 533a14fbc4..0000000000 --- a/common/accel_cal.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright 2020 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 "common.h" -#include "console.h" -#include "accel_cal.h" - -#define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ##args) - -#define TEMP_RANGE (CONFIG_ACCEL_CAL_MAX_TEMP - CONFIG_ACCEL_CAL_MIN_TEMP) - -void accel_cal_reset(struct accel_cal *cal) -{ - int i; - - for (i = 0; i < cal->num_temp_windows; ++i) { - kasa_reset(&(cal->algos[i].kasa_fit)); - newton_fit_reset(&(cal->algos[i].newton_fit)); - } -} - -static inline int compute_temp_gate(const struct accel_cal *cal, fp_t temp) -{ - int gate = (int) fp_div(fp_mul(temp - CONFIG_ACCEL_CAL_MIN_TEMP, - INT_TO_FP(cal->num_temp_windows)), - TEMP_RANGE); - - return gate < cal->num_temp_windows - ? gate : (cal->num_temp_windows - 1); -} - -test_mockable bool accel_cal_accumulate( - struct accel_cal *cal, uint32_t timestamp, fp_t x, fp_t y, fp_t z, - fp_t temp) -{ - struct accel_cal_algo *algo; - - /* Test that we're within the temperature range. */ - if (temp >= CONFIG_ACCEL_CAL_MAX_TEMP || - temp <= CONFIG_ACCEL_CAL_MIN_TEMP) - return false; - - /* Test that we have a still sample. */ - if (!still_det_update(&cal->still_det, timestamp, x, y, z)) - return false; - - /* We have a still sample, update x, y, and z to the mean. */ - x = cal->still_det.mean_x; - y = cal->still_det.mean_y; - z = cal->still_det.mean_z; - - /* Compute the temp gate. */ - algo = &cal->algos[compute_temp_gate(cal, temp)]; - - kasa_accumulate(&algo->kasa_fit, x, y, z); - if (newton_fit_accumulate(&algo->newton_fit, x, y, z)) { - fp_t radius; - - kasa_compute(&algo->kasa_fit, cal->bias, &radius); - if (ABS(radius - FLOAT_TO_FP(1.0f)) < - CONFIG_ACCEL_CAL_KASA_RADIUS_THRES) - goto accel_cal_accumulate_success; - - newton_fit_compute(&algo->newton_fit, cal->bias, &radius); - if (ABS(radius - FLOAT_TO_FP(1.0f)) < - CONFIG_ACCEL_CAL_NEWTON_RADIUS_THRES) - goto accel_cal_accumulate_success; - } - - return false; - -accel_cal_accumulate_success: - accel_cal_reset(cal); - - return true; -} diff --git a/common/acpi.c b/common/acpi.c deleted file mode 100644 index 941a8b2e56..0000000000 --- a/common/acpi.c +++ /dev/null @@ -1,435 +0,0 @@ -/* Copyright 2013 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 "acpi.h" -#include "battery.h" -#include "common.h" -#include "console.h" -#include "dptf.h" -#include "ec_commands.h" -#include "fan.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_backlight.h" -#include "lpc.h" -#include "pwm.h" -#include "timer.h" -#include "tablet_mode.h" -#include "usb_charge.h" -#include "usb_common.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LPC, outstr) -#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args) -#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) - -/* Last received ACPI command */ -static uint8_t __bss_slow acpi_cmd; -/* First byte of data after ACPI command */ -static uint8_t __bss_slow acpi_addr; -/* Number of data writes after command */ -static int __bss_slow acpi_data_count; -/* Test byte in ACPI memory space */ -static uint8_t __bss_slow acpi_mem_test; - -#ifdef CONFIG_DPTF -static int __bss_slow dptf_temp_sensor_id; /* last sensor ID written */ -static int __bss_slow dptf_temp_threshold; /* last threshold written */ - -/* - * Current DPTF profile number. - * This is by default initialized to 1 if multi-profile DPTF is not supported. - * If multi-profile DPTF is supported, this is by default initialized to 2 under - * the assumption that profile #2 corresponds to lower thresholds and is a safer - * profile to use until board or some EC driver sets the appropriate profile for - * device mode. - */ -static int current_dptf_profile = DPTF_PROFILE_DEFAULT; - -#endif - -/* - * Keep a read cache of four bytes when burst mode is enabled, which is the - * size of the largest non-string memmap data type. - */ -#define ACPI_READ_CACHE_SIZE 4 - -/* Start address that indicates read cache is flushed. */ -#define ACPI_READ_CACHE_FLUSHED (EC_ACPI_MEM_MAPPED_BEGIN - 1) - -/* Calculate size of valid cache based upon end of memmap data. */ -#define ACPI_VALID_CACHE_SIZE(addr) (MIN( \ - EC_ACPI_MEM_MAPPED_SIZE + EC_ACPI_MEM_MAPPED_BEGIN - (addr), \ - ACPI_READ_CACHE_SIZE)) - -/* - * In burst mode, read the requested memmap data and the data immediately - * following it into a cache. For future reads in burst mode, try to grab - * data from the cache. This ensures the continuity of multi-byte reads, - * which is important when dealing with data types > 8 bits. - */ -static struct { - int enabled; - uint8_t start_addr; - uint8_t data[ACPI_READ_CACHE_SIZE]; -} acpi_read_cache; - -/* - * Deferred function to ensure that ACPI burst mode doesn't remain enabled - * indefinitely. - */ -static void acpi_disable_burst_deferred(void) -{ - acpi_read_cache.enabled = 0; - lpc_clear_acpi_status_mask(EC_LPC_STATUS_BURST_MODE); - CPUTS("ACPI missed burst disable?"); -} -DECLARE_DEFERRED(acpi_disable_burst_deferred); - -#ifdef CONFIG_DPTF - -static int acpi_dptf_is_profile_valid(int n) -{ -#ifdef CONFIG_DPTF_MULTI_PROFILE - if ((n < DPTF_PROFILE_VALID_FIRST) || (n > DPTF_PROFILE_VALID_LAST)) - return EC_ERROR_INVAL; -#else - if (n != DPTF_PROFILE_DEFAULT) - return EC_ERROR_INVAL; -#endif - - return EC_SUCCESS; -} - -int acpi_dptf_set_profile_num(int n) -{ - int ret = acpi_dptf_is_profile_valid(n); - - if (ret == EC_SUCCESS) { - current_dptf_profile = n; - if (IS_ENABLED(CONFIG_DPTF_MULTI_PROFILE) && - IS_ENABLED(CONFIG_HOSTCMD_EVENTS)) { - /* Notify kernel to update DPTF profile */ - host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); - } - } - return ret; -} - -int acpi_dptf_get_profile_num(void) -{ - return current_dptf_profile; -} - -#endif - -/* Read memmapped data, returns read data or 0xff on error. */ -static int acpi_read(uint8_t addr) -{ - uint8_t *memmap_addr = (uint8_t *)(lpc_get_memmap_range() + addr - - EC_ACPI_MEM_MAPPED_BEGIN); - - /* Check for out-of-range read. */ - if (addr < EC_ACPI_MEM_MAPPED_BEGIN || - addr >= EC_ACPI_MEM_MAPPED_BEGIN + EC_ACPI_MEM_MAPPED_SIZE) { - CPRINTS("ACPI read 0x%02x (ignored)", - acpi_addr); - return 0xff; - } - - /* Read from cache if enabled (burst mode). */ - if (acpi_read_cache.enabled) { - /* Fetch to cache on miss. */ - if (acpi_read_cache.start_addr == ACPI_READ_CACHE_FLUSHED || - acpi_read_cache.start_addr > addr || - addr - acpi_read_cache.start_addr >= - ACPI_READ_CACHE_SIZE) { - memcpy(acpi_read_cache.data, - memmap_addr, - ACPI_VALID_CACHE_SIZE(addr)); - acpi_read_cache.start_addr = addr; - } - /* Return data from cache. */ - return acpi_read_cache.data[addr - acpi_read_cache.start_addr]; - } else { - /* Read directly from memmap data. */ - return *memmap_addr; - } -} - -/* - * This handles AP writes to the EC via the ACPI I/O port. There are only a few - * ACPI commands (EC_CMD_ACPI_*), but they are all handled here. - */ -int acpi_ap_to_ec(int is_cmd, uint8_t value, uint8_t *resultptr) -{ - int data = 0; - int retval = 0; - int result = 0xff; /* value for bogus read */ - - /* Read command/data; this clears the FRMH status bit. */ - if (is_cmd) { - acpi_cmd = value; - acpi_data_count = 0; - } else { - data = value; - /* - * The first data byte is the ACPI memory address for - * read/write commands. - */ - if (!acpi_data_count++) - acpi_addr = data; - } - - /* Process complete commands */ - if (acpi_cmd == EC_CMD_ACPI_READ && acpi_data_count == 1) { - /* ACPI read cmd + addr */ - switch (acpi_addr) { - case EC_ACPI_MEM_VERSION: - result = EC_ACPI_MEM_VERSION_CURRENT; - break; - case EC_ACPI_MEM_TEST: - result = acpi_mem_test; - break; - case EC_ACPI_MEM_TEST_COMPLIMENT: - result = 0xff - acpi_mem_test; - break; -#ifdef CONFIG_KEYBOARD_BACKLIGHT - case EC_ACPI_MEM_KEYBOARD_BACKLIGHT: - result = kblight_get(); - break; -#endif -#ifdef CONFIG_FANS - case EC_ACPI_MEM_FAN_DUTY: - result = dptf_get_fan_duty_target(); - break; -#endif -#ifdef CONFIG_DPTF - case EC_ACPI_MEM_TEMP_ID: - result = dptf_query_next_sensor_event(); - break; -#endif -#ifdef CONFIG_CHARGER - case EC_ACPI_MEM_CHARGING_LIMIT: - result = dptf_get_charging_current_limit(); - if (result >= 0) - result /= EC_ACPI_MEM_CHARGING_LIMIT_STEP_MA; - else - result = EC_ACPI_MEM_CHARGING_LIMIT_DISABLED; - break; -#endif - - case EC_ACPI_MEM_DEVICE_ORIENTATION: - result = 0; - -#ifdef CONFIG_TABLET_MODE - result = tablet_get_mode() << EC_ACPI_MEM_TBMD_SHIFT; -#endif - -#ifdef CONFIG_DPTF - result |= (acpi_dptf_get_profile_num() & - EC_ACPI_MEM_DDPN_MASK) - << EC_ACPI_MEM_DDPN_SHIFT; -#endif - break; - - case EC_ACPI_MEM_DEVICE_FEATURES0: - case EC_ACPI_MEM_DEVICE_FEATURES1: - case EC_ACPI_MEM_DEVICE_FEATURES2: - case EC_ACPI_MEM_DEVICE_FEATURES3: { - int off = acpi_addr - EC_ACPI_MEM_DEVICE_FEATURES0; - uint32_t val = get_feature_flags0(); - - /* Flush EC_FEATURE_LIMITED bit. Having it reset to 0 - * means that FEATURES[0-3] are supported in the first - * place, and the other bits are valid. - */ - val &= ~1; - - result = val >> (8 * off); - break; - } - case EC_ACPI_MEM_DEVICE_FEATURES4: - case EC_ACPI_MEM_DEVICE_FEATURES5: - case EC_ACPI_MEM_DEVICE_FEATURES6: - case EC_ACPI_MEM_DEVICE_FEATURES7: { - int off = acpi_addr - EC_ACPI_MEM_DEVICE_FEATURES4; - uint32_t val = get_feature_flags1(); - - result = val >> (8 * off); - break; - } - -#ifdef CONFIG_USB_PORT_POWER_DUMB - case EC_ACPI_MEM_USB_PORT_POWER: { - int i; - const int port_count = MIN(8, USB_PORT_COUNT); - - /* - * Convert each USB port power GPIO signal to a bit - * field with max size 8 bits. USB port ID (index) 0 is - * the least significant bit. - */ - result = 0; - for (i = 0; i < port_count; ++i) { - if (gpio_get_level(usb_port_enable[i]) != 0) - result |= 1 << i; - } - break; - } -#endif -#ifdef CONFIG_USBC_RETIMER_FW_UPDATE - case EC_ACPI_MEM_USB_RETIMER_FW_UPDATE: - result = usb_retimer_fw_update_get_result(); - break; -#endif - default: - result = acpi_read(acpi_addr); - break; - } - - /* Send the result byte */ - *resultptr = result; - retval = 1; - - } else if (acpi_cmd == EC_CMD_ACPI_WRITE && acpi_data_count == 2) { - /* ACPI write cmd + addr + data */ - switch (acpi_addr) { - case EC_ACPI_MEM_TEST: - acpi_mem_test = data; - break; -#ifdef CONFIG_BATTERY_V2 - case EC_ACPI_MEM_BATTERY_INDEX: - CPRINTS("ACPI battery %d", data); - battery_memmap_set_index(data); - break; -#endif -#ifdef CONFIG_KEYBOARD_BACKLIGHT - case EC_ACPI_MEM_KEYBOARD_BACKLIGHT: - /* - * Debug output with CR not newline, because the host - * does a lot of keyboard backlights and it scrolls the - * debug console. - */ - CPRINTF("\r[%pT ACPI kblight %d]", - PRINTF_TIMESTAMP_NOW, data); - kblight_set(data); - kblight_enable(data > 0); - break; -#endif -#ifdef CONFIG_FANS - case EC_ACPI_MEM_FAN_DUTY: - dptf_set_fan_duty_target(data); - break; -#endif -#ifdef CONFIG_DPTF - case EC_ACPI_MEM_TEMP_ID: - dptf_temp_sensor_id = data; - break; - case EC_ACPI_MEM_TEMP_THRESHOLD: - dptf_temp_threshold = data + EC_TEMP_SENSOR_OFFSET; - break; - case EC_ACPI_MEM_TEMP_COMMIT: - { - int idx = data & EC_ACPI_MEM_TEMP_COMMIT_SELECT_MASK; - int enable = data & EC_ACPI_MEM_TEMP_COMMIT_ENABLE_MASK; - dptf_set_temp_threshold(dptf_temp_sensor_id, - dptf_temp_threshold, - idx, enable); - break; - } -#endif -#ifdef CONFIG_CHARGER - case EC_ACPI_MEM_CHARGING_LIMIT: - if (data == EC_ACPI_MEM_CHARGING_LIMIT_DISABLED) { - dptf_set_charging_current_limit(-1); - } else { - data *= EC_ACPI_MEM_CHARGING_LIMIT_STEP_MA; - dptf_set_charging_current_limit(data); - } - break; -#endif - -#ifdef CONFIG_USB_PORT_POWER_DUMB - case EC_ACPI_MEM_USB_PORT_POWER: { - int i; - int mode_field = data; - const int port_count = MIN(8, USB_PORT_COUNT); - - /* - * Read the port power bit field (with max size 8 bits) - * and set the charge mode of each USB port accordingly. - * USB port ID 0 is the least significant bit. - */ - for (i = 0; i < port_count; ++i) { - int mode = USB_CHARGE_MODE_DISABLED; - - if (mode_field & 1) - mode = USB_CHARGE_MODE_ENABLED; - - if (usb_charge_set_mode(i, mode, - USB_ALLOW_SUSPEND_CHARGE)) { - CPRINTS("ERROR: could not set charge " - "mode of USB port p%d to %d", - i, mode); - } - mode_field >>= 1; - } - break; - } -#endif -#ifdef CONFIG_USBC_RETIMER_FW_UPDATE - case EC_ACPI_MEM_USB_RETIMER_FW_UPDATE: - usb_retimer_fw_update_process_op( - EC_ACPI_MEM_USB_RETIMER_PORT(data), - EC_ACPI_MEM_USB_RETIMER_OP(data)); - break; -#endif - default: - CPRINTS("ACPI write 0x%02x = 0x%02x (ignored)", - acpi_addr, data); - break; - } - } else if (acpi_cmd == EC_CMD_ACPI_QUERY_EVENT && !acpi_data_count) { - /* Clear and return the lowest host event */ - int evt_index = lpc_get_next_host_event(); - CPRINTS("ACPI query = %d", evt_index); - *resultptr = evt_index; - retval = 1; - } else if (acpi_cmd == EC_CMD_ACPI_BURST_ENABLE && !acpi_data_count) { - /* - * TODO: The kernel only enables BURST when doing multi-byte - * value reads over the ACPI port. We don't do such reads - * when our memmap data can be accessed directly over LPC, - * so on LM4, for example, this is dead code. We might want - * to add a config to skip this code for certain chips. - */ - acpi_read_cache.enabled = 1; - acpi_read_cache.start_addr = ACPI_READ_CACHE_FLUSHED; - - /* Enter burst mode */ - lpc_set_acpi_status_mask(EC_LPC_STATUS_BURST_MODE); - - /* - * Disable from deferred function in case burst mode is enabled - * for an extremely long time (ex. kernel bug / crash). - */ - hook_call_deferred(&acpi_disable_burst_deferred_data, 1*SECOND); - - /* ACPI 5.0-12.3.3: Burst ACK */ - *resultptr = 0x90; - retval = 1; - } else if (acpi_cmd == EC_CMD_ACPI_BURST_DISABLE && !acpi_data_count) { - acpi_read_cache.enabled = 0; - - /* Leave burst mode */ - hook_call_deferred(&acpi_disable_burst_deferred_data, -1); - lpc_clear_acpi_status_mask(EC_LPC_STATUS_BURST_MODE); - } - - return retval; -} diff --git a/common/adc.c b/common/adc.c deleted file mode 100644 index c9e3a36e57..0000000000 --- a/common/adc.c +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2013 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. - */ - -/* ADC module for Chrome EC */ - -#include "adc.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "util.h" - -/* 'adc' console command is not supported in continuous mode */ -#ifndef CONFIG_ADC_PROFILE_FAST_CONTINUOUS -static enum adc_channel find_adc_channel_by_name(const char *name) -{ - const struct adc_t *ch = adc_channels; - int i; - - if (!name || !*name) - return ADC_CH_COUNT; - - for (i = 0; i < ADC_CH_COUNT; i++, ch++) { - if (!strcasecmp(name, ch->name)) - return i; - } - - return ADC_CH_COUNT; -} - -static int print_one_adc(int channel) -{ - int v; - - v = adc_read_channel(channel); - if (v == ADC_READ_ERROR) - return EC_ERROR_UNKNOWN; - ccprintf(" %s = %d mV\n", adc_channels[channel].name, v); - return EC_SUCCESS; -} - -static int command_adc(int argc, char **argv) -{ - int i, ret; - - /* If a channel is specified, read only that one */ - if (argc == 2) { - i = find_adc_channel_by_name(argv[1]); - if (i == ADC_CH_COUNT) - return EC_ERROR_PARAM1; - return print_one_adc(i); - } else { - /* Otherwise print them all */ - for (i = 0; i < ADC_CH_COUNT; ++i) { - ret = print_one_adc(i); - if (ret) - return ret; - } - return EC_SUCCESS; - } -} -DECLARE_CONSOLE_COMMAND(adc, command_adc, - "[name]", - "Print ADC channel(s)"); - -static enum ec_status hc_adc_read(struct host_cmd_handler_args *args) -{ - const struct ec_params_adc_read *params = args->params; - struct ec_response_adc_read *resp = args->response; - enum adc_channel ch = (enum adc_channel)params->adc_channel; - int32_t adc_value; - - if (ch >= ADC_CH_COUNT) - return EC_RES_INVALID_PARAM; - - adc_value = adc_read_channel(ch); - if (adc_value == ADC_READ_ERROR) - return EC_RES_ERROR; - - resp->adc_value = adc_value; - args->response_size = sizeof(*resp); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_ADC_READ, hc_adc_read, EC_VER_MASK(0)); -#endif /* CONFIG_ADC_PROFILE_FAST_CONTINUOUS */ diff --git a/common/aes-gcm.c b/common/aes-gcm.c deleted file mode 120000 index 3176d85ff8..0000000000 --- a/common/aes-gcm.c +++ /dev/null @@ -1 +0,0 @@ -../third_party/boringssl/common/aes-gcm.c
\ No newline at end of file diff --git a/common/aes.c b/common/aes.c deleted file mode 120000 index ed10836943..0000000000 --- a/common/aes.c +++ /dev/null @@ -1 +0,0 @@ -../third_party/boringssl/common/aes.c
\ No newline at end of file diff --git a/common/als.c b/common/als.c deleted file mode 100644 index 2e9c7ba96c..0000000000 --- a/common/als.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright 2013 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. - */ - -/* This provides the interface for any Ambient Light Sensors that are connected - * to the EC instead of the AP. - */ - -#include "als.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_ALS, outstr) -#define CPRINTS(format, args...) cprints(CC_ALS, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_ALS, format, ## args) - - -#define ALS_POLL_PERIOD SECOND - -static int task_timeout = -1; - -int als_read(enum als_id id, int *lux) -{ - int af = als[id].attenuation_factor; - return als[id].read(lux, af); -} - -void als_task(void *u) -{ - int i, val; - uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_ALS); - uint16_t als_data; - - while (1) { - task_wait_event(task_timeout); - - /* If task was disabled while waiting do not read from ALS */ - if (task_timeout < 0) - continue; - - for (i = 0; i < EC_ALS_ENTRIES && i < ALS_COUNT; i++) { - als_data = als_read(i, &val) == EC_SUCCESS ? val : 0; - mapped[i] = als_data; - } - } -} - -static void als_task_enable(void) -{ - int fail_count = 0; - int err; - int i; - - for (i = 0; i < EC_ALS_ENTRIES && i < ALS_COUNT; i++) { - err = als[i].init(); - if (err) { - fail_count++; - CPRINTF("%s ALS sensor failed to initialize, err=%d\n", - als[i].name, err); - } - } - - /* - * If all the ALS filed to initialize, disable the ALS task. - */ - if (fail_count == ALS_COUNT) - task_timeout = -1; - else - task_timeout = ALS_POLL_PERIOD; - - task_wake(TASK_ID_ALS); -} - -static void als_task_disable(void) -{ - task_timeout = -1; -} - -static void als_task_init(void) -{ - /* - * Enable ALS task in S0 only and may need to re-enable - * when sysjumped. - */ - if (system_jumped_late() && - chipset_in_state(CHIPSET_STATE_ON)) - als_task_enable(); -} - -DECLARE_HOOK(HOOK_CHIPSET_RESUME, als_task_enable, HOOK_PRIO_ALS_INIT); -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, als_task_disable, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_INIT, als_task_init, HOOK_PRIO_ALS_INIT); - -/*****************************************************************************/ -/* Console commands */ - -#ifdef CONFIG_CMD_ALS -static int command_als(int argc, char **argv) -{ - int i, rv, val; - - for (i = 0; i < ALS_COUNT; i++) { - ccprintf("%s: ", als[i].name); - rv = als_read(i, &val); - switch (rv) { - case EC_SUCCESS: - ccprintf("%d lux\n", val); - break; - default: - ccprintf("Error %d\n", rv); - } - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(als, command_als, - NULL, - "Print ALS values"); -#endif diff --git a/common/ap_hang_detect.c b/common/ap_hang_detect.c deleted file mode 100644 index 0c9e7a186d..0000000000 --- a/common/ap_hang_detect.c +++ /dev/null @@ -1,238 +0,0 @@ -/* Copyright 2013 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. - */ - -/* AP hang detect logic */ - -#include "ap_hang_detect.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "lid_switch.h" -#include "power_button.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -static struct ec_params_hang_detect hdparams; - -static int active; /* Is hang detect timer active / counting? */ -static int timeout_will_reboot; /* Will the deferred call reboot the AP? */ - -/** - * Handle the hang detect timer expiring. - */ -static void hang_detect_deferred(void); -DECLARE_DEFERRED(hang_detect_deferred); - -static void hang_detect_deferred(void) -{ - /* If we're no longer active, nothing to do */ - if (!active) - return; - - /* If we're rebooting the AP, stop hang detection */ - if (timeout_will_reboot) { - CPRINTS("hang detect triggering warm reboot"); - host_set_single_event(EC_HOST_EVENT_HANG_REBOOT); - chipset_reset(CHIPSET_RESET_HANG_REBOOT); - active = 0; - return; - } - - /* Otherwise, we're starting with the host event */ - CPRINTS("hang detect sending host event"); - host_set_single_event(EC_HOST_EVENT_HANG_DETECT); - - /* If we're also rebooting, defer for the remaining delay */ - if (hdparams.warm_reboot_timeout_msec) { - CPRINTS("hang detect continuing (for reboot)"); - timeout_will_reboot = 1; - hook_call_deferred(&hang_detect_deferred_data, - (hdparams.warm_reboot_timeout_msec - - hdparams.host_event_timeout_msec) * MSEC); - } else { - /* Not rebooting, so go back to idle */ - active = 0; - } -} - -/** - * Start the hang detect timers. - */ -static void hang_detect_start(const char *why) -{ - /* If already active, don't restart timer */ - if (active) - return; - - if (hdparams.host_event_timeout_msec) { - CPRINTS("hang detect started on %s (for event)", why); - timeout_will_reboot = 0; - active = 1; - hook_call_deferred(&hang_detect_deferred_data, - hdparams.host_event_timeout_msec * MSEC); - } else if (hdparams.warm_reboot_timeout_msec) { - CPRINTS("hang detect started on %s (for reboot)", why); - timeout_will_reboot = 1; - active = 1; - hook_call_deferred(&hang_detect_deferred_data, - hdparams.warm_reboot_timeout_msec * MSEC); - } -} - -/** - * Stop the hang detect timers. - */ -static void hang_detect_stop(const char *why) -{ - if (active) - CPRINTS("hang detect stopped on %s", why); - - active = 0; -} - -void hang_detect_stop_on_host_command(void) -{ - if (hdparams.flags & EC_HANG_STOP_ON_HOST_COMMAND) - hang_detect_stop("host cmd"); -} - -/*****************************************************************************/ -/* Hooks */ - -static void hang_detect_power_button(void) -{ - if (power_button_is_pressed()) { - if (hdparams.flags & EC_HANG_START_ON_POWER_PRESS) - hang_detect_start("power button"); - } else { - if (hdparams.flags & EC_HANG_STOP_ON_POWER_RELEASE) - hang_detect_stop("power button"); - } -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, hang_detect_power_button, - HOOK_PRIO_DEFAULT); - -static void hang_detect_lid(void) -{ - if (lid_is_open()) { - if (hdparams.flags & EC_HANG_START_ON_LID_OPEN) - hang_detect_start("lid open"); - } else { - if (hdparams.flags & EC_HANG_START_ON_LID_CLOSE) - hang_detect_start("lid close"); - } -} -DECLARE_HOOK(HOOK_LID_CHANGE, hang_detect_lid, HOOK_PRIO_DEFAULT); - -static void hang_detect_resume(void) -{ - if (hdparams.flags & EC_HANG_START_ON_RESUME) - hang_detect_start("resume"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, hang_detect_resume, HOOK_PRIO_DEFAULT); - -static void hang_detect_suspend(void) -{ - if (hdparams.flags & EC_HANG_STOP_ON_SUSPEND) - hang_detect_stop("suspend"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, hang_detect_suspend, HOOK_PRIO_DEFAULT); - -static void hang_detect_shutdown(void) -{ - /* Stop the timers */ - hang_detect_stop("shutdown"); - - /* Disable hang detection; it must be enabled every boot */ - memset(&hdparams, 0, sizeof(hdparams)); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, hang_detect_shutdown, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Host command */ - -static enum ec_status -hang_detect_host_command(struct host_cmd_handler_args *args) -{ - const struct ec_params_hang_detect *p = args->params; - - /* Handle stopping hang timer on request */ - if (p->flags & EC_HANG_STOP_NOW) { - hang_detect_stop("ap request"); - - /* Ignore the other params */ - return EC_RES_SUCCESS; - } - - /* Handle starting hang timer on request */ - if (p->flags & EC_HANG_START_NOW) { - hang_detect_start("ap request"); - - /* Ignore the other params */ - return EC_RES_SUCCESS; - } - - /* If hang detect transitioning to disabled, stop timers */ - if (hdparams.flags && !p->flags) - hang_detect_stop("ap flags=0"); - - /* Save new params */ - hdparams = *p; - CPRINTS("hang detect flags=0x%x, event=%d ms, reboot=%d ms", - hdparams.flags, hdparams.host_event_timeout_msec, - hdparams.warm_reboot_timeout_msec); - - /* - * If warm reboot timeout is shorter than host event timeout, ignore - * the host event timeout because a warm reboot will win. - */ - if (hdparams.warm_reboot_timeout_msec && - hdparams.warm_reboot_timeout_msec <= - hdparams.host_event_timeout_msec) - hdparams.host_event_timeout_msec = 0; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_HANG_DETECT, - hang_detect_host_command, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Console command */ - -static int command_hang_detect(int argc, char **argv) -{ - ccprintf("flags: 0x%x\n", hdparams.flags); - - ccputs("event: "); - if (hdparams.host_event_timeout_msec) - ccprintf("%d ms\n", hdparams.host_event_timeout_msec); - else - ccputs("disabled\n"); - - ccputs("reboot: "); - if (hdparams.warm_reboot_timeout_msec) - ccprintf("%d ms\n", hdparams.warm_reboot_timeout_msec); - else - ccputs("disabled\n"); - - ccputs("status: "); - if (active) - ccprintf("active for %s\n", - timeout_will_reboot ? "reboot" : "event"); - else - ccputs("inactive\n"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hangdet, command_hang_detect, - NULL, - "Print hang detect state"); diff --git a/common/audio_codec.c b/common/audio_codec.c deleted file mode 100644 index 3f7203ad15..0000000000 --- a/common/audio_codec.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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. - */ - -#include "audio_codec.h" -#include "console.h" -#include "host_command.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args) - -static const uint32_t capabilities = - 0 -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM - | BIT(EC_CODEC_CAP_WOV_AUDIO_SHM) -#endif -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM - | BIT(EC_CODEC_CAP_WOV_LANG_SHM) -#endif - ; - -static struct { - uint8_t cap; - uint8_t type; - uintptr_t *addr; - uint32_t len; -} shms[EC_CODEC_SHM_ID_LAST]; - -static enum ec_status get_capabilities(struct host_cmd_handler_args *args) -{ - struct ec_response_ec_codec_get_capabilities *r = args->response; - - r->capabilities = capabilities; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -static enum ec_status get_shm_addr(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec *p = args->params; - struct ec_response_ec_codec_get_shm_addr *r = args->response; - const uint8_t shm_id = p->get_shm_addr_param.shm_id; - - if (shm_id >= EC_CODEC_SHM_ID_LAST) - return EC_RES_INVALID_PARAM; - if (!shms[shm_id].addr || !audio_codec_capable(shms[shm_id].cap)) - return EC_RES_INVALID_PARAM; - if (!*shms[shm_id].addr && - shms[shm_id].type == EC_CODEC_SHM_TYPE_EC_RAM) - return EC_RES_ERROR; - - r->len = shms[shm_id].len; - r->type = shms[shm_id].type; - r->phys_addr = *shms[shm_id].addr; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -static enum ec_status set_shm_addr(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec *p = args->params; - const uint8_t shm_id = p->set_shm_addr_param.shm_id; - uintptr_t ap_addr, ec_addr; - - if (shm_id >= EC_CODEC_SHM_ID_LAST) - return EC_RES_INVALID_PARAM; - if (!shms[shm_id].addr || !audio_codec_capable(shms[shm_id].cap)) - return EC_RES_INVALID_PARAM; - if (p->set_shm_addr_param.len < shms[shm_id].len) - return EC_RES_INVALID_PARAM; - if (*shms[shm_id].addr) - return EC_RES_BUSY; - - ap_addr = (uintptr_t)p->set_shm_addr_param.phys_addr; - if (audio_codec_memmap_ap_to_ec(ap_addr, &ec_addr) != EC_SUCCESS) - return EC_RES_ERROR; - *shms[shm_id].addr = ec_addr; - - args->response_size = 0; - return EC_RES_SUCCESS; -} - -static enum ec_status (*sub_cmds[])(struct host_cmd_handler_args *) = { - [EC_CODEC_GET_CAPABILITIES] = get_capabilities, - [EC_CODEC_GET_SHM_ADDR] = get_shm_addr, - [EC_CODEC_SET_SHM_ADDR] = set_shm_addr, -}; - -#ifdef DEBUG_AUDIO_CODEC -static char *strcmd[] = { - [EC_CODEC_GET_CAPABILITIES] = "EC_CODEC_GET_CAPABILITIES", - [EC_CODEC_GET_SHM_ADDR] = "EC_CODEC_GET_SHM_ADDR", - [EC_CODEC_SET_SHM_ADDR] = "EC_CODEC_SET_SHM_ADDR", -}; -BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd)); -#endif - -static enum ec_status host_command(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec *p = args->params; - -#ifdef DEBUG_AUDIO_CODEC - CPRINTS("subcommand: %s", strcmd[p->cmd]); -#endif - - if (p->cmd < EC_CODEC_SUBCMD_COUNT) - return sub_cmds[p->cmd](args); - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC, host_command, EC_VER_MASK(0)); - -/* - * Exported interfaces. - */ -int audio_codec_capable(uint8_t cap) -{ - return capabilities & BIT(cap); -} - -int audio_codec_register_shm(uint8_t shm_id, uint8_t cap, - uintptr_t *addr, uint32_t len, uint8_t type) -{ - if (shm_id >= EC_CODEC_SHM_ID_LAST) - return EC_ERROR_INVAL; - if (cap >= EC_CODEC_CAP_LAST) - return EC_ERROR_INVAL; - if (shms[shm_id].addr || shms[shm_id].len) - return EC_ERROR_BUSY; - - shms[shm_id].cap = cap; - shms[shm_id].addr = addr; - shms[shm_id].len = len; - shms[shm_id].type = type; - - return EC_SUCCESS; -} - -__attribute__((weak)) -int audio_codec_memmap_ap_to_ec(uintptr_t ap_addr, uintptr_t *ec_addr) -{ - return EC_ERROR_UNIMPLEMENTED; -} - -int16_t audio_codec_s16_scale_and_clip(int16_t orig, uint8_t scalar) -{ - int32_t val; - - val = (int32_t)orig * (int32_t)scalar; - val = MIN(val, (int32_t)INT16_MAX); - val = MAX(val, (int32_t)INT16_MIN); - return val; -} diff --git a/common/audio_codec_dmic.c b/common/audio_codec_dmic.c deleted file mode 100644 index c4f0b07a46..0000000000 --- a/common/audio_codec_dmic.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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. - */ - -#include "audio_codec.h" -#include "console.h" -#include "host_command.h" - -#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args) - -static enum ec_status dmic_get_max_gain(struct host_cmd_handler_args *args) -{ - struct ec_response_ec_codec_dmic_get_max_gain *r = args->response; - - if (audio_codec_dmic_get_max_gain(&r->max_gain) != EC_SUCCESS) - return EC_RES_ERROR; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -static enum ec_status dmic_set_gain_idx(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_dmic *p = args->params; - - if (audio_codec_dmic_set_gain_idx( - p->set_gain_idx_param.channel, - p->set_gain_idx_param.gain) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -static enum ec_status dmic_get_gain_idx(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_dmic *p = args->params; - struct ec_response_ec_codec_dmic_get_gain_idx *r = args->response; - - if (audio_codec_dmic_get_gain_idx( - p->get_gain_idx_param.channel, &r->gain) != EC_SUCCESS) - return EC_RES_ERROR; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -static enum ec_status (*sub_cmds[])(struct host_cmd_handler_args *) = { - [EC_CODEC_DMIC_GET_MAX_GAIN] = dmic_get_max_gain, - [EC_CODEC_DMIC_SET_GAIN_IDX] = dmic_set_gain_idx, - [EC_CODEC_DMIC_GET_GAIN_IDX] = dmic_get_gain_idx, -}; - -#ifdef DEBUG_AUDIO_CODEC -static char *strcmd[] = { - [EC_CODEC_DMIC_GET_MAX_GAIN] = "EC_CODEC_DMIC_GET_MAX_GAIN", - [EC_CODEC_DMIC_SET_GAIN_IDX] = "EC_CODEC_DMIC_SET_GAIN_IDX", - [EC_CODEC_DMIC_GET_GAIN_IDX] = "EC_CODEC_DMIC_GET_GAIN_IDX", -}; -BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd)); -#endif - -static enum ec_status dmic_host_command(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_dmic *p = args->params; - -#ifdef DEBUG_AUDIO_CODEC - CPRINTS("DMIC subcommand: %s", strcmd[p->cmd]); -#endif - - if (p->cmd < EC_CODEC_DMIC_SUBCMD_COUNT) - return sub_cmds[p->cmd](args); - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC_DMIC, dmic_host_command, EC_VER_MASK(0)); - -#ifdef CONFIG_AUDIO_CODEC_DMIC_SOFTWARE_GAIN -static uint8_t channel_gains[EC_CODEC_DMIC_CHANNEL_COUNT]; - -int audio_codec_dmic_get_max_gain(uint8_t *gain) -{ - *gain = CONFIG_AUDIO_CODEC_DMIC_MAX_SOFTWARE_GAIN; - return EC_SUCCESS; -} - -int audio_codec_dmic_set_gain_idx(uint8_t channel, uint8_t gain) -{ - if (channel >= ARRAY_SIZE(channel_gains)) - return EC_ERROR_INVAL; - if (gain > CONFIG_AUDIO_CODEC_DMIC_MAX_SOFTWARE_GAIN) - return EC_ERROR_INVAL; - - channel_gains[channel] = gain; - return EC_SUCCESS; -} - -int audio_codec_dmic_get_gain_idx(uint8_t channel, uint8_t *gain) -{ - if (channel >= ARRAY_SIZE(channel_gains)) - return EC_ERROR_INVAL; - - *gain = channel_gains[channel]; - return EC_SUCCESS; -} -#endif diff --git a/common/audio_codec_i2s_rx.c b/common/audio_codec_i2s_rx.c deleted file mode 100644 index aeae19bdca..0000000000 --- a/common/audio_codec_i2s_rx.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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. - */ - -#include "audio_codec.h" -#include "console.h" -#include "host_command.h" - -#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args) - -static uint8_t i2s_rx_enabled; - -static enum ec_status i2s_rx_enable(struct host_cmd_handler_args *args) -{ - if (i2s_rx_enabled) - return EC_RES_BUSY; - - if (audio_codec_i2s_rx_enable() != EC_SUCCESS) - return EC_RES_ERROR; - - i2s_rx_enabled = 1; - - return EC_RES_SUCCESS; -} - -static enum ec_status i2s_rx_disable(struct host_cmd_handler_args *args) -{ - if (!i2s_rx_enabled) - return EC_RES_BUSY; - - if (audio_codec_i2s_rx_disable() != EC_SUCCESS) - return EC_RES_ERROR; - - i2s_rx_enabled = 0; - - return EC_RES_SUCCESS; -} - -static enum ec_status -i2s_rx_set_sample_depth(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_i2s_rx *p = args->params; - const uint8_t depth = p->set_sample_depth_param.depth; - - if (i2s_rx_enabled) - return EC_RES_BUSY; - if (depth >= EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT) - return EC_RES_INVALID_PARAM; - - if (audio_codec_i2s_rx_set_sample_depth(depth) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -static enum ec_status i2s_rx_set_daifmt(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_i2s_rx *p = args->params; - const uint8_t daifmt = p->set_daifmt_param.daifmt; - - if (i2s_rx_enabled) - return EC_RES_BUSY; - if (daifmt >= EC_CODEC_I2S_RX_DAIFMT_COUNT) - return EC_RES_INVALID_PARAM; - - if (audio_codec_i2s_rx_set_daifmt(daifmt) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -static enum ec_status i2s_rx_set_bclk(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_i2s_rx *p = args->params; - - if (i2s_rx_enabled) - return EC_RES_BUSY; - - if (audio_codec_i2s_rx_set_bclk(p->set_bclk_param.bclk) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -static enum ec_status i2s_rx_reset(struct host_cmd_handler_args *args) -{ - if (audio_codec_i2s_rx_disable() != EC_SUCCESS) - return EC_RES_ERROR; - - i2s_rx_enabled = 0; - - return EC_RES_SUCCESS; -} - -static enum ec_status (*sub_cmds[])(struct host_cmd_handler_args *) = { - [EC_CODEC_I2S_RX_ENABLE] = i2s_rx_enable, - [EC_CODEC_I2S_RX_DISABLE] = i2s_rx_disable, - [EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH] = i2s_rx_set_sample_depth, - [EC_CODEC_I2S_RX_SET_DAIFMT] = i2s_rx_set_daifmt, - [EC_CODEC_I2S_RX_SET_BCLK] = i2s_rx_set_bclk, - [EC_CODEC_I2S_RX_RESET] = i2s_rx_reset, -}; - -#ifdef DEBUG_AUDIO_CODEC -static char *strcmd[] = { - [EC_CODEC_I2S_RX_ENABLE] = "EC_CODEC_I2S_RX_ENABLE", - [EC_CODEC_I2S_RX_DISABLE] = "EC_CODEC_I2S_RX_DISABLE", - [EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH] = "EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH", - [EC_CODEC_I2S_RX_SET_DAIFMT] = "EC_CODEC_I2S_RX_SET_DAIFMT", - [EC_CODEC_I2S_RX_SET_BCLK] = "EC_CODEC_I2S_RX_SET_BCLK", - [EC_CODEC_I2S_RX_RESET] = "EC_CODEC_I2S_RESET", -}; -BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd)); -#endif - -static enum ec_status i2s_rx_host_command(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_i2s_rx *p = args->params; - -#ifdef DEBUG_AUDIO_CODEC - CPRINTS("I2S RX subcommand: %s", strcmd[p->cmd]); -#endif - - if (p->cmd < EC_CODEC_I2S_RX_SUBCMD_COUNT) - return sub_cmds[p->cmd](args); - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC_I2S_RX, - i2s_rx_host_command, EC_VER_MASK(0)); diff --git a/common/audio_codec_wov.c b/common/audio_codec_wov.c deleted file mode 100644 index f84e45f342..0000000000 --- a/common/audio_codec_wov.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - * 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. - */ - -#include "audio_codec.h" -#include "console.h" -#include "host_command.h" -#include "hotword_dsp_api.h" -#include "sha256.h" -#include "system.h" -#include "task.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args) - -/* - * To shorten the variable names, or the following code is likely to greater - * than 80 columns. - */ -#define AUDIO_BUF_LEN CONFIG_AUDIO_CODEC_WOV_AUDIO_BUF_LEN -#define LANG_BUF_LEN CONFIG_AUDIO_CODEC_WOV_LANG_BUF_LEN - -static uint8_t lang_hash[SHA256_DIGEST_SIZE]; -static uint32_t lang_len; - -/* - * The variables below are shared between host command and WoV task. This lock - * is designed to protect them. - */ -static struct mutex lock; - -/* - * wov_enabled is shared. - * - * host command task: - * - is the only writer - * - no need to lock if read - */ -static uint8_t wov_enabled; - -/* - * hotword_detected is shared. - */ -static uint8_t hotword_detected; - -/* - * audio_buf_rp and audio_buf_wp are shared. - * - * Note that: sample width is 16-bit. - * - * Typical ring-buffer implementation: - * If audio_buf_rp == audio_buf_wp, empty. - * If (audio_buf_wp + 2) % buf_len == audio_buf_rp, full. - */ -static uint32_t audio_buf_rp, audio_buf_wp; - -static int is_buf_full(void) -{ - return ((audio_buf_wp + 2) % AUDIO_BUF_LEN) == audio_buf_rp; -} - -/* only used by host command */ -static uint8_t speech_lib_loaded; - -static int check_lang_buf(uint8_t *data, uint32_t len, const uint8_t *hash) -{ - /* - * Note: sizeof(struct sha256_ctx) = 200 bytes - * should put into .bss, or stack is likely to overflow (~640 bytes) - */ - static struct sha256_ctx ctx; - uint8_t *digest; - int i; - uint8_t *p = (uint8_t *)audio_codec_wov_lang_buf_addr; - - SHA256_init(&ctx); - SHA256_update(&ctx, data, len); - digest = SHA256_final(&ctx); - -#ifdef DEBUG_AUDIO_CODEC - CPRINTS("data=%08x len=%d", data, len); - hexdump(digest, SHA256_DIGEST_SIZE); -#endif - - if (memcmp(digest, hash, SHA256_DIGEST_SIZE) != 0) - return EC_ERROR_UNKNOWN; - - for (i = len; i < LANG_BUF_LEN; ++i) - if (p[i]) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -} - -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM -static enum ec_status wov_set_lang_shm(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_wov *p = args->params; - const struct ec_param_ec_codec_wov_set_lang_shm *pp = - &p->set_lang_shm_param; - - if (pp->total_len > LANG_BUF_LEN) - return EC_RES_INVALID_PARAM; - if (wov_enabled) - return EC_RES_BUSY; - - if (check_lang_buf((uint8_t *)audio_codec_wov_lang_buf_addr, - pp->total_len, pp->hash) != EC_SUCCESS) - return EC_RES_ERROR; - - memcpy(lang_hash, pp->hash, sizeof(lang_hash)); - lang_len = pp->total_len; - speech_lib_loaded = 0; - - args->response_size = 0; - return EC_RES_SUCCESS; -} -#else -static enum ec_status wov_set_lang(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_wov *p = args->params; - const struct ec_param_ec_codec_wov_set_lang *pp = &p->set_lang_param; - - if (pp->total_len > LANG_BUF_LEN) - return EC_RES_INVALID_PARAM; - if (pp->offset >= LANG_BUF_LEN) - return EC_RES_INVALID_PARAM; - if (pp->len > ARRAY_SIZE(pp->buf)) - return EC_RES_INVALID_PARAM; - if (pp->offset + pp->len > pp->total_len) - return EC_RES_INVALID_PARAM; - if (wov_enabled) - return EC_RES_BUSY; - - if (!pp->offset) - memset((uint8_t *)audio_codec_wov_lang_buf_addr, - 0, LANG_BUF_LEN); - - memcpy((uint8_t *)audio_codec_wov_lang_buf_addr + pp->offset, - pp->buf, pp->len); - - if (pp->offset + pp->len == pp->total_len) { - if (check_lang_buf((uint8_t *)audio_codec_wov_lang_buf_addr, - pp->total_len, pp->hash) != EC_SUCCESS) - return EC_RES_ERROR; - - memcpy(lang_hash, pp->hash, sizeof(lang_hash)); - lang_len = pp->total_len; - speech_lib_loaded = 0; - } - - args->response_size = 0; - return EC_RES_SUCCESS; -} -#endif /* CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM */ - -static enum ec_status wov_get_lang(struct host_cmd_handler_args *args) -{ - struct ec_response_ec_codec_wov_get_lang *r = args->response; - - memcpy(r->hash, lang_hash, sizeof(r->hash)); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -static enum ec_status wov_enable(struct host_cmd_handler_args *args) -{ - if (wov_enabled) - return EC_RES_BUSY; - - if (audio_codec_wov_enable() != EC_SUCCESS) - return EC_RES_ERROR; - - if (!speech_lib_loaded) { - if (!GoogleHotwordDspInit( - (void *)audio_codec_wov_lang_buf_addr)) - return EC_RES_ERROR; - speech_lib_loaded = 1; - } else { - GoogleHotwordDspReset(); - } - - mutex_lock(&lock); - wov_enabled = 1; - hotword_detected = 0; - audio_buf_rp = audio_buf_wp = 0; - mutex_unlock(&lock); - -#ifdef HAS_TASK_WOV - task_wake(TASK_ID_WOV); -#endif - - args->response_size = 0; - return EC_RES_SUCCESS; -} - -static enum ec_status wov_disable(struct host_cmd_handler_args *args) -{ - if (!wov_enabled) - return EC_RES_BUSY; - - if (audio_codec_wov_disable() != EC_SUCCESS) - return EC_RES_ERROR; - - mutex_lock(&lock); - wov_enabled = 0; - hotword_detected = 0; - audio_buf_rp = audio_buf_wp = 0; - mutex_unlock(&lock); - - args->response_size = 0; - return EC_RES_SUCCESS; -} - -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM -static enum ec_status wov_read_audio_shm(struct host_cmd_handler_args *args) -{ - struct ec_response_ec_codec_wov_read_audio_shm *r = args->response; - - if (!wov_enabled) - return EC_RES_ACCESS_DENIED; - - mutex_lock(&lock); - if (!hotword_detected) { - mutex_unlock(&lock); - return EC_RES_ACCESS_DENIED; - } - - r->offset = audio_buf_rp; - if (audio_buf_rp <= audio_buf_wp) - r->len = audio_buf_wp - audio_buf_rp; - else - r->len = AUDIO_BUF_LEN - audio_buf_rp; - - audio_buf_rp += r->len; - if (audio_buf_rp == AUDIO_BUF_LEN) - audio_buf_rp = 0; - mutex_unlock(&lock); - -#ifdef DEBUG_AUDIO_CODEC - if (!r->len) - CPRINTS("underrun detected"); -#endif - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -#else -static enum ec_status wov_read_audio(struct host_cmd_handler_args *args) -{ - struct ec_response_ec_codec_wov_read_audio *r = args->response; - uint8_t *p; - - if (!wov_enabled) - return EC_RES_ACCESS_DENIED; - - mutex_lock(&lock); - if (!hotword_detected) { - mutex_unlock(&lock); - return EC_RES_ACCESS_DENIED; - } - - if (audio_buf_rp <= audio_buf_wp) - r->len = audio_buf_wp - audio_buf_rp; - else - r->len = AUDIO_BUF_LEN - audio_buf_rp; - r->len = MIN(sizeof(r->buf), r->len); - - p = (uint8_t *)audio_codec_wov_audio_buf_addr + audio_buf_rp; - - audio_buf_rp += r->len; - if (audio_buf_rp == AUDIO_BUF_LEN) - audio_buf_rp = 0; - mutex_unlock(&lock); - -#ifdef DEBUG_AUDIO_CODEC - if (!r->len) - CPRINTS("underrun detected"); -#endif - /* - * Note: it is possible to copy corrupted audio data if overrun - * happened at the point. To keep it simple and align to SHM mode, - * we ignore the case if overrun happened. - */ - memcpy(r->buf, p, r->len); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -#endif /* CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM */ - -static enum ec_status (*sub_cmds[])(struct host_cmd_handler_args *) = { -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM - [EC_CODEC_WOV_SET_LANG_SHM] = wov_set_lang_shm, -#else - [EC_CODEC_WOV_SET_LANG] = wov_set_lang, -#endif - [EC_CODEC_WOV_GET_LANG] = wov_get_lang, - [EC_CODEC_WOV_ENABLE] = wov_enable, - [EC_CODEC_WOV_DISABLE] = wov_disable, -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM - [EC_CODEC_WOV_READ_AUDIO_SHM] = wov_read_audio_shm, -#else - [EC_CODEC_WOV_READ_AUDIO] = wov_read_audio, -#endif -}; - -#ifdef DEBUG_AUDIO_CODEC -static char *strcmd[] = { -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM - [EC_CODEC_WOV_SET_LANG_SHM] = "EC_CODEC_WOV_SET_LANG_SHM", -#else - [EC_CODEC_WOV_SET_LANG] = "EC_CODEC_WOV_SET_LANG", -#endif - [EC_CODEC_WOV_GET_LANG] = "EC_CODEC_WOV_GET_LANG", - [EC_CODEC_WOV_ENABLE] = "EC_CODEC_WOV_ENABLE", - [EC_CODEC_WOV_DISABLE] = "EC_CODEC_WOV_DISABLE", -#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM - [EC_CODEC_WOV_READ_AUDIO_SHM] = "EC_CODEC_WOV_READ_AUDIO_SHM", -#else - [EC_CODEC_WOV_READ_AUDIO] = "EC_CODEC_WOV_READ_AUDIO", -#endif -}; -BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd)); -#endif - -static enum ec_status wov_host_command(struct host_cmd_handler_args *args) -{ - const struct ec_param_ec_codec_wov *p = args->params; - -#ifdef DEBUG_AUDIO_CODEC - CPRINTS("WoV subcommand: %s", strcmd[p->cmd]); -#endif - - if (p->cmd < EC_CODEC_WOV_SUBCMD_COUNT && sub_cmds[p->cmd]) - return sub_cmds[p->cmd](args); - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC_WOV, wov_host_command, EC_VER_MASK(0)); - -/* - * Exported interfaces. - */ -void audio_codec_wov_task(void *arg) -{ - uint32_t n, req; - uint8_t *p; - int r; - - while (1) { - mutex_lock(&lock); - if (!wov_enabled) { - mutex_unlock(&lock); - task_wait_event(-1); - continue; - } - - - /* Clear the buffer if full. */ - if (is_buf_full()) { - audio_buf_wp = audio_buf_rp; - -#ifdef DEBUG_AUDIO_CODEC - if (hotword_detected) - CPRINTS("overrun detected"); -#endif - } - - /* - * Note: sample width is 16-bit. - * - * The linear ring buffer wastes one sample bytes to - * detect buffer full. - * - * If buffer is empty, maximum req is BUF_LEN - 2. - * If wp > rp, wp can fill to the end of linear buffer. - * If wp < rp, wp can fill up to rp - 2. - */ - if (audio_buf_wp == audio_buf_rp) - req = AUDIO_BUF_LEN - MAX(audio_buf_wp, 2); - else if (audio_buf_wp > audio_buf_rp) - req = AUDIO_BUF_LEN - audio_buf_wp; - else - req = audio_buf_rp - audio_buf_wp - 2; - - p = (uint8_t *)audio_codec_wov_audio_buf_addr + audio_buf_wp; - mutex_unlock(&lock); - - n = audio_codec_wov_read(p, req); - if (n < 0) { - CPRINTS("failed to read: %d", n); - break; - } else if (n == 0) { - if (audio_codec_wov_enable_notifier() != EC_SUCCESS) { - CPRINTS("failed to enable_notifier"); - break; - } - - task_wait_event(-1); - continue; - } - - mutex_lock(&lock); - audio_buf_wp += n; - if (audio_buf_wp == AUDIO_BUF_LEN) - audio_buf_wp = 0; - mutex_unlock(&lock); - - /* - * GoogleHotwordDspProcess() needs number of samples. In the - * case, sample is S16_LE. Thus, n / 2. - */ - if (!hotword_detected && - GoogleHotwordDspProcess(p, n / 2, &r)) { - CPRINTS("hotword detected"); - - mutex_lock(&lock); - /* - * Note: preserve 40% of buf size for AP to read - * (see go/cros-ec-codec#heading=h.582ga6pgfl2g) - */ - audio_buf_rp = audio_buf_wp + (AUDIO_BUF_LEN * 2 / 5); - if (audio_buf_rp >= AUDIO_BUF_LEN) - audio_buf_rp -= AUDIO_BUF_LEN; - - hotword_detected = 1; - mutex_unlock(&lock); - - host_set_single_event(EC_HOST_EVENT_WOV); - } - - /* - * Reasons to sleep here: - * 1. read the audio data in a fixed pace (10ms) - * 2. yield the processor in case of watchdog thought EC crashed - */ - task_wait_event(10 * MSEC); - } -} diff --git a/common/backlight_lid.c b/common/backlight_lid.c deleted file mode 100644 index 3b857df592..0000000000 --- a/common/backlight_lid.c +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Backlight control based on lid and optional request signal from AP */ - -#include "common.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "lid_switch.h" - - -/** - * Activate/Deactivate the backlight GPIO pin considering active high or low. - */ -void enable_backlight(int enabled) -{ -#ifdef CONFIG_BACKLIGHT_LID_ACTIVE_LOW - gpio_set_level(GPIO_ENABLE_BACKLIGHT_L, !enabled); -#else - gpio_set_level(GPIO_ENABLE_BACKLIGHT, enabled); -#endif -} - -/** - * Update backlight state. - */ -static void update_backlight(void) -{ -#ifdef CONFIG_BACKLIGHT_REQ_GPIO - /* Enable the backlight if lid is open AND requested by AP */ - enable_backlight(lid_is_open() && - gpio_get_level(CONFIG_BACKLIGHT_REQ_GPIO)); -#else - /* - * Enable backlight if lid is open; this is AND'd with the request from - * the AP in hardware. - */ - enable_backlight(lid_is_open()); -#endif -} -DECLARE_HOOK(HOOK_LID_CHANGE, update_backlight, HOOK_PRIO_DEFAULT); - -/** - * Initialize backlight module. - */ -static void backlight_init(void) -{ - update_backlight(); - -#ifdef CONFIG_BACKLIGHT_REQ_GPIO - gpio_enable_interrupt(CONFIG_BACKLIGHT_REQ_GPIO); -#endif -} -DECLARE_HOOK(HOOK_INIT, backlight_init, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_BACKLIGHT_REQ_GPIO -void backlight_interrupt(enum gpio_signal signal) -{ - update_backlight(); -} -#endif - -/** - * Host command to toggle backlight. - * - * The requested state will persist until the next lid-switch or request-gpio - * transition. - */ -static enum ec_status -switch_command_enable_backlight(struct host_cmd_handler_args *args) -{ - const struct ec_params_switch_enable_backlight *p = args->params; - - enable_backlight(p->enabled); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_SWITCH_ENABLE_BKLIGHT, - switch_command_enable_backlight, - EC_VER_MASK(0)); - - diff --git a/common/base32.c b/common/base32.c deleted file mode 100644 index a6be8409b1..0000000000 --- a/common/base32.c +++ /dev/null @@ -1,175 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Base-32 encoding/decoding */ - -#include "common.h" -#include "base32.h" -#include "util.h" - -static const unsigned char crc5_table1[] = { - 0x00, 0x0E, 0x1C, 0x12, 0x11, 0x1F, 0x0D, 0x03, - 0x0B, 0x05, 0x17, 0x19, 0x1A, 0x14, 0x06, 0x08 -}; - -static const unsigned char crc5_table0[] = { - 0x00, 0x16, 0x05, 0x13, 0x0A, 0x1C, 0x0F, 0x19, - 0x14, 0x02, 0x11, 0x07, 0x1E, 0x08, 0x1B, 0x0D -}; - -uint8_t crc5_sym(uint8_t sym, uint8_t previous_crc) -{ - uint8_t tmp = sym ^ previous_crc; - return crc5_table1[tmp & 0x0F] ^ crc5_table0[(tmp >> 4) & 0x0F]; -} - -/* A-Z0-9 with I,O,0,1 removed */ -const char base32_map[33] = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; - -/** - * Decode a base32 symbol. - * - * @param sym Input symbol - * @return The symbol value or -1 if error. - */ -static int decode_sym(int sym) -{ - int i = 0; - - for (i = 0; i < 32; i++) { - if (sym == base32_map[i]) - return i; - } - - return -1; -} - -int base32_encode(char *dest, int destlen_chars, - const void *srcbits, int srclen_bits, - int add_crc_every) -{ - const uint8_t *src = srcbits; - int destlen_needed; - int crc = 0, crc_count = 0; - int didx = 0; - int i; - - *dest = 0; - - /* Make sure destination is big enough */ - destlen_needed = (srclen_bits + 4) / 5; /* Symbols before adding CRC */ - if (add_crc_every) { - /* Must be an exact number of groups to add CRC */ - if (destlen_needed % add_crc_every) - return EC_ERROR_INVAL; - destlen_needed += destlen_needed / add_crc_every; - } - destlen_needed++; /* For terminating null */ - if (destlen_chars < destlen_needed) - return EC_ERROR_INVAL; - - for (i = 0; i < srclen_bits; i += 5) { - int sym; - int sidx = i / 8; - int bit_offs = i % 8; - - if (bit_offs <= 3) { - /* Entire symbol fits in that byte */ - sym = src[sidx] >> (3 - bit_offs); - } else { - /* Use the bits we have left */ - sym = src[sidx] << (bit_offs - 3); - - /* Use the bits from the next byte, if any */ - if (i + 1 < srclen_bits) - sym |= src[sidx + 1] >> (11 - bit_offs); - } - - sym &= 0x1f; - - /* Pad incomplete symbol with 0 bits */ - if (srclen_bits - i < 5) - sym &= 0x1f << (5 + i - srclen_bits); - - dest[didx++] = base32_map[sym]; - - /* Add CRC if needed */ - if (add_crc_every) { - crc = crc5_sym(sym, crc); - if (++crc_count == add_crc_every) { - dest[didx++] = base32_map[crc]; - crc_count = crc = 0; - } - } - } - - /* Terminate string and return */ - dest[didx] = 0; - return EC_SUCCESS; -} - -int base32_decode(uint8_t *dest, int destlen_bits, const char *src, - int crc_after_every) -{ - int crc = 0, crc_count = 0; - int out_bits = 0; - - for (; *src; src++) { - int sym, sbits, dbits, b; - - if (isspace((unsigned char)*src) || *src == '-') - continue; - - sym = decode_sym(*src); - if (sym < 0) - return -1; /* Bad input symbol */ - - /* Check CRC if needed */ - if (crc_after_every) { - if (crc_count == crc_after_every) { - if (crc != sym) - return -1; - crc_count = crc = 0; - continue; - } else { - crc = crc5_sym(sym, crc); - crc_count++; - } - } - - /* - * Stop if we're out of space. Have to do this after checking - * the CRC, or we might not check the last CRC. - */ - if (out_bits >= destlen_bits) - break; - - /* See how many bits we get to use from this symbol */ - sbits = MIN(5, destlen_bits - out_bits); - if (sbits < 5) - sym >>= (5 - sbits); - - /* Fill up the rest of the current byte */ - dbits = 8 - (out_bits & 7); - b = MIN(dbits, sbits); - if (dbits == 8) - dest[out_bits / 8] = 0; /* Starting a new byte */ - dest[out_bits / 8] |= (sym << (dbits - b)) >> (sbits - b); - out_bits += b; - sbits -= b; - - /* Start the next byte if there's space */ - if (sbits > 0) { - dest[out_bits / 8] = sym << (8 - sbits); - out_bits += sbits; - } - } - - /* If we have CRCs, should have a full group */ - if (crc_after_every && crc_count) - return -1; - - return out_bits; -} diff --git a/common/base_state.c b/common/base_state.c deleted file mode 100644 index 43e201cab9..0000000000 --- a/common/base_state.c +++ /dev/null @@ -1,68 +0,0 @@ -/* 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. - */ - -#include "base_state.h" -#include "console.h" -#include "host_command.h" -#include "hooks.h" - -#define CPRINTS(format, args...) cprints(CC_MOTION_LID, format, ## args) - -#ifdef CONFIG_BASE_ATTACHED_SWITCH -/* 1: base attached, 0: otherwise */ -static int base_state; - -int base_get_state(void) -{ - return base_state; -} - -void base_set_state(int state) -{ - if (base_state == !!state) - return; - - base_state = !!state; - CPRINTS("base state: %stached", state ? "at" : "de"); - hook_notify(HOOK_BASE_ATTACHED_CHANGE); - - /* Notify host of mode change. This likely will wake it up. */ - host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); -} -#endif - -static int command_setbasestate(int argc, char **argv) -{ - if (argc != 2) - return EC_ERROR_PARAM_COUNT; - if (argv[1][0] == 'a') - base_force_state(EC_SET_BASE_STATE_ATTACH); - else if (argv[1][0] == 'd') - base_force_state(EC_SET_BASE_STATE_DETACH); - else if (argv[1][0] == 'r') - base_force_state(EC_SET_BASE_STATE_RESET); - else - return EC_ERROR_PARAM1; - - return EC_SUCCESS; - -} -DECLARE_CONSOLE_COMMAND(basestate, command_setbasestate, - "[attach | detach | reset]", - "Manually force base state to attached, detached or reset."); - -static enum ec_status hostcmd_setbasestate(struct host_cmd_handler_args *args) -{ - const struct ec_params_set_base_state *params = args->params; - - if (params->cmd > EC_SET_BASE_STATE_RESET) - return EC_RES_INVALID_PARAM; - - base_force_state(params->cmd); - - return EC_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_SET_BASE_STATE, hostcmd_setbasestate, - EC_VER_MASK(0)); diff --git a/common/battery.c b/common/battery.c deleted file mode 100644 index 6791e4d3a2..0000000000 --- a/common/battery.c +++ /dev/null @@ -1,812 +0,0 @@ -/* Copyright 2012 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. - * - * Common battery command. - */ - -#include "battery.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "ec_ec_comm_client.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "math_util.h" -#include "timer.h" -#include "usb_pd.h" -#include "util.h" -#include "watchdog.h" - -#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) -#define CUTOFFPRINTS(info) CPRINTS("%s %s", "Battery cut off", info) - -/* See config.h for details */ -const static int batt_host_full_factor = CONFIG_BATT_HOST_FULL_FACTOR; -const static int batt_host_shutdown_pct = CONFIG_BATT_HOST_SHUTDOWN_PERCENTAGE; - -#ifdef CONFIG_BATTERY_V2 -/* - * Store battery information in these 2 structures. Main (lid) battery is always - * at index 0, and secondary (base) battery at index 1. - */ -struct ec_response_battery_static_info_v1 battery_static[CONFIG_BATTERY_COUNT]; -struct ec_response_battery_dynamic_info battery_dynamic[CONFIG_BATTERY_COUNT]; -#endif - -#ifdef CONFIG_BATTERY_CUT_OFF - -#ifndef CONFIG_BATTERY_CUTOFF_DELAY_US -#define CONFIG_BATTERY_CUTOFF_DELAY_US (1 * SECOND) -#endif - -static enum battery_cutoff_states battery_cutoff_state = - BATTERY_CUTOFF_STATE_NORMAL; - -#endif - -#ifdef CONFIG_BATTERY_PRESENT_GPIO -#ifdef CONFIG_BATTERY_PRESENT_CUSTOM -#error "Don't define both CONFIG_BATTERY_PRESENT_CUSTOM and" \ - "CONFIG_BATTERY_PRESENT_GPIO" -#endif -/** - * Physical detection of battery. - */ -enum battery_present battery_is_present(void) -{ - /* The GPIO is low when the battery is present */ - return gpio_get_level(CONFIG_BATTERY_PRESENT_GPIO) ? BP_NO : BP_YES; -} -#endif - -static const char *get_error_text(int rv) -{ - if (rv == EC_ERROR_UNIMPLEMENTED) - return "(unsupported)"; - else - return "(error)"; -} - -static void print_item_name(const char *name) -{ - ccprintf(" %-11s", name); -} - -static int check_print_error(int rv) -{ - if (rv != EC_SUCCESS) - ccprintf("%s\n", get_error_text(rv)); - return rv == EC_SUCCESS; -} - -static void print_battery_status(void) -{ - static const char * const st[] = {"EMPTY", "FULL", "DCHG", "INIT",}; - static const char * const al[] = {"RT", "RC", "--", "TD", - "OT", "--", "TC", "OC"}; - - int value, i; - - print_item_name("Status:"); - if (check_print_error(battery_status(&value))) { - ccprintf("0x%04x", value); - - /* bits 0-3 are only valid when the previous transaction - * failed, so ignore them */ - - /* bits 4-7 are status */ - for (i = 0; i < 4; i++) - if (value & (1 << (i+4))) - ccprintf(" %s", st[i]); - - /* bits 15-8 are alarms */ - for (i = 0; i < 8; i++) - if (value & (1 << (i+8))) - ccprintf(" %s", al[i]); - - ccprintf("\n"); - } -} - -static void print_battery_strings(void) -{ - char text[32]; - - print_item_name("Manuf:"); - if (check_print_error(battery_manufacturer_name(text, sizeof(text)))) - ccprintf("%s\n", text); - - print_item_name("Device:"); - if (check_print_error(battery_device_name(text, sizeof(text)))) - ccprintf("%s\n", text); - - print_item_name("Chem:"); - if (check_print_error(battery_device_chemistry(text, sizeof(text)))) - ccprintf("%s\n", text); -} - -static void print_battery_params(void) -{ -#if defined(HAS_TASK_CHARGER) - /* Ask charger so that we don't need to ask battery again. */ - const struct batt_params *batt = charger_current_battery_params(); -#else - /* This is for test code, where doesn't have charger task. */ - struct batt_params _batt; - const struct batt_params *batt = &_batt; - - battery_get_params(&_batt); -#endif - - print_item_name("Param flags:"); - ccprintf("%08x\n", batt->flags); - - print_item_name("Temp:"); - ccprintf("0x%04x = %.1d K (%.1d C)\n", - batt->temperature, - batt->temperature, - batt->temperature - 2731); - - print_item_name("V:"); - ccprintf("0x%04x = %d mV\n", batt->voltage, batt->voltage); - - print_item_name("V-desired:"); - ccprintf("0x%04x = %d mV\n", batt->desired_voltage, - batt->desired_voltage); - - print_item_name("I:"); - ccprintf("0x%04x = %d mA", batt->current & 0xffff, batt->current); - if (batt->current > 0) - ccputs("(CHG)"); - else if (batt->current < 0) - ccputs("(DISCHG)"); - ccputs("\n"); - - print_item_name("I-desired:"); - ccprintf("0x%04x = %d mA\n", batt->desired_current, - batt->desired_current); - - print_item_name("Charging:"); - ccprintf("%sAllowed\n", - batt->flags & BATT_FLAG_WANT_CHARGE ? "" : "Not "); - - print_item_name("Charge:"); - ccprintf("%d %%\n", batt->state_of_charge); - - if (IS_ENABLED(CONFIG_CHARGER)) { - int value; - - print_item_name(" Display:"); - value = charge_get_display_charge(); - ccprintf("%d.%d %%\n", value / 10, value % 10); - } -} - -static void print_battery_info(void) -{ - int value; - int hour, minute; - - print_item_name("Serial:"); - if (check_print_error(battery_serial_number(&value))) - ccprintf("0x%04x\n", value); - - print_item_name("V-design:"); - if (check_print_error(battery_design_voltage(&value))) - ccprintf("0x%04x = %d mV\n", value, value); - - print_item_name("Mode:"); - if (check_print_error(battery_get_mode(&value))) - ccprintf("0x%04x\n", value); - - print_item_name("Abs charge:"); - if (check_print_error(battery_state_of_charge_abs(&value))) - ccprintf("%d %%\n", value); - - print_item_name("Remaining:"); - if (check_print_error(battery_remaining_capacity(&value))) - ccprintf("%d mAh\n", value); - - print_item_name("Cap-full:"); - if (check_print_error(battery_full_charge_capacity(&value))) - ccprintf("%d mAh\n", value); - - print_item_name(" Design:"); - if (check_print_error(battery_design_capacity(&value))) - ccprintf("%d mAh\n", value); - - print_item_name("Time-full:"); - if (check_print_error(battery_time_to_full(&value))) { - if (value == 65535) { - hour = 0; - minute = 0; - } else { - hour = value / 60; - minute = value % 60; - } - ccprintf("%dh:%d\n", hour, minute); - } - - print_item_name(" Empty:"); - if (check_print_error(battery_time_to_empty(&value))) { - if (value == 65535) { - hour = 0; - minute = 0; - } else { - hour = value / 60; - minute = value % 60; - } - ccprintf("%dh:%d\n", hour, minute); - } - - print_item_name("full_factor:"); - ccprintf("0.%d\n", batt_host_full_factor); - - print_item_name("shutdown_soc:"); - ccprintf("%d %%\n", batt_host_shutdown_pct); -} - -void print_battery_debug(void) -{ - print_battery_status(); - print_battery_params(); - print_battery_strings(); - print_battery_info(); -} - -static int command_battery(int argc, char **argv) -{ - int repeat = 1; - int loop; - int sleep_ms = 0; - char *e; - - if (argc > 1) { - repeat = strtoi(argv[1], &e, 0); - if (*e) { - ccputs("Invalid repeat count\n"); - return EC_ERROR_INVAL; - } - } - - if (argc > 2) { - sleep_ms = strtoi(argv[2], &e, 0); - if (*e) { - ccputs("Invalid sleep ms\n"); - return EC_ERROR_INVAL; - } - } - - for (loop = 0; loop < repeat; loop++) { - print_battery_debug(); - - /* - * Running with a high repeat count will take so long the - * watchdog timer fires. So reset the watchdog timer each - * iteration. - */ - watchdog_reload(); - - if (sleep_ms) - msleep(sleep_ms); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(battery, command_battery, - "<repeat_count> <sleep_ms>", - "Print battery info"); - -#ifdef CONFIG_BATTERY_CUT_OFF -int battery_is_cut_off(void) -{ - return (battery_cutoff_state == BATTERY_CUTOFF_STATE_CUT_OFF); -} - -static void pending_cutoff_deferred(void) -{ - int rv; - - rv = board_cut_off_battery(); - - if (rv == EC_RES_SUCCESS) { - CUTOFFPRINTS("succeeded."); - battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF; - } else { - CUTOFFPRINTS("failed!"); - battery_cutoff_state = BATTERY_CUTOFF_STATE_NORMAL; - } -} -DECLARE_DEFERRED(pending_cutoff_deferred); - -static void clear_pending_cutoff(void) -{ - if (extpower_is_present()) { - battery_cutoff_state = BATTERY_CUTOFF_STATE_NORMAL; - hook_call_deferred(&pending_cutoff_deferred_data, -1); - } -} -DECLARE_HOOK(HOOK_AC_CHANGE, clear_pending_cutoff, HOOK_PRIO_DEFAULT); - -static enum ec_status battery_command_cutoff(struct host_cmd_handler_args *args) -{ - const struct ec_params_battery_cutoff *p; - int rv; - - if (args->version == 1) { - p = args->params; - if (p->flags & EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN) { - battery_cutoff_state = BATTERY_CUTOFF_STATE_PENDING; - CUTOFFPRINTS("at-shutdown is scheduled"); - return EC_RES_SUCCESS; - } - } - - rv = board_cut_off_battery(); - if (rv == EC_RES_SUCCESS) { - CUTOFFPRINTS("is successful."); - battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF; - } else { - CUTOFFPRINTS("has failed."); - } - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_BATTERY_CUT_OFF, battery_command_cutoff, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static void check_pending_cutoff(void) -{ - if (battery_cutoff_state == BATTERY_CUTOFF_STATE_PENDING) { - CPRINTS("Cutting off battery in %d second(s)", - CONFIG_BATTERY_CUTOFF_DELAY_US / SECOND); - hook_call_deferred(&pending_cutoff_deferred_data, - CONFIG_BATTERY_CUTOFF_DELAY_US); - } -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, check_pending_cutoff, HOOK_PRIO_LAST); - -static int command_cutoff(int argc, char **argv) -{ - int rv; - - if (argc > 1) { - if (!strcasecmp(argv[1], "at-shutdown")) { - battery_cutoff_state = BATTERY_CUTOFF_STATE_PENDING; - return EC_SUCCESS; - } else { - return EC_ERROR_INVAL; - } - } - - rv = board_cut_off_battery(); - if (rv == EC_RES_SUCCESS) { - ccprints("Battery cut off"); - battery_cutoff_state = BATTERY_CUTOFF_STATE_CUT_OFF; - return EC_SUCCESS; - } - - return EC_ERROR_UNKNOWN; -} -DECLARE_CONSOLE_COMMAND(cutoff, command_cutoff, - "[at-shutdown]", - "Cut off the battery output"); -#else -int battery_is_cut_off(void) -{ - return 0; /* Always return NOT cut off */ -} -#endif /* CONFIG_BATTERY_CUT_OFF */ - -#ifdef CONFIG_BATTERY_VENDOR_PARAM -static int console_command_battery_vendor_param(int argc, char **argv) -{ - uint32_t param; - uint32_t value; - char *e; - int rv; - - if (argc < 2) - return EC_ERROR_INVAL; - - param = strtoi(argv[1], &e, 0); - if (*e) { - ccputs("Invalid param\n"); - return EC_ERROR_INVAL; - } - - if (argc > 2) { - value = strtoi(argv[2], &e, 0); - if (*e) { - ccputs("Invalid value\n"); - return EC_ERROR_INVAL; - } - rv = battery_set_vendor_param(param, value); - if (rv != EC_SUCCESS) - return rv; - } - - rv = battery_get_vendor_param(param, &value); - if (rv != EC_SUCCESS) - return rv; - - ccprintf("0x%08x\n", value); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(battparam, console_command_battery_vendor_param, - "<param> [value]", - "Get or set battery vendor parameters"); - -static enum ec_status -host_command_battery_vendor_param(struct host_cmd_handler_args *args) -{ - int rv; - const struct ec_params_battery_vendor_param *p = args->params; - struct ec_response_battery_vendor_param *r = args->response; - - args->response_size = sizeof(*r); - - if (p->mode != BATTERY_VENDOR_PARAM_MODE_GET && - p->mode != BATTERY_VENDOR_PARAM_MODE_SET) - return EC_RES_INVALID_PARAM; - - if (p->mode == BATTERY_VENDOR_PARAM_MODE_SET) { - rv = battery_set_vendor_param(p->param, p->value); - if (rv != EC_SUCCESS) - return rv; - } - - rv = battery_get_vendor_param(p->param, &r->value); - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_BATTERY_VENDOR_PARAM, - host_command_battery_vendor_param, - EC_VER_MASK(0)); -#endif /* CONFIG_BATTERY_VENDOR_PARAM */ - -#ifdef CONFIG_BATTERY_V2 -#ifdef CONFIG_HOSTCMD_BATTERY_V2 -static void battery_update(enum battery_index i); -static enum ec_status -host_command_battery_get_static(struct host_cmd_handler_args *args) -{ - const struct ec_params_battery_static_info *p = args->params; - struct ec_response_battery_static_info_v1 *bat; - - if (p->index < 0 || p->index >= CONFIG_BATTERY_COUNT) - return EC_RES_INVALID_PARAM; - bat = &battery_static[p->index]; - - battery_update(p->index); - if (args->version == 0) { - struct ec_response_battery_static_info *r = args->response; - - args->response_size = sizeof(*r); - r->design_capacity = bat->design_capacity; - r->design_voltage = bat->design_voltage; - r->cycle_count = bat->cycle_count; - - /* Truncate strings to reduced v0 size */ - memcpy(&r->manufacturer, &bat->manufacturer_ext, - sizeof(r->manufacturer)); - r->manufacturer[sizeof(r->manufacturer) - 1] = 0; - memcpy(&r->model, &bat->model_ext, sizeof(r->model)); - r->model[sizeof(r->model) - 1] = 0; - memcpy(&r->serial, &bat->serial_ext, sizeof(r->serial)); - r->serial[sizeof(r->serial) - 1] = 0; - memcpy(&r->type, &bat->type_ext, sizeof(r->type)); - r->type[sizeof(r->type) - 1] = 0; - } else { - /* v1 command stores the same data internally */ - struct ec_response_battery_static_info_v1 *r = args->response; - - args->response_size = sizeof(*r); - memcpy(r, bat, sizeof(*r)); - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_BATTERY_GET_STATIC, - host_command_battery_get_static, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static enum ec_status -host_command_battery_get_dynamic(struct host_cmd_handler_args *args) -{ - const struct ec_params_battery_dynamic_info *p = args->params; - struct ec_response_battery_dynamic_info *r = args->response; - - if (p->index < 0 || p->index >= CONFIG_BATTERY_COUNT) - return EC_RES_INVALID_PARAM; - - args->response_size = sizeof(*r); - memcpy(r, &battery_dynamic[p->index], sizeof(*r)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_BATTERY_GET_DYNAMIC, - host_command_battery_get_dynamic, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_BATTERY_V2 */ - -#ifdef HAS_TASK_HOSTCMD -static void battery_update(enum battery_index i) -{ - char *batt_str; - int *memmap_dcap = (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP); - int *memmap_dvlt = (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT); - int *memmap_ccnt = (int *)host_get_memmap(EC_MEMMAP_BATT_CCNT); - int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT); - int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE); - int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP); - int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC); - uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); - - /* Smart battery serial number is 16 bits */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL); - memcpy(batt_str, battery_static[i].serial_ext, EC_MEMMAP_TEXT_MAX); - batt_str[EC_MEMMAP_TEXT_MAX - 1] = 0; - - /* Design Capacity of Full */ - *memmap_dcap = battery_static[i].design_capacity; - - /* Design Voltage */ - *memmap_dvlt = battery_static[i].design_voltage; - - /* Cycle Count */ - *memmap_ccnt = battery_static[i].cycle_count; - - /* Battery Manufacturer string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR); - memcpy(batt_str, battery_static[i].manufacturer_ext, - EC_MEMMAP_TEXT_MAX); - batt_str[EC_MEMMAP_TEXT_MAX - 1] = 0; - - /* Battery Model string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL); - memcpy(batt_str, battery_static[i].model_ext, EC_MEMMAP_TEXT_MAX); - batt_str[EC_MEMMAP_TEXT_MAX - 1] = 0; - - /* Battery Type string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE); - memcpy(batt_str, battery_static[i].type_ext, EC_MEMMAP_TEXT_MAX); - batt_str[EC_MEMMAP_TEXT_MAX - 1] = 0; - - *memmap_volt = battery_dynamic[i].actual_voltage; - *memmap_rate = battery_dynamic[i].actual_current; - *memmap_cap = battery_dynamic[i].remaining_capacity; - *memmap_lfcc = battery_dynamic[i].full_capacity; - *memmap_flags = battery_dynamic[i].flags; -} - -void battery_memmap_refresh(enum battery_index index) -{ - if (*host_get_memmap(EC_MEMMAP_BATT_INDEX) == index) - battery_update(index); -} - -void battery_memmap_set_index(enum battery_index index) -{ - if (*host_get_memmap(EC_MEMMAP_BATT_INDEX) == index) - return; - - *host_get_memmap(EC_MEMMAP_BATT_INDEX) = BATT_IDX_INVALID; - if (index < 0 || index >= CONFIG_BATTERY_COUNT) - return; - - battery_update(index); - *host_get_memmap(EC_MEMMAP_BATT_INDEX) = index; -} - -static void battery_init(void) -{ - *host_get_memmap(EC_MEMMAP_BATT_INDEX) = BATT_IDX_INVALID; - *host_get_memmap(EC_MEMMAP_BATT_COUNT) = CONFIG_BATTERY_COUNT; - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 2; - - battery_memmap_set_index(BATT_IDX_MAIN); -} -DECLARE_HOOK(HOOK_INIT, battery_init, HOOK_PRIO_DEFAULT); -#endif /* HAS_TASK_HOSTCMD */ -#endif /* CONFIG_BATTERY_V2 */ - -void battery_compensate_params(struct batt_params *batt) -{ - int numer, denom; - int *remain = &(batt->remaining_capacity); - int full = batt->full_capacity; - - if ((batt->flags & BATT_FLAG_BAD_FULL_CAPACITY) || - (batt->flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) - return; - - if (*remain <= 0 || full <= 0) - return; - - /* Some batteries don't update full capacity as often. */ - if (*remain > full) - *remain = full; - - /* - * EC calculates the display SoC like how Powerd used to do. Powerd - * reads the display SoC from the EC. This design allows the system to - * behave consistently on a single SoC value across all power states. - * - * Display SoC is computed as follows: - * - * actual_soc = 100 * remain / full - * - * actual_soc - shutdown_pct - * display_soc = --------------------------- x 1000 - * full_factor - shutdown_pct - * - * (100 * remain / full) - shutdown_pct - * = ------------------------------------ x 1000 - * full_factor - shutdown_pct - * - * 100 x remain - full x shutdown_pct - * = ----------------------------------- x 1000 - * full x (full_factor - shutdown_pct) - */ - numer = 1000 * ((100 * *remain) - (full * batt_host_shutdown_pct)); - denom = full * (batt_host_full_factor - batt_host_shutdown_pct); - /* Rounding (instead of truncating) */ - batt->display_charge = (numer + denom / 2) / denom; - if (batt->display_charge < 0) - batt->display_charge = 0; - if (batt->display_charge > 1000) - batt->display_charge = 1000; -} - -#ifdef CONFIG_CHARGER -static enum ec_status battery_display_soc(struct host_cmd_handler_args *args) -{ - struct ec_response_display_soc *r = args->response; - - r->display_soc = charge_get_display_charge(); - r->full_factor = batt_host_full_factor * 10; - r->shutdown_soc = batt_host_shutdown_pct * 10; - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_DISPLAY_SOC, battery_display_soc, EC_VER_MASK(0)); -#endif - -__overridable void board_battery_compensate_params(struct batt_params *batt) -{ -} - -__attribute__((weak)) int get_battery_manufacturer_name(char *dest, int size) -{ - strzcpy(dest, "<unkn>", size); - return EC_SUCCESS; -} - -__overridable int battery_get_avg_voltage(void) -{ - return -EC_ERROR_UNIMPLEMENTED; -} - -__overridable int battery_get_avg_current(void) -{ - return -EC_ERROR_UNIMPLEMENTED; -} - -int battery_manufacturer_name(char *dest, int size) -{ - return get_battery_manufacturer_name(dest, size); -} - -__overridable enum battery_disconnect_state battery_get_disconnect_state(void) -{ - return BATTERY_NOT_DISCONNECTED; -} - -#ifdef CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV - -#if CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV < 5000 || \ - CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV >= PD_MAX_VOLTAGE_MV - #error "Voltage limit must be between 5000 and PD_MAX_VOLTAGE_MV" -#endif - -#if !((defined(CONFIG_USB_PD_TCPMV1) && defined(CONFIG_USB_PD_DUAL_ROLE)) || \ - (defined(CONFIG_USB_PD_TCPMV2) && defined(CONFIG_USB_PE_SM))) - #error "Voltage reducing requires TCPM with Policy Engine" -#endif - -/* - * Returns true if input voltage should be reduced (chipset is in S5/G3) and - * battery is full, otherwise returns false - */ -static bool board_wants_reduced_input_voltage(void) { - struct batt_params batt; - - /* Chipset not in S5/G3, so we don't want to reduce voltage */ - if (!chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - return false; - - battery_get_params(&batt); - - /* Battery needs charge, so we don't want to reduce voltage */ - if (batt.flags & BATT_FLAG_WANT_CHARGE) - return false; - - return true; -} - -static void reduce_input_voltage_when_full(void) -{ - static int saved_input_voltage = -1; - int max_pd_voltage_mv = pd_get_max_voltage(); - int port; - - port = charge_manager_get_active_charge_port(); - if (port < 0 || port >= board_get_usb_pd_port_count()) - return; - - if (board_wants_reduced_input_voltage()) { - /* - * Board wants voltage to be reduced. Apply limit if current - * voltage is different. Save current voltage, it will be - * restored when board wants to stop reducing input voltage. - */ - if (max_pd_voltage_mv != - CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV) { - saved_input_voltage = max_pd_voltage_mv; - max_pd_voltage_mv = - CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV; - } - } else if (saved_input_voltage != -1) { - /* - * Board doesn't want to reduce input voltage. If current - * voltage is reduced we will restore previously saved voltage. - * If current voltage is different we will respect newer value. - */ - if (max_pd_voltage_mv == - CONFIG_BATT_FULL_CHIPSET_OFF_INPUT_LIMIT_MV) - max_pd_voltage_mv = saved_input_voltage; - - saved_input_voltage = -1; - } - - if (pd_get_max_voltage() != max_pd_voltage_mv) - pd_set_external_voltage_limit(port, max_pd_voltage_mv); -} -DECLARE_HOOK(HOOK_AC_CHANGE, reduce_input_voltage_when_full, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, reduce_input_voltage_when_full, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, reduce_input_voltage_when_full, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, reduce_input_voltage_when_full, - HOOK_PRIO_DEFAULT); -#endif - -void battery_validate_params(struct batt_params *batt) -{ - /* - * TODO(crosbug.com/p/27527). Sometimes the battery thinks its - * temperature is 6280C, which seems a bit high. Let's ignore - * anything above the boiling point of tungsten until this bug - * is fixed. If the battery is really that warm, we probably - * have more urgent problems. - */ - if (batt->temperature > CELSIUS_TO_DECI_KELVIN(5660)) { - CPRINTS("ignoring ridiculous batt.temp of %dC", - DECI_KELVIN_TO_CELSIUS(batt->temperature)); - batt->flags |= BATT_FLAG_BAD_TEMPERATURE; - } - - /* If the battery thinks it's above 100%, don't believe it */ - if (batt->state_of_charge > 100) { - CPRINTS("ignoring ridiculous batt.soc of %d%%", - batt->state_of_charge); - batt->flags |= BATT_FLAG_BAD_STATE_OF_CHARGE; - } -} diff --git a/common/battery_fuel_gauge.c b/common/battery_fuel_gauge.c deleted file mode 100644 index 528713d68f..0000000000 --- a/common/battery_fuel_gauge.c +++ /dev/null @@ -1,283 +0,0 @@ -/* 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. - * - * Battery fuel gauge parameters - */ - -#include "battery_fuel_gauge.h" -#include "battery_smart.h" -#include "console.h" -#include "hooks.h" -#include "i2c.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) - - -/* Get type of the battery connected on the board */ -static int get_battery_type(void) -{ - char manuf_name[32], device_name[32]; - int i; - static enum battery_type battery_type = BATTERY_TYPE_COUNT; - - /* - * If battery_type is not the default value, then can return here - * as there is no need to query the fuel gauge. - */ - if (battery_type != BATTERY_TYPE_COUNT) - return battery_type; - - /* Get the manufacturer name. If can't read then just exit */ - if (battery_manufacturer_name(manuf_name, sizeof(manuf_name))) - return battery_type; - - /* - * Compare the manufacturer name read from the fuel gauge to the - * manufacturer names defined in the board_battery_info table. If - * a device name has been specified in the board_battery_info table, - * then both the manufacturer and device name must match. - */ - for (i = 0; i < BATTERY_TYPE_COUNT; i++) { - const struct fuel_gauge_info * const fuel_gauge = - &board_battery_info[i].fuel_gauge; - int len = 0; - - if (strcasecmp(manuf_name, fuel_gauge->manuf_name)) - continue; - - if (fuel_gauge->device_name != NULL) { - - if (battery_device_name(device_name, - sizeof(device_name))) - continue; - - len = strlen(fuel_gauge->device_name); - if (strncasecmp(device_name, fuel_gauge->device_name, - len)) - continue; - } - - CPRINTS("found batt:%s", fuel_gauge->manuf_name); - battery_type = i; - break; - } - - return battery_type; -} - -__overridable int board_get_default_battery_type(void) -{ - return DEFAULT_BATTERY_TYPE; -} - -/* - * Initialize the battery type for the board. - * - * The first call to battery_get_info() is when the charger task starts, so - * initialize the battery type as soon as I2C is initialized. - */ -static void init_battery_type(void) -{ - if (get_battery_type() == BATTERY_TYPE_COUNT) - CPRINTS("battery not found"); -} -DECLARE_HOOK(HOOK_INIT, init_battery_type, HOOK_PRIO_INIT_I2C + 1); - -static inline const struct board_batt_params *get_batt_params(void) -{ - int type = get_battery_type(); - - return &board_battery_info[type == BATTERY_TYPE_COUNT ? - board_get_default_battery_type() : type]; -} - -const struct battery_info *battery_get_info(void) -{ - return &get_batt_params()->batt_info; -} - -int cut_off_battery_block_write(const struct ship_mode_info *ship_mode) -{ - int rv; - - uint8_t cutdata[3] = { - 0x02, - ship_mode->reg_data[0] & 0xFF, - ship_mode->reg_data[0] >> 8, - }; - - /* SMBus protocols are block write, which include byte count - * byte. Byte count segments are required to communicate - * required action and the number of data bytes. - * Due to ship mode command requires writing data values twice - * to cutoff the battery, so byte count is 0x02. - */ - rv = sb_write_block(ship_mode->reg_addr, cutdata, sizeof(cutdata)); - if (rv) - return rv; - - /* Use the next set of values */ - cutdata[1] = ship_mode->reg_data[1] & 0xFF; - cutdata[2] = ship_mode->reg_data[1] >> 8; - - return sb_write_block(ship_mode->reg_addr, cutdata, sizeof(cutdata)); -} - -int cut_off_battery_sb_write(const struct ship_mode_info *ship_mode) -{ - int rv; - - /* Ship mode command requires writing 2 data values */ - rv = sb_write(ship_mode->reg_addr, ship_mode->reg_data[0]); - if (rv) - return rv; - - return sb_write(ship_mode->reg_addr, ship_mode->reg_data[1]); -} - -int board_cut_off_battery(void) -{ - int rv; - int type = get_battery_type(); - - /* If battery type is unknown can't send ship mode command */ - if (type == BATTERY_TYPE_COUNT) - return EC_RES_ERROR; - - if (board_battery_info[type].fuel_gauge.ship_mode.wb_support) - rv = cut_off_battery_block_write( - &board_battery_info[type].fuel_gauge.ship_mode); - else - rv = cut_off_battery_sb_write( - &board_battery_info[type].fuel_gauge.ship_mode); - - return rv ? EC_RES_ERROR : EC_RES_SUCCESS; -} - -enum ec_error_list battery_sleep_fuel_gauge(void) -{ - const struct sleep_mode_info *sleep_command; - int type = get_battery_type(); - - /* Sleep entry command must be supplied as it will vary by gauge */ - if (type == BATTERY_TYPE_COUNT) - return EC_ERROR_UNKNOWN; - - sleep_command = &board_battery_info[type].fuel_gauge.sleep_mode; - - if (!sleep_command->sleep_supported) - return EC_ERROR_UNIMPLEMENTED; - - return sb_write(sleep_command->reg_addr, sleep_command->reg_data); -} - -static enum ec_error_list battery_get_fet_status_regval(int *regval) -{ - int rv; - uint8_t data[6]; - int type = get_battery_type(); - - /* If battery type is not known, can't check CHG/DCHG FETs */ - if (type == BATTERY_TYPE_COUNT) { - /* Still don't know, so return here */ - return EC_ERROR_BUSY; - } - - /* Read the status of charge/discharge FETs */ - if (board_battery_info[type].fuel_gauge.fet.mfgacc_support == 1) { - rv = sb_read_mfgacc(PARAM_OPERATION_STATUS, - SB_ALT_MANUFACTURER_ACCESS, data, - sizeof(data)); - /* Get the lowest 16bits of the OperationStatus() data */ - *regval = data[2] | data[3] << 8; - } else - rv = sb_read(board_battery_info[type].fuel_gauge.fet.reg_addr, - regval); - - return rv; -} - -int battery_is_charge_fet_disabled(void) -{ - int rv; - int reg; - int type = get_battery_type(); - - /* If battery type is not known, can't check CHG/DCHG FETs */ - if (type == BATTERY_TYPE_COUNT) { - /* Still don't know, so return here */ - return -1; - } - - /* - * If the CFET mask hasn't been defined, assume that it's not disabled. - */ - if (!board_battery_info[type].fuel_gauge.fet.cfet_mask) - return 0; - - rv = battery_get_fet_status_regval(®); - if (rv) - return -1; - - return (reg & board_battery_info[type].fuel_gauge.fet.cfet_mask) == - board_battery_info[type].fuel_gauge.fet.cfet_off_val; -} - -/* - * This function checks the charge/discharge FET status bits. Each battery type - * supported provides the register address, mask, and disconnect value for these - * 2 FET status bits. If the FET status matches the disconnected value, then - * BATTERY_DISCONNECTED is returned. This function is required to handle the - * cases when the fuel gauge is awake and will return a non-zero state of - * charge, but is not able yet to provide power (i.e. discharge FET is not - * active). By returning BATTERY_DISCONNECTED the AP will not be powered up - * until either the external charger is able to provided enough power, or - * the battery is able to provide power and thus prevent a brownout when the - * AP is powered on by the EC. - */ -enum battery_disconnect_state battery_get_disconnect_state(void) -{ - int reg; - int type = get_battery_type(); - - /* If battery type is not known, can't check CHG/DCHG FETs */ - if (type == BATTERY_TYPE_COUNT) { - /* Still don't know, so return here */ - return BATTERY_DISCONNECT_ERROR; - } - - if (battery_get_fet_status_regval(®)) - return BATTERY_DISCONNECT_ERROR; - - if ((reg & board_battery_info[type].fuel_gauge.fet.reg_mask) == - board_battery_info[type].fuel_gauge.fet.disconnect_val) { - CPRINTS("Batt disconnected: reg 0x%04x mask 0x%04x disc 0x%04x", - reg, - board_battery_info[type].fuel_gauge.fet.reg_mask, - board_battery_info[type].fuel_gauge.fet.disconnect_val); - return BATTERY_DISCONNECTED; - } - - return BATTERY_NOT_DISCONNECTED; -} - -#ifdef CONFIG_BATTERY_MEASURE_IMBALANCE -int battery_imbalance_mv(void) -{ - int type = get_battery_type(); - - /* - * If battery type is unknown, we cannot safely access non-standard - * registers. - */ - return (type == BATTERY_TYPE_COUNT) ? 0 : - board_battery_info[type].fuel_gauge.imbalance_mv(); -} - -int battery_default_imbalance_mv(void) -{ - return 0; -} -#endif /* CONFIG_BATTERY_MEASURE_IMBALANCE */ diff --git a/common/blink.c b/common/blink.c deleted file mode 100644 index ed16146f5a..0000000000 --- a/common/blink.c +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright 2020 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. - */ - -/* This is a confidence check program for boards that have LEDs. */ - -#include "common.h" -#include "gpio.h" -#include "hooks.h" - -#ifndef CONFIG_BLINK_LEDS - #error The macro CONFIG_BLINK_LEDS must be specified to use BLINK. -#endif - -static const enum gpio_signal leds[] = { CONFIG_BLINK_LEDS }; - -BUILD_ASSERT(ARRAY_SIZE(leds) <= sizeof(int)*8, "Too many LEDs to drive."); -BUILD_ASSERT(ARRAY_SIZE(leds) > 0, "Must have at least one LED to blink."); - -static void blink(void) -{ - static int led_values; - - int i; - for (i = 0; i < ARRAY_SIZE(leds); i++) - gpio_set_level(leds[i], BIT(i) & led_values); - led_values++; -} -DECLARE_HOOK(HOOK_TICK, blink, HOOK_PRIO_DEFAULT); diff --git a/common/bluetooth_le.c b/common/bluetooth_le.c deleted file mode 100644 index 2e68893223..0000000000 --- a/common/bluetooth_le.c +++ /dev/null @@ -1,198 +0,0 @@ -/* Copyright 2016 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 "bluetooth_le.h" -#include "util.h" -#include "console.h" - -#define CPRINTF(format, args...) cprintf(CC_BLUETOOTH_LE, format, ## args) - -/* - * Convert from BLE Channel to frequency - * - * Bluetooth 4.1 Vol 6 pg 36 4.1 Table 1.1 - */ - -#define CHAN_0_MHZ 2404 -#define CHAN_11_MHZ 2428 -#define CHAN_37_MHZ 2402 -#define CHAN_38_MHZ 2426 -#define CHAN_39_MHZ 2480 - -int chan2freq(int channel) -{ - int freq; - - ASSERT(channel < 40 && channel >= 0); - - switch (channel) { - case 37: /* Advertising */ - freq = CHAN_37_MHZ; - break; - case 38: /* Advertising */ - freq = CHAN_38_MHZ; - break; - case 39: /* Advertising */ - freq = CHAN_39_MHZ; - break; - default: - /* Data Channels */ - if (channel < 11) - freq = channel * 2 + CHAN_0_MHZ; - else - freq = (channel - 11) * 2 + CHAN_11_MHZ; - } - return freq; -} - -/* BLE 4.1 Vol 6 2.3.3.1 */ - -void fill_remapping_table(struct remapping_table *rt, uint8_t map[5], - int hop_increment) -{ - int i; - - rt->num_used_channels = 0; - rt->last_unmapped_channel = 0; - rt->hop_increment = hop_increment; - - for (i = 0; i < 37; i++) - if (map[i / 8] & (1 << (i % 8))) - rt->remapping_index[rt->num_used_channels++] = i; - memcpy(rt->map, map, sizeof(rt->map)); -} - -/* BLE 4.1 Vol 6 4.5.8 */ -uint8_t get_next_data_channel(struct remapping_table *rt) -{ - rt->last_unmapped_channel = - (rt->last_unmapped_channel + rt->hop_increment) % 37; - - /* Check if the channel is mapped */ - if (rt->map[rt->last_unmapped_channel / 8] & - (1 << (rt->last_unmapped_channel % 8))) - return rt->last_unmapped_channel; - else - return rt->remapping_index - [rt->last_unmapped_channel % rt->num_used_channels]; -} - -/* BLE 4.1 Vol 3 Part C 11 */ - -/* Pack advertising structures for sending */ -uint8_t *pack_adv(uint8_t *dest, int length, int type, const uint8_t *data) -{ - /* Add the structure length */ - dest[0] = (uint8_t)length+1; - /* Add the structure type */ - dest[1] = (uint8_t)type; - /* Add the data */ - memcpy(&dest[2], data, length); - - /* Return a pointer to the next structure */ - return &dest[2+length]; -} - -uint8_t *pack_adv_int(uint8_t *dest, int length, int type, int data) -{ - /* Add the structure length */ - dest[0] = (uint8_t)length+1; - /* Add the structure type */ - dest[1] = (uint8_t)type; - /* Add the data */ - memcpy(&dest[2], &data, length); - - /* Return a pointer to the next structure */ - return &dest[2+length]; -} - -uint8_t *pack_adv_addr(uint8_t *dest, uint64_t addr) -{ - memcpy(&dest[0], &addr, BLUETOOTH_ADDR_OCTETS); - - /* Return a pointer to the next structure */ - return &dest[BLUETOOTH_ADDR_OCTETS]; -} - -/* Parse advertising structures that have been received */ -const uint8_t *unpack_adv(const uint8_t *src, int *length, int *type, - const uint8_t **data) -{ - /* Get the structure length */ - *length = *(src++); - /* Get the structure type */ - *type = *(src++); - /* Get the data */ - *data = src; - - /* Return a pointer to the next structure */ - return src + *length; -} - -static void mem_dump(uint8_t *mem, int len) -{ - int i; - uint8_t value; - - for (i = 0; i < len; i++) { - value = mem[i]; - if (i % 8 == 0) - CPRINTF("\n%pP: %02x", &mem[i], value); - else - CPRINTF(" %02x", value); - } - CPRINTF("\n"); -} - -void dump_ble_addr(uint8_t *mem, char *name) -{ - int i; - - for (i = 5; i > 0; i--) - CPRINTF("%02x.", mem[i]); - CPRINTF("%02x %s\n", mem[0], name); -} - -void dump_ble_packet(struct ble_pdu *ble_p) -{ - int curr_offs; - - if (ble_p->header_type_adv) { - CPRINTF("BLE packet @ %pP: type %d, len %d, %s %s\n", - ble_p, ble_p->header.adv.type, ble_p->header.adv.length, - (ble_p->header.adv.txaddr ? " TXADDR" : ""), - (ble_p->header.adv.rxaddr ? " RXADDR" : "")); - - curr_offs = 0; - - if (ble_p->header.adv.type == - BLE_ADV_HEADER_PDU_TYPE_SCAN_REQ) { - dump_ble_addr(ble_p->payload, "ScanA"); - curr_offs += BLUETOOTH_ADDR_OCTETS; - } else if (ble_p->header.adv.type == - BLE_ADV_HEADER_PDU_TYPE_CONNECT_REQ) { - dump_ble_addr(ble_p->payload, "InitA"); - curr_offs += BLUETOOTH_ADDR_OCTETS; - } - /* All packets have AdvA */ - dump_ble_addr(ble_p->payload + curr_offs, "AdvA"); - curr_offs += BLUETOOTH_ADDR_OCTETS; - - if (ble_p->header.adv.type == - BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND) - dump_ble_addr(ble_p->payload + curr_offs, "InitA"); - else - mem_dump(ble_p->payload + curr_offs, - ble_p->header.adv.length - curr_offs); - } else { /* Data PDUs */ - CPRINTF("BLE data packet @%pP: LLID %d," - " nesn %d, sn %d, md %d, length %d\n", - ble_p, ble_p->header.data.llid, ble_p->header.data.nesn, - ble_p->header.data.sn, ble_p->header.data.md, - ble_p->header.data.length); - mem_dump(ble_p->payload, ble_p->header.data.length); - } -} - diff --git a/common/body_detection.c b/common/body_detection.c deleted file mode 100644 index 4fbc88e852..0000000000 --- a/common/body_detection.c +++ /dev/null @@ -1,256 +0,0 @@ -/* Copyright 2020 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 "accelgyro.h" -#include "body_detection.h" -#include "console.h" -#include "hwtimer.h" -#include "lid_switch.h" -#include "math_util.h" -#include "motion_sense_fifo.h" -#include "timer.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_ACCEL, outstr) -#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) - -static struct motion_sensor_t *body_sensor = - &motion_sensors[CONFIG_BODY_DETECTION_SENSOR]; - -static int window_size = CONFIG_BODY_DETECTION_MAX_WINDOW_SIZE; -static uint64_t var_threshold_scaled, confidence_delta_scaled; -static int stationary_timeframe; - -static int history_idx; -static enum body_detect_states motion_state = BODY_DETECTION_OFF_BODY; - -static bool history_initialized; -static bool body_detect_enable; -STATIC_IF(CONFIG_ACCEL_SPOOF_MODE) bool spoof_enable; - -static struct body_detect_motion_data -{ - int history[CONFIG_BODY_DETECTION_MAX_WINDOW_SIZE]; /* acceleration */ - int sum; /* sum(history) */ - uint64_t n2_variance; /* n^2 * var(history) */ -} data[2]; /* motion data for X-axis and Y-axis */ - -/* - * This function will update new variance and new sum according to incoming - * value, previous value, previous sum and previous variance. - * In order to prevent inaccuracy, we use integer to calculate instead of float - * - * n: window size - * x: data in the old window - * x': data in the new window - * x_0: oldest value in the window, will be replaced by x_n - * x_n: new coming value - * - * n^2 * var(x') = n^2 * var(x) + (x_n - x_0) * - * (n * (x_n + x_0) - sum(x') - sum(x)) - */ -static void update_motion_data(struct body_detect_motion_data *x, int x_n) -{ - const int n = window_size; - const int x_0 = x->history[history_idx]; - const int sum_diff = x_n - x_0; - const int new_sum = x->sum + sum_diff; - - x->n2_variance += sum_diff * - ((int64_t)n * (x_n + x_0) - new_sum - x->sum); - x->sum = new_sum; - x->history[history_idx] = x_n; -} - -/* Update motion data of X, Y with new sensor data. */ -static void update_motion_variance(void) -{ - update_motion_data(&data[X], body_sensor->xyz[X]); - update_motion_data(&data[Y], body_sensor->xyz[Y]); - history_idx = (history_idx + 1 >= window_size) ? 0 : history_idx + 1; -} - -/* return Var(X) + Var(Y) */ -static uint64_t get_motion_variance(void) -{ - return (data[X].n2_variance + data[Y].n2_variance) - / window_size / window_size; -} - -static int calculate_motion_confidence(uint64_t var) -{ - if (var < var_threshold_scaled - confidence_delta_scaled) - return 0; - if (var > var_threshold_scaled + confidence_delta_scaled) - return 100; - return 100 * (var - var_threshold_scaled + confidence_delta_scaled) / - (2 * confidence_delta_scaled); -} - -/* Change the motion state and commit the change to AP. */ -void body_detect_change_state(enum body_detect_states state, bool spoof) -{ - if (IS_ENABLED(CONFIG_ACCEL_SPOOF_MODE) && spoof_enable && !spoof) - return; - if (IS_ENABLED(CONFIG_GESTURE_HOST_DETECTION)) { - struct ec_response_motion_sensor_data vector = { - .flags = MOTIONSENSE_SENSOR_FLAG_BYPASS_FIFO, - .activity_data = { - .activity = MOTIONSENSE_ACTIVITY_BODY_DETECTION, - .state = state, - }, - .sensor_num = MOTION_SENSE_ACTIVITY_SENSOR_ID, - }; - motion_sense_fifo_stage_data(&vector, NULL, 0, - __hw_clock_source_read()); - motion_sense_fifo_commit_data(); - } - /* change the motion state */ - motion_state = state; - if (state == BODY_DETECTION_ON_BODY) { - /* reset time counting of stationary */ - stationary_timeframe = 0; - } - /* state changing log */ - CPRINTS("body_detect changed state to: %s body", - motion_state ? "on" : "off"); -} - -enum body_detect_states body_detect_get_state(void) -{ - return motion_state; -} - -/* Determine window size for 1 second by sensor data rate. */ -static void determine_window_size(int odr) -{ - window_size = odr / 1000; - /* Normally, window_size should not exceed MAX_WINDOW_SIZE. */ - if (window_size > CONFIG_BODY_DETECTION_MAX_WINDOW_SIZE) { - /* This will cause window size not enough for 1 second */ - CPRINTS("ODR exceeds CONFIG_BODY_DETECTION_MAX_WINDOW_SIZE"); - window_size = CONFIG_BODY_DETECTION_MAX_WINDOW_SIZE; - } -} - -/* Determine variance threshold scale by range and resolution. */ -static void determine_threshold_scale(int range, int resolution, int rms_noise) -{ - /* - * range: g - * resolution: bits - * data_1g: LSB/g - * data_1g / 9800: LSB/(mm/s^2) - * (data_1g / 9800)^2: (LSB^2)/(mm^2/s^4), which number of - * var(sensor data) will represents 1 (mm^2/s^4) - * rms_noise: ug - * var_noise: mm^2/s^4 - */ - const int data_1g = BIT(resolution - 1) / range; - const int multiplier = POW2(data_1g); - const int divisor = POW2(9800); - /* - * We are measuring the var(X) + var(Y), so theoretically, the - * var(noise) should be 2 * rms_noise^2. However, in most case, on a - * very stationary plane, the average of var(noise) are less than 2 * - * rms_noise^2. We can multiply the rms_noise^2 with the - * CONFIG_BODY_DETECTION_VAR_NOISE_FACTOR / 100. - */ - const int var_noise = POW2((uint64_t)rms_noise) * - CONFIG_BODY_DETECTION_VAR_NOISE_FACTOR * POW2(98) - / 100 / POW2(10000); - - var_threshold_scaled = (uint64_t) - (CONFIG_BODY_DETECTION_VAR_THRESHOLD + var_noise) * - multiplier / divisor; - confidence_delta_scaled = (uint64_t) - CONFIG_BODY_DETECTION_CONFIDENCE_DELTA * - multiplier / divisor; -} - -void body_detect_reset(void) -{ - int odr = body_sensor->drv->get_data_rate(body_sensor); - int resolution = body_sensor->drv->get_resolution(body_sensor); - int rms_noise = body_sensor->drv->get_rms_noise(body_sensor); - - body_detect_change_state(BODY_DETECTION_ON_BODY, false); - /* - * The sensor is suspended since its ODR is 0, - * there is no need to reset until sensor is up again - */ - if (odr == 0) - return; - determine_window_size(odr); - determine_threshold_scale(body_sensor->current_range, - resolution, rms_noise); - /* initialize motion data and state */ - memset(data, 0, sizeof(data)); - history_idx = 0; - history_initialized = 0; -} - -void body_detect(void) -{ - uint64_t motion_var; - int motion_confidence; - - if (!body_detect_enable) - return; - - update_motion_variance(); - if (!history_initialized) { - if (history_idx == window_size - 1) - history_initialized = 1; - return; - } - - motion_var = get_motion_variance(); - motion_confidence = calculate_motion_confidence(motion_var); - switch (motion_state) { - case BODY_DETECTION_OFF_BODY: - if (motion_confidence > CONFIG_BODY_DETECTION_ON_BODY_CON) - body_detect_change_state(BODY_DETECTION_ON_BODY, false); - break; - case BODY_DETECTION_ON_BODY: - stationary_timeframe += 1; - /* confidence exceeds the limit, reset time counting */ - if (motion_confidence >= CONFIG_BODY_DETECTION_OFF_BODY_CON) - stationary_timeframe = 0; - /* if no motion for enough time, change state to off_body */ - if (stationary_timeframe >= - CONFIG_BODY_DETECTION_STATIONARY_DURATION * window_size) - body_detect_change_state(BODY_DETECTION_OFF_BODY, - false); - break; - } -} - -void body_detect_set_enable(int enable) -{ - body_detect_enable = enable; - body_detect_change_state(BODY_DETECTION_ON_BODY, false); -} - -int body_detect_get_enable(void) -{ - return body_detect_enable; -} - -#ifdef CONFIG_ACCEL_SPOOF_MODE -void body_detect_set_spoof(int enable) -{ - spoof_enable = enable; - /* After disabling spoof mode, commit current state. */ - if (!enable) - body_detect_change_state(motion_state, false); -} - -bool body_detect_get_spoof(void) -{ - return spoof_enable; -} -#endif diff --git a/common/btle_hci_controller.c b/common/btle_hci_controller.c deleted file mode 100644 index cc5b872b19..0000000000 --- a/common/btle_hci_controller.c +++ /dev/null @@ -1,668 +0,0 @@ -/* Copyright 2016 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 "btle_hci_int.h" -#include "btle_hci2.h" -#include "bluetooth_le_ll.h" -#include "console.h" - -#ifdef CONFIG_BLUETOOTH_HCI_DEBUG - -#define CPUTS(outstr) cputs(CC_BLUETOOTH_HCI, outstr) -#define CPRINTS(format, args...) cprints(CC_BLUETOOTH_HCI, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_BLUETOOTH_HCI, format, ## args) - -#else /* CONFIG_BLUETOOTH_HCI_DEBUG */ - -#define CPUTS(outstr) -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) - -#endif /* CONFIG_BLUETOOTH_HCI_DEBUG */ - -static uint64_t hci_event_mask; -static uint64_t hci_le_event_mask; - -#define MAX_MESSAGE 24 - -#define STATUS (return_params[0]) -#define RPARAMS (&(return_params[1])) - -void hci_cmd(uint8_t *hciCmdbuf) -{ - static struct hciCmdHdr *hdr; - static uint8_t *params; - static uint8_t return_params[32]; - - uint8_t rparam_count = 1; /* Just status */ - uint16_t event = HCI_EVT_Command_Complete; /* default */ - - STATUS = 0xff; - - hdr = (struct hciCmdHdr *)hciCmdbuf; - params = hciCmdbuf + sizeof(struct hciCmdHdr); - - CPRINTF("opcode %x OGF %d OCF %d\n", hdr->opcode, - CMD_GET_OGF(hdr->opcode), CMD_GET_OCF(hdr->opcode)); - if (hdr->paramLen) { - int i; - - CPRINTF("paramLen %d\n", hdr->paramLen); - for (i = 0; i < hdr->paramLen; i++) - CPRINTF("%x ", params[i]); - CPRINTF("\n"); - } - - switch (hdr->opcode) { - case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband, - HCI_CMD_Reset): - STATUS = ll_reset(); - break; - case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband, - HCI_CMD_Set_Event_Mask): - if (hdr->paramLen != sizeof(hci_event_mask)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = HCI_SUCCESS; - memcpy(&hci_event_mask, params, sizeof(hci_event_mask)); - break; - case CMD_MAKE_OPCODE(HCI_OGF_Controller_and_Baseband, - HCI_CMD_Read_Transmit_Power_Level): - case CMD_MAKE_OPCODE(HCI_OGF_Informational, - HCI_CMD_Read_Local_Supported_Features): - case CMD_MAKE_OPCODE(HCI_OGF_Informational, - HCI_CMD_Read_Local_Supported_Commands): - case CMD_MAKE_OPCODE(HCI_OGF_Informational, - HCI_CMD_Read_Local_Version_Information): - case CMD_MAKE_OPCODE(HCI_OGF_Informational, - HCI_CMD_Read_BD_ADDR): - case CMD_MAKE_OPCODE(HCI_OGF_Link_Control, - HCI_CMD_Read_Remote_Version_Information): - case CMD_MAKE_OPCODE(HCI_OGF_Status, - HCI_CMD_Read_RSSI): - event = 0; - break; - - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Event_Mask): - if (hdr->paramLen != sizeof(hci_le_event_mask)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = HCI_SUCCESS; - memcpy(&hci_le_event_mask, params, sizeof(hci_le_event_mask)); - break; - - /* LE Information */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Buffer_Size): - if (hdr->paramLen != 0) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_read_buffer_size(RPARAMS); - rparam_count = sizeof(struct hciCmplLeReadBufferSize); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Local_Supported_Features): - if (hdr->paramLen != 0) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_read_local_supported_features(RPARAMS); - rparam_count = - sizeof(struct hciCmplLeReadLocalSupportedFeatures); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Supported_States): - if (hdr->paramLen != 0) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_read_supported_states(RPARAMS); - rparam_count = sizeof(struct hciCmplLeReadSupportedStates); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Host_Channel_Classification): - if (hdr->paramLen != - sizeof(struct hciLeSetHostChannelClassification)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_set_host_channel_classification(params); - break; - - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Random_Address): - if (hdr->paramLen != sizeof(struct hciLeSetRandomAddress)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_set_random_address(params); - break; - - /* Advertising */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Advertise_Enable): - STATUS = ll_set_advertising_enable(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Advertising_Data): - STATUS = ll_set_adv_data(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Adv_Params): - if (hdr->paramLen != sizeof(struct hciLeSetAdvParams)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_set_advertising_params(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Adv_Channel_TX_Power): - STATUS = ll_read_tx_power(); - rparam_count = sizeof(struct hciCmplLeReadAdvChannelTxPower); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Scan_Response_Data): - STATUS = ll_set_scan_response_data(params); - break; - - /* Connections */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Remote_Used_Features): - if (hdr->paramLen != sizeof(struct hciLeReadRemoteUsedFeatures)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_read_remote_used_features(params); - event = HCI_EVT_Command_Status; - break; - case CMD_MAKE_OPCODE(HCI_OGF_Link_Control, - HCI_CMD_Disconnect): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Connection_Update): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Create_Connection): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Create_Connection_Cancel): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Channel_Map): - event = 0; - break; - - /* Encryption */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Encrypt): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_LTK_Request_Reply): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_LTK_Request_Negative_Reply): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Rand): - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Start_Encryption): - event = 0; - break; - - /* Scanning */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Scan_Enable): - if (hdr->paramLen != sizeof(struct hciLeSetScanEnable)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_set_scan_enable(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Scan_Parameters): - if (hdr->paramLen != sizeof(struct hciLeSetScanParams)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_set_scan_params(params); - break; - - /* Allow List */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Clear_Allow_List): - if (hdr->paramLen != 0) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_clear_allow_list(); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Read_Allow_List_Size): - if (hdr->paramLen != 0) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_read_allow_list_size(RPARAMS); - rparam_count = sizeof(struct hciCmplLeReadAllowListSize); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Add_Device_To_Allow_List): - if (hdr->paramLen != sizeof(struct hciLeAddDeviceToAllowList)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_add_device_to_allow_list(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Remove_Device_From_Allow_List): - if (hdr->paramLen != - sizeof(struct hciLeRemoveDeviceFromAllowList)) - STATUS = HCI_ERR_Invalid_HCI_Command_Parameters; - else - STATUS = ll_remove_device_from_allow_list(params); - break; - - /* RFPHY Testing Support */ - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Receiver_Test): - STATUS = ll_receiver_test(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Transmitter_Test): - STATUS = ll_transmitter_test(params); - break; - case CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Test_End): - STATUS = ll_test_end(RPARAMS); - rparam_count = sizeof(struct hciCmplLeTestEnd); - break; - - default: - STATUS = HCI_ERR_Unknown_HCI_Command; - break; - } - - hci_event(event, rparam_count, return_params); -} - -void hci_acl_to_host(uint8_t *data, uint16_t hdr, uint16_t len) -{ - int i; - - /* Enqueue hdr, len, len bytes of data */ - CPRINTF("Sending %d bytes of data from handle %d with PB=%x.\n", - len, hdr & ACL_HDR_MASK_CONN_ID, - hdr & ACL_HDR_MASK_PB); - for (i = 0; i < len; i++) - CPRINTF("0x%x, ", data[i]); - CPRINTF("\n"); -} - -void hci_acl_from_host(uint8_t *hciAclbuf) -{ - struct hciAclHdr *hdr = (struct hciAclHdr *)hciAclbuf; - uint8_t *data = hciAclbuf + sizeof(struct hciAclHdr); - int i; - - /* Send the data to the link layer */ - CPRINTF("Sending %d bytes of data to handle %d with PB=%x.\n", - hdr->len, hdr->hdr & ACL_HDR_MASK_CONN_ID, - hdr->hdr & ACL_HDR_MASK_PB); - for (i = 0; i < hdr->len; i++) - CPRINTF("0x%x, ", data[i]); - CPRINTF("\n"); -} - -/* - * Required Events - * - * HCI_EVT_Command_Complete - * HCI_EVT_Command_Status - * HCI_EVTLE_Advertising_Report - * HCI_EVT_Disconnection_Complete - * HCI_EVTLE_Connection_Complete - * HCI_EVTLE_Connection_Update_Complete - * HCI_EVTLE_Read_Remote_Used_Features_Complete - * HCI_EVT_Number_Of_Completed_Packets - * HCI_EVT_Read_Remote_Version_Complete - * HCI_EVT_Encryption_Change - * HCI_EVT_Encryption_Key_Refresh_Complete - * HCI_EVTLE_Long_Term_Key_Request - */ -void hci_event(uint8_t event_code, uint8_t len, uint8_t *params) -{ - int i; - - /* Copy it to the queue. */ - CPRINTF("Event 0x%x len %d\n", event_code, len); - for (i = 0; i < len; i++) - CPRINTF("%x ", params[i]); - CPRINTF("\n"); -} - -#ifdef CONFIG_BLUETOOTH_HCI_DEBUG - -/* - * LE_Set_Advertising_Data - * hcitool lcmd 0x2008 19 0x42410907 0x46454443 0x3c11903 0x3050102 0x181203 - * hcitool cmd 8 8 7 9 41 42 43 44 45 46 3 19 c1 3 2 1 5 3 3 12 18 - * - * hcitool lcmd 0x2008 18 0x42410906 0x03454443 0x203c119 0x3030501 0x1812 - * hcitool cmd 8 8 6 9 41 42 43 44 45 3 19 c1 3 2 1 5 3 3 12 18 - */ -uint8_t adv0[19] = {0x07, 0x09, 'A', 'B', 'C', 'D', 'E', 'F', /* Name */ - 0x03, 0x19, 0xc1, 0x03, /* Keyboard */ - 0x02, 0x01, 0x05, /* Flags */ - 0x03, 0x03, 0x12, 0x18}; /* UUID */ - -uint8_t adv1[18] = {0x06, 0x09, 'A', 'B', 'C', 'D', 'E', /* Name */ - 0x02, 0x01, 0x05, /* Flags */ - 0x03, 0x19, 0xc1, 0x03, /* Keyboard */ - 0x03, 0x03, 0x12, 0x18}; /* UUID */ - -uint8_t *adverts[] = {adv0, adv1}; -uint8_t adv_lengths[] = {sizeof(adv0), sizeof(adv1)}; - -uint8_t scan0[4] = {0x03, 0x08, 'A', 'B'}; /* Short Name */ - -uint8_t scan1[] = {}; /* Empty */ - -uint8_t *scans[] = {scan0, scan1}; -uint8_t scan_lengths[] = {sizeof(scan0), sizeof(scan1)}; - -/* - * LE_Set_Adv_Params - * hcitool lcmd 0x2006 15 0x010000f0 0xb0010100 0xb4b3b2b1 0x0007c5 - * hcitool cmd 8 6 f0 0 0 1 0 1 1 b0 b1 b2 b3 b4 c5 7 0 - */ -uint8_t adv_param0[15] = { - 0xf0, 0x00, /* IntervalMin */ - 0x00, 0x01, /* IntervalMax */ - 0x00, /* Adv Type */ - 0x01, /* Use Random Addr */ - 0x01, /* Direct Random */ - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xc5, /* Direct Addr */ - 0x07, /* Channel Map */ - 0x00}; /* Filter Policy */ - -uint8_t adv_param1[15] = { - 0xf0, 0x00, /* IntervalMin */ - 0x00, 0x01, /* IntervalMax */ - 0x02, /* Adv Type */ - 0x01, /* Use Random Addr */ - 0x01, /* Direct Random */ - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xc5, /* Direct Addr */ - 0x07, /* Channel Map */ - 0x00}; /* Filter Policy */ - -uint8_t *adv_params[] = {adv_param0, adv_param1}; - -/* - * LE Information - * - * LE Read Buffer Size - * hcitool cmd 8 2 - * - * LE_Read_Local_Supported_Features - * hcitool cmd 8 3 - * - * LE_Read_Supported_States - * hcitool cmd 8 1c - * - * LE_Set_Host_Channel_Classification - * hcitool cmd 8 14 0 1 2 3 4 - * hcitool cmd 8 14 ff ff 02 ff 1f - */ - -/* - * Scan commands: - * - * Set Scan Parameters: - * hcitool cmd 8 B 0 10 0 10 0 0 0 (passive 10 10 public all) - * hcitool lcmd 0x200B 7 0x10001000 0x0000 (passive 10 10 public all) - * - * hcitool cmd 8 B 1 30 0 20 0 1 1 (active 30 20 rand white) - * hcitool lcmd 0x200B 7 0x20003001 0x0101 (active 30 20 rand white) - * - * Set Scan Enable: - * hcitool cmd 8 C 0 0 (disabled) - * hcitool cmd 8 C 1 0 (enabled no_filtering) - * hcitool cmd 8 C 1 1 (enabled filter_duplicates) - * - */ - -/* Allow list commands: - * - * Read allow list size - * hcitool cmd 8 F - * - * Clear allow list - * hcitool cmd 8 10 - * - * Add device to allow list (Public C5A4A3A2A1A0) - * hcitool cmd 8 11 0 a0 a1 a2 a3 a4 c5 - * hcitool lcmd 0x2011 7 0xA2A1A000 0xC5A4A3 - * - * Add device to allow list (Random C5B4B3B2B1B0) - * hcitool cmd 8 11 1 b0 b1 b2 b4 b5 c5 - * hcitool lcmd 0x2011 7 0xB2B1B001 0xC5B4B3 - * - * Remove device from allow list (Public C5A4A3A2A1A0) - * hcitool cmd 8 12 0 a0 a1 a2 a3 a4 c5 - * hcitool lcmd 0x2012 7 0xA2A1A000 0xC5A4A3 - * - * Remove device from allow list (Random C5B4B3B2B1B0) - * hcitool cmd 8 12 1 b0 b1 b2 b4 b5 c5 - * hcitool lcmd 0x2012 7 0xB2B1B001 0xC5B4B3 - * - * Tested by checking dumping the allow list and checking its size when: - * - adding devices - * - removing devices - * - removing non-existent devices - * - adding more than 8 devices - * - */ - -/* - * Test commands: - * - * Rx Test channel 37 - * hcitool cmd 8 1D 25 - * - * Tx Test channel 37 20 bytes type 2 - * hcitool cmd 8 1e 25 14 2 - * - * Test end - * hcitool cmd 8 1f - */ - -static uint8_t hci_buf[200]; - -#define MAX_BLE_HCI_PARAMS 8 -static uint32_t param[MAX_BLE_HCI_PARAMS]; - -static int command_ble_hci_cmd(int argc, char **argv) -{ - static struct hciCmdHdr header; - int length, opcode, i; - char *e; - - if (argc < 3 || argc > MAX_BLE_HCI_PARAMS + 3) - return EC_ERROR_PARAM_COUNT; - - opcode = strtoi(argv[1], &e, 0); - if (*e || opcode < 0 || opcode > 0xffff) - return EC_ERROR_PARAM1; - - length = strtoi(argv[2], &e, 0); - if (*e || length < 0 || length > 32) - return EC_ERROR_PARAM2; - - if ((length + 3) / 4 != argc - 3) { - CPRINTF("Remember to pass HCI params in 32-bit chunks.\n"); - return EC_ERROR_PARAM_COUNT; - } - - for (i = 3; i < argc; i++) { - param[i-3] = strtoi(argv[i], &e, 0); - if (*e) - return EC_ERROR_PARAM3 + i; - } - - header.opcode = opcode; - header.paramLen = length; - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - memcpy(hci_buf + sizeof(struct hciCmdHdr), - param, length); - - hci_cmd(hci_buf); - - CPRINTS("hci cmd @%pP", hci_buf); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ble_hci_cmd, command_ble_hci_cmd, - "opcode len uint32 uint32 uint32... (little endian)", - "Send an hci command of length len"); - -static int command_hcitool(int argc, char **argv) -{ - static struct hciCmdHdr header; - int i, ogf, ocf; - char *e; - - if (argc < 4 || argc > MAX_BLE_HCI_PARAMS + 3) - return EC_ERROR_PARAM_COUNT; - - if (argv[1][0] == 'l') /* strcmp lcmd */ - return command_ble_hci_cmd(argc-1, &argv[1]); - - ogf = strtoi(argv[2], &e, 16); - if (*e) - return EC_ERROR_PARAM2; - - ocf = strtoi(argv[3], &e, 16); - if (*e) - return EC_ERROR_PARAM3; - - header.opcode = CMD_MAKE_OPCODE(ogf, ocf); - header.paramLen = argc-4; - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - - for (i = 4; i < argc; i++) { - hci_buf[i - 4 + 3] = strtoi(argv[i], &e, 16); - if (*e) - return EC_ERROR_PARAM4 + i; - } - - hci_cmd(hci_buf); - - CPRINTS("hci cmd @%pP", hci_buf); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hcitool, command_hcitool, - "cmd ogf ocf b0 b1 b2 b3... or lcmd opcode len uint32.. (little endian)", - "Send an hci command of length len"); - -static int command_ble_hci_acl(int argc, char **argv) -{ - static struct hciAclHdr header; - int length, hdr, i; - char *e; - - if (argc < 3 || argc > MAX_BLE_HCI_PARAMS + 3) - return EC_ERROR_PARAM_COUNT; - - hdr = strtoi(argv[1], &e, 0); - if (*e || hdr < 0 || hdr > 0xffff) - return EC_ERROR_PARAM1; - - length = strtoi(argv[2], &e, 0); - if (*e || length < 0 || length > 32) - return EC_ERROR_PARAM2; - - if ((length + 3) / 4 != argc - 3) { - CPRINTF("Remember to pass HCI params in 32-bit chunks.\n"); - return EC_ERROR_PARAM_COUNT; - } - - for (i = 3; i < argc; i++) { - param[i-3] = strtoi(argv[i], &e, 0); - if (*e) - return EC_ERROR_PARAM3 + i; - } - - header.hdr = hdr; - header.len = length; - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - memcpy(hci_buf + sizeof(struct hciCmdHdr), - param, length); - - hci_cmd(hci_buf); - - CPRINTS("hci acl @%pP", hci_buf); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ble_hci_acl, command_ble_hci_acl, - "hdr len uint32 uint32 uint32... (little endian)", - "Send hci acl data of length len"); - -static int command_ble_hci_adv(int argc, char **argv) -{ - static struct hciCmdHdr header; - int adv, p = 0, scan_rsp = 0; - char *e; - - if (argc < 2 || argc > 4) - return EC_ERROR_PARAM_COUNT; - - adv = strtoi(argv[1], &e, 0); - if (*e || adv < 0 || adv > sizeof(adverts)) - return EC_ERROR_PARAM1; - - if (argc > 2) { - p = strtoi(argv[2], &e, 0); - if (*e || p < 0 || p > sizeof(adv_params)) - return EC_ERROR_PARAM2; - } - - if (argc > 3) { - scan_rsp = strtoi(argv[3], &e, 0); - if (*e || scan_rsp < 0 || scan_rsp > sizeof(scans)) - return EC_ERROR_PARAM3; - } - - header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE, HCI_CMD_LE_Set_Adv_Params); - header.paramLen = sizeof(struct hciLeSetAdvParams); - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - memcpy(hci_buf + sizeof(struct hciCmdHdr), - adv_params[p], header.paramLen); - - hci_cmd(hci_buf); - - header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Advertising_Data); - header.paramLen = adv_lengths[adv]; - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - memcpy(hci_buf + sizeof(struct hciCmdHdr), - adverts[adv], header.paramLen); - - hci_cmd(hci_buf); - - header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Scan_Response_Data); - header.paramLen = scan_lengths[scan_rsp]; - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - memcpy(hci_buf + sizeof(struct hciCmdHdr), - scans[scan_rsp], header.paramLen); - - hci_cmd(hci_buf); - - header.opcode = CMD_MAKE_OPCODE(HCI_OGF_LE, - HCI_CMD_LE_Set_Advertise_Enable); - header.paramLen = sizeof(struct hciLeSetAdvEnable); - - memcpy(hci_buf, &header, sizeof(struct hciCmdHdr)); - hci_buf[sizeof(struct hciCmdHdr)] = 1; - - hci_cmd(hci_buf); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ble_hci_adv, command_ble_hci_adv, - "adv [params=0] [scan_rsp=0]", - "Use pre-defined parameters to start advertising"); - -#endif /* CONFIG_BLUETOOTH_HCI_DEBUG */ diff --git a/common/btle_ll.c b/common/btle_ll.c deleted file mode 100644 index 20ede4d4a0..0000000000 --- a/common/btle_ll.c +++ /dev/null @@ -1,861 +0,0 @@ -/* Copyright 2016 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 "bluetooth_le_ll.h" -#include "bluetooth_le.h" -#include "btle_hci_int.h" -#include "util.h" -#include "console.h" -#include "radio.h" -#include "radio_test.h" -#include "task.h" -#include "timer.h" - -#ifdef CONFIG_BLUETOOTH_LL_DEBUG - -#define CPUTS(outstr) cputs(CC_BLUETOOTH_LL, outstr) -#define CPRINTS(format, args...) cprints(CC_BLUETOOTH_LL, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_BLUETOOTH_LL, format, ## args) - -#else /* CONFIG_BLUETOOTH_LL_DEBUG */ - -#define CPUTS(outstr) -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) - -#endif /* CONFIG_BLUETOOTH_LL_DEBUG */ - -/* Link Layer */ - -enum ll_state_t ll_state = UNINITIALIZED; - -static struct hciLeSetAdvParams ll_adv_params; -static struct hciLeSetScanParams ll_scan_params; -static int ll_adv_interval_us; -static int ll_adv_timeout_us; - -static struct ble_pdu ll_adv_pdu; -static struct ble_pdu ll_scan_rsp_pdu; -static struct ble_pdu tx_packet_1; -static struct ble_pdu *packet_tb_sent; -static struct ble_connection_params conn_params; -static int connection_initialized; -static struct remapping_table remap_table; - -static uint64_t receive_time, last_receive_time; -static uint8_t num_consecutive_failures; - -static uint32_t tx_end, tx_rsp_end, time_of_connect_req; -struct ble_pdu ll_rcv_packet; -static uint32_t ll_conn_events; -static uint32_t errors_recovered; - -int ll_power; -uint8_t is_first_data_packet; - -static uint64_t ll_random_address = 0xC5BADBADBAD1; /* Uninitialized */ -static uint64_t ll_public_address = 0xC5BADBADBADF; /* Uninitialized */ -static uint8_t ll_channel_map[5] = {0xff, 0xff, 0xff, 0xff, 0x1f}; - -static uint8_t ll_filter_duplicates; - -int ll_pseudo_rand(int max_plus_one) -{ - static uint32_t lfsr = 0x55555; - int lsb = lfsr & 1; - - lfsr = lfsr >> 1; - if (lsb) - lfsr ^= 0x80020003; /* Bits 32, 22, 2, 1 */ - return lfsr % max_plus_one; -} - -uint8_t ll_set_tx_power(uint8_t *params) -{ - /* Add checking */ - ll_power = params[0]; - return HCI_SUCCESS; -} - -uint8_t ll_read_tx_power(void) -{ - return ll_power; -} - -/* LE Information */ -uint8_t ll_read_buffer_size(uint8_t *return_params) -{ - return_params[0] = LL_MAX_DATA_PACKET_LENGTH & 0xff; - return_params[1] = (LL_MAX_DATA_PACKET_LENGTH >> 8) & 0xff; - return_params[2] = LL_MAX_DATA_PACKETS; - return HCI_SUCCESS; -} - -uint8_t ll_read_local_supported_features(uint8_t *return_params) -{ - uint64_t supported_features = LL_SUPPORTED_FEATURES; - - memcpy(return_params, &supported_features, sizeof(supported_features)); - return HCI_SUCCESS; -} - -uint8_t ll_read_supported_states(uint8_t *return_params) -{ - uint64_t supported_states = LL_SUPPORTED_STATES; - - memcpy(return_params, &supported_states, sizeof(supported_states)); - return HCI_SUCCESS; -} - -uint8_t ll_set_host_channel_classification(uint8_t *params) -{ - memcpy(ll_channel_map, params, sizeof(ll_channel_map)); - return HCI_SUCCESS; -} - -/* Advertising */ -uint8_t ll_set_scan_response_data(uint8_t *params) -{ - if (params[0] > BLE_MAX_ADV_PAYLOAD_OCTETS) - return HCI_ERR_Invalid_HCI_Command_Parameters; - - if (ll_state == ADVERTISING) - return HCI_ERR_Controller_Busy; - - memcpy(&ll_scan_rsp_pdu.payload[BLUETOOTH_ADDR_OCTETS], ¶ms[1], - params[0]); - ll_scan_rsp_pdu.header.adv.length = params[0] + BLUETOOTH_ADDR_OCTETS; - - return HCI_SUCCESS; -} - -uint8_t ll_set_adv_data(uint8_t *params) -{ - if (params[0] > BLE_MAX_ADV_PAYLOAD_OCTETS) - return HCI_ERR_Invalid_HCI_Command_Parameters; - - if (ll_state == ADVERTISING) - return HCI_ERR_Controller_Busy; - - /* Skip the address */ - memcpy(&ll_adv_pdu.payload[BLUETOOTH_ADDR_OCTETS], ¶ms[1], - params[0]); - ll_adv_pdu.header.adv.length = params[0] + BLUETOOTH_ADDR_OCTETS; - - return HCI_SUCCESS; -} - -uint8_t ll_reset(void) -{ - ll_state = UNINITIALIZED; - radio_disable(); - - ble_radio_clear_allow_list(); - - return HCI_SUCCESS; -} - -static uint8_t ll_state_change_request(enum ll_state_t next_state) -{ - /* Initialize the radio if it hasn't been initialized */ - if (ll_state == UNINITIALIZED) { - if (ble_radio_init(BLE_ADV_ACCESS_ADDRESS, BLE_ADV_CRCINIT) - != EC_SUCCESS) - return HCI_ERR_Hardware_Failure; - ll_state = STANDBY; - } - - /* Only change states when the link layer is in STANDBY */ - if (next_state != STANDBY && ll_state != STANDBY) - return HCI_ERR_Controller_Busy; - - ll_state = next_state; - - return HCI_SUCCESS; -} - -uint8_t ll_set_advertising_enable(uint8_t *params) -{ - uint8_t rv; - - if (params[0]) { - rv = ll_state_change_request(ADVERTISING); - if (rv == HCI_SUCCESS) - task_wake(TASK_ID_BLE_LL); - } else { - rv = ll_state_change_request(STANDBY); - } - - return rv; -} - -uint8_t ll_set_scan_enable(uint8_t *params) -{ - uint8_t rv; - - if (params[0]) { - ll_filter_duplicates = params[1]; - rv = ll_state_change_request(SCANNING); - if (rv == HCI_SUCCESS) - task_wake(TASK_ID_BLE_LL); - } else { - rv = ll_state_change_request(STANDBY); - } - - return HCI_SUCCESS; -} - -void set_empty_data_packet(struct ble_pdu *pdu) -{ - /* LLID == 1 means incomplete or empty data packet */ - pdu->header.data.llid = 1; - pdu->header.data.nesn = 1; - pdu->header.data.sn = 0; - pdu->header.data.md = 0; - pdu->header.data.length = 0; - pdu->header_type_adv = 0; -} - -/* Connection state */ - -/** - * This function serves to take data from a CONNECT_REQ packet and copy it - * into a struct, conn_params, which defines the parameter of the connection. - * It also fills a remapping table, another essential element of the link - * layer connection. - */ -uint8_t initialize_connection(void) -{ - int cur_offset = 0, i = 0; - uint8_t final_octet = 0; - uint8_t remap_arr[5]; - uint8_t *payload_start = (uint8_t *)(ll_rcv_packet.payload); - - num_consecutive_failures = 0; - - /* Copy data into the appropriate portions of memory */ - memcpy((uint8_t *)&(conn_params.init_a), - payload_start, CONNECT_REQ_INITA_LEN); - cur_offset += CONNECT_REQ_INITA_LEN; - - memcpy((uint8_t *)&(conn_params.adv_a), - payload_start+cur_offset, CONNECT_REQ_ADVA_LEN); - cur_offset += CONNECT_REQ_ADVA_LEN; - - memcpy(&(conn_params.access_addr), - payload_start+cur_offset, CONNECT_REQ_ACCESS_ADDR_LEN); - cur_offset += CONNECT_REQ_ACCESS_ADDR_LEN; - - conn_params.crc_init_val = 0; - memcpy(&(conn_params.crc_init_val), - payload_start+cur_offset, CONNECT_REQ_CRC_INIT_VAL_LEN); - cur_offset += CONNECT_REQ_CRC_INIT_VAL_LEN; - - memcpy(&(conn_params.win_size), - payload_start+cur_offset, CONNECT_REQ_WIN_SIZE_LEN); - cur_offset += CONNECT_REQ_WIN_SIZE_LEN; - - memcpy(&(conn_params.win_offset), - payload_start+cur_offset, CONNECT_REQ_WIN_OFFSET_LEN); - cur_offset += CONNECT_REQ_WIN_OFFSET_LEN; - - memcpy(&(conn_params.interval), - payload_start+cur_offset, CONNECT_REQ_INTERVAL_LEN); - cur_offset += CONNECT_REQ_INTERVAL_LEN; - - memcpy(&(conn_params.latency), - payload_start+cur_offset, CONNECT_REQ_LATENCY_LEN); - cur_offset += CONNECT_REQ_LATENCY_LEN; - - memcpy(&(conn_params.timeout), - payload_start+cur_offset, CONNECT_REQ_TIMEOUT_LEN); - cur_offset += CONNECT_REQ_TIMEOUT_LEN; - - conn_params.channel_map = 0; - memcpy(&(conn_params.channel_map), - payload_start+cur_offset, CONNECT_REQ_CHANNEL_MAP_LEN); - cur_offset += CONNECT_REQ_CHANNEL_MAP_LEN; - - memcpy(&final_octet, payload_start+cur_offset, - CONNECT_REQ_HOP_INCREMENT_AND_SCA_LEN); - - /* last 5 bits of final_octet: */ - conn_params.hop_increment = final_octet & 0x1f; - /* first 3 bits of final_octet: */ - conn_params.sleep_clock_accuracy = (final_octet & 0xe0) >> 5; - - /* Set up channel mapping table */ - for (i = 0; i < 5; ++i) - remap_arr[i] = *(((uint8_t *)&(conn_params.channel_map))+i); - fill_remapping_table(&remap_table, remap_arr, - conn_params.hop_increment); - - /* Calculate transmission window parameters */ - conn_params.transmitWindowSize = conn_params.win_size * 1250; - conn_params.transmitWindowOffset = conn_params.win_offset * 1250; - conn_params.connInterval = conn_params.interval * 1250; - /* The following two lines convert ms -> microseconds */ - conn_params.connLatency = 1000 * conn_params.latency; - conn_params.connSupervisionTimeout = 10000 * conn_params.timeout; - /* All these times are in microseconds! */ - - /* Check for common transmission errors */ - if (conn_params.hop_increment < 5 || conn_params.hop_increment > 16) { - for (i = 0; i < 5; ++i) - CPRINTF("ERROR!! ILLEGAL HOP_INCREMENT!!\n"); - return HCI_ERR_Invalid_LMP_Parameters; - } - - is_first_data_packet = 1; - return HCI_SUCCESS; -} - -/* Allow List */ -uint8_t ll_clear_allow_list(void) -{ - if (ble_radio_clear_allow_list() == EC_SUCCESS) - return HCI_SUCCESS; - else - return HCI_ERR_Hardware_Failure; -} - -uint8_t ll_read_allow_list_size(uint8_t *return_params) -{ - if (ble_radio_read_allow_list_size(return_params) == EC_SUCCESS) - return HCI_SUCCESS; - else - return HCI_ERR_Hardware_Failure; -} - -uint8_t ll_add_device_to_allow_list(uint8_t *params) -{ - if (ble_radio_add_device_to_allow_list(¶ms[1], params[0]) == - EC_SUCCESS) - return HCI_SUCCESS; - else - return HCI_ERR_Host_Rejected_Due_To_Limited_Resources; -} - -uint8_t ll_remove_device_from_allow_list(uint8_t *params) -{ - if (ble_radio_remove_device_from_allow_list(¶ms[1], params[0]) == - EC_SUCCESS) - return HCI_SUCCESS; - else - return HCI_ERR_Hardware_Failure; -} - -/* Connections */ -uint8_t ll_read_remote_used_features(uint8_t *params) -{ - uint16_t handle = params[0] | (((uint16_t)params[1]) << 8); - - CPRINTS("Read remote used features for handle %d", handle); - /* Check handle */ - return HCI_SUCCESS; -} - -/* RF PHY Testing */ -static int ll_test_packets; - -uint8_t ll_receiver_test(uint8_t *params) -{ - int rv; - - ll_test_packets = 0; - - /* See if the link layer is busy */ - rv = ll_state_change_request(TEST_RX); - if (rv) - return rv; - - rv = ble_test_rx_init(params[0]); - if (rv) - return rv; - - CPRINTS("Start Rx test"); - task_wake(TASK_ID_BLE_LL); - - return HCI_SUCCESS; -} - -uint8_t ll_transmitter_test(uint8_t *params) -{ - int rv; - - ll_test_packets = 0; - - /* See if the link layer is busy */ - rv = ll_state_change_request(TEST_TX); - if (rv) - return rv; - - rv = ble_test_tx_init(params[0], params[1], params[2]); - if (rv) - return rv; - - CPRINTS("Start Tx test"); - task_wake(TASK_ID_BLE_LL); - - return HCI_SUCCESS; -} - -uint8_t ll_test_end(uint8_t *return_params) -{ - CPRINTS("End (%d packets)", ll_test_packets); - - ble_test_stop(); - - if (ll_state == TEST_RX) { - return_params[0] = ll_test_packets & 0xff; - return_params[1] = (ll_test_packets >> 8); - ll_test_packets = 0; - } else { - return_params[0] = 0; - return_params[1] = 0; - ll_test_packets = 0; - } - return ll_reset(); -} - -uint8_t ll_set_random_address(uint8_t *params) -{ - /* No checking. The host should know the rules. */ - memcpy(&ll_random_address, params, - sizeof(struct hciLeSetRandomAddress)); - return HCI_SUCCESS; -} - -uint8_t ll_set_scan_params(uint8_t *params) -{ - if (ll_state == SCANNING) - return HCI_ERR_Controller_Busy; - - memcpy(&ll_scan_params, params, sizeof(struct hciLeSetScanParams)); - - return HCI_SUCCESS; -} - -uint8_t ll_set_advertising_params(uint8_t *params) -{ - if (ll_state == ADVERTISING) - return HCI_ERR_Controller_Busy; - - memcpy(&ll_adv_params, params, sizeof(struct hciLeSetAdvParams)); - - switch (ll_adv_params.advType) { - case BLE_ADV_HEADER_PDU_TYPE_ADV_NONCONN_IND: - case BLE_ADV_HEADER_PDU_TYPE_ADV_SCAN_IND: - if (ll_adv_params.advIntervalMin < - (100000 / LL_ADV_INTERVAL_UNIT_US)) /* 100ms */ - return HCI_ERR_Invalid_HCI_Command_Parameters; - /* Fall through */ - case BLE_ADV_HEADER_PDU_TYPE_ADV_IND: - if (ll_adv_params.advIntervalMin > ll_adv_params.advIntervalMax) - return HCI_ERR_Invalid_HCI_Command_Parameters; - if (ll_adv_params.advIntervalMin < - (20000 / LL_ADV_INTERVAL_UNIT_US) || /* 20ms */ - ll_adv_params.advIntervalMax > - (10240000 / LL_ADV_INTERVAL_UNIT_US)) /* 10.24s */ - return HCI_ERR_Invalid_HCI_Command_Parameters; - ll_adv_interval_us = (((ll_adv_params.advIntervalMin + - ll_adv_params.advIntervalMax) / 2) * - LL_ADV_INTERVAL_UNIT_US); - /* Don't time out */ - ll_adv_timeout_us = -1; - break; - case BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND: - ll_adv_interval_us = LL_ADV_DIRECT_INTERVAL_US; - ll_adv_timeout_us = LL_ADV_DIRECT_TIMEOUT_US; - break; - default: - return HCI_ERR_Invalid_HCI_Command_Parameters; - } - - /* Initialize the ADV PDU */ - ll_adv_pdu.header_type_adv = 1; - ll_adv_pdu.header.adv.type = ll_adv_params.advType; - ll_adv_pdu.header.adv.txaddr = ll_adv_params.useRandomAddress; - - if (ll_adv_params.useRandomAddress) - memcpy(ll_adv_pdu.payload, &ll_random_address, - BLUETOOTH_ADDR_OCTETS); - else - memcpy(ll_adv_pdu.payload, &ll_public_address, - BLUETOOTH_ADDR_OCTETS); - - if (ll_adv_params.advType == BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND) { - ll_adv_pdu.header.adv.rxaddr = - ll_adv_params.directRandomAddress; - memcpy(&ll_adv_pdu.payload[BLUETOOTH_ADDR_OCTETS], - ll_adv_params.directAddr, - sizeof(ll_adv_params.directAddr)); - ll_adv_pdu.header.adv.length = 12; - } else { - ll_adv_pdu.header.adv.rxaddr = 0; - } - - /* All other types get data from SetAdvertisingData */ - - /* Initialize the Scan Rsp PDU */ - ll_scan_rsp_pdu.header_type_adv = 1; - ll_scan_rsp_pdu.header.adv.type = BLE_ADV_HEADER_PDU_TYPE_SCAN_RSP; - ll_scan_rsp_pdu.header.adv.txaddr = ll_adv_params.useRandomAddress; - - if (ll_adv_params.useRandomAddress) - memcpy(ll_scan_rsp_pdu.payload, &ll_random_address, - BLUETOOTH_ADDR_OCTETS); - else - memcpy(ll_scan_rsp_pdu.payload, &ll_public_address, - BLUETOOTH_ADDR_OCTETS); - - ll_scan_rsp_pdu.header.adv.rxaddr = 0; - - return HCI_SUCCESS; -} - -static uint32_t tx_end, rsp_end, tx_rsp_end; -struct ble_pdu ll_rcv_packet; - -/** - * Advertises packet that has already been generated on given channel. - * - * This function also processes any incoming scan requests. - * - * @param chan The channel on which to advertise. - * @returns EC_SUCCESS on packet reception, otherwise error. - */ -int ble_ll_adv(int chan) -{ - int rv; - - ble_radio_init(BLE_ADV_ACCESS_ADDRESS, BLE_ADV_CRCINIT); - - /* Change channel */ - NRF51_RADIO_FREQUENCY = NRF51_RADIO_FREQUENCY_VAL(chan2freq(chan)); - NRF51_RADIO_DATAWHITEIV = chan; - - ble_tx(&ll_adv_pdu); - - while (!RADIO_DONE) - ; - - tx_end = get_time().le.lo; - - if (ll_adv_pdu.header.adv.type == - BLE_ADV_HEADER_PDU_TYPE_ADV_NONCONN_IND) - return EC_SUCCESS; - - rv = ble_rx(&ll_rcv_packet, 16000, 1); - - if (rv != EC_SUCCESS) - return rv; - - while (!RADIO_DONE) - ; - - tx_rsp_end = get_time().le.lo; - - /* Check for valid responses */ - switch (ll_rcv_packet.header.adv.type) { - case BLE_ADV_HEADER_PDU_TYPE_SCAN_REQ: - /* Scan requests are only allowed for ADV_IND and SCAN_IND */ - if ((ll_adv_pdu.header.adv.type != - BLE_ADV_HEADER_PDU_TYPE_ADV_IND && - ll_adv_pdu.header.adv.type != - BLE_ADV_HEADER_PDU_TYPE_ADV_SCAN_IND) || - /* The advertising address needs to match */ - (memcmp(&ll_rcv_packet.payload[BLUETOOTH_ADDR_OCTETS], - &ll_adv_pdu.payload[0], BLUETOOTH_ADDR_OCTETS))) { - /* Don't send the scan response */ - radio_disable(); - return rv; - } - break; - case BLE_ADV_HEADER_PDU_TYPE_CONNECT_REQ: - /* Don't send a scan response */ - radio_disable(); - /* Connecting is only allowed for ADV_IND and ADV_DIRECT_IND */ - if (ll_adv_pdu.header.adv.type != - BLE_ADV_HEADER_PDU_TYPE_ADV_IND && - ll_adv_pdu.header.adv.type != - BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND) - return rv; - /* The advertising address needs to match */ - if (memcmp(&ll_rcv_packet.payload[BLUETOOTH_ADDR_OCTETS], - &ll_adv_pdu.payload[0], BLUETOOTH_ADDR_OCTETS)) - return rv; - /* The InitAddr address needs to match for ADV_DIRECT_IND */ - if (ll_adv_pdu.header.adv.type == - BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND && - memcmp(&ll_adv_pdu.payload[BLUETOOTH_ADDR_OCTETS], - &ll_rcv_packet.payload[0], BLUETOOTH_ADDR_OCTETS)) - return rv; - - /* Mark time that connect was received */ - time_of_connect_req = NRF51_TIMER_CC(0, 1); - - /* - * Enter connection state upon receiving - * a connect request packet - */ - ll_state = CONNECTION; - - return rv; - break; - default: /* Unhandled response packet */ - radio_disable(); - return rv; - break; - } - - CPRINTF("ADV %u Response %u %u\n", tx_end, rsp_end, tx_rsp_end); - - return rv; -} - -int ble_ll_adv_event(void) -{ - int chan_idx; - int rv = EC_SUCCESS; - - for (chan_idx = 0; chan_idx < 3; chan_idx++) { - if (ll_adv_params.advChannelMap & BIT(chan_idx)) { - rv = ble_ll_adv(chan_idx + 37); - if (rv != EC_SUCCESS) - return rv; - } - } - - return rv; -} - - -void print_connection_state(void) -{ - CPRINTF("vvvvvvvvvvvvvvvvvvvCONNECTION STATEvvvvvvvvvvvvvvvvvvv\n"); - CPRINTF("Number of connections events processed: %d\n", ll_conn_events); - CPRINTF("Recovered from %d bad receives.\n", errors_recovered); - CPRINTF("Access addr(hex): %x\n", conn_params.access_addr); - CPRINTF("win_size(hex): %x\n", conn_params.win_size); - CPRINTF("win_offset(hex): %x\n", conn_params.win_offset); - CPRINTF("interval(hex): %x\n", conn_params.interval); - CPRINTF("latency(hex): %x\n", conn_params.latency); - CPRINTF("timeout(hex): %x\n", conn_params.timeout); - CPRINTF("channel_map(hex): %llx\n", conn_params.channel_map); - CPRINTF("hop(hex): %x\n", conn_params.hop_increment); - CPRINTF("SCA(hex): %x\n", conn_params.sleep_clock_accuracy); - CPRINTF("transmitWindowOffset: %d\n", conn_params.transmitWindowOffset); - CPRINTF("connInterval: %d\n", conn_params.connInterval); - CPRINTF("transmitWindowSize: %d\n", conn_params.transmitWindowSize); - CPRINTF("^^^^^^^^^^^^^^^^^^^CONNECTION STATE^^^^^^^^^^^^^^^^^^^\n"); -} - -int connected_communicate(void) -{ - int rv; - long sleep_time; - int offset = 0; - uint64_t listen_time; - uint8_t comm_channel = get_next_data_channel(&remap_table); - - if (num_consecutive_failures > 0) { - ble_radio_init(conn_params.access_addr, - conn_params.crc_init_val); - NRF51_RADIO_FREQUENCY = - NRF51_RADIO_FREQUENCY_VAL(chan2freq(comm_channel)); - NRF51_RADIO_DATAWHITEIV = comm_channel; - listen_time = last_receive_time + conn_params.connInterval - - get_time().val + conn_params.transmitWindowSize; - - /* - * This listens for 1.25 times the expected amount - * of time. This is a margin of error. This line is - * only called when a connection has failed (a missed - * packet). The peripheral and the controller could have - * missed this packet due to a disagreement on when - * the packet should have arrived. We listen for - * slightly longer than expected in the case that - * there was a timing disagreement. - */ - rv = ble_rx(&ll_rcv_packet, - listen_time + (listen_time >> 2), 0); - } else { - if (!is_first_data_packet) { - sleep_time = receive_time + - conn_params.connInterval - get_time().val; - /* - * The time slept is 31/32 (96.875%) of the calculated - * required sleep time because the code to receive - * packets requires time to set up. - */ - usleep(sleep_time - (sleep_time >> 5)); - } else { - last_receive_time = time_of_connect_req; - sleep_time = TRANSMIT_WINDOW_OFFSET_CONSTANT + - conn_params.transmitWindowOffset + - time_of_connect_req - get_time().val; - if (sleep_time >= 0) { - /* - * Radio is on for longer than needed for first - * packet to make sure that it is received. - */ - usleep(sleep_time - (sleep_time >> 2)); - } else { - return EC_ERROR_TIMEOUT; - } - } - - ble_radio_init(conn_params.access_addr, - conn_params.crc_init_val); - NRF51_RADIO_FREQUENCY = - NRF51_RADIO_FREQUENCY_VAL(chan2freq(comm_channel)); - NRF51_RADIO_DATAWHITEIV = comm_channel; - - /* - * Timing the transmit window is very hard to do when the code - * executing has actual effect on the timing. To combat this, - * the radio starts a little early, and terminates when the - * window normally should. The variable 'offset' represents - * how early the window opens in microseconds. - */ - if (!is_first_data_packet) - offset = last_receive_time + conn_params.connInterval - - get_time().val; - else - offset = 0; - - rv = ble_rx(&ll_rcv_packet, - offset + conn_params.transmitWindowSize, - 0); - } - - /* - * The radio shortcuts have been set up so that transmission - * occurs automatically after receiving. The radio just needs - * to know where to find the packet to be sent. - */ - NRF51_RADIO_PACKETPTR = (uint32_t)packet_tb_sent; - - receive_time = NRF51_TIMER_CC(0, 1); - if (rv != EC_SUCCESS) - receive_time = last_receive_time + conn_params.connInterval; - - while (!RADIO_DONE) - ; - - last_receive_time = receive_time; - is_first_data_packet = 0; - - return rv; -} - -static uint32_t ll_adv_events; -static timestamp_t deadline; -static uint32_t start, end; - -void bluetooth_ll_task(void) -{ - uint64_t last_rx_time = 0; - CPRINTS("LL task init"); - - while (1) { - switch (ll_state) { - case ADVERTISING: - - if (deadline.val == 0) { - CPRINTS("ADV @%pP", &ll_adv_pdu); - deadline.val = get_time().val + - (uint32_t)ll_adv_timeout_us; - ll_adv_events = 0; - } - - ble_ll_adv_event(); - ll_adv_events++; - - if (ll_state == CONNECTION) { - receive_time = 0; - break; - } - /* sleep for 0-10ms */ - usleep(ll_adv_interval_us + ll_pseudo_rand(10000)); - - if (get_time().val > deadline.val) { - ll_state = STANDBY; - break; - } - break; - case STANDBY: - deadline.val = 0; - CPRINTS("Standby %d events", ll_adv_events); - ll_adv_events = 0; - ll_conn_events = 0; - task_wait_event(-1); - connection_initialized = 0; - errors_recovered = 0; - break; - case TEST_RX: - if (ble_test_rx() == HCI_SUCCESS) - ll_test_packets++; - /* Packets come every 625us, sleep to save power */ - usleep(300); - break; - case TEST_TX: - start = get_time().le.lo; - ble_test_tx(); - ll_test_packets++; - end = get_time().le.lo; - usleep(625 - 82 - (end-start)); /* 625us */ - break; - case UNINITIALIZED: - ble_radio_init(BLE_ADV_ACCESS_ADDRESS, BLE_ADV_CRCINIT); - ll_adv_events = 0; - task_wait_event(-1); - connection_initialized = 0; - packet_tb_sent = &tx_packet_1; - set_empty_data_packet(&tx_packet_1); - break; - case CONNECTION: - if (!connection_initialized) { - if (initialize_connection() != HCI_SUCCESS) { - ll_state = STANDBY; - break; - } - connection_initialized = 1; - last_rx_time = NRF51_TIMER_CC(0, 1); - } - - if (connected_communicate() == EC_SUCCESS) { - if (num_consecutive_failures > 0) - ++errors_recovered; - num_consecutive_failures = 0; - last_rx_time = get_time().val; - } else { - num_consecutive_failures++; - if ((get_time().val - last_rx_time) > - conn_params.connSupervisionTimeout) { - - ll_state = STANDBY; - CPRINTF("EXITING CONNECTION STATE " - "DUE TO TIMEOUT.\n"); - } - } - ++ll_conn_events; - - if (ll_state == STANDBY) { - CPRINTF("Exiting connection state/Entering " - "Standby state after %d connections " - "events\n", ll_conn_events); - print_connection_state(); - } - break; - default: - CPRINTS("Unhandled State ll_state = %d", ll_state); - ll_state = UNINITIALIZED; - task_wait_event(-1); - } - } -} - diff --git a/common/build.mk b/common/build.mk index a1a956f9ab..cfc023be5a 100644 --- a/common/build.mk +++ b/common/build.mk @@ -306,9 +306,3 @@ $(out)/rma_key_from_blob.h: board/$(BOARD)/$(BLOB_FILE) util/bin2h.sh $(Q)util/bin2h.sh RMA_KEY_BLOB $< $@ endif - -include $(_common_dir)fpsensor/build.mk -include $(_common_dir)usbc/build.mk - -include $(_common_dir)mock/build.mk -common-y+=$(foreach m,$(mock-y),mock/$(m)) diff --git a/common/button.c b/common/button.c deleted file mode 100644 index 03bdb1234f..0000000000 --- a/common/button.c +++ /dev/null @@ -1,892 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Button module for Chrome EC */ - -#include "atomic.h" -#include "button.h" -#include "chipset.h" -#include "common.h" -#include "compile_time_macros.h" -#include "console.h" -#include "gpio.h" -#include "host_command.h" -#include "hooks.h" -#include "keyboard_protocol.h" -#include "led_common.h" -#include "mkbp_input_devices.h" -#include "power_button.h" -#include "system.h" -#include "timer.h" -#include "util.h" -#include "watchdog.h" - -/* Console output macro */ -#define CPRINTS(format, args...) cprints(CC_SWITCH, format, ## args) - -struct button_state_t { - uint64_t debounce_time; - int debounced_pressed; -}; - -static struct button_state_t __bss_slow state[BUTTON_COUNT]; - -static uint64_t __bss_slow next_deferred_time; - -#if defined(CONFIG_CMD_BUTTON) || defined(CONFIG_HOSTCMD_BUTTON) -#define CONFIG_SIMULATED_BUTTON -#endif - -#ifdef CONFIG_SIMULATED_BUTTON -/* Bitmask to keep track of simulated state of each button. - * Bit numbers are aligned to enum button. - */ -static int sim_button_state; - -/* - * Flip state of associated button type in sim_button_state bitmask. - * In bitmask, if bit is 1, button is pressed. If bit is 0, button is - * released. - * - * Returns the appropriate GPIO value based on table below: - * +----------+--------+--------+ - * | state | active | return | - * +----------+--------+--------+ - * | pressed | high | 1 | - * | pressed | low | 0 | - * | released | high | 0 | - * | released | low | 1 | - * +----------+--------+--------+ - */ -static int simulated_button_pressed(const struct button_config *button) -{ - return !!(sim_button_state & BIT(button->type)); -} -#endif - -/* - * Whether a button is currently pressed. - */ -static int raw_button_pressed(const struct button_config *button) -{ - int physical_value = 0; - int simulated_value = 0; - if (!(button->flags & BUTTON_FLAG_DISABLED)) { - if (IS_ENABLED(CONFIG_ADC_BUTTONS) && - button_is_adc_detected(button->gpio)) { - physical_value = - adc_to_physical_value(button->gpio); - } else { - physical_value = (!!gpio_get_level(button->gpio) == - !!(button->flags & BUTTON_FLAG_ACTIVE_HIGH)); - } -#ifdef CONFIG_SIMULATED_BUTTON - simulated_value = simulated_button_pressed(button); -#endif - } - - return (simulated_value || physical_value); -} - -#ifdef CONFIG_BUTTON_TRIGGERED_RECOVERY - -#ifdef CONFIG_LED_COMMON -static void button_blink_hw_reinit_led(void) -{ - int led_state = LED_STATE_ON; - timestamp_t deadline; - timestamp_t now = get_time(); - - /* Blink LED for 3 seconds. */ - deadline.val = now.val + (3 * SECOND); - - while (!timestamp_expired(deadline, &now)) { - led_control(EC_LED_ID_RECOVERY_HW_REINIT_LED, led_state); - led_state = !led_state; - watchdog_reload(); - msleep(100); - now = get_time(); - } - - /* Reset LED to default state. */ - led_control(EC_LED_ID_RECOVERY_HW_REINIT_LED, LED_STATE_RESET); -} -#endif - -/* - * Whether recovery button (or combination of equivalent buttons) is pressed - * If a dedicated recovery button is used, any of the buttons can be pressed, - * otherwise, all the buttons must be pressed. - */ -static int is_recovery_button_pressed(void) -{ - int i, pressed; - for (i = 0; i < recovery_buttons_count; i++) { - pressed = raw_button_pressed(recovery_buttons[i]); - if (IS_ENABLED(CONFIG_DEDICATED_RECOVERY_BUTTON)) { - if (pressed) - return 1; - } else { - if (!pressed) - return 0; - } - } - return IS_ENABLED(CONFIG_DEDICATED_RECOVERY_BUTTON) ? 0 : 1; -} - -/* - * If the EC is reset and recovery is requested, then check if HW_REINIT is - * requested as well. Since the EC reset occurs after volup+voldn+power buttons - * are held down for 10 seconds, check the state of these buttons for 20 more - * seconds. If they are still held down all this time, then set host event to - * indicate HW_REINIT is requested. Also, make sure watchdog is reloaded in - * order to prevent watchdog from resetting the EC. - */ -static void button_check_hw_reinit_required(void) -{ - timestamp_t deadline; - timestamp_t now = get_time(); -#ifdef CONFIG_LED_COMMON - uint8_t led_on = 0; -#endif - - deadline.val = now.val + (20 * SECOND); - - CPRINTS("Checking for HW_REINIT request"); - - while (!timestamp_expired(deadline, &now)) { - if (!is_recovery_button_pressed() || - !power_button_signal_asserted()) { - CPRINTS("No HW_REINIT request"); -#ifdef CONFIG_LED_COMMON - if (led_on) - led_control(EC_LED_ID_RECOVERY_HW_REINIT_LED, - LED_STATE_RESET); -#endif - return; - } - -#ifdef CONFIG_LED_COMMON - if (!led_on) { - led_control(EC_LED_ID_RECOVERY_HW_REINIT_LED, - LED_STATE_ON); - led_on = 1; - } -#endif - - now = get_time(); - watchdog_reload(); - } - - CPRINTS("HW_REINIT requested"); - host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT); - -#ifdef CONFIG_LED_COMMON - button_blink_hw_reinit_led(); -#endif -} - -static int is_recovery_boot(void) -{ - if (system_jumped_to_this_image()) - return 0; - if (!(system_get_reset_flags() & - (EC_RESET_FLAG_RESET_PIN | EC_RESET_FLAG_POWER_ON))) - return 0; - if (!is_recovery_button_pressed()) - return 0; - return 1; -} -#endif /* CONFIG_BUTTON_TRIGGERED_RECOVERY */ - -static void button_reset(enum button button_type, - const struct button_config *button) -{ - state[button_type].debounced_pressed = raw_button_pressed(button); - state[button_type].debounce_time = 0; - gpio_enable_interrupt(button->gpio); -} - -/* - * Button initialization. - */ -void button_init(void) -{ - int i; - - CPRINTS("init buttons"); - next_deferred_time = 0; - for (i = 0; i < BUTTON_COUNT; i++) - button_reset(i, &buttons[i]); - -#ifdef CONFIG_BUTTON_TRIGGERED_RECOVERY - if (is_recovery_boot()) { - system_clear_reset_flags(EC_RESET_FLAG_AP_OFF); - host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY); - button_check_hw_reinit_required(); - } -#endif /* defined(CONFIG_BUTTON_TRIGGERED_RECOVERY) */ -} - -#ifdef CONFIG_BUTTONS_RUNTIME_CONFIG -int button_reassign_gpio(enum button button_type, enum gpio_signal gpio) -{ - if (button_type >= BUTTON_COUNT) - return EC_ERROR_INVAL; - - /* Disable currently assigned interrupt */ - gpio_disable_interrupt(buttons[button_type].gpio); - - /* Reconfigure GPIO and enable the new interrupt */ - buttons[button_type].gpio = gpio; - button_reset(button_type, &buttons[button_type]); - - return EC_SUCCESS; -} - -int button_disable_gpio(enum button button_type) -{ - if (button_type >= BUTTON_COUNT) - return EC_ERROR_INVAL; - - /* Disable GPIO interrupt */ - gpio_disable_interrupt(buttons[button_type].gpio); - /* Mark button as disabled */ - buttons[button_type].flags |= BUTTON_FLAG_DISABLED; - - return EC_SUCCESS; -} -#endif - - -/* - * Handle debounced button changing state. - */ - -static void button_change_deferred(void); -DECLARE_DEFERRED(button_change_deferred); - -#ifdef CONFIG_EMULATED_SYSRQ -static void debug_mode_handle(void); -DECLARE_DEFERRED(debug_mode_handle); -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, debug_mode_handle, HOOK_PRIO_LAST); -#endif - -static void button_change_deferred(void) -{ - int i; - int new_pressed; - uint64_t soonest_debounce_time = 0; - uint64_t time_now = get_time().val; - - for (i = 0; i < BUTTON_COUNT; i++) { - /* Skip this button if we are not waiting to debounce */ - if (state[i].debounce_time == 0) - continue; - - if (state[i].debounce_time <= time_now) { - /* Check if the state has changed */ - new_pressed = raw_button_pressed(&buttons[i]); - if (state[i].debounced_pressed != new_pressed) { - state[i].debounced_pressed = new_pressed; -#ifdef CONFIG_EMULATED_SYSRQ - /* - * Calling deferred function for handling debug - * mode so that button change processing is not - * delayed. - */ -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON - /* - * Only the direct signal is used for sysrq. - * H1_EC_RECOVERY_BTN_ODL doesn't reflect the - * true state of the recovery button. - */ - if (i == BUTTON_RECOVERY) -#endif - hook_call_deferred( - &debug_mode_handle_data, 0); -#endif - CPRINTS("Button '%s' was %s", - buttons[i].name, new_pressed ? - "pressed" : "released"); - if (IS_ENABLED(CONFIG_MKBP_INPUT_DEVICES)) { - mkbp_button_update(buttons[i].type, - new_pressed); - } else if (IS_ENABLED(HAS_TASK_KEYPROTO)) { - keyboard_update_button(buttons[i].type, - new_pressed); - } - } - - /* Clear the debounce time to stop checking it */ - state[i].debounce_time = 0; - } else { - /* - * Make sure the next deferred call happens on or before - * each button needs it. - */ - soonest_debounce_time = (soonest_debounce_time == 0) ? - state[i].debounce_time : - MIN(soonest_debounce_time, - state[i].debounce_time); - } - } - - if (soonest_debounce_time != 0) { - next_deferred_time = soonest_debounce_time; - hook_call_deferred(&button_change_deferred_data, - next_deferred_time - time_now); - } -} - -/* - * Handle a button interrupt. - */ -void button_interrupt(enum gpio_signal signal) -{ - int i; - uint64_t time_now = get_time().val; - - for (i = 0; i < BUTTON_COUNT; i++) { - if (buttons[i].gpio != signal || - (buttons[i].flags & BUTTON_FLAG_DISABLED)) - continue; - - state[i].debounce_time = time_now + buttons[i].debounce_us; - if (next_deferred_time <= time_now || - next_deferred_time > state[i].debounce_time) { - next_deferred_time = state[i].debounce_time; - hook_call_deferred(&button_change_deferred_data, - next_deferred_time - time_now); - } - break; - } -} - -#ifdef CONFIG_SIMULATED_BUTTON -static int button_present(enum keyboard_button_type type) -{ - int i; - - for (i = 0; i < BUTTON_COUNT; i++) - if (buttons[i].type == type) - break; - - return i; -} - -static void button_interrupt_simulate(int button) -{ - button_interrupt(buttons[button].gpio); -} - -static void simulate_button_release_deferred(void) -{ - int button_idx; - - /* Release the button */ - for (button_idx = 0; button_idx < BUTTON_COUNT; button_idx++) { - /* Check state for button pressed */ - if (sim_button_state & BIT(buttons[button_idx].type)) { - /* Set state of the button as released */ - atomic_clear_bits(&sim_button_state, - BIT(buttons[button_idx].type)); - - button_interrupt_simulate(button_idx); - } - } -} -DECLARE_DEFERRED(simulate_button_release_deferred); - -static void simulate_button(uint32_t button_mask, int press_ms) -{ - int button_idx; - - /* Press the button */ - for (button_idx = 0; button_idx < BUTTON_COUNT; button_idx++) { - if (button_mask & BIT(button_idx)) { - /* Set state of the button as pressed */ - atomic_or(&sim_button_state, - BIT(buttons[button_idx].type)); - - button_interrupt_simulate(button_idx); - } - } - - /* Defer the button release for specified duration */ - hook_call_deferred(&simulate_button_release_deferred_data, - press_ms * MSEC); -} -#endif /* #ifdef CONFIG_SIMULATED_BUTTON */ - -#ifdef CONFIG_CMD_BUTTON -static int console_command_button(int argc, char **argv) -{ - int press_ms = 50; - char *e; - int argv_idx; - int button = BUTTON_COUNT; - uint32_t button_mask = 0; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - for (argv_idx = 1; argv_idx < argc; argv_idx++) { - if (!strcasecmp(argv[argv_idx], "vup")) - button = button_present(KEYBOARD_BUTTON_VOLUME_UP); - else if (!strcasecmp(argv[argv_idx], "vdown")) - button = button_present(KEYBOARD_BUTTON_VOLUME_DOWN); - else if (!strcasecmp(argv[argv_idx], "rec")) - button = button_present(KEYBOARD_BUTTON_RECOVERY); - else { - /* If last parameter check if it is an integer. */ - if (argv_idx == argc - 1) { - press_ms = strtoi(argv[argv_idx], &e, 0); - /* If integer, break out of the loop. */ - if (!*e) - break; - } - button = BUTTON_COUNT; - } - - if (button == BUTTON_COUNT) - return EC_ERROR_PARAM1 + argv_idx - 1; - - button_mask |= BIT(button); - } - - if (!button_mask) - return EC_SUCCESS; - - simulate_button(button_mask, press_ms); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(button, console_command_button, - "vup|vdown|rec msec", - "Simulate button press"); -#endif /* CONFIG_CMD_BUTTON */ - -#ifdef CONFIG_HOSTCMD_BUTTON -static enum ec_status host_command_button(struct host_cmd_handler_args *args) -{ - const struct ec_params_button *p = args->params; - int idx; - uint32_t button_mask = 0; - - /* Only available on unlocked systems */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - for (idx = 0; idx < KEYBOARD_BUTTON_COUNT; idx++) { - if (p->btn_mask & BIT(idx)) - button_mask |= BIT(button_present(idx)); - } - - simulate_button(button_mask, p->press_ms); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_BUTTON, host_command_button, EC_VER_MASK(0)); - -#endif /* CONFIG_HOSTCMD_BUTTON */ - - -#ifdef CONFIG_EMULATED_SYSRQ - -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON - -/* - * Simplified sysrq handler - * - * In simplified sysrq, user can - * - press and release recovery button to send one sysrq event to the host - * - press and hold recovery button for 4 seconds to reset the AP (warm reset) - */ -static void debug_mode_handle(void) -{ - static int recovery_button_pressed = 0; - - if (!recovery_button_pressed) { - if (is_recovery_button_pressed()) { - /* User pressed recovery button. Wait for 4 seconds - * to see if warm reset is requested. */ - recovery_button_pressed = 1; - hook_call_deferred(&debug_mode_handle_data, 4 * SECOND); - } - } else { - /* We come here when recovery button is released or when - * 4 sec elapsed with recovery button still pressed. */ - if (!is_recovery_button_pressed()) { - /* Cancel pending timer */ - hook_call_deferred(&debug_mode_handle_data, -1); - host_send_sysrq('x'); - CPRINTS("DEBUG MODE: sysrq-x sent"); - } else { - chipset_reset(CHIPSET_RESET_DBG_WARM_REBOOT); - CPRINTS("DEBUG MODE: Warm reset triggered"); - } - recovery_button_pressed = 0; - } -} - -#else /* CONFIG_DEDICATED_RECOVERY_BUTTON */ - -enum debug_state { - STATE_DEBUG_NONE, - STATE_DEBUG_CHECK, - STATE_STAGING, - STATE_DEBUG_MODE_ACTIVE, - STATE_SYSRQ_PATH, - STATE_WARM_RESET_PATH, - STATE_SYSRQ_EXEC, - STATE_WARM_RESET_EXEC, -}; - -#define DEBUG_BTN_POWER BIT(0) -#define DEBUG_BTN_VOL_UP BIT(1) -#define DEBUG_BTN_VOL_DN BIT(2) -#define DEBUG_TIMEOUT (10 * SECOND) - -static enum debug_state curr_debug_state = STATE_DEBUG_NONE; -static enum debug_state next_debug_state = STATE_DEBUG_NONE; -static timestamp_t debug_state_deadline; -static int debug_button_hit_count; - -static int debug_button_mask(void) -{ - int mask = 0; - - /* Get power button state */ - if (power_button_is_pressed()) - mask |= DEBUG_BTN_POWER; - - /* Get volume up state */ - if (state[BUTTON_VOLUME_UP].debounced_pressed) - mask |= DEBUG_BTN_VOL_UP; - - /* Get volume down state */ - if (state[BUTTON_VOLUME_DOWN].debounced_pressed) - mask |= DEBUG_BTN_VOL_DN; - - return mask; -} - -static int debug_button_pressed(int mask) -{ - return debug_button_mask() == mask; -} - -#ifdef CONFIG_LED_COMMON -static int debug_mode_blink_led(void) -{ - return ((curr_debug_state != STATE_DEBUG_NONE) && - (curr_debug_state != STATE_DEBUG_CHECK)); -} -#endif - -static void debug_mode_transition(enum debug_state next_state) -{ - timestamp_t now = get_time(); -#ifdef CONFIG_LED_COMMON - int curr_blink_state = debug_mode_blink_led(); -#endif - - /* Cancel any deferred calls. */ - hook_call_deferred(&debug_mode_handle_data, -1); - - /* Update current debug mode state. */ - curr_debug_state = next_state; - - /* Set deadline to 10seconds from current time. */ - debug_state_deadline.val = now.val + DEBUG_TIMEOUT; - - switch (curr_debug_state) { - case STATE_DEBUG_NONE: - /* - * Nothing is done here since some states can transition to - * STATE_DEBUG_NONE in this function. Wait until all other - * states are evaluated to take the action for STATE_NONE. - */ - break; - case STATE_DEBUG_CHECK: - case STATE_STAGING: - break; - case STATE_DEBUG_MODE_ACTIVE: - debug_button_hit_count = 0; - break; - case STATE_SYSRQ_PATH: - /* - * Increment debug_button_hit_count and ensure it does not go - * past 3. If it exceeds the limit transition to STATE_NONE. - */ - debug_button_hit_count++; - if (debug_button_hit_count == 4) - curr_debug_state = STATE_DEBUG_NONE; - break; - case STATE_WARM_RESET_PATH: - break; - case STATE_SYSRQ_EXEC: - /* - * Depending upon debug_button_hit_count, send appropriate - * number of sysrq events to host and transition to STATE_NONE. - */ - while (debug_button_hit_count) { - host_send_sysrq('x'); - CPRINTS("DEBUG MODE: sysrq-x sent"); - debug_button_hit_count--; - } - curr_debug_state = STATE_DEBUG_NONE; - break; - case STATE_WARM_RESET_EXEC: - /* Warm reset the host and transition to STATE_NONE. */ - chipset_reset(CHIPSET_RESET_DBG_WARM_REBOOT); - CPRINTS("DEBUG MODE: Warm reset triggered"); - curr_debug_state = STATE_DEBUG_NONE; - break; - default: - curr_debug_state = STATE_DEBUG_NONE; - } - - if (curr_debug_state != STATE_DEBUG_NONE) { - /* - * Schedule a deferred call after DEBUG_TIMEOUT to check for - * button state if it does not change during the timeout - * duration. - */ - hook_call_deferred(&debug_mode_handle_data, DEBUG_TIMEOUT); - return; - } - - /* If state machine reached initial state, reset all variables. */ - CPRINTS("DEBUG MODE: Exit!"); - next_debug_state = STATE_DEBUG_NONE; - debug_state_deadline.val = 0; - debug_button_hit_count = 0; -#ifdef CONFIG_LED_COMMON - if (curr_blink_state) - led_control(EC_LED_ID_SYSRQ_DEBUG_LED, LED_STATE_RESET); -#endif -} - -static void debug_mode_handle(void) -{ - int mask; - - switch (curr_debug_state) { - case STATE_DEBUG_NONE: - /* - * If user pressed Vup+Vdn, check for next 10 seconds to see if - * user keeps holding the keys. - */ - if (debug_button_pressed(DEBUG_BTN_VOL_UP | DEBUG_BTN_VOL_DN)) - debug_mode_transition(STATE_DEBUG_CHECK); - break; - case STATE_DEBUG_CHECK: - /* - * If no key is pressed or any key combo other than Vup+Vdn is - * held, then quit debug check mode. - */ - if (!debug_button_pressed(DEBUG_BTN_VOL_UP | DEBUG_BTN_VOL_DN)) - debug_mode_transition(STATE_DEBUG_NONE); - else if (timestamp_expired(debug_state_deadline, NULL)) { - /* - * If Vup+Vdn are held down for 10 seconds, then its - * time to enter debug mode. - */ - CPRINTS("DEBUG MODE: Active!"); - next_debug_state = STATE_DEBUG_MODE_ACTIVE; - debug_mode_transition(STATE_STAGING); - } - break; - case STATE_STAGING: - mask = debug_button_mask(); - - /* If no button is pressed, transition to next state. */ - if (!mask) { - debug_mode_transition(next_debug_state); - return; - } - - /* Exit debug mode if keys are stuck for > 10 seconds. */ - if (timestamp_expired(debug_state_deadline, NULL)) - debug_mode_transition(STATE_DEBUG_NONE); - else { - timestamp_t now = get_time(); - - /* - * Schedule a deferred call in case timeout hasn't - * occurred yet. - */ - hook_call_deferred(&debug_mode_handle_data, - (debug_state_deadline.val - now.val)); - } - - break; - case STATE_DEBUG_MODE_ACTIVE: - mask = debug_button_mask(); - - /* - * Continue in this state if button is not pressed and timeout - * has not occurred. - */ - if (!mask && !timestamp_expired(debug_state_deadline, NULL)) - return; - - /* Exit debug mode if valid buttons are not pressed. */ - if ((mask != DEBUG_BTN_VOL_UP) && (mask != DEBUG_BTN_VOL_DN)) { - debug_mode_transition(STATE_DEBUG_NONE); - return; - } - - /* - * Transition to STAGING state with next state set to: - * 1. SYSRQ_PATH : If Vup was pressed. - * 2. WARM_RESET_PATH: If Vdn was pressed. - */ - if (mask == DEBUG_BTN_VOL_UP) - next_debug_state = STATE_SYSRQ_PATH; - else - next_debug_state = STATE_WARM_RESET_PATH; - - debug_mode_transition(STATE_STAGING); - break; - case STATE_SYSRQ_PATH: - mask = debug_button_mask(); - - /* - * Continue in this state if button is not pressed and timeout - * has not occurred. - */ - if (!mask && !timestamp_expired(debug_state_deadline, NULL)) - return; - - /* Exit debug mode if valid buttons are not pressed. */ - if ((mask != DEBUG_BTN_VOL_UP) && (mask != DEBUG_BTN_VOL_DN)) { - debug_mode_transition(STATE_DEBUG_NONE); - return; - } - - if (mask == DEBUG_BTN_VOL_UP) { - /* - * Else transition to STAGING state with next state set - * to SYSRQ_PATH. - */ - next_debug_state = STATE_SYSRQ_PATH; - } else { - /* - * Else if Vdn is pressed, transition to STAGING with - * next state set to SYSRQ_EXEC. - */ - next_debug_state = STATE_SYSRQ_EXEC; - } - debug_mode_transition(STATE_STAGING); - break; - case STATE_WARM_RESET_PATH: - mask = debug_button_mask(); - - /* - * Continue in this state if button is not pressed and timeout - * has not occurred. - */ - if (!mask && !timestamp_expired(debug_state_deadline, NULL)) - return; - - /* Exit debug mode if valid buttons are not pressed. */ - if (mask != DEBUG_BTN_VOL_UP) { - debug_mode_transition(STATE_DEBUG_NONE); - return; - } - - next_debug_state = STATE_WARM_RESET_EXEC; - debug_mode_transition(STATE_STAGING); - break; - case STATE_SYSRQ_EXEC: - case STATE_WARM_RESET_EXEC: - default: - debug_mode_transition(STATE_DEBUG_NONE); - break; - } -} - -#ifdef CONFIG_LED_COMMON -static void debug_led_tick(void) -{ - static int led_state = LED_STATE_OFF; - - if (debug_mode_blink_led()) { - led_state = !led_state; - led_control(EC_LED_ID_SYSRQ_DEBUG_LED, led_state); - } -} -DECLARE_HOOK(HOOK_TICK, debug_led_tick, HOOK_PRIO_DEFAULT); -#endif /* CONFIG_LED_COMMON */ - -#endif /* !CONFIG_DEDICATED_RECOVERY_BUTTON */ -#endif /* CONFIG_EMULATED_SYSRQ */ - -#ifndef CONFIG_BUTTONS_RUNTIME_CONFIG -const struct button_config buttons[BUTTON_COUNT] = { -#else -struct button_config buttons[BUTTON_COUNT] = { -#endif -#ifdef CONFIG_VOLUME_BUTTONS - [BUTTON_VOLUME_UP] = { - .name = "Volume Up", - .type = KEYBOARD_BUTTON_VOLUME_UP, - .gpio = GPIO_VOLUME_UP_L, - .debounce_us = BUTTON_DEBOUNCE_US, - .flags = 0, - }, - - [BUTTON_VOLUME_DOWN] = { - .name = "Volume Down", - .type = KEYBOARD_BUTTON_VOLUME_DOWN, - .gpio = GPIO_VOLUME_DOWN_L, - .debounce_us = BUTTON_DEBOUNCE_US, - .flags = 0, - }, - -#endif -#if defined(CONFIG_DEDICATED_RECOVERY_BUTTON) - [BUTTON_RECOVERY] = { - .name = "Recovery", - .type = KEYBOARD_BUTTON_RECOVERY, - .gpio = GPIO_RECOVERY_L, - .debounce_us = BUTTON_DEBOUNCE_US, - .flags = 0, - }, -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON_2 - [BUTTON_RECOVERY_2] = { - .name = "Recovery2", - .type = KEYBOARD_BUTTON_RECOVERY, - .gpio = GPIO_RECOVERY_L_2, - .debounce_us = BUTTON_DEBOUNCE_US, - .flags = 0, - } -#endif /* defined(CONFIG_DEDICATED_RECOVERY_BUTTON_2) */ -#endif /* defined(CONFIG_DEDICATED_RECOVERY_BUTTON) */ -}; - -#ifdef CONFIG_BUTTON_TRIGGERED_RECOVERY -/* - * Prefer the dedicated recovery button over the volume buttons if - * both are present. - */ -const struct button_config *recovery_buttons[] = { -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON - &buttons[BUTTON_RECOVERY], - -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON_2 - &buttons[BUTTON_RECOVERY_2], -#endif /* defined(CONFIG_BUTTON_TRIGGERED_RECOVERY_2) */ - -#elif defined(CONFIG_VOLUME_BUTTONS) - &buttons[BUTTON_VOLUME_DOWN], - &buttons[BUTTON_VOLUME_UP], -#endif /* defined(CONFIG_VOLUME_BUTTONS) */ -}; -const int recovery_buttons_count = ARRAY_SIZE(recovery_buttons); -#endif /* defined(CONFIG_BUTTON_TRIGGERED_RECOVERY) */ diff --git a/common/capsense.c b/common/capsense.c deleted file mode 100644 index b2413ac61f..0000000000 --- a/common/capsense.c +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright 2014 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 "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "i2c.h" -#include "keyboard_protocol.h" -#include "timer.h" - -/* Console output macro */ -#define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -#define CAPSENSE_I2C_ADDR 0x08 -#define CAPSENSE_MASK_BITS 8 -#define CAPSENSE_POLL_INTERVAL (20 * MSEC) - -static int capsense_read_bitmask(void) -{ - int rv; - uint8_t val = 0; - - rv = i2c_xfer(I2C_PORT_CAPSENSE, CAPSENSE_I2C_ADDR, - 0, 0, &val, 1); - - if (rv) - CPRINTS("%s failed: error %d", __func__, rv); - - return val; -} - -static void capsense_init(void) -{ - gpio_enable_interrupt(GPIO_CAPSENSE_INT_L); -} -DECLARE_HOOK(HOOK_INIT, capsense_init, HOOK_PRIO_DEFAULT); - -/* - * Keep checking polling the capsense until all the buttons are released. - * We're not worrying about debouncing, since the capsense module should do - * that for us. - */ -static void capsense_change_deferred(void) -{ - static uint8_t cur_val; - uint8_t new_val; - int i, n, c; - - new_val = capsense_read_bitmask(); - if (new_val != cur_val) { - CPRINTF("[%pT capsense 0x%02x: ", - PRINTF_TIMESTAMP_NOW, new_val); - for (i = 0; i < CAPSENSE_MASK_BITS; i++) { - /* See what changed */ - n = (new_val >> i) & 0x01; - c = (cur_val >> i) & 0x01; - CPRINTF("%s", n ? " X " : " _ "); - if (n == c) - continue; -#ifdef HAS_TASK_KEYPROTO - /* Treat it as a keyboard event. */ - keyboard_update_button(i + KEYBOARD_BUTTON_CAPSENSE_1, - n); -#endif - } - CPRINTF("]\n"); - cur_val = new_val; - } - - if (cur_val) - hook_call_deferred(&capsense_change_deferred_data, - CAPSENSE_POLL_INTERVAL); -} -DECLARE_DEFERRED(capsense_change_deferred); - -/* - * Somebody's poking at us. - */ -void capsense_interrupt(enum gpio_signal signal) -{ - hook_call_deferred(&capsense_change_deferred_data, 0); -} diff --git a/common/cbi.c b/common/cbi.c deleted file mode 100644 index 345e313c54..0000000000 --- a/common/cbi.c +++ /dev/null @@ -1,578 +0,0 @@ -/* 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. - * - * Cros Board Info - */ - -#include "common.h" -#include "console.h" -#include "crc8.h" -#include "cros_board_info.h" -#include "gpio.h" -#include "host_command.h" -#include "i2c.h" -#include "timer.h" - -#ifdef HOST_TOOLS_BUILD -#include <string.h> -#else -#include "util.h" -#endif - -/* - * Functions and variables defined here shared with host tools (e.g. cbi-util). - * TODO: Move these to common/cbi/cbi.c and common/cbi/utils.c if they grow. - */ -uint8_t cbi_crc8(const struct cbi_header *h) -{ - return cros_crc8((uint8_t *)&h->crc + 1, - h->total_size - sizeof(h->magic) - sizeof(h->crc)); -} - -uint8_t *cbi_set_data(uint8_t *p, enum cbi_data_tag tag, - const void *buf, int size) -{ - struct cbi_data *d = (struct cbi_data *)p; - - /* - * If size of the data to be added is zero, then no need to add the tag - * as well. - */ - if (size == 0) - return p; - - d->tag = tag; - d->size = size; - memcpy(d->value, buf, size); - p += sizeof(*d) + size; - return p; -} - -uint8_t *cbi_set_string(uint8_t *p, enum cbi_data_tag tag, const char *str) -{ - if (str == NULL) - return p; - - return cbi_set_data(p, tag, str, strlen(str) + 1); -} - -struct cbi_data *cbi_find_tag(const void *buf, enum cbi_data_tag tag) -{ - struct cbi_data *d; - const struct cbi_header *h = buf; - const uint8_t *p; - for (p = h->data; p + sizeof(*d) < (uint8_t *)buf + h->total_size;) { - d = (struct cbi_data *)p; - if (d->tag == tag) - return d; - p += sizeof(*d) + d->size; - } - return NULL; -} - -/* - * Functions and variables specific to EC firmware - */ -#ifndef HOST_TOOLS_BUILD - -#define CPRINTS(format, args...) cprints(CC_SYSTEM, "CBI " format, ## args) - -static int cache_status = CBI_CACHE_STATUS_INVALID; -static uint8_t cbi[CBI_IMAGE_SIZE]; -static struct cbi_header * const head = (struct cbi_header *)cbi; - -int cbi_create(void) -{ - memset(cbi, 0, sizeof(cbi)); - memcpy(head->magic, cbi_magic, sizeof(cbi_magic)); - head->total_size = sizeof(*head); - head->major_version = CBI_VERSION_MAJOR; - head->minor_version = CBI_VERSION_MINOR; - head->crc = cbi_crc8(head); - cache_status = CBI_CACHE_STATUS_SYNCED; - - return EC_SUCCESS; -} - -void cbi_invalidate_cache(void) -{ - cache_status = CBI_CACHE_STATUS_INVALID; -} - -int cbi_get_cache_status(void) -{ - return cache_status; -} - -static int do_cbi_read(void) -{ - CPRINTS("Reading board info"); - - /* Read header */ - if (cbi_config.drv->load(0, cbi, sizeof(*head))) { - CPRINTS("Failed to read header"); - return EC_ERROR_INVAL; - } - - /* Check magic */ - if (memcmp(head->magic, cbi_magic, sizeof(head->magic))) { - CPRINTS("Bad magic"); - return EC_ERROR_INVAL; - } - - /* check version */ - if (head->major_version > CBI_VERSION_MAJOR) { - CPRINTS("Version mismatch"); - return EC_ERROR_INVAL; - } - - /* - * Check the data size. It's expected to support up to 64k but our - * buffer has practical limitation. - */ - if (head->total_size < sizeof(*head) || - head->total_size > CBI_IMAGE_SIZE) { - CPRINTS("Bad size: %d", head->total_size); - return EC_ERROR_OVERFLOW; - } - - /* Read the data */ - if (cbi_config.drv->load(sizeof(*head), head->data, - head->total_size - sizeof(*head))) { - CPRINTS("Failed to read body"); - return EC_ERROR_INVAL; - } - - /* Check CRC. This supports new fields unknown to this parser. */ - if (cbi_config.storage_type != CBI_STORAGE_TYPE_GPIO && - cbi_crc8(head) != head->crc) { - CPRINTS("Bad CRC"); - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -static int cbi_read(void) -{ - int i; - int rv; - - if (cbi_get_cache_status() == CBI_CACHE_STATUS_SYNCED) - return EC_SUCCESS; - - for (i = 0; i < 2; i++) { - rv = do_cbi_read(); - if (rv == EC_SUCCESS) { - cache_status = CBI_CACHE_STATUS_SYNCED; - return EC_SUCCESS; - } - /* On error (I2C or bad contents), retry a read */ - } - - return rv; -} - -__attribute__((weak)) -int cbi_board_override(enum cbi_data_tag tag, uint8_t *buf, uint8_t *size) -{ - return EC_SUCCESS; -} - -int cbi_get_board_info(enum cbi_data_tag tag, uint8_t *buf, uint8_t *size) -{ - const struct cbi_data *d; - - if (cbi_read()) - return EC_ERROR_UNKNOWN; - - d = cbi_find_tag(cbi, tag); - if (!d) - /* Not found */ - return EC_ERROR_UNKNOWN; - if (*size < d->size) - /* Insufficient buffer size */ - return EC_ERROR_INVAL; - - /* Clear the buffer in case len < *size */ - memset(buf, 0, *size); - /* Copy the value */ - memcpy(buf, d->value, d->size); - *size = d->size; - - return cbi_board_override(tag, buf, size); -} - -static void cbi_remove_tag(void *const cbi, struct cbi_data *const d) -{ - struct cbi_header *const h = cbi; - const size_t size = sizeof(*d) + d->size; - const uint8_t *next = (uint8_t *)d + size; - const size_t bytes_after = ((uint8_t *)cbi + h->total_size) - next; - - memmove(d, next, bytes_after); - h->total_size -= size; -} - -int cbi_set_board_info(enum cbi_data_tag tag, const uint8_t *buf, uint8_t size) -{ - struct cbi_data *d; - - d = cbi_find_tag(cbi, tag); - - /* If we found the entry, but the size doesn't match, delete it */ - if (d && d->size != size) { - cbi_remove_tag(cbi, d); - d = NULL; - } - - if (!d) { - uint8_t *p; - /* Not found. Check if new item would fit */ - if (sizeof(cbi) < head->total_size + sizeof(*d) + size) - return EC_ERROR_OVERFLOW; - /* Append new item */ - p = cbi_set_data(&cbi[head->total_size], tag, buf, size); - head->total_size = p - cbi; - } else { - /* Overwrite existing item */ - memcpy(d->value, buf, d->size); - } - - return EC_SUCCESS; -} - -int cbi_write(void) -{ - if (cbi_config.drv->is_protected()) { - CPRINTS("Failed to write due to WP"); - return EC_ERROR_ACCESS_DENIED; - } - - return cbi_config.drv->store(cbi); -} - -int cbi_get_board_version(uint32_t *ver) -{ - uint8_t size = sizeof(*ver); - - return cbi_get_board_info(CBI_TAG_BOARD_VERSION, (uint8_t *)ver, &size); -} - -int cbi_get_sku_id(uint32_t *id) -{ - uint8_t size = sizeof(*id); - - return cbi_get_board_info(CBI_TAG_SKU_ID, (uint8_t *)id, &size); -} - -int cbi_get_oem_id(uint32_t *id) -{ - uint8_t size = sizeof(*id); - - return cbi_get_board_info(CBI_TAG_OEM_ID, (uint8_t *)id, &size); -} - -int cbi_get_model_id(uint32_t *id) -{ - uint8_t size = sizeof(*id); - - return cbi_get_board_info(CBI_TAG_MODEL_ID, (uint8_t *)id, &size); -} - -int cbi_get_fw_config(uint32_t *fw_config) -{ - uint8_t size = sizeof(*fw_config); - - return cbi_get_board_info(CBI_TAG_FW_CONFIG, (uint8_t *)fw_config, - &size); -} - -int cbi_get_ssfc(uint32_t *ssfc) -{ - uint8_t size = sizeof(*ssfc); - - return cbi_get_board_info(CBI_TAG_SSFC, (uint8_t *)ssfc, - &size); -} - -int cbi_get_pcb_supplier(uint32_t *pcb_supplier) -{ - uint8_t size = sizeof(*pcb_supplier); - - return cbi_get_board_info(CBI_TAG_PCB_SUPPLIER, (uint8_t *)pcb_supplier, - &size); -} - -int cbi_get_rework_id(uint64_t *id) -{ - uint8_t size = sizeof(*id); - return cbi_get_board_info(CBI_TAG_REWORK_ID, (uint8_t *)id, &size); -} - -static enum ec_status hc_cbi_get(struct host_cmd_handler_args *args) -{ - const struct __ec_align4 ec_params_get_cbi *p = args->params; - uint8_t size = MIN(args->response_max, UINT8_MAX); - - if (p->flag & CBI_GET_RELOAD) - cbi_invalidate_cache(); - - if (cbi_get_board_info(p->tag, args->response, &size)) - return EC_RES_INVALID_PARAM; - - args->response_size = size; - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_CROS_BOARD_INFO, - hc_cbi_get, - EC_VER_MASK(0)); - -static enum ec_status common_cbi_set(const struct __ec_align4 - ec_params_set_cbi * p) -{ - /* - * If we ultimately cannot write to the flash, then fail early unless - * we are explicitly trying to write to the in-memory CBI only - */ - if (cbi_config.drv->is_protected() && - !(p->flag & CBI_SET_NO_SYNC)) { - CPRINTS("Failed to write due to WP"); - return EC_RES_ACCESS_DENIED; - } - -#ifndef CONFIG_SYSTEM_UNLOCKED - /* - * These fields are not allowed to be reprogrammed regardless the - * hardware WP state. They're considered as a part of the hardware. - */ - if (p->tag == CBI_TAG_BOARD_VERSION || p->tag == CBI_TAG_OEM_ID) - return EC_RES_ACCESS_DENIED; -#endif - - if (p->flag & CBI_SET_INIT) { - memset(cbi, 0, sizeof(cbi)); - memcpy(head->magic, cbi_magic, sizeof(cbi_magic)); - head->total_size = sizeof(*head); - } else { - if (cbi_read()) - return EC_RES_ERROR; - } - - if (cbi_set_board_info(p->tag, p->data, p->size)) - return EC_RES_INVALID_PARAM; - - /* - * Whether we're modifying existing data or creating new one, - * we take over the format. - */ - head->major_version = CBI_VERSION_MAJOR; - head->minor_version = CBI_VERSION_MINOR; - head->crc = cbi_crc8(head); - cache_status = CBI_CACHE_STATUS_SYNCED; - - /* Skip write if client asks so. */ - if (p->flag & CBI_SET_NO_SYNC) - return EC_RES_SUCCESS; - - /* We already checked write protect failure case. */ - if (cbi_write()) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -static enum ec_status hc_cbi_set(struct host_cmd_handler_args *args) -{ - const struct __ec_align4 ec_params_set_cbi * p = args->params; - - /* Given data size exceeds the packet size. */ - if (args->params_size < sizeof(*p) + p->size) - return EC_RES_INVALID_PARAM; - - return common_cbi_set(p); -} -DECLARE_HOST_COMMAND(EC_CMD_SET_CROS_BOARD_INFO, - hc_cbi_set, - EC_VER_MASK(0)); - -#ifdef CONFIG_CMD_CBI -static void print_tag(const char * const tag, int rv, const uint32_t *val) -{ - ccprintf("%s", tag); - if (rv == EC_SUCCESS && val) - ccprintf(": %u (0x%x)\n", *val, *val); - else - ccprintf(": (Error %d)\n", rv); -} - -static void print_uint64_tag(const char * const tag, int rv, - const uint64_t *lval) -{ - ccprintf("%s", tag); - if (rv == EC_SUCCESS && lval) - ccprintf(": %llu (0x%llx)\n", *(unsigned long long *)lval, - *(unsigned long long *)lval); - else - ccprintf(": (Error %d)\n", rv); -} - -static void dump_cbi(void) -{ - uint32_t val; - uint64_t lval; - - /* Ensure we read the latest data from flash. */ - cbi_invalidate_cache(); - cbi_read(); - - if (cbi_get_cache_status() != CBI_CACHE_STATUS_SYNCED) { - ccprintf("Cannot Read CBI (Error %d)\n", cbi_get_cache_status()); - return; - } - - ccprintf("CBI_VERSION: 0x%04x\n", head->version); - ccprintf("TOTAL_SIZE: %u\n", head->total_size); - - print_tag("BOARD_VERSION", cbi_get_board_version(&val), &val); - print_tag("OEM_ID", cbi_get_oem_id(&val), &val); - print_tag("MODEL_ID", cbi_get_model_id(&val), &val); - print_tag("SKU_ID", cbi_get_sku_id(&val), &val); - print_tag("FW_CONFIG", cbi_get_fw_config(&val), &val); - print_tag("PCB_SUPPLIER", cbi_get_pcb_supplier(&val), &val); - print_tag("SSFC", cbi_get_ssfc(&val), &val); - print_uint64_tag("REWORK_ID", cbi_get_rework_id(&lval), &lval); -} - -/* - * Space for the set command (does not include data space) plus maximum - * possible console input - */ -static uint8_t buf[sizeof(struct ec_params_set_cbi) + \ - CONFIG_CONSOLE_INPUT_LINE_SIZE]; - -static int cc_cbi(int argc, char **argv) -{ - struct __ec_align4 ec_params_set_cbi * setter = - (struct __ec_align4 ec_params_set_cbi *)buf; - int last_arg; - char *e; - - if (argc == 1) { - dump_cbi(); - if (cbi_get_cache_status() == CBI_CACHE_STATUS_SYNCED) - hexdump(cbi, CBI_IMAGE_SIZE); - return EC_SUCCESS; - } - - if (strcasecmp(argv[1], "set") == 0) { - if (argc < 5) { - ccprintf("Set requires: <tag> <value> <size>\n"); - return EC_ERROR_PARAM_COUNT; - } - - setter->tag = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - if (setter->tag == CBI_TAG_DRAM_PART_NUM || - setter->tag == CBI_TAG_OEM_NAME) { - setter->size = strlen(argv[3]) + 1; - memcpy(setter->data, argv[3], setter->size); - } else { - uint64_t val = strtoull(argv[3], &e, 0); - - if (*e) - return EC_ERROR_PARAM3; - - setter->size = strtoi(argv[4], &e, 0); - if (*e) - return EC_ERROR_PARAM4; - - if (setter->size < 1) { - ccprintf("Set size too small\n"); - return EC_ERROR_PARAM4; - } else if (setter->tag == CBI_TAG_REWORK_ID && - setter->size > 8) { - ccprintf("Set size too large\n"); - return EC_ERROR_PARAM4; - } else if (setter->size > 4) { - ccprintf("Set size too large\n"); - return EC_ERROR_PARAM4; - } - - memcpy(setter->data, &val, setter->size); - } - - last_arg = 5; - } else if (strcasecmp(argv[1], "remove") == 0) { - if (argc < 3) { - ccprintf("Remove requires: <tag>\n"); - return EC_ERROR_PARAM_COUNT; - } - - setter->tag = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - setter->size = 0; - last_arg = 3; - } else { - return EC_ERROR_PARAM1; - } - - setter->flag = 0; - - if (argc > last_arg) { - int i; - - for (i = last_arg; i < argc; i++) { - if (strcasecmp(argv[i], "init") == 0) { - setter->flag |= CBI_SET_INIT; - } else if (strcasecmp(argv[i], "skip_write") == 0) { - setter->flag |= CBI_SET_NO_SYNC; - } else { - ccprintf("Invalid additional option\n"); - return EC_ERROR_PARAM1 + i - 1; - } - } - } - - if (common_cbi_set(setter) == EC_RES_SUCCESS) - return EC_SUCCESS; - - return EC_ERROR_UNKNOWN; -} -DECLARE_CONSOLE_COMMAND(cbi, cc_cbi, "[set <tag> <value> <size> | " - "remove <tag>] [init | skip_write]", - "Print or change Cros Board Info from flash"); -#endif /* CONFIG_CMD_CBI */ - -#ifndef HAS_TASK_CHIPSET -int cbi_set_fw_config(uint32_t fw_config) -{ - /* Check write protect status */ - if (cbi_config.drv->is_protected()) - return EC_ERROR_ACCESS_DENIED; - - /* Ensure that CBI has been configured */ - if (cbi_read()) - cbi_create(); - - /* Update the FW_CONFIG field */ - cbi_set_board_info(CBI_TAG_FW_CONFIG, (uint8_t *)&fw_config, - sizeof(int)); - - /* Update CRC calculation and write to the storage */ - head->crc = cbi_crc8(head); - if (cbi_write()) - return EC_ERROR_UNKNOWN; - - dump_cbi(); - - return EC_SUCCESS; -} -#endif - -#endif /* !HOST_TOOLS_BUILD */ diff --git a/common/cbi_eeprom.c b/common/cbi_eeprom.c deleted file mode 100644 index 2761f0b977..0000000000 --- a/common/cbi_eeprom.c +++ /dev/null @@ -1,84 +0,0 @@ -/* 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. - */ - -/* Support Cros Board Info EEPROM */ - -#include "console.h" -#include "cros_board_info.h" -#include "gpio.h" -#include "i2c.h" -#include "system.h" -#include "timer.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_SYSTEM, "CBI " format, ## args) - -/* - * We allow EEPROMs with page size of 8 or 16. Use 8 to be the most compatible. - * This causes a little more overhead for writes, but we are not writing to the - * EEPROM outside of the factory process. - */ -#define EEPROM_PAGE_WRITE_SIZE 8 -#define EEPROM_PAGE_WRITE_MS 5 - -static int eeprom_read(uint8_t offset, uint8_t *data, int len) -{ - return i2c_read_block(I2C_PORT_EEPROM, I2C_ADDR_EEPROM_FLAGS, - offset, data, len); -} - -static int eeprom_is_write_protected(void) -{ - if (IS_ENABLED(CONFIG_BYPASS_CBI_EEPROM_WP_CHECK)) - return 0; -#if defined(CONFIG_WP_ACTIVE_HIGH) - return gpio_get_level(GPIO_WP); -#else - return !gpio_get_level(GPIO_WP_L); -#endif -} - -static int eeprom_write(uint8_t *cbi) -{ - uint8_t *p = cbi; - int rest = ((struct cbi_header *)p)->total_size; - - while (rest > 0) { - int size = MIN(EEPROM_PAGE_WRITE_SIZE, rest); - int rv; - - rv = i2c_write_block(I2C_PORT_EEPROM, I2C_ADDR_EEPROM_FLAGS, - p - cbi, p, size); - if (rv) { - CPRINTS("Failed to write for %d", rv); - return rv; - } - /* Wait for internal write cycle completion */ - msleep(EEPROM_PAGE_WRITE_MS); - p += size; - rest -= size; - } - - return EC_SUCCESS; -} - -#ifdef CONFIG_EEPROM_CBI_WP -void cbi_latch_eeprom_wp(void) -{ - CPRINTS("WP latched"); - gpio_set_level(GPIO_EC_CBI_WP, 1); -} -#endif /* CONFIG_EEPROM_CBI_WP */ - -const struct cbi_storage_driver eeprom_drv = { - .store = eeprom_write, - .load = eeprom_read, - .is_protected = eeprom_is_write_protected, -}; - -const struct cbi_storage_config_t cbi_config = { - .storage_type = CBI_STORAGE_TYPE_EEPROM, - .drv = &eeprom_drv, -}; diff --git a/common/cbi_gpio.c b/common/cbi_gpio.c deleted file mode 100644 index 7b9fb25ebb..0000000000 --- a/common/cbi_gpio.c +++ /dev/null @@ -1,72 +0,0 @@ -/* 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. - */ - -/* Support Cros Board Info GPIO */ - -#include "console.h" -#include "cros_board_info.h" -#include "gpio.h" -#include "system.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_SYSTEM, "CBI " format, ## args) - -static int cbi_gpio_read(uint8_t offset, uint8_t *data, int len) -{ - int board_id; - int sku_id; - int rv; - int err = 0; - - if (cbi_get_cache_status() == CBI_CACHE_STATUS_SYNCED) - return EC_SUCCESS; - - cbi_create(); - - board_id = system_get_board_version(); - if (board_id < 0) { - CPRINTS("Failed (%d) to get a valid board id", -board_id); - err++; - } else { - rv = cbi_set_board_info(CBI_TAG_BOARD_VERSION, - (uint8_t *)&board_id, sizeof(int)); - if (rv) { - CPRINTS("Failed (%d) to set BOARD_VERSION tag", rv); - err++; - } - } - - sku_id = system_get_sku_id(); - rv = cbi_set_board_info(CBI_TAG_SKU_ID, - (uint8_t *)&sku_id, sizeof(int)); - if (rv) { - CPRINTS("Failed (%d) to set SKU_ID tag", rv); - err++; - } - - if (err > 0) - return EC_ERROR_UNKNOWN; - - return EC_SUCCESS; -} - -static int cbi_gpio_is_write_protected(void) -{ - /* - * When CBI comes from strapping pins, any attempts for updating CBI - * storage should be rejected. - */ - return 1; -} - -const struct cbi_storage_driver gpio_drv = { - .load = cbi_gpio_read, - .is_protected = cbi_gpio_is_write_protected, -}; - -const struct cbi_storage_config_t cbi_config = { - .storage_type = CBI_STORAGE_TYPE_GPIO, - .drv = &gpio_drv, -}; diff --git a/common/cec.c b/common/cec.c deleted file mode 100644 index 1bc3273c1d..0000000000 --- a/common/cec.c +++ /dev/null @@ -1,137 +0,0 @@ -/* 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. - */ - -#include "cec.h" -#include "console.h" -#include "task.h" - -#define CPRINTF(format, args...) cprintf(CC_CEC, format, ## args) -#define CPRINTS(format, args...) cprints(CC_CEC, format, ## args) - -/* - * Mutex for the read-offset of the rx queue. Needed since the - * queue is read and flushed from different contexts - */ -static struct mutex rx_queue_readoffset_mutex; - -int cec_transfer_get_bit(const struct cec_msg_transfer *transfer) -{ - if (transfer->byte >= MAX_CEC_MSG_LEN) - return 0; - - return transfer->buf[transfer->byte] & (0x80 >> transfer->bit); -} - -void cec_transfer_set_bit(struct cec_msg_transfer *transfer, int val) -{ - uint8_t bit_flag; - - if (transfer->byte >= MAX_CEC_MSG_LEN) - return; - bit_flag = 0x80 >> transfer->bit; - transfer->buf[transfer->byte] &= ~bit_flag; - if (val) - transfer->buf[transfer->byte] |= bit_flag; -} - -void cec_transfer_inc_bit(struct cec_msg_transfer *transfer) -{ - if (++(transfer->bit) == 8) { - if (transfer->byte >= MAX_CEC_MSG_LEN) - return; - transfer->bit = 0; - transfer->byte++; - } -} - -int cec_transfer_is_eom(const struct cec_msg_transfer *transfer, int len) -{ - if (transfer->bit) - return 0; - return (transfer->byte == len); -} - -void cec_rx_queue_flush(struct cec_rx_queue *queue) -{ - mutex_lock(&rx_queue_readoffset_mutex); - queue->read_offset = 0; - mutex_unlock(&rx_queue_readoffset_mutex); - queue->write_offset = 0; -} - -int cec_rx_queue_push(struct cec_rx_queue *queue, const uint8_t *msg, - uint8_t msg_len) -{ - int i; - uint32_t offset; - - if (msg_len > MAX_CEC_MSG_LEN || msg_len == 0) - return EC_ERROR_INVAL; - - offset = queue->write_offset; - /* Fill in message length last, if successful. Set to zero for now */ - queue->buf[offset] = 0; - offset = (offset + 1) % CEC_RX_BUFFER_SIZE; - - for (i = 0 ; i < msg_len; i++) { - if (offset == queue->read_offset) { - /* Buffer full */ - return EC_ERROR_OVERFLOW; - } - - queue->buf[offset] = msg[i]; - offset = (offset + 1) % CEC_RX_BUFFER_SIZE; - } - - /* - * Don't commit if we caught up with read-offset - * since that would indicate an empty buffer - */ - if (offset == queue->read_offset) { - /* Buffer full */ - return EC_ERROR_OVERFLOW; - } - - /* Commit the push */ - queue->buf[queue->write_offset] = msg_len; - queue->write_offset = offset; - - return EC_SUCCESS; -} - -int cec_rx_queue_pop(struct cec_rx_queue *queue, uint8_t *msg, - uint8_t *msg_len) -{ - int i; - - mutex_lock(&rx_queue_readoffset_mutex); - if (queue->read_offset == queue->write_offset) { - /* Queue empty */ - mutex_unlock(&rx_queue_readoffset_mutex); - *msg_len = 0; - return -1; - } - - /* The first byte in the buffer is the message length */ - *msg_len = queue->buf[queue->read_offset]; - if (*msg_len == 0 || *msg_len > MAX_CEC_MSG_LEN) { - mutex_unlock(&rx_queue_readoffset_mutex); - *msg_len = 0; - CPRINTF("Invalid CEC msg size: %u\n", *msg_len); - return -1; - } - - queue->read_offset = (queue->read_offset + 1) % CEC_RX_BUFFER_SIZE; - for (i = 0; i < *msg_len; i++) { - msg[i] = queue->buf[queue->read_offset]; - queue->read_offset = (queue->read_offset + 1) % - CEC_RX_BUFFER_SIZE; - - } - - mutex_unlock(&rx_queue_readoffset_mutex); - - return 0; -} diff --git a/common/charge_manager.c b/common/charge_manager.c deleted file mode 100644 index 862bb28725..0000000000 --- a/common/charge_manager.c +++ /dev/null @@ -1,1625 +0,0 @@ -/* Copyright 2014 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 "adc.h" -#include "atomic.h" -#include "battery.h" -#include "charge_manager.h" -#include "charge_ramp.h" -#include "charge_state_v2.h" -#include "charger.h" -#include "console.h" -#include "dps.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#ifdef HAS_MOCK_CHARGE_MANAGER -#error Mock defined HAS_MOCK_CHARGE_MANAGER -#endif - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#define POWER(charge_port) ((charge_port.current) * (charge_port.voltage)) - -/* Timeout for delayed override power swap, allow for 500ms extra */ -#define POWER_SWAP_TIMEOUT (PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON + \ - PD_T_SAFE_0V + 500 * MSEC) - -/* - * Default charge supplier priority - * - * - Always pick dedicated charge if present since that is the best product - * decision. - * - Pick PD negotiated chargers over everything else since they have the most - * power potential and they may not currently be negotiated at a high power. - * (and they can at least provide 15W) - * - Pick Type-C which supplier current >= 1.5A, which has higher prioirty - * than the BC1.2 and Type-C with current under 1.5A. (USB-C spec 1.3 - * Table 4-17: TYPEC 3.0A, 1.5A > BC1.2 > TYPEC under 1.5A) - * - Then pick among the propreitary and BC1.2 chargers which ever has the - * highest available power. - * - Last, pick one from the rest suppliers. Also note that some boards assume - * wireless suppliers as low priority. - */ -__overridable const int supplier_priority[] = { -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - [CHARGE_SUPPLIER_DEDICATED] = 0, -#endif - [CHARGE_SUPPLIER_PD] = 1, - [CHARGE_SUPPLIER_TYPEC] = 2, - [CHARGE_SUPPLIER_TYPEC_DTS] = 2, -#ifdef CHARGE_MANAGER_BC12 - [CHARGE_SUPPLIER_PROPRIETARY] = 3, - [CHARGE_SUPPLIER_BC12_DCP] = 3, - [CHARGE_SUPPLIER_BC12_CDP] = 3, - [CHARGE_SUPPLIER_BC12_SDP] = 3, - [CHARGE_SUPPLIER_TYPEC_UNDER_1_5A] = 4, - [CHARGE_SUPPLIER_OTHER] = 4, - [CHARGE_SUPPLIER_VBUS] = 4, -#endif -#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7 - [CHARGE_SUPPLIER_WPC_BPP] = 5, - [CHARGE_SUPPLIER_WPC_EPP] = 5, - [CHARGE_SUPPLIER_WPC_GPP] = 5, -#endif - -}; -BUILD_ASSERT(ARRAY_SIZE(supplier_priority) == CHARGE_SUPPLIER_COUNT); - -/* Keep track of available charge for each charge port. */ -static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT] - [CHARGE_PORT_COUNT]; - -/* Keep track of when the supplier on each port is registered. */ -static timestamp_t registration_time[CHARGE_PORT_COUNT]; - -/* - * Charge current ceiling (mA) for ports. This can be set to temporarily limit - * the charge pulled from a port, without influencing the port selection logic. - * The ceiling can be set independently from several requestors, with the - * minimum ceiling taking effect. - */ -static int charge_ceil[CHARGE_PORT_COUNT][CEIL_REQUESTOR_COUNT]; - -/* Dual-role capability of attached partner port */ -static enum dualrole_capabilities dualrole_capability[CHARGE_PORT_COUNT]; - -#ifdef CONFIG_USB_PD_LOGGING -/* Mark port as dirty when making changes, for later logging */ -static int save_log[CHARGE_PORT_COUNT]; -#endif - -/* Store current state of port enable / charge current. */ -static int charge_port = CHARGE_PORT_NONE; -static int charge_current = CHARGE_CURRENT_UNINITIALIZED; -static int charge_current_uncapped = CHARGE_CURRENT_UNINITIALIZED; -static int charge_voltage; -static int charge_supplier = CHARGE_SUPPLIER_NONE; -static int override_port = OVERRIDE_OFF; - -static int delayed_override_port = OVERRIDE_OFF; -static timestamp_t delayed_override_deadline; - -/* Source-out Rp values for TCPMv1 */ -__maybe_unused static uint8_t source_port_rp[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT -/* 3A on one port and 1.5A on the rest */ -BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT * 1500 + 1500 <= - CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT); -#endif - -/* - * charge_manager initially operates in safe mode until asked to leave (through - * charge_manager_leave_safe_mode()). While in safe mode, the following - * behavior is altered: - * - * 1) All chargers are considered dedicated (and thus are valid charge source - * candidates) for the purpose of port selection. - * 2) Charge ceilings are ignored. Most significantly, ILIM won't drop on PD - * voltage transition. If current load is high during transition, some - * chargers may brown-out. - * 3) CHARGE_PORT_NONE will not be selected (POR default charge port will - * remain selected rather than CHARGE_PORT_NONE). - * - * After leaving safe mode, charge_manager reverts to its normal behavior and - * immediately selects charge port and current using standard rules. - */ -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -static int left_safe_mode; -#else -static const int left_safe_mode = 1; -#endif - -enum charge_manager_change_type { - CHANGE_CHARGE, - CHANGE_DUALROLE, -}; - -static int is_pd_port(int port) -{ - return port >= 0 && port < board_get_usb_pd_port_count(); -} - -static int is_sink(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_sink(port); - - return pd_get_power_role(port) == PD_ROLE_SINK; -} - -/** - * Some of the SKUs in certain boards have less number of USB PD ports than - * defined in CONFIG_USB_PD_PORT_MAX_COUNT. With the charge port configuration - * for DEDICATED_PORT towards the end, this will lead to holes in the static - * configuration. The ports that fall in that hole are invalid and this function - * is used to check the validity of the ports. - */ -static int is_valid_port(int port) -{ - if (port < 0 || port >= CHARGE_PORT_COUNT) - return 0; - - /* Check if the port falls in the hole */ - if (port >= board_get_usb_pd_port_count() && - port < CONFIG_USB_PD_PORT_MAX_COUNT) - return 0; - return 1; -} - -#ifndef TEST_BUILD -static int is_connected(int port) -{ - if (!is_pd_port(port)) - return board_charge_port_is_connected(port); - - return pd_is_connected(port); -} -#endif /* !TEST_BUILD */ - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING -/** - * In certain cases we need to override the default behavior of not charging - * from non-dedicated chargers. If the system is in RO and locked, we have no - * way of determining the actual dualrole capability of the charger because - * PD communication is not allowed, so we must assume that it is dedicated. - * Also, if no battery is present, the charger may be our only source of power, - * so again we must assume that the charger is dedicated. - * - * @return 1 when we need to override the a non-dedicated charger - * to be a dedicated one, 0 otherwise. - */ -static int charge_manager_spoof_dualrole_capability(void) -{ - return (system_get_image_copy() == EC_IMAGE_RO && - system_is_locked()) || !left_safe_mode; - -} -#endif /* !CONFIG_CHARGE_MANAGER_DRP_CHARGING */ - -/** - * Initialize available charge. Run before board init, so board init can - * initialize data, if needed. - */ -static void charge_manager_init(void) -{ - int i, j; - - for (i = 0; i < CHARGE_PORT_COUNT; ++i) { - if (!is_valid_port(i)) - continue; - for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j) { - available_charge[j][i].current = - CHARGE_CURRENT_UNINITIALIZED; - available_charge[j][i].voltage = - CHARGE_VOLTAGE_UNINITIALIZED; - } - for (j = 0; j < CEIL_REQUESTOR_COUNT; ++j) - charge_ceil[i][j] = CHARGE_CEIL_NONE; - if (!is_pd_port(i)) - dualrole_capability[i] = CAP_DEDICATED; - if (is_pd_port(i) && !IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - source_port_rp[i] = CONFIG_USB_PD_PULLUP; - } -} -DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_CHARGE_MANAGER_INIT); - -/** - * Check if the charge manager is seeded. - * - * @return 1 if all ports/suppliers have reported - * with some initial charge, 0 otherwise. - */ -static int charge_manager_is_seeded(void) -{ - /* Once we're seeded, we don't need to check again. */ - static int is_seeded; - int i, j; - - if (is_seeded) - return 1; - - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - if (!is_valid_port(j)) - continue; - if (available_charge[i][j].current == - CHARGE_CURRENT_UNINITIALIZED || - available_charge[i][j].voltage == - CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - } - } - is_seeded = 1; - return 1; -} - -#ifndef TEST_BUILD -/** - * Get the maximum charge current for a port. - * - * @param port Charge port. - * @return Charge current (mA). - */ -__maybe_unused static int charge_manager_get_source_current(int port) -{ - if (!is_pd_port(port)) - return 0; - - switch (source_port_rp[port]) { - case TYPEC_RP_3A0: - return 3000; - case TYPEC_RP_1A5: - return 1500; - case TYPEC_RP_USB: - default: - return 500; - } -} - -/* - * Find a supplier considering available current, voltage, power, and priority. - */ -static enum charge_supplier find_supplier(int port, enum charge_supplier sup, - int min_cur) -{ - int i; - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - if (available_charge[i][port].current <= min_cur || - available_charge[i][port].voltage <= 0) - /* Doesn't meet volt or current requirement. Skip it. */ - continue; - if (sup == CHARGE_SUPPLIER_NONE) - /* Haven't found any yet. Take it unconditionally. */ - sup = i; - else if (supplier_priority[sup] < supplier_priority[i]) - /* There is already a higher priority supplier. */ - continue; - else if (supplier_priority[i] < supplier_priority[sup]) - /* This has a higher priority. Take it. */ - sup = i; - else if (POWER(available_charge[i][port]) > - POWER(available_charge[sup][port])) - /* Priority is tie. Take it if power is higher. */ - sup = i; - } - return sup; -} - -static enum charge_supplier get_current_supplier(int port) -{ - enum charge_supplier supplier = CHARGE_SUPPLIER_NONE; - - /* Determine supplier information to show. */ - if (port == charge_port) { - supplier = charge_supplier; - } else { - /* Consider available current */ - supplier = find_supplier(port, supplier, 0); - if (supplier == CHARGE_SUPPLIER_NONE) - /* Ignore available current */ - supplier = find_supplier(port, supplier, -1); - } - - return supplier; -} -static enum usb_power_roles get_current_power_role(int port, - enum charge_supplier supplier) -{ - enum usb_power_roles role; - if (charge_port == port) - role = USB_PD_PORT_POWER_SINK; - else if (is_connected(port) && !is_sink(port)) - role = USB_PD_PORT_POWER_SOURCE; - else if (supplier != CHARGE_SUPPLIER_NONE) - role = USB_PD_PORT_POWER_SINK_NOT_CHARGING; - else - role = USB_PD_PORT_POWER_DISCONNECTED; - return role; -} - -__overridable int board_get_vbus_voltage(int port) -{ - return 0; -} - -static int get_vbus_voltage(int port, enum usb_power_roles current_role) -{ - int voltage_mv; - - /* - * If we are sourcing power or sinking but not charging, then VBUS must - * be 5V. If we are charging, then read VBUS ADC. - */ - if (current_role == USB_PD_PORT_POWER_SINK_NOT_CHARGING) { - voltage_mv = 5000; - } else { -#if defined(CONFIG_USB_PD_VBUS_MEASURE_CHARGER) - /* - * Try to get VBUS from the charger. If that fails, default to 0 - * mV. - */ - if (charger_get_vbus_voltage(port, &voltage_mv)) - voltage_mv = 0; -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_TCPC) - voltage_mv = tcpc_get_vbus_voltage(port); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_ADC_EACH_PORT) - voltage_mv = adc_read_channel(board_get_vbus_adc(port)); -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_NOT_PRESENT) - /* No VBUS ADC channel - voltage is unknown */ - voltage_mv = 0; -#elif defined(CONFIG_USB_PD_VBUS_MEASURE_BY_BOARD) - voltage_mv = board_get_vbus_voltage(port); -#else - /* There is a single ADC that measures joint Vbus */ - voltage_mv = adc_read_channel(ADC_VBUS); -#endif - } - return voltage_mv; -} - -int charge_manager_get_vbus_voltage(int port) -{ - return get_vbus_voltage(port, get_current_power_role(port, - get_current_supplier(port))); -} - -/** - * Fills passed power_info structure with current info about the passed port. - * - * @param port Charge port. - * @param r USB PD power info to be updated. - */ -static void charge_manager_fill_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - enum charge_supplier sup = get_current_supplier(port); - - /* Fill in power role */ - r->role = get_current_power_role(port, sup); - - /* Is port partner dual-role capable */ - r->dualrole = (dualrole_capability[port] == CAP_DUALROLE); - - if (sup == CHARGE_SUPPLIER_NONE || - r->role == USB_PD_PORT_POWER_SOURCE) { - if (is_pd_port(port)) { - r->type = USB_CHG_TYPE_NONE; - r->meas.voltage_max = 0; - r->meas.voltage_now = - r->role == USB_PD_PORT_POWER_SOURCE ? 5000 : 0; - /* TCPMv2 tracks source-out current in the DPM */ - if (IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - r->meas.current_max = - dpm_get_source_current(port); - else - r->meas.current_max = - charge_manager_get_source_current(port); - r->max_power = 0; - } else { - r->type = USB_CHG_TYPE_NONE; - board_fill_source_power_info(port, r); - } - } else { - int use_ramp_current; - switch (sup) { - case CHARGE_SUPPLIER_PD: - r->type = USB_CHG_TYPE_PD; - break; - case CHARGE_SUPPLIER_TYPEC: - case CHARGE_SUPPLIER_TYPEC_DTS: - r->type = USB_CHG_TYPE_C; - break; -#ifdef CHARGE_MANAGER_BC12 - case CHARGE_SUPPLIER_PROPRIETARY: - r->type = USB_CHG_TYPE_PROPRIETARY; - break; - case CHARGE_SUPPLIER_BC12_DCP: - r->type = USB_CHG_TYPE_BC12_DCP; - break; - case CHARGE_SUPPLIER_BC12_CDP: - r->type = USB_CHG_TYPE_BC12_CDP; - break; - case CHARGE_SUPPLIER_BC12_SDP: - r->type = USB_CHG_TYPE_BC12_SDP; - break; - case CHARGE_SUPPLIER_VBUS: - 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; - - /* - * Report unknown charger CHARGE_DETECT_DELAY after supplier - * change since PD negotiation may take time. - * - * Do not debounce on batteryless systems because - * USB_CHG_TYPE_UNKNOWN implies the system is still on battery - * while some kind of negotiation happens, but by the time the - * host might request this in a battery-free configuration we - * must be stable (if not, the system is either up or about to - * lose power again). - */ -#ifdef CONFIG_BATTERY - if (get_time().val < registration_time[port].val + - CHARGE_DETECT_DELAY) - r->type = USB_CHG_TYPE_UNKNOWN; -#endif - -#if defined(HAS_TASK_CHG_RAMP) || defined(CONFIG_CHARGE_RAMP_HW) - /* Read ramped current if active charging port */ - use_ramp_current = - (charge_port == port) && chg_ramp_allowed(port, sup); -#else - use_ramp_current = 0; -#endif - if (use_ramp_current) { - /* Current limit is output of ramp module */ - r->meas.current_lim = chg_ramp_get_current_limit(); - - /* - * If ramp is allowed, then the max current depends - * on if ramp is stable. If ramp is stable, then - * max current is same as input current limit. If - * ramp is not stable, then we report the maximum - * current we could ramp up to for this supplier. - * If ramp is not allowed, max current is just the - * available charge current. - */ - r->meas.current_max = chg_ramp_is_stable() ? - r->meas.current_lim : chg_ramp_max(port, sup, - available_charge[sup][port].current); - - r->max_power = - r->meas.current_max * r->meas.voltage_max; - } else { - r->meas.current_max = r->meas.current_lim = - available_charge[sup][port].current; - r->max_power = POWER(available_charge[sup][port]); - } - - r->meas.voltage_now = get_vbus_voltage(port, r->role); - } -} -#endif /* TEST_BUILD */ - -#ifdef CONFIG_USB_PD_LOGGING -/** - * Saves a power state log entry with the current info about the passed port. - */ -void charge_manager_save_log(int port) -{ - uint16_t flags = 0; - struct ec_response_usb_pd_power_info pinfo; - - if (!is_pd_port(port)) - return; - - save_log[port] = 0; - charge_manager_fill_power_info(port, &pinfo); - - /* Flags are stored in the data field */ - if (port == override_port) - flags |= CHARGE_FLAGS_OVERRIDE; - if (port == delayed_override_port) - flags |= CHARGE_FLAGS_DELAYED_OVERRIDE; - flags |= pinfo.role | (pinfo.type << CHARGE_FLAGS_TYPE_SHIFT) | - (pinfo.dualrole ? CHARGE_FLAGS_DUAL_ROLE : 0); - - pd_log_event(PD_EVENT_MCU_CHARGE, - PD_LOG_PORT_SIZE(port, sizeof(pinfo.meas)), - flags, &pinfo.meas); -} -#endif /* CONFIG_USB_PD_LOGGING */ - -/** - * Attempt to switch to power source on port if applicable. - * - * @param port USB-C port to be swapped. - */ -static void charge_manager_switch_to_source(int port) -{ - if (!is_pd_port(port)) - return; - - /* If connected to dual-role device, then ask for a swap */ - if (dualrole_capability[port] == CAP_DUALROLE && is_sink(port)) - pd_request_power_swap(port); -} - -/** - * Return the computed charge ceiling for a port, which represents the - * minimum ceiling among all valid requestors. - * - * @param port Charge port. - * @return Charge ceiling (mA) or CHARGE_CEIL_NONE. - */ -static int charge_manager_get_ceil(int port) -{ - int ceil = CHARGE_CEIL_NONE; - int val, i; - - if (!is_valid_port(port)) - return ceil; - - for (i = 0; i < CEIL_REQUESTOR_COUNT; ++i) { - val = charge_ceil[port][i]; - if (val != CHARGE_CEIL_NONE && - (ceil == CHARGE_CEIL_NONE || val < ceil)) - ceil = val; - } - - return ceil; -} - -/** - * Select the 'best' charge port, as defined by the supplier heirarchy and the - * ability of the port to provide power. - * - * @param new_port Pointer to the best charge port by definition. - * @param new_supplier Pointer to the best charge supplier by definition. - */ -static void charge_manager_get_best_charge_port(int *new_port, - int *new_supplier) -{ - int supplier = CHARGE_SUPPLIER_NONE; - int port = CHARGE_PORT_NONE; - int best_port_power = -1, candidate_port_power; - int i, j; - - /* Skip port selection on OVERRIDE_DONT_CHARGE. */ - if (override_port != OVERRIDE_DONT_CHARGE) { - - /* - * Charge supplier selection logic: - * 1. Prefer DPS charge port. - * 2. Prefer higher priority supply. - * 3. Prefer higher power over lower in case priority is tied. - * 4. Prefer current charge port over new port in case (1) - * and (2) are tied. - * available_charge can be changed at any time by other tasks, - * so make no assumptions about its consistency. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - for (j = 0; j < CHARGE_PORT_COUNT; ++j) { - /* Skip this port if it is not valid. */ - if (!is_valid_port(j)) - continue; - - /* - * Skip this supplier if there is no - * available charge. - */ - if (available_charge[i][j].current == 0 || - available_charge[i][j].voltage == 0) - continue; - - /* - * Don't select this port if we have a - * charge on another override port. - */ - if (override_port != OVERRIDE_OFF && - override_port == port && - override_port != j) - continue; - -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* - * Don't charge from a dual-role port unless - * it is our override port. - */ - if (dualrole_capability[j] != CAP_DEDICATED && - override_port != j && - !charge_manager_spoof_dualrole_capability()) - continue; -#endif - - candidate_port_power = - POWER(available_charge[i][j]); - - /* Select DPS port if provided. */ - if (IS_ENABLED(CONFIG_USB_PD_DPS) && - override_port == OVERRIDE_OFF && - i == CHARGE_SUPPLIER_PD && - j == dps_get_charge_port()) { - supplier = i; - port = j; - break; - /* Select if no supplier chosen yet. */ - } else if (supplier == CHARGE_SUPPLIER_NONE || - /* ..or if supplier priority is higher. */ - supplier_priority[i] < - supplier_priority[supplier] || - /* ..or if this is our override port. */ - (j == override_port && - port != override_port) || - /* ..or if priority is tied and.. */ - (supplier_priority[i] == - supplier_priority[supplier] && - /* candidate port can supply more power or.. */ - (candidate_port_power > best_port_power || - /* - * candidate port is the active port and can - * supply the same amount of power. - */ - (candidate_port_power == best_port_power && - charge_port == j)))) { - supplier = i; - port = j; - best_port_power = candidate_port_power; - } - } - - } - -#ifdef CONFIG_BATTERY - /* - * if no battery present then retain same charge port - * and charge supplier to avoid the port switching - */ - if (charge_port != CHARGE_SUPPLIER_NONE && - charge_port != port && - (battery_is_present() == BP_NO || - (battery_is_present() == BP_YES && - battery_is_cut_off() != BATTERY_CUTOFF_STATE_NORMAL))) { - port = charge_port; - supplier = charge_supplier; - } -#endif - - *new_port = port; - *new_supplier = supplier; -} - -/** - * Charge manager refresh -- responsible for selecting the active charge port - * and charge power. Called as a deferred task. - */ -static void charge_manager_refresh(void) -{ - /* Always initialize charge port on first pass */ - static int active_charge_port_initialized; - int new_supplier, new_port; - int new_charge_current, new_charge_current_uncapped; - int new_charge_voltage, i; - int updated_new_port = CHARGE_PORT_NONE; - int updated_old_port = CHARGE_PORT_NONE; - int ceil; - int power_changed = 0; - - /* Hunt for an acceptable charge port */ - while (1) { - charge_manager_get_best_charge_port(&new_port, &new_supplier); - - if (!left_safe_mode && new_port == CHARGE_PORT_NONE) - return; - - /* - * If the port or supplier changed, make an attempt to switch to - * the port. We will re-set the active port on a supplier change - * to give the board-level function another chance to reject - * the port, for example, if the port has become a charge - * source. - */ - if (active_charge_port_initialized && - new_port == charge_port && - new_supplier == charge_supplier) - break; - - /* - * For OCPC systems, reset the OCPC state to prevent current - * spikes. - */ - if (IS_ENABLED(CONFIG_OCPC)) { - charge_set_active_chg_chip(new_port); - trigger_ocpc_reset(); - } - - if (board_set_active_charge_port(new_port) == EC_SUCCESS) { - if (IS_ENABLED(CONFIG_EXTPOWER)) - board_check_extpower(); - break; - } - - /* 'Dont charge' request must be accepted. */ - ASSERT(new_port != CHARGE_PORT_NONE); - - /* - * Zero the available charge on the rejected port so that - * it is no longer chosen. - */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) { - available_charge[i][new_port].current = 0; - available_charge[i][new_port].voltage = 0; - } - } - - active_charge_port_initialized = 1; - - /* - * Clear override if it wasn't selected as the 'best' port -- it means - * that no charge is available on the port, or the port was rejected. - */ - if (override_port >= 0 && override_port != new_port) - override_port = OVERRIDE_OFF; - - if (new_supplier == CHARGE_SUPPLIER_NONE) { - new_charge_current = 0; - new_charge_current_uncapped = 0; - new_charge_voltage = 0; - } else { - new_charge_current_uncapped = - available_charge[new_supplier][new_port].current; -#ifdef CONFIG_CHARGE_RAMP_HW - /* - * Allow to set the maximum current value, so the hardware can - * know the range of acceptable current values for its ramping. - */ - if (chg_ramp_allowed(new_port, new_supplier)) - new_charge_current_uncapped = - chg_ramp_max(new_port, new_supplier, - new_charge_current_uncapped); -#endif /* CONFIG_CHARGE_RAMP_HW */ - /* Enforce port charge ceiling. */ - ceil = charge_manager_get_ceil(new_port); - if (left_safe_mode && ceil != CHARGE_CEIL_NONE) - new_charge_current = MIN(ceil, - new_charge_current_uncapped); - else - new_charge_current = new_charge_current_uncapped; - - new_charge_voltage = - available_charge[new_supplier][new_port].voltage; - } - - /* Change the charge limit + charge port/supplier if modified. */ - if (new_port != charge_port || new_charge_current != charge_current || - new_supplier != charge_supplier) { -#ifdef HAS_TASK_CHG_RAMP - chg_ramp_charge_supplier_change( - new_port, new_supplier, new_charge_current, - registration_time[new_port], - new_charge_voltage); -#else -#ifdef CONFIG_CHARGE_RAMP_HW - /* Enable or disable charge ramp */ - charger_set_hw_ramp(chg_ramp_allowed(new_port, new_supplier)); -#endif - board_set_charge_limit(new_port, new_supplier, - new_charge_current, - new_charge_current_uncapped, - new_charge_voltage); -#endif /* HAS_TASK_CHG_RAMP */ - - power_changed = 1; - - CPRINTS("CL: p%d s%d i%d v%d", new_port, new_supplier, - new_charge_current, new_charge_voltage); - - /* - * (b:192638664) We try to check AC OK again to avoid - * unsuccessful detection in the initial detection. - */ - if (IS_ENABLED(CONFIG_EXTPOWER)) - board_check_extpower(); - } - - /* - * Signal new power request only if the port changed, the voltage - * on the same port changed, or the actual uncapped current - * on the same port changed (don't consider ceil). - */ - if (new_port != CHARGE_PORT_NONE && - (new_port != charge_port || - new_charge_current_uncapped != charge_current_uncapped || - new_charge_voltage != charge_voltage)) - updated_new_port = new_port; - - /* If charge port changed, cleanup old port */ - if (charge_port != new_port && charge_port != CHARGE_PORT_NONE) { - /* Check if need power swap */ - charge_manager_switch_to_source(charge_port); - /* Signal new power request on old port */ - updated_old_port = charge_port; - } - - /* Update globals to reflect current state. */ - charge_current = new_charge_current; - charge_current_uncapped = new_charge_current_uncapped; - charge_voltage = new_charge_voltage; - charge_supplier = new_supplier; - charge_port = new_port; - -#ifdef CONFIG_USB_PD_LOGGING - /* - * Write a log under the following conditions: - * 1. A port becomes active or - * 2. A port becomes inactive or - * 3. The active charge port power limit changes or - * 4. Any supplier change on an inactive port - */ - if (updated_new_port != CHARGE_PORT_NONE) - save_log[updated_new_port] = 1; - /* Don't log non-meaningful changes on charge port */ - else if (charge_port != CHARGE_PORT_NONE) - save_log[charge_port] = 0; - - if (updated_old_port != CHARGE_PORT_NONE) - save_log[updated_old_port] = 1; - - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (save_log[i]) - charge_manager_save_log(i); -#endif - - /* New power requests must be set only after updating the globals. */ - if (is_pd_port(updated_new_port)) { - /* Check if we can get requested voltage/current */ - if ((IS_ENABLED(CONFIG_USB_PD_TCPMV1) && - IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) || - (IS_ENABLED(CONFIG_USB_PD_TCPMV2) && - IS_ENABLED(CONFIG_USB_PE_SM))) { - uint32_t pdo; - uint32_t max_voltage; - uint32_t max_current; - uint32_t unused; - /* - * Check if new voltage/current is different - * than requested. If yes, send new power request - */ - if (pd_get_requested_voltage(updated_new_port) != - charge_voltage || - pd_get_requested_current(updated_new_port) != - charge_current_uncapped) - pd_set_new_power_request(updated_new_port); - - /* - * Check if we can get more power from this port. - * If yes, send new power request - */ - pd_find_pdo_index(pd_get_src_cap_cnt(updated_new_port), - pd_get_src_caps(updated_new_port), - pd_get_max_voltage(), &pdo); - pd_extract_pdo_power(pdo, &max_current, &max_voltage, - &unused); - if (charge_voltage != max_voltage || - charge_current_uncapped != max_current) - pd_set_new_power_request(updated_new_port); - } else { - /* - * Functions for getting requested voltage/current - * are not available. Send new power request. - */ - pd_set_new_power_request(updated_new_port); - } - } - if (is_pd_port(updated_old_port)) - pd_set_new_power_request(updated_old_port); - - if (power_changed) - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_manager_refresh); - -/** - * Called when charge override times out waiting for power swap. - */ -static void charge_override_timeout(void) -{ - delayed_override_port = OVERRIDE_OFF; - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charge_override_timeout); - -/** - * Called CHARGE_DETECT_DELAY after the most recent charge change on a port. - */ -static void charger_detect_debounced(void) -{ - /* Inform host that charger detection is debounced. */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); -} -DECLARE_DEFERRED(charger_detect_debounced); - -/** - * Update charge parameters for a given port / supplier. - * - * @param change Type of change. - * @param supplier Charge supplier to be updated. - * @param port Charge port to be updated. - * @param charge Charge port current / voltage. - */ -static void charge_manager_make_change(enum charge_manager_change_type change, - int supplier, - int port, - const struct charge_port_info *charge) -{ - int i; - int clear_override = 0; - - if (!is_valid_port(port)) { - CPRINTS("%s: p%d invalid", __func__, port); - return; - } - - /* Determine if this is a change which can affect charge status */ - switch (change) { - case CHANGE_CHARGE: - /* Ignore changes where charge is identical */ - if (available_charge[supplier][port].current == - charge->current && - available_charge[supplier][port].voltage == - charge->voltage) - return; - if (charge->current > 0 && - available_charge[supplier][port].current == 0) - clear_override = 1; -#ifdef CONFIG_USB_PD_LOGGING - save_log[port] = 1; -#endif - break; - case CHANGE_DUALROLE: - /* - * Ignore all except for transition to non-dualrole, - * which may occur some time after we see a charge - */ -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - if (dualrole_capability[port] != CAP_DEDICATED) -#endif - return; - /* Clear override only if a charge is present on the port */ - for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) - if (available_charge[i][port].current > 0) { - clear_override = 1; - break; - } - /* - * If there is no charge present on the port, the dualrole - * change is meaningless to charge_manager. - */ - if (!clear_override) - return; - break; - } - - /* Remove override when a charger is plugged */ - if (clear_override && override_port != port -#ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING - /* only remove override when it's a dedicated charger */ - && dualrole_capability[port] == CAP_DEDICATED -#endif - ) { - override_port = OVERRIDE_OFF; - if (delayed_override_port != OVERRIDE_OFF) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - } - } - - if (change == CHANGE_CHARGE) { - available_charge[supplier][port].current = charge->current; - available_charge[supplier][port].voltage = charge->voltage; - registration_time[port] = get_time(); - - /* - * After CHARGE_DETECT_DELAY, inform the host that charger - * detection has been debounced. Since only one deferred - * routine exists for all ports, the deferred call for a given - * port may potentially be cancelled. This is mostly harmless - * since cancellation implies that PD_EVENT_POWER_CHANGE was - * just sent due to the power change on another port. - */ - if (charge->current > 0) - hook_call_deferred(&charger_detect_debounced_data, - CHARGE_DETECT_DELAY); - - /* - * If we have a charge on our delayed override port within - * the deadline, make it our override port. - */ - if (port == delayed_override_port && charge->current > 0 && - is_sink(delayed_override_port) && - get_time().val < delayed_override_deadline.val) { - delayed_override_port = OVERRIDE_OFF; - hook_call_deferred(&charge_override_timeout_data, -1); - charge_manager_set_override(port); - } - } - - /* - * Don't call charge_manager_refresh unless all ports + - * suppliers have reported in. We don't want to make changes - * to our charge port until we are certain we know what is - * attached. - */ - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} - -void pd_set_input_current_limit(int port, uint32_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) - charge_reset_stable_current(); - - charge.current = max_ma; - charge.voltage = supply_voltage; - charge_manager_update_charge(CHARGE_SUPPLIER_PD, port, &charge); -} - -void typec_set_input_current_limit(int port, typec_current_t max_ma, - uint32_t supply_voltage) -{ - struct charge_port_info charge; - int i; - int supplier; - int dts = !!(max_ma & TYPEC_CURRENT_DTS_MASK); - static const enum charge_supplier typec_suppliers[] = { - CHARGE_SUPPLIER_TYPEC, - CHARGE_SUPPLIER_TYPEC_DTS, -#ifdef CHARGE_MANAGER_BC12 - CHARGE_SUPPLIER_TYPEC_UNDER_1_5A, -#endif /* CHARGE_MANAGER_BC12 */ - }; - - charge.current = max_ma & TYPEC_CURRENT_ILIM_MASK; - charge.voltage = supply_voltage; -#if !defined(HAS_TASK_CHG_RAMP) && !defined(CONFIG_CHARGE_RAMP_HW) - /* - * DTS sources such as suzy-q may not be able to actually deliver - * their advertised current, so limit it to reduce chance of OC, - * if we can't ramp. - */ - if (dts) - charge.current = MIN(charge.current, 500); -#endif - - supplier = dts ? CHARGE_SUPPLIER_TYPEC_DTS : CHARGE_SUPPLIER_TYPEC; - -#ifdef CHARGE_MANAGER_BC12 - /* - * According to USB-C spec 1.3 Table 4-17 "Precedence of power source - * usage", the priority should be: USB-C 3.0A, 1.5A > BC1.2 > USB-C - * under 1.5A. Choosed the corresponding supplier type, according to - * charge current, to update. - */ - if (charge.current < 1500) - supplier = CHARGE_SUPPLIER_TYPEC_UNDER_1_5A; -#endif /* CHARGE_MANAGER_BC12 */ - - charge_manager_update_charge(supplier, port, &charge); - - /* - * TYPEC / TYPEC-DTS / TYPEC-UNDER_1_5A should be mutually exclusive. - * Zero'ing all the other suppliers. - */ - for (i = 0; i < ARRAY_SIZE(typec_suppliers); ++i) - if (supplier != typec_suppliers[i]) - charge_manager_update_charge(typec_suppliers[i], port, - NULL); -} - -void charge_manager_update_charge(int supplier, - int port, - const struct charge_port_info *charge) -{ - struct charge_port_info zero = {0}; - if (!charge) - charge = &zero; - charge_manager_make_change(CHANGE_CHARGE, supplier, port, charge); -} - -void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap) -{ - if (!is_pd_port(port)) - return; - - /* Ignore when capability is unchanged */ - if (cap != dualrole_capability[port]) { - dualrole_capability[port] = cap; - charge_manager_make_change(CHANGE_DUALROLE, 0, port, NULL); - } -} - -#ifdef CONFIG_CHARGE_MANAGER_SAFE_MODE -void charge_manager_leave_safe_mode(void) -{ - if (left_safe_mode) - return; - - CPRINTS("%s()", __func__); - cflush(); - left_safe_mode = 1; - if (charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); -} -#endif - -void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil) -{ - if (!is_valid_port(port)) - return; - - if (charge_ceil[port][requestor] != ceil) { - charge_ceil[port][requestor] = ceil; - if (port == charge_port && charge_manager_is_seeded()) - hook_call_deferred(&charge_manager_refresh_data, 0); - } -} - -void charge_manager_force_ceil(int port, int ceil) -{ - /* - * Force our input current to ceil if we're exceeding it, without - * waiting for our deferred task to run. - */ - if (left_safe_mode && port == charge_port && ceil < charge_current) - board_set_charge_limit(port, CHARGE_SUPPLIER_PD, ceil, - charge_current_uncapped, charge_voltage); - - /* - * Now inform charge_manager so it stays in sync with the state of - * the world. - */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, ceil); -} - -int charge_manager_set_override(int port) -{ - int retval = EC_SUCCESS; - - CPRINTS("Charge Override: %d", port); - - /* - * If attempting to change the override port, then return - * error. Since we may be in the middle of a power swap on - * the original override port, it's too complicated to - * guarantee that the original override port is switched back - * to source. - */ - if (delayed_override_port != OVERRIDE_OFF) - return EC_ERROR_BUSY; - - /* Set the override port if it's a sink. */ - if (port < 0 || is_sink(port)) { - if (override_port != port) { - override_port = port; - if (charge_manager_is_seeded()) - hook_call_deferred( - &charge_manager_refresh_data, 0); - } - } - /* - * If the attached device is capable of being a sink, request a - * power swap and set the delayed override for swap completion. - */ - else if (!is_sink(port) && dualrole_capability[port] == CAP_DUALROLE) { - delayed_override_deadline.val = get_time().val + - POWER_SWAP_TIMEOUT; - delayed_override_port = port; - hook_call_deferred(&charge_override_timeout_data, - POWER_SWAP_TIMEOUT); - pd_request_power_swap(port); - /* Can't charge from requested port -- return error. */ - } else - retval = EC_ERROR_INVAL; - - return retval; -} - -int charge_manager_get_override(void) -{ - return override_port; -} - -int charge_manager_get_active_charge_port(void) -{ - return charge_port; -} - -int charge_manager_get_selected_charge_port(void) -{ - int port, supplier; - - charge_manager_get_best_charge_port(&port, &supplier); - return port; -} - -int charge_manager_get_charger_current(void) -{ - return charge_current; -} - -int charge_manager_get_charger_voltage(void) -{ - return charge_voltage; -} - -enum charge_supplier charge_manager_get_supplier(void) -{ - return charge_supplier; -} - -int charge_manager_get_power_limit_uw(void) -{ - int current_ma = charge_current; - int voltage_mv = charge_voltage; - - if (current_ma == CHARGE_CURRENT_UNINITIALIZED || - voltage_mv == CHARGE_VOLTAGE_UNINITIALIZED) - return 0; - else - return current_ma * voltage_mv; -} - -#if defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) && \ - !defined(CONFIG_USB_PD_TCPMV2) -/* Note: this functionality is a part of the TCPMv2 Device Poicy Manager */ - -/* Bitmap of ports used as power source */ -static volatile uint32_t source_port_bitmap; -BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_MAX_COUNT); - -static inline int has_other_active_source(int port) -{ - return source_port_bitmap & ~BIT(port); -} - -static inline int is_active_source(int port) -{ - return source_port_bitmap & BIT(port); -} - -static int can_supply_max_current(int port) -{ -#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT - /* - * This guarantees active 3A source continues to supply 3A. - * - * Since redistribution occurs sequentially, younger ports get - * priority. Priority surfaces only when 3A source is released. - * That is, when 3A source is released, the youngest active - * port gets 3A. - */ - int p; - if (!is_active_source(port)) - /* Non-active ports don't get 3A */ - return 0; - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - if (p == port) - continue; - if (source_port_rp[p] == - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - return 0; - } - return 1; -#else - return is_active_source(port) && !has_other_active_source(port); -#endif /* CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT */ -} - -void charge_manager_source_port(int port, int enable) -{ - uint32_t prev_bitmap = source_port_bitmap; - int p, rp; - - if (enable) - atomic_or((uint32_t *)&source_port_bitmap, 1 << port); - else - atomic_clear_bits((uint32_t *)&source_port_bitmap, 1 << port); - - /* No change, exit early. */ - if (prev_bitmap == source_port_bitmap) - return; - - /* Set port limit according to policy */ - for (p = 0; p < board_get_usb_pd_port_count(); p++) { - rp = can_supply_max_current(p) ? - CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT : - CONFIG_USB_PD_PULLUP; - source_port_rp[p] = rp; - -#ifdef CONFIG_USB_PD_LOGGING - if (is_connected(p) && !is_sink(p)) - charge_manager_save_log(p); -#endif - - typec_set_source_current_limit(p, rp); - if (IS_ENABLED(CONFIG_USB_PD_TCPMV2)) - typec_select_src_current_limit_rp(p, rp); - else - tcpm_select_rp_value(p, rp); - pd_update_contract(p); - } -} - -int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - if (can_supply_max_current(port)) { - *src_pdo = pd_src_pdo_max; - return pd_src_pdo_max_cnt; - } - - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} -#endif /* CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT && !CONFIG_USB_PD_TCPMV2 */ - -#ifndef TEST_BUILD -static enum ec_status hc_pd_power_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_power_info *p = args->params; - struct ec_response_usb_pd_power_info *r = args->response; - int port = p->port; - - /* If host is asking for the charging port, set port appropriately */ - if (port == PD_POWER_CHARGING_PORT) - port = charge_port; - - /* - * Not checking for invalid port here, because it might break existing - * contract with ectool users. The invalid ports will have the response - * voltage, current and power parameters set to 0. - */ - if (port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - charge_manager_fill_power_info(port, r); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_POWER_INFO, - hc_pd_power_info, - EC_VER_MASK(0)); -#endif /* TEST_BUILD */ - -static enum ec_status hc_charge_port_count(struct host_cmd_handler_args *args) -{ - struct ec_response_charge_port_count *resp = args->response; - - args->response_size = sizeof(*resp); - resp->port_count = CHARGE_PORT_COUNT; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_PORT_COUNT, - hc_charge_port_count, - EC_VER_MASK(0)); - -static enum ec_status -hc_charge_port_override(struct host_cmd_handler_args *args) -{ - const struct ec_params_charge_port_override *p = args->params; - const int16_t override_port = p->override_port; - - if (override_port < OVERRIDE_DONT_CHARGE || - override_port >= CHARGE_PORT_COUNT) - return EC_RES_INVALID_PARAM; - - return charge_manager_set_override(override_port) == EC_SUCCESS ? - EC_RES_SUCCESS : EC_RES_ERROR; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHARGE_PORT_OVERRIDE, - hc_charge_port_override, - EC_VER_MASK(0)); - -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 -static enum ec_status hc_override_dedicated_charger_limit( - struct host_cmd_handler_args *args) -{ - const struct ec_params_dedicated_charger_limit *p = args->params; - struct charge_port_info ci = { - .current = p->current_lim, - .voltage = p->voltage_lim, - }; - - /* - * Allow a change only if the dedicated charge port is used. Host needs - * to apply a change every time a dedicated charger is plugged. - */ - if (charge_port != DEDICATED_CHARGE_PORT) - return EC_RES_UNAVAILABLE; - - charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, - DEDICATED_CHARGE_PORT, &ci); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_OVERRIDE_DEDICATED_CHARGER_LIMIT, - hc_override_dedicated_charger_limit, - EC_VER_MASK(0)); -#endif - -static int command_charge_port_override(int argc, char **argv) -{ - int port = OVERRIDE_OFF; - int ret = EC_SUCCESS; - char *e; - - if (argc >= 2) { - port = strtoi(argv[1], &e, 0); - if (*e || port < OVERRIDE_DONT_CHARGE || - port >= CHARGE_PORT_COUNT) - return EC_ERROR_PARAM1; - ret = charge_manager_set_override(port); - } - - ccprintf("Override: %d\n", (argc >= 2 && ret == EC_SUCCESS) ? - port : override_port); - return ret; -} -DECLARE_CONSOLE_COMMAND(chgoverride, command_charge_port_override, - "[port | -1 | -2]", - "Force charging from a given port (-1 = off, -2 = disable charging)"); - -#ifdef CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT -static void charge_manager_set_external_power_limit(int current_lim, - int voltage_lim) -{ - int port; - - if (current_lim == EC_POWER_LIMIT_NONE) - current_lim = CHARGE_CEIL_NONE; - if (voltage_lim == EC_POWER_LIMIT_NONE) - voltage_lim = PD_MAX_VOLTAGE_MV; - - for (port = 0; port < board_get_usb_pd_port_count(); ++port) { - charge_manager_set_ceil(port, CEIL_REQUESTOR_HOST, current_lim); - pd_set_external_voltage_limit(port, voltage_lim); - } -} - -/* - * On transition out of S0, disable all external power limits, in case AP - * failed to clear them. - */ -static void charge_manager_external_power_limit_off(void) -{ - charge_manager_set_external_power_limit(EC_POWER_LIMIT_NONE, - EC_POWER_LIMIT_NONE); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, charge_manager_external_power_limit_off, - HOOK_PRIO_DEFAULT); - -static enum ec_status -hc_external_power_limit(struct host_cmd_handler_args *args) -{ - const struct ec_params_external_power_limit_v1 *p = args->params; - - charge_manager_set_external_power_limit(p->current_lim, - p->voltage_lim); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_EXTERNAL_POWER_LIMIT, - hc_external_power_limit, - EC_VER_MASK(1)); - -static int command_external_power_limit(int argc, char **argv) -{ - int max_current; - int max_voltage; - char *e; - - if (argc >= 2) { - max_current = strtoi(argv[1], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_current = EC_POWER_LIMIT_NONE; - - if (argc >= 3) { - max_voltage = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - } else - max_voltage = EC_POWER_LIMIT_NONE; - - charge_manager_set_external_power_limit(max_current, max_voltage); - ccprintf("max req: %dmA %dmV\n", max_current, max_voltage); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chglim, command_external_power_limit, - "[max_current (mA)] [max_voltage (mV)]", - "Set max charger current / voltage"); -#endif /* CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT */ - -#ifdef CONFIG_CMD_CHARGE_SUPPLIER_INFO -static int charge_supplier_info(int argc, char **argv) -{ - ccprintf("port=%d, type=%d, cur=%dmA, vtg=%dmV, lsm=%d\n", - charge_manager_get_active_charge_port(), - charge_supplier, - charge_current, - charge_voltage, - left_safe_mode); - - return 0; -} -DECLARE_CONSOLE_COMMAND(chgsup, charge_supplier_info, - NULL, "print chg supplier info"); -#endif - -__overridable -int board_charge_port_is_sink(int port) -{ - return 1; -} - -__overridable -int board_charge_port_is_connected(int port) -{ - return 1; -} - -__overridable -void board_fill_source_power_info(int port, - struct ec_response_usb_pd_power_info *r) -{ - r->meas.voltage_now = 0; - r->meas.voltage_max = 0; - r->meas.current_max = 0; - r->meas.current_lim = 0; - r->max_power = 0; -} diff --git a/common/charge_ramp.c b/common/charge_ramp.c deleted file mode 100644 index a408771f40..0000000000 --- a/common/charge_ramp.c +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Charge input current limit ramp module for Chrome EC */ - -#include "charge_manager.h" -#include "common.h" -#include "system.h" -#include "usb_charge.h" -#include "util.h" - -test_mockable int chg_ramp_allowed(int port, int supplier) -{ - /* Don't allow ramping in RO when write protected. */ - if (!system_is_in_rw() && system_is_locked()) - return 0; - - switch (supplier) { - /* Use ramping for USB-C DTS suppliers (debug accessory eg suzy-q). */ - case CHARGE_SUPPLIER_TYPEC_DTS: - return 1; - /* - * Use HW ramping for USB-C chargers. Don't use SW ramping since the - * slow ramp causes issues with auto power on (b/169634979). - */ - case CHARGE_SUPPLIER_PD: - case CHARGE_SUPPLIER_TYPEC: - return IS_ENABLED(CONFIG_CHARGE_RAMP_HW); - /* default: fall through */ - } - - /* Otherwise ask the BC1.2 detect module */ - return usb_charger_ramp_allowed(port, supplier); -} - -test_mockable int chg_ramp_max(int port, int supplier, int sup_curr) -{ - switch (supplier) { - case CHARGE_SUPPLIER_PD: - case CHARGE_SUPPLIER_TYPEC: - case CHARGE_SUPPLIER_TYPEC_DTS: - /* - * We should not ramp DTS beyond what they advertise, otherwise - * we may brownout the systems they are connected to. - */ - return sup_curr; - /* default: fall through */ - } - - /* Otherwise ask the BC1.2 detect module */ - return usb_charger_ramp_max(port, supplier, sup_curr); -} diff --git a/common/charge_ramp_sw.c b/common/charge_ramp_sw.c deleted file mode 100644 index bfd6db057b..0000000000 --- a/common/charge_ramp_sw.c +++ /dev/null @@ -1,383 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Charge input current limit ramp module for Chrome EC */ - -#include "charge_manager.h" -#include "charge_ramp.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "task.h" -#include "timer.h" -#include "usb_pd.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -/* Number of times to ramp current searching for limit before stable charging */ -#define RAMP_COUNT 3 - -/* Maximum allowable time charger can be unplugged to be considered an OCP */ -#define OC_RECOVER_MAX_TIME (SECOND) - -/* Delay for running state machine when board is not consuming full current */ -#define CURRENT_DRAW_DELAY (5*SECOND) - -/* Current ramp increment */ -#define RAMP_CURR_INCR_MA 64 -#define RAMP_CURR_DELAY (500*MSEC) -#define RAMP_CURR_START_MA 500 - -/* How much to backoff the input current limit when limit has been found */ -#define RAMP_ICL_BACKOFF (2*RAMP_CURR_INCR_MA) - -/* Interval at which VBUS voltage is monitored in stable state */ -#define STABLE_VBUS_MONITOR_INTERVAL (SECOND) - -/* Time to delay for stablizing the charging current */ -#define STABLIZE_DELAY (5*SECOND) - -enum chg_ramp_state { - CHG_RAMP_DISCONNECTED, - CHG_RAMP_CHARGE_DETECT_DELAY, - CHG_RAMP_OVERCURRENT_DETECT, - CHG_RAMP_RAMP, - CHG_RAMP_STABILIZE, - CHG_RAMP_STABLE, -}; -static enum chg_ramp_state ramp_st; - -struct oc_info { - timestamp_t ts; - int oc_detected; - int sup; - int icl; -}; - -/* OCP info for each over-current */ -static struct oc_info oc_info[CONFIG_USB_PD_PORT_MAX_COUNT][RAMP_COUNT]; -static int oc_info_idx[CONFIG_USB_PD_PORT_MAX_COUNT]; -#define ACTIVE_OC_INFO (oc_info[active_port][oc_info_idx[active_port]]) - -/* Active charging information */ -static int active_port = CHARGE_PORT_NONE; -static int active_sup; -static int active_icl; -static int active_vtg; -static timestamp_t reg_time; - -static int stablize_port; -static int stablize_sup; - -/* Maximum/minimum input current limit for active charger */ -static int max_icl; -static int min_icl; - -void chg_ramp_charge_supplier_change(int port, int supplier, int current, - timestamp_t registration_time, int voltage) -{ - /* - * If the last active port was a valid port and the port - * has changed, then this may have been an over-current. - */ - if (active_port != CHARGE_PORT_NONE && - port != active_port) { - if (oc_info_idx[active_port] == RAMP_COUNT - 1) - oc_info_idx[active_port] = 0; - else - oc_info_idx[active_port]++; - ACTIVE_OC_INFO.ts = get_time(); - ACTIVE_OC_INFO.sup = active_sup; - ACTIVE_OC_INFO.icl = active_icl; - } - - /* Set new active port, set ramp state, and wake ramp task */ - active_port = port; - active_sup = supplier; - active_vtg = voltage; - - /* Set min and max input current limit based on if ramp is allowed */ - if (chg_ramp_allowed(active_port, active_sup)) { - min_icl = RAMP_CURR_START_MA; - max_icl = chg_ramp_max(active_port, active_sup, current); - } else { - min_icl = max_icl = current; - } - - reg_time = registration_time; - if (ramp_st != CHG_RAMP_STABILIZE) { - ramp_st = (active_port == CHARGE_PORT_NONE) ? - CHG_RAMP_DISCONNECTED : CHG_RAMP_CHARGE_DETECT_DELAY; - CPRINTS("Ramp reset: st%d", ramp_st); - task_wake(TASK_ID_CHG_RAMP); - } -} - -int chg_ramp_get_current_limit(void) -{ - /* - * If we are ramping or stable, then use the active input - * current limit. Otherwise, use the minimum input current - * limit. - */ - switch (ramp_st) { - case CHG_RAMP_RAMP: - case CHG_RAMP_STABILIZE: - case CHG_RAMP_STABLE: - return active_icl; - default: - return min_icl; - } -} - -int chg_ramp_is_detected(void) -{ - /* Charger detected (charge detect delay has passed) */ - return ramp_st > CHG_RAMP_CHARGE_DETECT_DELAY; -} - -int chg_ramp_is_stable(void) -{ - return ramp_st == CHG_RAMP_STABLE; -} - -void chg_ramp_task(void *u) -{ - int task_wait_time = -1; - int i, lim; - uint64_t detect_end_time_us = 0, time_us; - int last_active_port = CHARGE_PORT_NONE; - - enum chg_ramp_state ramp_st_prev = CHG_RAMP_DISCONNECTED, - ramp_st_new = CHG_RAMP_DISCONNECTED; - int active_icl_new; - - /* Clear last OCP supplier to guarantee we ramp on first connect */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - oc_info[i][0].sup = CHARGE_SUPPLIER_NONE; - - /* - * Sleep until chg_ramp_charge_supplier_change is called to avoid - * setting input current limit to zero. chg_ramp_charge_supplier_change - * won't be called until charge_manager is ready to call - * board_set_charge_limit by itself (if there is no chg_ramp_task). - */ - if (!IS_ENABLED(TEST_BUILD)) - task_wait_event(-1); - - while (1) { - ramp_st_new = ramp_st; - active_icl_new = active_icl; - switch (ramp_st) { - case CHG_RAMP_DISCONNECTED: - /* Do nothing */ - task_wait_time = -1; - break; - case CHG_RAMP_CHARGE_DETECT_DELAY: - /* Delay for charge_manager to determine supplier */ - /* - * On entry to state, or if port changes, check - * timestamps to determine if this was likely an - * OC event (check if we lost VBUS and it came back - * within OC_RECOVER_MAX_TIME). - */ - if (ramp_st_prev != ramp_st || - active_port != last_active_port) { - last_active_port = active_port; - if (reg_time.val < - ACTIVE_OC_INFO.ts.val + - OC_RECOVER_MAX_TIME) { - ACTIVE_OC_INFO.oc_detected = 1; - } else { - for (i = 0; i < RAMP_COUNT; ++i) - oc_info[active_port][i]. - oc_detected = 0; - } - detect_end_time_us = get_time().val + - CHARGE_DETECT_DELAY; - task_wait_time = CHARGE_DETECT_DELAY; - break; - } - - /* If detect delay has not passed, set wait time */ - time_us = get_time().val; - if (time_us < detect_end_time_us) { - task_wait_time = detect_end_time_us - time_us; - break; - } - - /* Detect delay is over, fall through to next state */ - ramp_st_new = CHG_RAMP_OVERCURRENT_DETECT; - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); - case CHG_RAMP_OVERCURRENT_DETECT: - /* Check if we should ramp or go straight to stable */ - task_wait_time = SECOND; - - /* Skip ramp for specific suppliers */ - if (!chg_ramp_allowed(active_port, active_sup)) { - active_icl_new = min_icl; - ramp_st_new = CHG_RAMP_STABLE; - break; - } - - /* - * If we are not drawing full charge, then don't ramp, - * just wait in this state, until we are. - */ - if (!charge_is_consuming_full_input_current()) { - task_wait_time = CURRENT_DRAW_DELAY; - break; - } - - /* - * Compare recent OCP events, if all info matches, - * then we don't need to ramp anymore. - */ - for (i = 0; i < RAMP_COUNT; i++) { - if (oc_info[active_port][i].sup != active_sup || - !oc_info[active_port][i].oc_detected) - break; - } - - if (i == RAMP_COUNT) { - /* Found OC threshold! */ - active_icl_new = ACTIVE_OC_INFO.icl - - RAMP_ICL_BACKOFF; - ramp_st_new = CHG_RAMP_STABLE; - } else { - /* - * Need to ramp to find OC threshold, start - * at the minimum input current limit. - */ - active_icl_new = min_icl; - ramp_st_new = CHG_RAMP_RAMP; - } - break; - case CHG_RAMP_RAMP: - /* Keep ramping until we find the limit */ - task_wait_time = RAMP_CURR_DELAY; - - /* Pause ramping if we are not drawing full current */ - if (!charge_is_consuming_full_input_current()) { - task_wait_time = CURRENT_DRAW_DELAY; - break; - } - - /* If VBUS is sagging a lot, then stop ramping */ - if (board_is_vbus_too_low(active_port, - CHG_RAMP_VBUS_RAMPING)) { - CPRINTS("VBUS low"); - active_icl_new = MAX(min_icl, active_icl - - RAMP_ICL_BACKOFF); - ramp_st_new = CHG_RAMP_STABILIZE; - task_wait_time = STABLIZE_DELAY; - stablize_port = active_port; - stablize_sup = active_sup; - break; - } - - /* Ramp the current limit if we haven't reached max */ - if (active_icl == max_icl) - ramp_st_new = CHG_RAMP_STABLE; - else if (active_icl + RAMP_CURR_INCR_MA > max_icl) - active_icl_new = max_icl; - else - active_icl_new = active_icl + RAMP_CURR_INCR_MA; - break; - case CHG_RAMP_STABILIZE: - /* Wait for current to stabilize after ramp is done */ - /* Use default delay for exiting this state */ - task_wait_time = SECOND; - if (active_port == stablize_port && - active_sup == stablize_sup) { - ramp_st_new = CHG_RAMP_STABLE; - break; - } - - ramp_st_new = active_port == CHARGE_PORT_NONE ? - CHG_RAMP_DISCONNECTED : - CHG_RAMP_CHARGE_DETECT_DELAY; - break; - case CHG_RAMP_STABLE: - /* Maintain input current limit */ - /* On entry log charging stats */ - if (ramp_st_prev != ramp_st) { -#ifdef CONFIG_USB_PD_LOGGING - charge_manager_save_log(active_port); -#endif - /* notify host of power info change */ - pd_send_host_event(PD_EVENT_POWER_CHANGE); - } - - /* Keep an eye on VBUS and restart ramping if it dips */ - if (chg_ramp_allowed(active_port, active_sup) && - board_is_vbus_too_low(active_port, - CHG_RAMP_VBUS_STABLE)) { - CPRINTS("VBUS low; Re-ramp"); - max_icl = MAX(min_icl, - max_icl - RAMP_ICL_BACKOFF); - active_icl_new = min_icl; - ramp_st_new = CHG_RAMP_RAMP; - } - task_wait_time = STABLE_VBUS_MONITOR_INTERVAL; - break; - } - - ramp_st_prev = ramp_st; - ramp_st = ramp_st_new; - active_icl = active_icl_new; - - /* Skip setting limit if status is stable twice in a row */ - if (ramp_st_prev != CHG_RAMP_STABLE || - ramp_st != CHG_RAMP_STABLE) { - CPRINTS("Ramp p%d st%d %dmA %dmA", - active_port, ramp_st, min_icl, active_icl); - /* Set the input current limit */ - lim = chg_ramp_get_current_limit(); - board_set_charge_limit(active_port, active_sup, lim, - lim, active_vtg); - } - - if (ramp_st == CHG_RAMP_STABILIZE) - /* - * When in stabilize state, supplier/port may change - * and we don't want to wake up task until we have - * slept this amount of time. - */ - usleep(task_wait_time); - else - task_wait_event(task_wait_time); - } -} - -#ifdef CONFIG_CMD_CHGRAMP -static int command_chgramp(int argc, char **argv) -{ - int i; - int port; - - ccprintf("Chg Ramp:\nState: %d\nMin ICL: %d\nActive ICL: %d\n", - ramp_st, min_icl, active_icl); - - for (port = 0; port < board_get_usb_pd_port_count(); port++) { - ccprintf("Port %d:\n", port); - ccprintf(" OC idx:%d\n", oc_info_idx[port]); - for (i = 0; i < RAMP_COUNT; i++) { - ccprintf(" OC %d: s%d oc_det%d icl%d\n", i, - oc_info[port][i].sup, - oc_info[port][i].oc_detected, - oc_info[port][i].icl); - } - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chgramp, command_chgramp, - "", - "Dump charge ramp state info"); -#endif diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c deleted file mode 100644 index 110d63c7bf..0000000000 --- a/common/charge_state_v2.c +++ /dev/null @@ -1,3108 +0,0 @@ -/* Copyright 2014 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. - * - * Battery charging task and state machine. - */ - -#include "battery.h" -#include "battery_smart.h" -#include "charge_manager.h" -#include "charger_profile_override.h" -#include "charge_state.h" -#include "charger.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "ec_ec_comm_client.h" -#include "ec_ec_comm_server.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "i2c.h" -#include "math_util.h" -#include "power.h" -#include "printf.h" -#include "system.h" -#include "task.h" -#include "throttle_ap.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHARGER, outstr) -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) - -/* Extra debugging prints when allocating power between lid and base. */ -#undef CHARGE_ALLOCATE_EXTRA_DEBUG - -#define CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US \ - (CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT * SECOND) -#define PRECHARGE_TIMEOUT_US (PRECHARGE_TIMEOUT * SECOND) -#define LFCC_EVENT_THRESH 5 /* Full-capacity change reqd for host event */ - -#ifdef CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT -#ifndef CONFIG_HOSTCMD_EVENTS -#error "CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT needs CONFIG_HOSTCMD_EVENTS" -#endif /* CONFIG_HOSTCMD_EVENTS */ -#define BAT_OCP_TIMEOUT_US (60 * SECOND) -/* BAT_OCP_HYSTERESIS_PCT can be optionally overridden in board.h. */ -#ifndef BAT_OCP_HYSTERESIS_PCT -#define BAT_OCP_HYSTERESIS_PCT 10 -#endif /* BAT_OCP_HYSTERESIS_PCT */ -#define BAT_OCP_HYSTERESIS \ - (BAT_MAX_DISCHG_CURRENT * BAT_OCP_HYSTERESIS_PCT / 100) /* mA */ -#endif /* CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT */ - -#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE -#ifndef CONFIG_HOSTCMD_EVENTS -#error "CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE needs CONFIG_HOSTCMD_EVENTS" -#endif /* CONFIG_HOSTCMD_EVENTS */ -#define BAT_UVP_TIMEOUT_US (60 * SECOND) -/* BAT_UVP_HYSTERESIS_PCT can be optionally overridden in board.h. */ -#ifndef BAT_UVP_HYSTERESIS_PCT -#define BAT_UVP_HYSTERESIS_PCT 3 -#endif /* BAT_UVP_HYSTERESIS_PCT */ -#define BAT_UVP_HYSTERESIS \ - (BAT_LOW_VOLTAGE_THRESH * BAT_UVP_HYSTERESIS_PCT / 100) /* mV */ -static timestamp_t uvp_throttle_start_time; -#endif /* CONFIG_THROTTLE_AP_ON_BAT_OLTAGE */ - -static int charge_request(int voltage, int current); - -static uint8_t battery_level_shutdown; - -/* - * State for charger_task(). Here so we can reset it on a HOOK_INIT, and - * because stack space is more limited than .bss - */ -static const struct battery_info *batt_info; -static struct charge_state_data curr; -static enum charge_state_v2 prev_state; -static int prev_ac, prev_charge, prev_full, prev_disp_charge; -static enum battery_present prev_bp; -static int is_full; /* battery not accepting current */ -static enum ec_charge_control_mode chg_ctl_mode; -static int manual_voltage; /* Manual voltage override (-1 = no override) */ -static int manual_current; /* Manual current override (-1 = no override) */ -static unsigned int user_current_limit = -1U; -test_export_static timestamp_t shutdown_target_time; -static timestamp_t precharge_start_time; -static struct sustain_soc sustain_soc; - -/* - * The timestamp when the battery charging current becomes stable. - * When a new charging status happens, charger needs several seconds to - * stabilize the battery charging current. - * stable_current should be evaluated when stable_ts expired. - * stable_ts should be reset if the charger input voltage/current changes, - * or a new battery charging voltage/request happened. - * By evaluating stable_current, we can evaluate the battery's desired charging - * power desired_mw. This allow us to have a better charging efficiency by - * negotiating the most fit PDO, i.e. the PDO provides the power just enough for - * the system and battery, or the PDO with preferred voltage. - */ -STATIC_IF(CONFIG_USB_PD_PREFER_MV) timestamp_t stable_ts; -/* battery charging current evaluated after stable_ts expired */ -STATIC_IF(CONFIG_USB_PD_PREFER_MV) int stable_current; -/* battery desired power in mW. This is used to negotiate the suitable PDO */ -STATIC_IF(CONFIG_USB_PD_PREFER_MV) int desired_mw; -STATIC_IF_NOT(CONFIG_USB_PD_PREFER_MV) struct pd_pref_config_t pd_pref_config; - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT -static int base_connected; -/* Base has responded to one of our commands already. */ -static int base_responsive; -static int charge_base; -static int prev_charge_base; -static int prev_current_base; -static int prev_allow_charge_base; -static int prev_current_lid; - -/* - * In debugging mode, with AC, input current to allocate to base. Negative - * value disables manual mode. - */ -static int manual_ac_current_base = -1; -/* - * In debugging mode, when discharging, current to transfer from lid to base - * (negative to transfer from base to lid). Only valid when enabled is true. - */ -static int manual_noac_enabled; -static int manual_noac_current_base; -#else -static const int base_connected; -#endif - -/* Is battery connected but unresponsive after precharge? */ -static int battery_seems_dead; - -static int battery_seems_disconnected; - -/* - * Was battery removed? Set when we see BP_NO, cleared after the battery is - * reattached and becomes responsive. Used to indicate an error state after - * removal and trigger re-reading the battery static info when battery is - * reattached and responsive. - */ -static int battery_was_removed; - -static int problems_exist; -static int debugging; - - -/* Track problems in communicating with the battery or charger */ -enum problem_type { - PR_STATIC_UPDATE, - PR_SET_VOLTAGE, - PR_SET_CURRENT, - PR_SET_MODE, - PR_SET_INPUT_CURR, - PR_POST_INIT, - PR_CHG_FLAGS, - PR_BATT_FLAGS, - PR_CUSTOM, - PR_CFG_SEC_CHG, - - NUM_PROBLEM_TYPES -}; -static const char * const prob_text[] = { - "static update", - "set voltage", - "set current", - "set mode", - "set input current", - "post init", - "chg params", - "batt params", - "custom profile", - "cfg secondary chg" -}; -BUILD_ASSERT(ARRAY_SIZE(prob_text) == NUM_PROBLEM_TYPES); - -/* - * TODO(crosbug.com/p/27639): When do we decide a problem is real and not - * just intermittent? And what do we do about it? - */ -static void problem(enum problem_type p, int v) -{ - static int __bss_slow last_prob_val[NUM_PROBLEM_TYPES]; - static timestamp_t __bss_slow last_prob_time[NUM_PROBLEM_TYPES]; - timestamp_t t_now, t_diff; - - if (last_prob_val[p] != v) { - t_now = get_time(); - t_diff.val = t_now.val - last_prob_time[p].val; - CPRINTS("charge problem: %s, 0x%x -> 0x%x after %.6" PRId64 "s", - prob_text[p], last_prob_val[p], v, t_diff.val); - last_prob_val[p] = v; - last_prob_time[p] = t_now; - } - problems_exist = 1; -} - -test_export_static enum ec_charge_control_mode get_chg_ctrl_mode(void) -{ - return chg_ctl_mode; -} - -static int battery_sustainer_set(int8_t lower, int8_t upper) -{ - if (lower == -1 || upper == -1) { - CPRINTS("Sustain mode disabled"); - sustain_soc.lower = -1; - sustain_soc.upper = -1; - return EC_SUCCESS; - } - - if (lower <= upper && 0 <= lower && upper <= 100) { - /* Currently sustainer requires discharge_on_ac. */ - if (!IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC)) - return EC_RES_UNAVAILABLE; - sustain_soc.lower = lower; - sustain_soc.upper = upper; - return EC_SUCCESS; - } - - CPRINTS("Invalid param: %s(%d, %d)", __func__, lower, upper); - return EC_ERROR_INVAL; -} - -static void battery_sustainer_disable(void) -{ - battery_sustainer_set(-1, -1); -} - -static bool battery_sustainer_enabled(void) -{ - return sustain_soc.lower != -1 && sustain_soc.upper != -1; -} - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT -/* - * Parameters for dual-battery policy. - * TODO(b:71881017): This should be made configurable by AP in the future. - */ -struct dual_battery_policy { - /*** Policies when AC is not connected. ***/ - /* Voltage to use when using OTG mode between lid and base (mV) */ - uint16_t otg_voltage; - /* Maximum current to apply from base to lid (mA) */ - uint16_t max_base_to_lid_current; - /* - * Margin to apply between provided OTG output current and input current - * limit, to make sure that input charger does not overcurrent output - * charger. input_current = (1-margin) * output_current. (/128) - */ - uint8_t margin_otg_current; - - /* Only do base to lid OTG when base battery above this value (%) */ - uint8_t min_charge_base_otg; - - /* - * When base/lid battery percentage is below this value, do - * battery-to-battery charging. (%) - */ - uint8_t max_charge_base_batt_to_batt; - uint8_t max_charge_lid_batt_to_batt; - - /*** Policies when AC is connected. ***/ - /* Minimum power to allocate to base (mW), includes some margin to allow - * base to charge when critically low. - */ - uint16_t min_base_system_power; - - /* Smoothing factor for lid power (/128) */ - uint8_t lid_system_power_smooth; - /* - * Smoothing factor for base/lid battery power, when the battery power - * is decreasing only: we try to estimate the maximum power that the - * battery is willing to take and always reset it when it draws more - * than the estimate. (/128) - */ - uint8_t battery_power_smooth; - - /* - * Margin to add to requested base/lid battery power, to figure out how - * much current to allocate. allocation = (1+margin) * request. (/128) - */ - uint8_t margin_base_battery_power; - uint8_t margin_lid_battery_power; - - /* Maximum current to apply from lid to base (mA) */ - uint16_t max_lid_to_base_current; -}; - -static const struct dual_battery_policy db_policy = { - .otg_voltage = 12000, /* mV */ - .max_base_to_lid_current = 1800, /* mA, about 2000mA with margin. */ - .margin_otg_current = 13, /* /128 = 10.1% */ - .min_charge_base_otg = 5, /* % */ - .max_charge_base_batt_to_batt = 4, /* % */ - .max_charge_lid_batt_to_batt = 10, /* % */ - .min_base_system_power = 1300, /* mW */ - .lid_system_power_smooth = 32, /* 32/128 = 0.25 */ - .battery_power_smooth = 1, /* 1/128 = 0.008 */ - .margin_base_battery_power = 32, /* 32/128 = 0.25 */ - .margin_lid_battery_power = 32, /* 32/128 = 0.25 */ - .max_lid_to_base_current = 2000, /* mA */ -}; - -/* Add at most "value" to power_var, subtracting from total_power budget. */ -#define CHG_ALLOCATE(power_var, total_power, value) do { \ - int val_capped = MIN(value, total_power); \ - (power_var) += val_capped; \ - (total_power) -= val_capped; \ -} while (0) - -/* Update base battery information */ -static void update_base_battery_info(void) -{ - struct ec_response_battery_dynamic_info *const bd = - &battery_dynamic[BATT_IDX_BASE]; - - base_connected = board_is_base_connected(); - - if (!base_connected) { - const int invalid_flags = EC_BATT_FLAG_INVALID_DATA; - /* Invalidate static/dynamic information */ - if (bd->flags != invalid_flags) { - bd->flags = invalid_flags; - - host_set_single_event(EC_HOST_EVENT_BATTERY); - host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); - } - charge_base = -1; - base_responsive = 0; - prev_current_base = 0; - prev_allow_charge_base = 0; - } else if (base_responsive) { - int old_flags = bd->flags; - int flags_changed; - int old_full_capacity = bd->full_capacity; - - ec_ec_client_base_get_dynamic_info(); - flags_changed = (old_flags != bd->flags); - /* Fetch static information when flags change. */ - if (flags_changed) - ec_ec_client_base_get_static_info(); - - battery_memmap_refresh(BATT_IDX_BASE); - - /* Newly connected battery, or change in capacity. */ - if (old_flags & EC_BATT_FLAG_INVALID_DATA || - ((old_flags & EC_BATT_FLAG_BATT_PRESENT) != - (bd->flags & EC_BATT_FLAG_BATT_PRESENT)) || - old_full_capacity != bd->full_capacity) - host_set_single_event(EC_HOST_EVENT_BATTERY); - - if (flags_changed) - host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); - - /* Update charge_base */ - if (bd->flags & (BATT_FLAG_BAD_FULL_CAPACITY | - BATT_FLAG_BAD_REMAINING_CAPACITY)) - charge_base = -1; - else if (bd->full_capacity > 0) - charge_base = 100 * bd->remaining_capacity - / bd->full_capacity; - else - charge_base = 0; - } -} - -/** - * Setup current settings for base, and record previous values, if the base - * is responsive. - * - * @param current_base Current to be drawn by base (negative to provide power) - * @param allow_charge_base Whether base battery should be charged (only makes - * sense with positive current) - */ -static int set_base_current(int current_base, int allow_charge_base) -{ - /* "OTG" voltage from base to lid. */ - const int otg_voltage = db_policy.otg_voltage; - int ret; - - ret = ec_ec_client_base_charge_control(current_base, - otg_voltage, allow_charge_base); - if (ret) { - /* Ignore errors until the base is responsive. */ - if (base_responsive) - return ret; - } else { - base_responsive = 1; - prev_current_base = current_base; - prev_allow_charge_base = allow_charge_base; - } - - return EC_RES_SUCCESS; -} - -/** - * Setup current settings for lid and base, in a safe way. - * - * @param current_base Current to be drawn by base (negative to provide power) - * @param allow_charge_base Whether base battery should be charged (only makes - * sense with positive current) - * @param current_lid Current to be drawn by lid (negative to provide power) - * @param allow_charge_lid Whether lid battery should be charged - */ -static void set_base_lid_current(int current_base, int allow_charge_base, - int current_lid, int allow_charge_lid) -{ - /* "OTG" voltage from lid to base. */ - const int otg_voltage = db_policy.otg_voltage; - - int lid_first; - int ret; - int chgnum = 0; - - /* TODO(b:71881017): This is still quite verbose during charging. */ - if (prev_current_base != current_base || - prev_allow_charge_base != allow_charge_base || - prev_current_lid != current_lid) { - CPRINTS("Base/Lid: %d%s/%d%s mA", - current_base, allow_charge_base ? "+" : "", - current_lid, allow_charge_lid ? "+" : ""); - } - - /* - * To decide whether to first control the lid or the base, we first - * control the side that _reduces_ current that would be drawn, then - * setup one that would start providing power, then increase current. - */ - if (current_lid >= 0 && current_lid < prev_current_lid) - lid_first = 1; /* Lid decreases current */ - else if (current_base >= 0 && current_base < prev_current_base) - lid_first = 0; /* Base decreases current */ - else if (current_lid < 0) - lid_first = 1; /* Lid provide power */ - else - lid_first = 0; /* All other cases: control the base first */ - - if (!lid_first && base_connected) { - ret = set_base_current(current_base, allow_charge_base); - if (ret) - return; - } - - if (current_lid >= 0) { - ret = charge_set_output_current_limit(CHARGER_SOLO, 0, 0); - if (ret) - return; - ret = charger_set_input_current_limit(chgnum, current_lid); - if (ret) - return; - if (allow_charge_lid) - ret = charge_request(curr.requested_voltage, - curr.requested_current); - else - ret = charge_request(0, 0); - } else { - ret = charge_set_output_current_limit(CHARGER_SOLO, - -current_lid, otg_voltage); - } - - if (ret) - return; - - prev_current_lid = current_lid; - - if (lid_first && base_connected) { - ret = set_base_current(current_base, allow_charge_base); - if (ret) - return; - } - - /* - * Make sure cross-power is enabled (it might not be enabled right after - * plugging the base, or when an adapter just got connected). - */ - if (base_connected && current_base != 0) - board_enable_base_power(1); -} - -/** - * Smooth power value, covering some edge cases. - * Compute s*curr+(1-s)*prev, where s is in 1/128 unit. - */ -static int smooth_value(int prev, int curr, int s) -{ - if (curr < 0) - curr = 0; - if (prev < 0) - return curr; - - return prev + s * (curr - prev) / 128; -} - -/** - * Add margin m to value. Compute (1+m)*value, where m is in 1/128 unit. - */ -static int add_margin(int value, int m) -{ - return value + m * value / 128; -} - -static void charge_allocate_input_current_limit(void) -{ - /* - * All the power numbers are in mW. - * - * Since we work with current and voltage in mA and mV, multiplying them - * gives numbers in uW, which are dangerously close to overflowing when - * doing intermediate computations (60W * 100 overflows a 32-bit int, - * for example). We therefore divide the product by 1000 and re-multiply - * the power numbers by 1000 when converting them back to current. - */ - int total_power = 0; - - static int prev_base_battery_power = -1; - int base_battery_power = 0; - int base_battery_power_max = 0; - - static int prev_lid_system_power = -1; - int lid_system_power; - - static int prev_lid_battery_power = -1; - int lid_battery_power = 0; - int lid_battery_power_max = 0; - - int power_base = 0; - int power_lid = 0; - - int current_base = 0; - int current_lid = 0; - - int charge_lid = charge_get_percent(); - - const struct ec_response_battery_dynamic_info *const base_bd = - &battery_dynamic[BATT_IDX_BASE]; - - - if (!base_connected) { - set_base_lid_current(0, 0, curr.desired_input_current, 1); - prev_base_battery_power = -1; - return; - } - - /* Charging */ - if (curr.desired_input_current > 0 && curr.input_voltage > 0) - total_power = - curr.desired_input_current * curr.input_voltage / 1000; - - /* - * TODO(b:71723024): We should be able to replace this test by curr.ac, - * but the value is currently wrong, especially during transitions. - */ - if (total_power <= 0) { - int base_critical = charge_base >= 0 && - charge_base < db_policy.max_charge_base_batt_to_batt; - - /* Discharging */ - prev_base_battery_power = -1; - prev_lid_system_power = -1; - prev_lid_battery_power = -1; - - /* Manual control */ - if (manual_noac_enabled) { - int lid_current, base_current; - - if (manual_noac_current_base > 0) { - base_current = -manual_noac_current_base; - lid_current = - add_margin(manual_noac_current_base, - db_policy.margin_otg_current); - } else { - lid_current = manual_noac_current_base; - base_current = - add_margin(-manual_noac_current_base, - db_policy.margin_otg_current); - } - - set_base_lid_current(base_current, 0, lid_current, 0); - return; - } - - /* - * System is off, cut power to the base. We'll reset the base - * when system restarts, or when AC is plugged. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - set_base_lid_current(0, 0, 0, 0); - if (base_responsive) { - /* Base still responsive, put it to sleep. */ - CPRINTF("Hibernating base\n"); - ec_ec_client_hibernate(); - base_responsive = 0; - board_enable_base_power(0); - } - return; - } - - /* - * System is suspended, let the lid and base run on their - * own power. However, if the base battery is critically low, we - * still want to provide power to the base, to make sure it - * stays alive to be able to wake the system on keyboard or - * touchpad events. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) && - !base_critical) { - set_base_lid_current(0, 0, 0, 0); - return; - } - - if (charge_base > db_policy.min_charge_base_otg) { - int lid_current = db_policy.max_base_to_lid_current; - int base_current = add_margin(lid_current, - db_policy.margin_otg_current); - /* Draw current from base to lid */ - set_base_lid_current(-base_current, 0, lid_current, - charge_lid < db_policy.max_charge_lid_batt_to_batt); - } else { - /* - * Base battery is too low, apply power to it, and allow - * it to charge if it is critically low. - * - * TODO(b:71881017): When suspended, this will make the - * battery charge oscillate between 3 and 4 percent, - * which might not be great for battery life. We need - * some hysteresis. - */ - /* - * TODO(b:71881017): Precompute (ideally, at build time) - * the base_current, so we do not need to do a division - * here. - */ - int base_current = - (db_policy.min_base_system_power * 1000) / - db_policy.otg_voltage; - int lid_current = add_margin(base_current, - db_policy.margin_otg_current); - - set_base_lid_current(base_current, base_critical, - -lid_current, 0); - } - - return; - } - - /* Manual control */ - if (manual_ac_current_base >= 0) { - int current_base = manual_ac_current_base; - int current_lid = - curr.desired_input_current - manual_ac_current_base; - - if (current_lid < 0) { - current_base = curr.desired_input_current; - current_lid = 0; - } - - set_base_lid_current(current_base, 1, current_lid, 1); - return; - } - - /* Estimate system power. */ - lid_system_power = charger_get_system_power() / 1000; - - /* Smooth system power, as it is very spiky */ - lid_system_power = smooth_value(prev_lid_system_power, - lid_system_power, db_policy.lid_system_power_smooth); - prev_lid_system_power = lid_system_power; - - /* - * TODO(b:71881017): Smoothing the battery power isn't necessarily a - * good idea: if the system takes up too much power, we may reduce the - * estimate power too quickly, leading to oscillations when the system - * power goes down. Instead, we should probably estimate the current - * based on remaining capacity. - */ - /* Estimate lid battery power. */ - if (!(curr.batt.flags & - (BATT_FLAG_BAD_VOLTAGE | BATT_FLAG_BAD_CURRENT))) - lid_battery_power = curr.batt.current * - curr.batt.voltage / 1000; - if (lid_battery_power < prev_lid_battery_power) - lid_battery_power = smooth_value(prev_lid_battery_power, - lid_battery_power, db_policy.battery_power_smooth); - if (!(curr.batt.flags & - (BATT_FLAG_BAD_DESIRED_VOLTAGE | - BATT_FLAG_BAD_DESIRED_CURRENT))) - lid_battery_power_max = curr.batt.desired_current * - curr.batt.desired_voltage / 1000; - - lid_battery_power = MIN(lid_battery_power, lid_battery_power_max); - - /* Estimate base battery power. */ - if (!(base_bd->flags & EC_BATT_FLAG_INVALID_DATA)) { - base_battery_power = base_bd->actual_current * - base_bd->actual_voltage / 1000; - base_battery_power_max = base_bd->desired_current * - base_bd->desired_voltage / 1000; - } - if (base_battery_power < prev_base_battery_power) - base_battery_power = smooth_value(prev_base_battery_power, - base_battery_power, db_policy.battery_power_smooth); - base_battery_power = MIN(base_battery_power, base_battery_power_max); - - if (debugging) { - CPRINTF("%s:\n", __func__); - CPRINTF("total power: %d\n", total_power); - CPRINTF("base battery power: %d (%d)\n", - base_battery_power, base_battery_power_max); - CPRINTF("lid system power: %d\n", lid_system_power); - CPRINTF("lid battery power: %d\n", lid_battery_power); - CPRINTF("percent base/lid: %d%% %d%%\n", - charge_base, charge_lid); - } - - prev_lid_battery_power = lid_battery_power; - prev_base_battery_power = base_battery_power; - - if (total_power > 0) { /* Charging */ - /* Allocate system power */ - CHG_ALLOCATE(power_base, total_power, - db_policy.min_base_system_power); - CHG_ALLOCATE(power_lid, total_power, lid_system_power); - - /* Allocate lid, then base battery power */ - lid_battery_power = add_margin(lid_battery_power, - db_policy.margin_lid_battery_power); - CHG_ALLOCATE(power_lid, total_power, lid_battery_power); - - base_battery_power = add_margin(base_battery_power, - db_policy.margin_base_battery_power); - CHG_ALLOCATE(power_base, total_power, base_battery_power); - - /* Give everything else to the lid. */ - CHG_ALLOCATE(power_lid, total_power, total_power); - if (debugging) - CPRINTF("power: base %d mW / lid %d mW\n", - power_base, power_lid); - - current_base = 1000 * power_base / curr.input_voltage; - current_lid = 1000 * power_lid / curr.input_voltage; - - if (current_base > db_policy.max_lid_to_base_current) { - current_lid += (current_base - - db_policy.max_lid_to_base_current); - current_base = db_policy.max_lid_to_base_current; - } - - if (debugging) - CPRINTF("current: base %d mA / lid %d mA\n", - current_base, current_lid); - - set_base_lid_current(current_base, 1, current_lid, 1); - } else { /* Discharging */ - } - - if (debugging) - CPRINTF("====\n"); -} -#endif /* CONFIG_EC_EC_COMM_BATTERY_CLIENT */ - -#ifndef CONFIG_BATTERY_V2 -/* Returns zero if every item was updated. */ -static int update_static_battery_info(void) -{ - char *batt_str; - int batt_serial; - uint8_t batt_flags = 0; - /* - * The return values have type enum ec_error_list, but EC_SUCCESS is - * zero. We'll just look for any failures so we can try them all again. - */ - int rv; - - /* Smart battery serial number is 16 bits */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL); - memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); - rv = battery_serial_number(&batt_serial); - if (!rv) - snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X", batt_serial); - - /* Design Capacity of Full */ - rv |= battery_design_capacity( - (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP)); - - /* Design Voltage */ - rv |= battery_design_voltage( - (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT)); - - /* Last Full Charge Capacity (this is only mostly static) */ - rv |= battery_full_charge_capacity( - (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)); - - /* Cycle Count */ - rv |= battery_cycle_count((int *)host_get_memmap(EC_MEMMAP_BATT_CCNT)); - - /* Battery Manufacturer string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR); - memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); - rv |= battery_manufacturer_name(batt_str, EC_MEMMAP_TEXT_MAX); - - /* Battery Model string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL); - memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); - rv |= battery_device_name(batt_str, EC_MEMMAP_TEXT_MAX); - - /* Battery Type string */ - batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE); - rv |= battery_device_chemistry(batt_str, EC_MEMMAP_TEXT_MAX); - - /* Zero the dynamic entries. They'll come next. */ - *(int *)host_get_memmap(EC_MEMMAP_BATT_VOLT) = 0; - *(int *)host_get_memmap(EC_MEMMAP_BATT_RATE) = 0; - *(int *)host_get_memmap(EC_MEMMAP_BATT_CAP) = 0; - *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = 0; - if (extpower_is_present()) - batt_flags |= EC_BATT_FLAG_AC_PRESENT; - *host_get_memmap(EC_MEMMAP_BATT_FLAG) = batt_flags; - - if (rv) - problem(PR_STATIC_UPDATE, rv); - else - /* No errors seen. Battery data is now present */ - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 1; - - return rv; -} - -static void update_dynamic_battery_info(void) -{ - /* The memmap address is constant. We should fix these calls somehow. */ - int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT); - int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE); - int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP); - int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC); - uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); - uint8_t tmp; - int send_batt_status_event = 0; - int send_batt_info_event = 0; - static int __bss_slow batt_present; - - tmp = 0; - if (curr.ac) - tmp |= EC_BATT_FLAG_AC_PRESENT; - - if (curr.batt.is_present == BP_YES) { - tmp |= EC_BATT_FLAG_BATT_PRESENT; - batt_present = 1; - /* Tell the AP to read battery info if it is newly present. */ - if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)) - send_batt_info_event++; - } else { - /* - * Require two consecutive updates with BP_NOT_SURE - * before reporting it gone to the host. - */ - if (batt_present) - tmp |= EC_BATT_FLAG_BATT_PRESENT; - else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT) - send_batt_info_event++; - batt_present = 0; - } - - if (curr.batt.flags & EC_BATT_FLAG_INVALID_DATA) - tmp |= EC_BATT_FLAG_INVALID_DATA; - - if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE)) - *memmap_volt = curr.batt.voltage; - - if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT)) - *memmap_rate = ABS(curr.batt.current); - - if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) { - /* - * If we're running off the battery, it must have some charge. - * Don't report zero charge, as that has special meaning - * to Chrome OS powerd. - */ - if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging) - *memmap_cap = 1; - else - *memmap_cap = curr.batt.remaining_capacity; - } - - if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) && - (curr.batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) || - curr.batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) { - *memmap_lfcc = curr.batt.full_capacity; - /* Poke the AP if the full_capacity changes. */ - send_batt_info_event++; - } - - if (curr.batt.is_present == BP_YES && - !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && - curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL) - tmp |= EC_BATT_FLAG_LEVEL_CRITICAL; - - tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING : - EC_BATT_FLAG_DISCHARGING; - - /* Tell the AP to re-read battery status if charge state changes */ - if (*memmap_flags != tmp) - send_batt_status_event++; - - /* Update flags before sending host events. */ - *memmap_flags = tmp; - - if (send_batt_info_event) - host_set_single_event(EC_HOST_EVENT_BATTERY); - if (send_batt_status_event) - host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); -} -#else /* CONFIG_BATTERY_V2 */ - -static int is_battery_string_reliable(const char *buf) -{ - /* - * From is_string_printable rule, 0xFF is not printable. - * So, EC should think battery string is unreliable if string - * include 0xFF. - */ - while (*buf) { - if ((*buf) == 0xFF) - return 0; - buf++; - } - - return 1; -} - -static int update_static_battery_info(void) -{ - int batt_serial; - int val; - /* - * The return values have type enum ec_error_list, but EC_SUCCESS is - * zero. We'll just look for any failures so we can try them all again. - */ - int rv, ret; - - struct ec_response_battery_static_info_v1 *const bs = - &battery_static[BATT_IDX_MAIN]; - - /* Clear all static information. */ - memset(bs, 0, sizeof(*bs)); - - /* Smart battery serial number is 16 bits */ - rv = battery_serial_number(&batt_serial); - if (!rv) - snprintf(bs->serial_ext, sizeof(bs->serial_ext), - "%04X", batt_serial); - - /* Design Capacity of Full */ - ret = battery_design_capacity(&val); - if (!ret) - bs->design_capacity = val; - rv |= ret; - - /* Design Voltage */ - ret = battery_design_voltage(&val); - if (!ret) - bs->design_voltage = val; - rv |= ret; - - /* Cycle Count */ - ret = battery_cycle_count(&val); - if (!ret) - bs->cycle_count = val; - rv |= ret; - - /* Battery Manufacturer string */ - rv |= battery_manufacturer_name(bs->manufacturer_ext, - sizeof(bs->manufacturer_ext)); - - /* Battery Model string */ - rv |= battery_device_name(bs->model_ext, sizeof(bs->model_ext)); - - /* Battery Type string */ - rv |= battery_device_chemistry(bs->type_ext, sizeof(bs->type_ext)); - - /* - * b/181639264: Battery gauge follow SMBus SPEC and SMBus define - * cumulative clock low extend time for both controller (master) and - * peripheral (slave). However, I2C doesn't. - * Regarding this issue, we observe EC sometimes pull I2C CLK low - * a while after EC start running. Actually, we are not sure the - * reason until now. - * If EC pull I2C CLK low too long, and it may cause battery fw timeout - * because battery count cumulative clock extend time over 25ms. - * When it happened, battery will release both its CLK and DATA and - * reset itself. So, EC may get 0xFF when EC keep reading data from - * battery. Battery static information will be unreliable and need to - * be updated. - * This change is improvement that EC should retry if battery string is - * unreliable. - */ - if (!is_battery_string_reliable(bs->serial_ext) || - !is_battery_string_reliable(bs->manufacturer_ext) || - !is_battery_string_reliable(bs->model_ext) || - !is_battery_string_reliable(bs->type_ext)) - rv |= EC_ERROR_UNKNOWN; - - /* Zero the dynamic entries. They'll come next. */ - memset(&battery_dynamic[BATT_IDX_MAIN], 0, - sizeof(battery_dynamic[BATT_IDX_MAIN])); - - if (rv) - problem(PR_STATIC_UPDATE, rv); - -#ifdef HAS_TASK_HOSTCMD - battery_memmap_refresh(BATT_IDX_MAIN); -#endif - - return rv; -} - -static void update_dynamic_battery_info(void) -{ - static int __bss_slow batt_present; - uint8_t tmp; - int send_batt_status_event = 0; - int send_batt_info_event = 0; - - struct ec_response_battery_dynamic_info *const bd = - &battery_dynamic[BATT_IDX_MAIN]; - - tmp = 0; - if (curr.ac) - tmp |= EC_BATT_FLAG_AC_PRESENT; - - if (curr.batt.is_present == BP_YES) { - tmp |= EC_BATT_FLAG_BATT_PRESENT; - batt_present = 1; - /* Tell the AP to read battery info if it is newly present. */ - if (!(bd->flags & EC_BATT_FLAG_BATT_PRESENT)) - send_batt_info_event++; - } else { - /* - * Require two consecutive updates with BP_NOT_SURE - * before reporting it gone to the host. - */ - if (batt_present) - tmp |= EC_BATT_FLAG_BATT_PRESENT; - else if (bd->flags & EC_BATT_FLAG_BATT_PRESENT) - send_batt_info_event++; - batt_present = 0; - } - - if (curr.batt.flags & EC_BATT_FLAG_INVALID_DATA) - tmp |= EC_BATT_FLAG_INVALID_DATA; - - if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE)) - bd->actual_voltage = curr.batt.voltage; - - if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT)) - bd->actual_current = curr.batt.current; - - if (!(curr.batt.flags & BATT_FLAG_BAD_DESIRED_VOLTAGE)) - bd->desired_voltage = curr.batt.desired_voltage; - - if (!(curr.batt.flags & BATT_FLAG_BAD_DESIRED_CURRENT)) - bd->desired_current = curr.batt.desired_current; - - if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) { - /* - * If we're running off the battery, it must have some charge. - * Don't report zero charge, as that has special meaning - * to Chrome OS powerd. - */ - if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging) - bd->remaining_capacity = 1; - else - bd->remaining_capacity = curr.batt.remaining_capacity; - } - - if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) && - (curr.batt.full_capacity <= - (bd->full_capacity - LFCC_EVENT_THRESH) || - curr.batt.full_capacity >= - (bd->full_capacity + LFCC_EVENT_THRESH))) { - bd->full_capacity = curr.batt.full_capacity; - /* Poke the AP if the full_capacity changes. */ - send_batt_info_event++; - } - - if (curr.batt.is_present == BP_YES && - !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && - curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL) - tmp |= EC_BATT_FLAG_LEVEL_CRITICAL; - - tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING : - EC_BATT_FLAG_DISCHARGING; - - /* Tell the AP to re-read battery status if charge state changes */ - if (bd->flags != tmp) - send_batt_status_event++; - - bd->flags = tmp; - -#ifdef HAS_TASK_HOSTCMD - battery_memmap_refresh(BATT_IDX_MAIN); -#endif - -#ifdef CONFIG_HOSTCMD_EVENTS - if (send_batt_info_event) - host_set_single_event(EC_HOST_EVENT_BATTERY); - if (send_batt_status_event) - host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); -#endif -} -#endif /* CONFIG_BATTERY_V2 */ - -static const char * const state_list[] = { - "idle", "discharge", "charge", "precharge" -}; -BUILD_ASSERT(ARRAY_SIZE(state_list) == NUM_STATES_V2); -static const char * const batt_pres[] = { - "NO", "YES", "NOT_SURE", -}; - -const char *mode_text[] = EC_CHARGE_MODE_TEXT; -BUILD_ASSERT(ARRAY_SIZE(mode_text) == CHARGE_CONTROL_COUNT); - -static void dump_charge_state(void) -{ -#define DUMP(FLD, FMT) ccprintf(#FLD " = " FMT "\n", curr.FLD) -#define DUMP_CHG(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.chg. FLD) -#define DUMP_BATT(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.batt. FLD) -#define DUMP_OCPC(FLD, FMT) ccprintf("\t" #FLD " = " FMT "\n", curr.ocpc. FLD) - - enum ec_charge_control_mode cmode = get_chg_ctrl_mode(); - - ccprintf("state = %s\n", state_list[curr.state]); - DUMP(ac, "%d"); - DUMP(batt_is_charging, "%d"); - ccprintf("chg.*:\n"); - DUMP_CHG(voltage, "%dmV"); - DUMP_CHG(current, "%dmA"); - DUMP_CHG(input_current, "%dmA"); - DUMP_CHG(status, "0x%x"); - DUMP_CHG(option, "0x%x"); - DUMP_CHG(flags, "0x%x"); - cflush(); - ccprintf("batt.*:\n"); - ccprintf("\ttemperature = %dC\n", - DECI_KELVIN_TO_CELSIUS(curr.batt.temperature)); - DUMP_BATT(state_of_charge, "%d%%"); - DUMP_BATT(voltage, "%dmV"); - DUMP_BATT(current, "%dmA"); - DUMP_BATT(desired_voltage, "%dmV"); - DUMP_BATT(desired_current, "%dmA"); - DUMP_BATT(flags, "0x%x"); - DUMP_BATT(remaining_capacity, "%dmAh"); - DUMP_BATT(full_capacity, "%dmAh"); - ccprintf("\tis_present = %s\n", batt_pres[curr.batt.is_present]); - cflush(); -#ifdef CONFIG_OCPC - ccprintf("ocpc.*:\n"); - DUMP_OCPC(active_chg_chip, "%d"); - DUMP_OCPC(combined_rsys_rbatt_mo, "%dmOhm"); - if ((curr.ocpc.active_chg_chip != -1) && - !(curr.ocpc.chg_flags[curr.ocpc.active_chg_chip] & - OCPC_NO_ISYS_MEAS_CAP)) { - DUMP_OCPC(rbatt_mo, "%dmOhm"); - DUMP_OCPC(rsys_mo, "%dmOhm"); - DUMP_OCPC(isys_ma, "%dmA"); - } - DUMP_OCPC(vsys_aux_mv, "%dmV"); - DUMP_OCPC(vsys_mv, "%dmV"); - DUMP_OCPC(primary_vbus_mv, "%dmV"); - DUMP_OCPC(primary_ibus_ma, "%dmA"); - DUMP_OCPC(secondary_vbus_mv, "%dmV"); - DUMP_OCPC(secondary_ibus_ma, "%dmA"); - DUMP_OCPC(last_error, "%d"); - DUMP_OCPC(integral, "%d"); - DUMP_OCPC(last_vsys, "%dmV"); - cflush(); -#endif /* CONFIG_OCPC */ - DUMP(requested_voltage, "%dmV"); - DUMP(requested_current, "%dmA"); -#ifdef CONFIG_CHARGER_OTG - DUMP(output_current, "%dmA"); -#endif -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - DUMP(input_voltage, "%dmV"); -#endif - ccprintf("chg_ctl_mode = %s (%d)\n", - cmode < CHARGE_CONTROL_COUNT ? mode_text[cmode] : "UNDEF", - cmode); - ccprintf("manual_voltage = %d\n", manual_voltage); - ccprintf("manual_current = %d\n", manual_current); - ccprintf("user_current_limit = %dmA\n", user_current_limit); - ccprintf("battery_seems_dead = %d\n", battery_seems_dead); - ccprintf("battery_seems_disconnected = %d\n", - battery_seems_disconnected); - ccprintf("battery_was_removed = %d\n", battery_was_removed); - ccprintf("debug output = %s\n", debugging ? "on" : "off"); - ccprintf("Battery sustainer = %s (%d%% ~ %d%%)\n", - battery_sustainer_enabled() ? "on" : "off", - sustain_soc.lower, sustain_soc.upper); -#undef DUMP -} - -static void show_charging_progress(void) -{ - int rv = 0, minutes, to_full, chgnum = 0; - int dsoc; - -#ifdef CONFIG_BATTERY_SMART - /* - * Predicted remaining battery capacity based on AverageCurrent(). - * 65535 = Battery is not being discharged. - */ - if (!battery_time_to_empty(&minutes) && minutes != 65535) - to_full = 0; - /* - * Predicted time-to-full charge based on AverageCurrent(). - * 65535 = Battery is not being discharged. - */ - else if (!battery_time_to_full(&minutes) && minutes != 65535) - to_full = 1; - /* - * If both time to empty and time to full have invalid data, consider - * measured current from the coulomb counter and ac present status to - * decide whether battery is about to full or empty. - */ - else { - to_full = curr.batt_is_charging; - rv = EC_ERROR_UNKNOWN; - } -#else - if (!curr.batt_is_charging) { - rv = battery_time_to_empty(&minutes); - to_full = 0; - } else { - rv = battery_time_to_full(&minutes); - to_full = 1; - } -#endif - - dsoc = charge_get_display_charge(); - if (rv) - CPRINTS("Battery %d%% (Display %d.%d %%) / ??h:?? %s%s", - curr.batt.state_of_charge, - dsoc / 10, dsoc % 10, - to_full ? "to full" : "to empty", - is_full ? ", not accepting current" : ""); - else - CPRINTS("Battery %d%% (Display %d.%d %%) / %dh:%d %s%s", - curr.batt.state_of_charge, - dsoc / 10, dsoc % 10, minutes / 60, minutes % 60, - to_full ? "to full" : "to empty", - is_full ? ", not accepting current" : ""); - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - CPRINTS("Base battery %d%%", charge_base); -#endif - - if (debugging) { - ccprintf("battery:\n"); - print_battery_debug(); - ccprintf("charger:\n"); - if (IS_ENABLED(CONFIG_OCPC)) - chgnum = charge_get_active_chg_chip(); - print_charger_debug(chgnum); - ccprintf("chg:\n"); - dump_charge_state(); - } -} - -/* Calculate if battery is full based on whether it is accepting charge */ -test_mockable int calc_is_full(void) -{ - static int __bss_slow ret; - - /* If bad state of charge reading, return last value */ - if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE || - curr.batt.state_of_charge > 100) - return ret; - /* - * Battery is full when SoC is above 90% and battery desired current - * is 0. This is necessary because some batteries stop charging when - * the SoC still reports <100%, so we need to check desired current - * to know if it is actually full. - */ - ret = (curr.batt.state_of_charge >= 90 && - curr.batt.desired_current == 0); - return ret; -} - -/* - * Ask the charger for some voltage and current. If either value is 0, - * charging is disabled; otherwise it's enabled. Negative values are ignored. - */ -static int charge_request(int voltage, int current) -{ - int r1 = EC_SUCCESS, r2 = EC_SUCCESS, r3 = EC_SUCCESS, r4 = EC_SUCCESS; - static int __bss_slow prev_volt, prev_curr; - - if (!voltage || !current) { -#ifdef CONFIG_CHARGER_NARROW_VDC - current = 0; - /* - * With NVDC charger, keep VSYS voltage higher than battery, - * otherwise the BGATE FET body diode would conduct and - * discharge the battery. - */ - voltage = charger_closest_voltage( - curr.batt.voltage + charger_get_info()->voltage_step); - /* If the battery is full, request the max voltage. */ - if (is_full) - voltage = battery_get_info()->voltage_max; - /* And handle dead battery case */ - voltage = MAX(voltage, battery_get_info()->voltage_normal); -#else - voltage = current = 0; -#endif - } - - if (curr.ac) { - if (prev_volt != voltage || prev_curr != current) - CPRINTS("%s(%dmV, %dmA)", __func__, voltage, current); - } - - /* - * Set current before voltage so that if we are just starting - * to charge, we allow some time (i2c delay) for charging circuit to - * start at a voltage just above battery voltage before jumping - * up. This helps avoid large current spikes when connecting - * battery. - */ - if (current >= 0) { -#ifdef CONFIG_OCPC - /* - * For OCPC systems, don't unconditionally modify the primary - * charger IC's charge current. It may be handled by the - * charger drivers directly. - */ - if (curr.ocpc.active_chg_chip == CHARGER_PRIMARY) -#endif - r2 = charger_set_current(0, current); - } - if (r2 != EC_SUCCESS) - problem(PR_SET_CURRENT, r2); - - if (voltage >= 0) - r1 = charger_set_voltage(0, voltage); - if (r1 != EC_SUCCESS) - problem(PR_SET_VOLTAGE, r1); - -#ifdef CONFIG_OCPC - /* - * For OCPC systems, if the secondary charger is active, we need to - * configure that charge IC as well. Note that if OCPC ever supports - * more than 2 charger ICs, we'll need to refactor things a bit. The - * following check should be comparing against CHARGER_PRIMARY and - * config_secondary_charger should probably be config_auxiliary_charger - * and take the active chgnum as a parameter. - */ - if (curr.ocpc.active_chg_chip == CHARGER_SECONDARY) { - if ((current >= 0) || (voltage >= 0)) - r3 = ocpc_config_secondary_charger(&curr.desired_input_current, - &curr.ocpc, - voltage, current); - if (r3 != EC_SUCCESS) - problem(PR_CFG_SEC_CHG, r3); - } -#endif /* CONFIG_OCPC */ - - /* - * Set the charge inhibit bit when possible as it appears to save - * power in some cases (e.g. Nyan with BQ24735). - */ - if (voltage > 0 || current > 0) - r4 = charger_set_mode(0); - else - r4 = charger_set_mode(CHARGE_FLAG_INHIBIT_CHARGE); - if (r4 != EC_SUCCESS) - problem(PR_SET_MODE, r4); - - /* - * Only update if the request worked, so we'll keep trying on failures. - */ - if (r1 || r2) - return r1 ? r1 : r2; - if (IS_ENABLED(CONFIG_OCPC) && r3) - return r3; - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV) && - (prev_volt != voltage || prev_curr != current)) - charge_reset_stable_current(); - - prev_volt = voltage; - prev_curr = current; - - return EC_SUCCESS; -} - -void chgstate_set_manual_current(int curr_ma) -{ - if (curr_ma < 0) - manual_current = -1; - else - manual_current = charger_closest_current(curr_ma); -} - -void chgstate_set_manual_voltage(int volt_mv) -{ - manual_voltage = charger_closest_voltage(volt_mv); -} - -/* Force charging off before the battery is full. */ -static int set_chg_ctrl_mode(enum ec_charge_control_mode mode) -{ - bool discharge_on_ac = false; - int current, voltage; - int rv; - - current = manual_current; - voltage = manual_voltage; - - if (mode >= CHARGE_CONTROL_COUNT) - return EC_ERROR_INVAL; - - if (mode == CHARGE_CONTROL_NORMAL) { - current = -1; - voltage = -1; - } else { - /* Changing mode is only meaningful if AC is present. */ - if (!curr.ac) - return EC_ERROR_NOT_POWERED; - - if (mode == CHARGE_CONTROL_DISCHARGE) { - if (!IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC)) - return EC_ERROR_UNIMPLEMENTED; - discharge_on_ac = true; - } else if (mode == CHARGE_CONTROL_IDLE) { - current = 0; - voltage = 0; - } - } - - if (IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC)) { - rv = charger_discharge_on_ac(discharge_on_ac); - if (rv != EC_SUCCESS) - return rv; - } - - /* Commit all atomically */ - chg_ctl_mode = mode; - manual_current = current; - manual_voltage = voltage; - - return EC_SUCCESS; -} - -static inline int battery_too_hot(int batt_temp_c) -{ - return (!(curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) && - (batt_temp_c > batt_info->discharging_max_c)); -} - -static inline int battery_too_cold_for_discharge(int batt_temp_c) -{ - return (!(curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) && - (batt_temp_c < batt_info->discharging_min_c)); -} - -__attribute__((weak)) uint8_t board_set_battery_level_shutdown(void) -{ - return BATTERY_LEVEL_SHUTDOWN; -} - -/* True if we know the charge is too low, or we know the voltage is too low. */ -static inline int battery_too_low(void) -{ - return ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && - curr.batt.state_of_charge < battery_level_shutdown) || - (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE) && - curr.batt.voltage <= batt_info->voltage_min)); -} - -__attribute__((weak)) -enum critical_shutdown board_critical_shutdown_check( - struct charge_state_data *curr) -{ -#ifdef CONFIG_BATTERY_CRITICAL_SHUTDOWN_CUT_OFF - return CRITICAL_SHUTDOWN_CUTOFF; -#elif defined(CONFIG_HIBERNATE) - return CRITICAL_SHUTDOWN_HIBERNATE; -#else - return CRITICAL_SHUTDOWN_IGNORE; -#endif -} - -static int is_battery_critical(void) -{ - int batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature); - - /* - * TODO(crosbug.com/p/27642): The thermal loop should watch the battery - * temp, so it can turn fans on. - */ - if (battery_too_hot(batt_temp_c)) { - CPRINTS("Batt too hot: %dC", batt_temp_c); - return 1; - } - - /* Note: the battery may run on AC without discharging when too cold */ - if (!curr.ac && battery_too_cold_for_discharge(batt_temp_c)) { - CPRINTS("Batt too cold: %dC", batt_temp_c); - return 1; - } - - if (battery_too_low() && !curr.batt_is_charging) { - CPRINTS("Low battery: %d%%, %dmV", - curr.batt.state_of_charge, curr.batt.voltage); - return 1; - } - - return 0; -} - - /* - * If the battery is at extremely low charge (and discharging) or extremely - * high temperature, the EC will notify the AP and start a timer. If the - * critical condition is not corrected before the timeout expires, the EC - * will shut down the AP (if the AP is not already off) and then optionally - * hibernate or cut off battery. - */ -static int shutdown_on_critical_battery(void) -{ - if (!is_battery_critical()) { - /* Reset shutdown warning time */ - shutdown_target_time.val = 0; - return 0; - } - - if (!shutdown_target_time.val) { - /* Start count down timer */ - CPRINTS("Start shutdown due to critical battery"); - shutdown_target_time.val = get_time().val - + CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US; -#ifdef CONFIG_HOSTCMD_EVENTS - if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) - host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN); -#endif - return 1; - } - - if (!timestamp_expired(shutdown_target_time, 0)) - return 1; - - /* Timer has expired */ - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { - switch (board_critical_shutdown_check(&curr)) { - case CRITICAL_SHUTDOWN_HIBERNATE: - if (IS_ENABLED(CONFIG_HIBERNATE)) { - if (power_get_state() == POWER_S3S5) - sleep(1); - CPRINTS("Hibernate due to critical battery"); - cflush(); - system_hibernate(0, 0); - } - break; - case CRITICAL_SHUTDOWN_CUTOFF: - if (power_get_state() == POWER_S3S5) - sleep(1); - CPRINTS("Cutoff due to critical battery"); - cflush(); - board_cut_off_battery(); - break; - case CRITICAL_SHUTDOWN_IGNORE: - default: - break; - } - } else { - /* Timeout waiting for AP to shut down, so kill it */ - CPRINTS( - "charge force shutdown due to critical battery"); - chipset_force_shutdown(CHIPSET_SHUTDOWN_BATTERY_CRIT); - } - - return 1; -} - -/* - * Send host events as the battery charge drops below certain thresholds. - * We handle forced shutdown and other actions elsewhere; this is just for the - * host events. We send these even if the AP is off, since the AP will read and - * discard any events it doesn't care about the next time it wakes up. - */ -static void notify_host_of_low_battery_charge(void) -{ - /* We can't tell what the current charge is. Assume it's okay. */ - if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) - return; - -#ifdef CONFIG_HOSTCMD_EVENTS - if (curr.batt.state_of_charge <= BATTERY_LEVEL_LOW && - prev_charge > BATTERY_LEVEL_LOW) - host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); - - if (curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL && - prev_charge > BATTERY_LEVEL_CRITICAL) - host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); -#endif -} - -static void set_charge_state(enum charge_state_v2 state) -{ - prev_state = curr.state; - curr.state = state; -} - -static void notify_host_of_low_battery_voltage(void) -{ -#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE - if ((curr.batt.flags & BATT_FLAG_BAD_VOLTAGE) || - chipset_in_state(CHIPSET_STATE_ANY_OFF)) - return; - - if (!uvp_throttle_start_time.val && - (curr.batt.voltage < BAT_LOW_VOLTAGE_THRESH)) { - throttle_ap(THROTTLE_ON, THROTTLE_SOFT, - THROTTLE_SRC_BAT_VOLTAGE); - uvp_throttle_start_time = get_time(); - } else if (uvp_throttle_start_time.val && - (curr.batt.voltage < BAT_LOW_VOLTAGE_THRESH + - BAT_UVP_HYSTERESIS)) { - /* - * Reset the timer when we are not sure if VBAT can stay - * above BAT_LOW_VOLTAGE_THRESH after we stop throttling. - */ - uvp_throttle_start_time = get_time(); - } else if (uvp_throttle_start_time.val && - (get_time().val > uvp_throttle_start_time.val + - BAT_UVP_TIMEOUT_US)) { - throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, - THROTTLE_SRC_BAT_VOLTAGE); - uvp_throttle_start_time.val = 0; - } -#endif -} - -static void notify_host_of_over_current(struct batt_params *batt) -{ -#ifdef CONFIG_THROTTLE_AP_ON_BAT_DISCHG_CURRENT - static timestamp_t ocp_throttle_start_time; - - if (batt->flags & BATT_FLAG_BAD_CURRENT) - return; - - if ((!ocp_throttle_start_time.val && - (batt->current < -BAT_MAX_DISCHG_CURRENT)) || - (ocp_throttle_start_time.val && - (batt->current < -BAT_MAX_DISCHG_CURRENT + BAT_OCP_HYSTERESIS))) { - ocp_throttle_start_time = get_time(); - throttle_ap(THROTTLE_ON, THROTTLE_SOFT, - THROTTLE_SRC_BAT_DISCHG_CURRENT); - } else if (ocp_throttle_start_time.val && - (get_time().val > ocp_throttle_start_time.val + - BAT_OCP_TIMEOUT_US)) { - /* - * Clear the timer and notify AP to stop throttling if - * we haven't seen over current for BAT_OCP_TIMEOUT_US. - */ - ocp_throttle_start_time.val = 0; - throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, - THROTTLE_SRC_BAT_DISCHG_CURRENT); - } -#endif -} - -const struct batt_params *charger_current_battery_params(void) -{ - return &curr.batt; -} - -/* Determine if the battery is outside of allowable temperature range */ -static int battery_outside_charging_temperature(void) -{ - const struct battery_info *batt_info = battery_get_info(); - int batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature); - int max_c, min_c; - - if (curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) - return 0; - - if((curr.batt.desired_voltage == 0) && - (curr.batt.desired_current == 0)){ - max_c = batt_info->start_charging_max_c; - min_c = batt_info->start_charging_min_c; - } else { - max_c = batt_info->charging_max_c; - min_c = batt_info->charging_min_c; - } - - - if ((batt_temp_c >= max_c) || - (batt_temp_c <= min_c)) { - return 1; - } - return 0; -} - -static void sustain_battery_soc(void) -{ - enum ec_charge_control_mode mode = get_chg_ctrl_mode(); - int soc; - int rv; - - /* If either AC or battery is not present, nothing to do. */ - if (!curr.ac || curr.batt.is_present != BP_YES - || !battery_sustainer_enabled()) - return; - - soc = charge_get_display_charge() / 10; - - /* - * When lower < upper, the sustainer discharges using DISCHARGE. When - * lower == upper, the sustainer discharges using IDLE. The following - * switch statement handle both cases but in reality either DISCHARGE - * or IDLE is used but not both. - */ - switch (mode) { - case CHARGE_CONTROL_NORMAL: - /* Going up */ - if (sustain_soc.upper < soc) - mode = sustain_soc.upper == sustain_soc.lower ? - CHARGE_CONTROL_IDLE : CHARGE_CONTROL_DISCHARGE; - break; - case CHARGE_CONTROL_IDLE: - /* Discharging naturally */ - if (soc < sustain_soc.lower) - mode = CHARGE_CONTROL_NORMAL; - break; - case CHARGE_CONTROL_DISCHARGE: - /* Discharging actively. */ - if (soc < sustain_soc.lower) - mode = CHARGE_CONTROL_NORMAL; - break; - default: - return; - } - - if (mode == get_chg_ctrl_mode()) - return; - - rv = set_chg_ctrl_mode(mode); - CPRINTS("%s: %s control mode to %s", - __func__, rv == EC_SUCCESS ? "Switched" : "Failed to switch", - mode_text[mode]); -} - -/*****************************************************************************/ -/* Hooks */ -void charger_init(void) -{ - /* Initialize current state */ - memset(&curr, 0, sizeof(curr)); - curr.batt.is_present = BP_NOT_SURE; - /* Manual voltage/current set to off */ - manual_voltage = -1; - manual_current = -1; - /* - * Other tasks read the params like state_of_charge at the beginning of - * their tasks. Make them ready first. - */ - battery_get_params(&curr.batt); - - battery_sustainer_disable(); -} -DECLARE_HOOK(HOOK_INIT, charger_init, HOOK_PRIO_DEFAULT); - -/* Wake up the task when something important happens */ -static void charge_wakeup(void) -{ - task_wake(TASK_ID_CHARGER); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, charge_wakeup, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_AC_CHANGE, charge_wakeup, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT -/* Reset the base on S5->S0 transition. */ -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_base_reset, HOOK_PRIO_DEFAULT); -#endif - -#ifdef CONFIG_THROTTLE_AP_ON_BAT_VOLTAGE -static void bat_low_voltage_throttle_reset(void) -{ - uvp_throttle_start_time.val = 0; -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, - bat_low_voltage_throttle_reset, - HOOK_PRIO_DEFAULT); -#endif - -static int get_desired_input_current(enum battery_present batt_present, - const struct charger_info * const info) -{ - if (batt_present == BP_YES || system_is_locked() || base_connected) { -#ifdef CONFIG_CHARGE_MANAGER - int ilim = charge_manager_get_charger_current(); - return ilim == CHARGE_CURRENT_UNINITIALIZED ? - CHARGE_CURRENT_UNINITIALIZED : - MAX(CONFIG_CHARGER_INPUT_CURRENT, ilim); -#else - return CONFIG_CHARGER_INPUT_CURRENT; -#endif - } else { -#ifdef CONFIG_USB_POWER_DELIVERY - return MIN(PD_MAX_CURRENT_MA, info->input_current_max); -#else - return info->input_current_max; -#endif - } -} - -static void wakeup_battery(int *need_static) -{ - if (battery_seems_dead || battery_is_cut_off()) { - /* It's dead, do nothing */ - set_charge_state(ST_IDLE); - curr.requested_voltage = 0; - curr.requested_current = 0; - } else if (curr.state == ST_PRECHARGE - && (get_time().val > precharge_start_time.val + - PRECHARGE_TIMEOUT_US)) { - /* We've tried long enough, give up */ - CPRINTS("battery seems to be dead"); - battery_seems_dead = 1; - set_charge_state(ST_IDLE); - curr.requested_voltage = 0; - curr.requested_current = 0; - } else { - /* See if we can wake it up */ - if (curr.state != ST_PRECHARGE) { - CPRINTS("try to wake battery"); - precharge_start_time = get_time(); - *need_static = 1; - } - set_charge_state(ST_PRECHARGE); - curr.requested_voltage = batt_info->voltage_max; - curr.requested_current = batt_info->precharge_current; - } -} - -static void revive_battery(int *need_static) -{ - if (IS_ENABLED(CONFIG_BATTERY_REQUESTS_NIL_WHEN_DEAD) - && curr.requested_voltage == 0 - && curr.requested_current == 0 - && curr.batt.state_of_charge == 0) { - /* - * Battery is dead, give precharge current - * TODO (crosbug.com/p/29467): remove this workaround - * for dead battery that requests no voltage/current - */ - curr.requested_voltage = batt_info->voltage_max; - curr.requested_current = batt_info->precharge_current; - } else if (IS_ENABLED(CONFIG_BATTERY_REVIVE_DISCONNECT) - && curr.requested_voltage == 0 - && curr.requested_current == 0 - && battery_seems_disconnected) { - /* - * Battery is in disconnect state. Apply a - * current to kick it out of this state. - */ - CPRINTS("found battery in disconnect state"); - curr.requested_voltage = batt_info->voltage_max; - curr.requested_current = batt_info->precharge_current; - } else if (curr.state == ST_PRECHARGE - || battery_seems_dead || battery_was_removed) { - CPRINTS("battery woke up"); - /* Update the battery-specific values */ - batt_info = battery_get_info(); - *need_static = 1; - } - - battery_seems_dead = battery_was_removed = 0; -} - -/* Main loop */ -void charger_task(void *u) -{ - int sleep_usec; - int battery_critical; - int need_static = 1; - const struct charger_info * const info = charger_get_info(); - int prev_plt_and_desired_mw; - int chgnum = 0; - - /* Get the battery-specific values */ - batt_info = battery_get_info(); - - prev_ac = prev_charge = prev_disp_charge = -1; - chg_ctl_mode = CHARGE_CONTROL_NORMAL; - shutdown_target_time.val = 0UL; - battery_seems_dead = 0; -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - base_responsive = 0; - curr.input_voltage = CHARGE_VOLTAGE_UNINITIALIZED; - battery_dynamic[BATT_IDX_BASE].flags = EC_BATT_FLAG_INVALID_DATA; - charge_base = -1; -#endif -#ifdef CONFIG_OCPC - ocpc_init(&curr.ocpc); - charge_set_active_chg_chip(CHARGE_PORT_NONE); -#endif /* CONFIG_OCPC */ - - /* - * If system is not locked and we don't have a battery to live on, - * then use max input current limit so that we can pull as much power - * as needed. - */ - prev_bp = BP_NOT_INIT; - curr.desired_input_current = get_desired_input_current( - curr.batt.is_present, info); - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) { - /* init battery desired power */ - desired_mw = - curr.batt.desired_current * curr.batt.desired_voltage; - /* - * Battery charging current needs time to be stable when a - * new charge happens. Start the timer so we can evaluate the - * stable current when timeout. - */ - charge_reset_stable_current(); - } - - battery_level_shutdown = board_set_battery_level_shutdown(); - - while (1) { - - /* Let's see what's going on... */ - curr.ts = get_time(); - sleep_usec = 0; - problems_exist = 0; - battery_critical = 0; - curr.ac = extpower_is_present(); -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - /* - * When base is powering the system, make sure curr.ac stays 0. - * TODO(b:71723024): Fix extpower_is_present() in hardware - * instead. - */ - if (base_responsive && prev_current_base < 0) - curr.ac = 0; - - /* System is off: if AC gets connected, reset the base. */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF) && - !prev_ac && curr.ac) - board_base_reset(); -#endif - if (curr.ac != prev_ac) { - /* - * We've noticed a change in AC presence, let the board - * know. - */ - board_check_extpower(); - if (curr.ac) { - /* - * Some chargers are unpowered when the AC is - * off, so we'll reinitialize it when AC - * comes back and set the input current limit. - * Try again if it fails. - */ - int rv = charger_post_init(); - - if (rv != EC_SUCCESS) { - problem(PR_POST_INIT, rv); - } else if (curr.desired_input_current != - CHARGE_CURRENT_UNINITIALIZED) { - rv = charger_set_input_current_limit( - chgnum, - curr.desired_input_current); - if (rv != EC_SUCCESS) - problem(PR_SET_INPUT_CURR, rv); - } - - if (rv == EC_SUCCESS) - prev_ac = curr.ac; - } else { - /* Some things are only meaningful on AC */ - set_chg_ctrl_mode(CHARGE_CONTROL_NORMAL); - battery_seems_dead = 0; - prev_ac = curr.ac; - - /* - * b/187967523, we should clear charge current, - * otherwise it will effect typeC output.this - * should be ok for all chargers. - */ - charger_set_current(chgnum, 0); - } - } - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - update_base_battery_info(); -#endif - - charger_get_params(&curr.chg); - battery_get_params(&curr.batt); -#ifdef CONFIG_OCPC - if (curr.ac) - ocpc_get_adcs(&curr.ocpc); -#endif /* CONFIG_OCPC */ - - if (prev_bp != curr.batt.is_present) { - prev_bp = curr.batt.is_present; - - /* Update battery info due to change of battery */ - batt_info = battery_get_info(); - need_static = 1; - - curr.desired_input_current = - get_desired_input_current(prev_bp, info); - if (curr.desired_input_current != - CHARGE_CURRENT_UNINITIALIZED) - charger_set_input_current_limit(chgnum, - curr.desired_input_current); - hook_notify(HOOK_BATTERY_SOC_CHANGE); - } - - battery_validate_params(&curr.batt); - - notify_host_of_over_current(&curr.batt); - - /* battery current stable now, saves the current. */ - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV) && - get_time().val > stable_ts.val && curr.batt.current >= 0) - stable_current = curr.batt.current; - - /* - * Now decide what we want to do about it. We'll normally just - * pass along whatever the battery wants to the charger. Note - * that if battery_get_params() can't get valid values from the - * battery it uses (0, 0), which is probably safer than blindly - * applying power to a battery we can't talk to. - */ - if (curr.batt.flags & (BATT_FLAG_BAD_DESIRED_VOLTAGE | - BATT_FLAG_BAD_DESIRED_CURRENT)) { - curr.requested_voltage = 0; - curr.requested_current = 0; - } else { - curr.requested_voltage = curr.batt.desired_voltage; - curr.requested_current = curr.batt.desired_current; - } - - /* If we *know* there's no battery, wait for one to appear. */ - if (curr.batt.is_present == BP_NO) { - if (!curr.ac) - CPRINTS("running with no battery and no AC"); - set_charge_state(ST_IDLE); - curr.batt_is_charging = 0; - battery_was_removed = 1; - goto wait_for_it; - } - - /* - * If we had trouble talking to the battery or the charger, we - * should probably do nothing for a bit, and if it doesn't get - * better then flag it as an error. - */ - if (curr.chg.flags & CHG_FLAG_BAD_ANY) - problem(PR_CHG_FLAGS, curr.chg.flags); - if (curr.batt.flags & BATT_FLAG_BAD_ANY) - problem(PR_BATT_FLAGS, curr.batt.flags); - - /* - * If AC is present, check if input current is sufficient to - * actually charge battery. - */ - curr.batt_is_charging = curr.ac && (curr.batt.current >= 0); - - /* Don't let the battery hurt itself. */ - battery_critical = shutdown_on_critical_battery(); - - if (!curr.ac) { - set_charge_state(ST_DISCHARGE); - goto wait_for_it; - } - - /* Okay, we're on AC and we should have a battery. */ - - /* Used for factory tests. */ - if (get_chg_ctrl_mode() != CHARGE_CONTROL_NORMAL) { - set_charge_state(ST_IDLE); - goto wait_for_it; - } - - /* If the battery is not responsive, try to wake it up. */ - if (!(curr.batt.flags & BATT_FLAG_RESPONSIVE)) { - wakeup_battery(&need_static); - goto wait_for_it; - } - - /* The battery is responding. Yay. Try to use it. */ - - /* - * Always check the disconnect state. This is because - * the battery disconnect state is one of the items used - * to decide whether or not to leave safe mode. - */ - battery_seems_disconnected = - battery_get_disconnect_state() == BATTERY_DISCONNECTED; - - revive_battery(&need_static); - - set_charge_state(ST_CHARGE); - -wait_for_it: - if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE) - && get_chg_ctrl_mode() == CHARGE_CONTROL_NORMAL) { - sleep_usec = charger_profile_override(&curr); - if (sleep_usec < 0) - problem(PR_CUSTOM, sleep_usec); - } - - if (IS_ENABLED(CONFIG_BATTERY_CHECK_CHARGE_TEMP_LIMITS) - && battery_outside_charging_temperature()) { - curr.requested_current = 0; - curr.requested_voltage = 0; - curr.batt.flags &= ~BATT_FLAG_WANT_CHARGE; - if (curr.state != ST_DISCHARGE) - curr.state = ST_IDLE; - } - -#ifdef CONFIG_CHARGE_MANAGER - if (curr.batt.state_of_charge >= - CONFIG_CHARGE_MANAGER_BAT_PCT_SAFE_MODE_EXIT && - !battery_seems_disconnected) { - /* - * Sometimes the fuel gauge will report that it has - * sufficient state of charge and remaining capacity, - * but in actuality it doesn't. When the EC sees that - * information, it trusts it and leaves charge manager - * safe mode. Doing so will allow CHARGE_PORT_NONE to - * be selected, thereby cutting off the input FETs. - * When the battery cannot provide the charge it claims, - * the system loses power, shuts down, and the battery - * is not charged even though the charger is plugged in. - * By waiting 500ms, we can avoid the selection of - * CHARGE_PORT_NONE around init time and not cut off the - * input FETs. - */ - msleep(500); - charge_manager_leave_safe_mode(); - } -#endif - - /* Keep the AP informed */ - if (need_static) - need_static = update_static_battery_info(); - /* Wait on the dynamic info until the static info is good. */ - if (!need_static) - update_dynamic_battery_info(); - notify_host_of_low_battery_charge(); - notify_host_of_low_battery_voltage(); - - /* And the EC console */ - is_full = calc_is_full(); - - /* Run battery sustainer (no-op if not applicable). */ - sustain_battery_soc(); - - if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && - curr.batt.state_of_charge != prev_charge) || -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - (charge_base != prev_charge_base) || -#endif - (is_full != prev_full) || - (curr.state != prev_state) || - (charge_get_display_charge() != prev_disp_charge)) { - show_charging_progress(); - prev_charge = curr.batt.state_of_charge; - prev_disp_charge = charge_get_display_charge(); -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - prev_charge_base = charge_base; -#endif - hook_notify(HOOK_BATTERY_SOC_CHANGE); - } - prev_full = is_full; - -#ifndef CONFIG_CHARGER_MAINTAIN_VBAT - /* Turn charger off if it's not needed */ - if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) { - curr.requested_voltage = 0; - curr.requested_current = 0; - } -#endif - - /* Apply external limits */ - if (curr.requested_current > user_current_limit) - curr.requested_current = user_current_limit; - - /* Round to valid values */ - curr.requested_voltage = - charger_closest_voltage(curr.requested_voltage); - curr.requested_current = - charger_closest_current(curr.requested_current); - - /* Charger only accpets request when AC is on. */ - if (curr.ac) { - /* - * Some batteries would wake up after cut-off if we keep - * charging it. Thus, we only charge when AC is on and - * battery is not cut off yet. - */ - if (battery_is_cut_off()) { - curr.requested_voltage = 0; - curr.requested_current = 0; - } - /* - * As a safety feature, some chargers will stop - * charging if we don't communicate with it frequently - * enough. In manual mode, we'll just tell it what it - * knows. - */ - else { - if (manual_voltage != -1) - curr.requested_voltage = manual_voltage; - if (manual_current != -1) - curr.requested_current = manual_current; - } - } else { -#ifndef CONFIG_CHARGER_MAINTAIN_VBAT - curr.requested_voltage = charger_closest_voltage( - curr.batt.voltage + info->voltage_step); - curr.requested_current = -1; -#endif -#ifdef CONFIG_EC_EC_COMM_BATTERY_SERVER - /* - * On EC-EC server, do not charge if curr.ac is 0: there - * might still be some external power available but we - * do not want to use it for charging. - */ - curr.requested_current = 0; -#endif - } - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - charge_allocate_input_current_limit(); -#else - charge_request(curr.requested_voltage, curr.requested_current); -#endif - - /* How long to sleep? */ - if (problems_exist) - /* If there are errors, don't wait very long. */ - sleep_usec = CHARGE_POLL_PERIOD_SHORT; - else if (sleep_usec <= 0) { - /* default values depend on the state */ - if (!curr.ac && - (curr.state == ST_IDLE || - curr.state == ST_DISCHARGE)) { -#ifdef CONFIG_CHARGER_OTG - int output_current = curr.output_current; -#else - int output_current = 0; -#endif - /* - * If AP is off and we do not provide power, we - * can sleep a long time. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF | - CHIPSET_STATE_ANY_SUSPEND) - && output_current == 0) - sleep_usec = - CHARGE_POLL_PERIOD_VERY_LONG; - else - /* Discharging, not too urgent */ - sleep_usec = CHARGE_POLL_PERIOD_LONG; - } else { - /* AC present, so pay closer attention */ - sleep_usec = CHARGE_POLL_PERIOD_CHARGE; - } - } - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) { - int is_pd_supply = charge_manager_get_supplier() == - CHARGE_SUPPLIER_PD; - int port = charge_manager_get_active_charge_port(); - int bat_spec_desired_mw = curr.batt.desired_current * - curr.batt.desired_voltage / - 1000; - - /* - * save the previous plt_and_desired_mw, since it - * will be updated below - */ - prev_plt_and_desired_mw = - charge_get_plt_plus_bat_desired_mw(); - - /* - * Update desired power by the following rules: - * 1. If the battery is not charging with PD, we reset - * the desired_mw to the battery spec. The actual - * desired_mw will be evaluated when it starts charging - * with PD again. - * 2. If the battery SoC under battery's constant - * voltage percent (this is a rough value that can be - * applied to most batteries), the battery can fully - * sink the power, the desired power should be the - * same as the battery spec, and we don't need to use - * evaluated value stable_current. - * 3. If the battery SoC is above battery's constant - * voltage percent, the real battery desired charging - * power will decrease slowly and so does the charging - * current. We can evaluate the battery desired power - * by the product of stable_current and battery voltage. - */ - if (!is_pd_supply) - desired_mw = bat_spec_desired_mw; - else if (curr.batt.state_of_charge < pd_pref_config.cv) - desired_mw = bat_spec_desired_mw; - else if (stable_current != CHARGE_CURRENT_UNINITIALIZED) - desired_mw = curr.batt.voltage * - stable_current / 1000; - - /* if the plt_and_desired_mw changes, re-evaluate PDO */ - if (is_pd_supply && - prev_plt_and_desired_mw != - charge_get_plt_plus_bat_desired_mw()) - pd_set_new_power_request(port); - } - - /* Adjust for time spent in this loop */ - sleep_usec -= (int)(get_time().val - curr.ts.val); - if (sleep_usec < CHARGE_MIN_SLEEP_USEC) - sleep_usec = CHARGE_MIN_SLEEP_USEC; - else if (sleep_usec > CHARGE_MAX_SLEEP_USEC) - sleep_usec = CHARGE_MAX_SLEEP_USEC; - - /* - * If battery is critical, ensure that the sleep time is not - * very long since we might want to hibernate or cut-off - * battery sooner. - */ - if (battery_critical && - (sleep_usec > CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US)) - sleep_usec = CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US; - - task_wait_event(sleep_usec); - } -} - - -/*****************************************************************************/ -/* Exported functions */ - -int charge_want_shutdown(void) -{ - return (curr.state == ST_DISCHARGE) && - !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && - (curr.batt.state_of_charge < battery_level_shutdown); -} - -int charge_prevent_power_on(int power_button_pressed) -{ - int prevent_power_on = 0; - struct batt_params params; - struct batt_params *current_batt_params = &curr.batt; -#ifdef CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON - static int automatic_power_on = 1; -#endif - - /* If battery params seem uninitialized then retrieve them */ - if (current_batt_params->is_present == BP_NOT_SURE) { - battery_get_params(¶ms); - current_batt_params = ¶ms; - } - -#ifdef CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON - - /* - * Remember that a power button was pressed, and assume subsequent - * power-ups are user-requested and non-automatic. - */ - if (power_button_pressed) - automatic_power_on = 0; - /* - * Require a minimum battery level to power on and ensure that the - * battery can provide power to the system. - */ - if (current_batt_params->is_present != BP_YES || -#ifdef CONFIG_BATTERY_MEASURE_IMBALANCE - (current_batt_params->flags & BATT_FLAG_IMBALANCED_CELL && - current_batt_params->state_of_charge < - CONFIG_CHARGER_MIN_BAT_PCT_IMBALANCED_POWER_ON) || -#endif -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED || -#endif - current_batt_params->state_of_charge < - CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON) - prevent_power_on = 1; - -#if defined(CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON) && \ - defined(CONFIG_CHARGE_MANAGER) - /* However, we can power on if a sufficient charger is present. */ - if (prevent_power_on) { - if (charge_manager_get_power_limit_uw() >= - CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON * 1000) - prevent_power_on = 0; -#if defined(CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON_WITH_BATT) && \ - defined(CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON_WITH_AC) - else if (charge_manager_get_power_limit_uw() >= - CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON_WITH_BATT * 1000 -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - && battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED -#endif - && (current_batt_params->state_of_charge >= - CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON_WITH_AC)) - prevent_power_on = 0; -#endif - } -#endif /* CONFIG_CHARGE_MANAGER && CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON */ - - /* - * Factory override: Always allow power on if WP is disabled, - * except when auto-power-on at EC startup and the battery - * is physically present. - */ - prevent_power_on &= (system_is_locked() || (automatic_power_on -#ifdef CONFIG_BATTERY_HW_PRESENT_CUSTOM - && battery_hw_present() == BP_YES -#endif - )); -#endif /* CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON */ - -#ifdef CONFIG_CHARGE_MANAGER - /* Always prevent power on until charge current is initialized */ - if (extpower_is_present() && - (charge_manager_get_charger_current() == - CHARGE_CURRENT_UNINITIALIZED)) - prevent_power_on = 1; -#ifdef CONFIG_BATTERY_HW_PRESENT_CUSTOM - /* - * If battery is NOT physically present then prevent power on until - * a sufficient charger is present. - */ - if (extpower_is_present() && battery_hw_present() == BP_NO -#ifdef CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON - && charge_manager_get_power_limit_uw() < - CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON * 1000 -#endif /* CONFIG_CHARGER_MIN_POWER_MW_FOR_POWER_ON */ - ) - prevent_power_on = 1; -#endif /* CONFIG_BATTERY_HW_PRESENT_CUSTOM */ -#endif /* CONFIG_CHARGE_MANAGER */ - - /* - * Prevent power on if there is no battery nor ac power. This - * happens when the servo is powering the EC to flash it. Only include - * this logic for boards in initial bring up phase since this won't - * happen for released boards. - */ -#ifdef CONFIG_SYSTEM_UNLOCKED - if (!current_batt_params->is_present && !curr.ac) - prevent_power_on = 1; -#endif /* CONFIG_SYSTEM_UNLOCKED */ - - return prevent_power_on; -} - -static int battery_near_full(void) -{ - if (charge_get_percent() < BATTERY_LEVEL_NEAR_FULL) - return 0; - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - if (charge_base > -1 && charge_base < BATTERY_LEVEL_NEAR_FULL) - return 0; -#endif - - return 1; -} - -enum charge_state charge_get_state(void) -{ - switch (curr.state) { - case ST_IDLE: - if (battery_seems_dead || curr.batt.is_present == BP_NO) - return PWR_STATE_ERROR; - return PWR_STATE_IDLE; - case ST_DISCHARGE: -#ifdef CONFIG_PWR_STATE_DISCHARGE_FULL - if (battery_near_full()) - return PWR_STATE_DISCHARGE_FULL; - else -#endif - return PWR_STATE_DISCHARGE; - case ST_CHARGE: - /* The only difference here is what the LEDs display. */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER) && - charge_manager_get_active_charge_port() == CHARGE_PORT_NONE) - return PWR_STATE_DISCHARGE; - else if (battery_near_full()) - return PWR_STATE_CHARGE_NEAR_FULL; - else - return PWR_STATE_CHARGE; - case ST_PRECHARGE: - /* we're in battery discovery mode */ - return PWR_STATE_IDLE; - default: - /* Anything else can be considered an error for LED purposes */ - return PWR_STATE_ERROR; - } -} - -uint32_t charge_get_flags(void) -{ - uint32_t flags = 0; - - if (get_chg_ctrl_mode() != CHARGE_CONTROL_NORMAL) - flags |= CHARGE_FLAG_FORCE_IDLE; - if (curr.ac) - flags |= CHARGE_FLAG_EXTERNAL_POWER; - if (curr.batt.flags & BATT_FLAG_RESPONSIVE) - flags |= CHARGE_FLAG_BATT_RESPONSIVE; - - return flags; -} - -int charge_get_percent(void) -{ - /* - * Since there's no way to indicate an error to the caller, we'll just - * return the last known value. Even if we've never been able to talk - * to the battery, that'll be zero, which is probably as good as - * anything. - */ - return is_full ? 100 : curr.batt.state_of_charge; -} - -test_mockable int charge_get_display_charge(void) -{ - return curr.batt.display_charge; -} - -int charge_get_battery_temp(int idx, int *temp_ptr) -{ - if (curr.batt.flags & BATT_FLAG_BAD_TEMPERATURE) - return EC_ERROR_UNKNOWN; - - /* Battery temp is 10ths of degrees K, temp wants degrees K */ - *temp_ptr = curr.batt.temperature / 10; - return EC_SUCCESS; -} - -__overridable int charge_is_consuming_full_input_current(void) -{ - int chg_pct = charge_get_percent(); - - return chg_pct > 2 && chg_pct < 95; -} - -#ifdef CONFIG_CHARGER_OTG -int charge_set_output_current_limit(int chgnum, int ma, int mv) -{ - int ret; - int enable = ma > 0; - - if (enable) { - ret = charger_set_otg_current_voltage(chgnum, ma, mv); - if (ret != EC_SUCCESS) - return ret; - } - - ret = charger_enable_otg_power(chgnum, enable); - if (ret != EC_SUCCESS) - return ret; - - /* If we start/stop providing power, wake the charger task. */ - if ((curr.output_current == 0 && enable) || - (curr.output_current > 0 && !enable)) - task_wake(TASK_ID_CHARGER); - - curr.output_current = ma; - - return EC_SUCCESS; -} -#endif - -int charge_set_input_current_limit(int ma, int mv) -{ - __maybe_unused int chgnum = 0; - - if (IS_ENABLED(CONFIG_OCPC)) - chgnum = charge_get_active_chg_chip(); -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - curr.input_voltage = mv; -#endif - /* - * If battery is not present, we are not locked, and base is not - * connected then allow system to pull as much input current as needed. - * Yes, we might overcurrent the charger but this is no worse than - * browning out due to insufficient input current. - */ - if (curr.batt.is_present != BP_YES && !system_is_locked() && - !base_connected) { - - int prev_input = 0; - - charger_get_input_current_limit(chgnum, &prev_input); - -#ifdef CONFIG_USB_POWER_DELIVERY -#if ((PD_MAX_POWER_MW * 1000) / PD_MAX_VOLTAGE_MV != PD_MAX_CURRENT_MA) - /* - * If battery is not present, input current is set to - * PD_MAX_CURRENT_MA. If the input power set is greater than - * the maximum allowed system power, system might get damaged. - * Hence, limit the input current to meet maximum allowed - * input system power. - */ - - if (mv > 0 && mv * curr.desired_input_current > - PD_MAX_POWER_MW * 1000) - ma = (PD_MAX_POWER_MW * 1000) / mv; - /* - * If the active charger has already been initialized to at - * least this current level, nothing left to do. - */ - else if (prev_input >= ma) - return EC_SUCCESS; -#else - if (prev_input >= ma) - return EC_SUCCESS; -#endif - /* - * If the current needs lowered due to PD max power - * considerations, or needs raised for the selected active - * charger chip, fall through to set. - */ -#endif /* CONFIG_USB_POWER_DELIVERY */ - } - -#ifdef CONFIG_CHARGER_MAX_INPUT_CURRENT - /* Limit input current limit to max limit for this board */ - ma = MIN(ma, CONFIG_CHARGER_MAX_INPUT_CURRENT); -#endif - curr.desired_input_current = ma; -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT - /* Wake up charger task to allocate current between lid and base. */ - charge_wakeup(); - return EC_SUCCESS; -#else - return charger_set_input_current_limit(chgnum, ma); -#endif -} - -#ifdef CONFIG_OCPC -void charge_set_active_chg_chip(int idx) -{ - ASSERT(idx < (int)board_get_charger_chip_count()); - - if (idx == curr.ocpc.active_chg_chip) - return; - - CPRINTS("Act Chg: %d", idx); - curr.ocpc.active_chg_chip = idx; -} -#endif /* CONFIG_OCPC */ - -int charge_get_active_chg_chip(void) -{ -#ifdef CONFIG_OCPC - return curr.ocpc.active_chg_chip; -#else - return 0; -#endif -} - -#ifdef CONFIG_USB_PD_PREFER_MV -bool charge_is_current_stable(void) -{ - return get_time().val >= stable_ts.val; -} - -int charge_get_plt_plus_bat_desired_mw(void) -{ - /* - * Ideally, the system consuming power could be evaluated by - * "IBus * VBus - battery charging power". But in practice, - * most charger drivers don't implement IBUS ADC reading, - * so we use system PLT instead as an alterntaive approach. - */ - return pd_pref_config.plt_mw + desired_mw; -} - -int charge_get_stable_current(void) -{ - return stable_current; -} - -void charge_set_stable_current(int ma) -{ - stable_current = ma; -} - -void charge_reset_stable_current_us(uint64_t us) -{ - timestamp_t now = get_time(); - - if (stable_ts.val < now.val + us) - stable_ts.val = now.val + us; - - stable_current = CHARGE_CURRENT_UNINITIALIZED; -} - -void charge_reset_stable_current(void) -{ - /* it takes 8 to 10 seconds to stabilize battery current in practice */ - charge_reset_stable_current_us(10 * SECOND); -} -#endif - -#ifdef CONFIG_OCPC -void trigger_ocpc_reset(void) -{ - ocpc_reset(&curr.ocpc); -} -#endif - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -charge_command_charge_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_charge_control *p = args->params; - struct ec_response_charge_control *r = args->response; - int rv; - - if (args->version >= 2) { - if (p->cmd == EC_CHARGE_CONTROL_CMD_SET) { - if (p->mode == CHARGE_CONTROL_NORMAL) { - rv = battery_sustainer_set( - p->sustain_soc.lower, - p->sustain_soc.upper); - if (rv == EC_RES_UNAVAILABLE) - return EC_RES_UNAVAILABLE; - if (rv) - return EC_RES_INVALID_PARAM; - } else { - battery_sustainer_disable(); - } - } else if (p->cmd == EC_CHARGE_CONTROL_CMD_GET) { - r->mode = get_chg_ctrl_mode(); - r->sustain_soc.lower = sustain_soc.lower; - r->sustain_soc.upper = sustain_soc.upper; - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - } else { - return EC_RES_INVALID_PARAM; - } - } - - rv = set_chg_ctrl_mode(p->mode); - if (rv != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CONTROL, charge_command_charge_control, - EC_VER_MASK(1) | EC_VER_MASK(2)); - -static void reset_current_limit(void) -{ - user_current_limit = -1U; -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, reset_current_limit, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, reset_current_limit, HOOK_PRIO_DEFAULT); - -static enum ec_status -charge_command_current_limit(struct host_cmd_handler_args *args) -{ - const struct ec_params_current_limit *p = args->params; - - user_current_limit = p->limit; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CURRENT_LIMIT, charge_command_current_limit, - EC_VER_MASK(0)); - -/* - * Expose charge/battery related state - * - * @param param command to get corresponding data - * @param value the corresponding data - * @return EC_SUCCESS or error - */ -static int charge_get_charge_state_debug(int param, uint32_t *value) -{ - switch (param) { - case CS_PARAM_DEBUG_CTL_MODE: - *value = get_chg_ctrl_mode(); - break; - case CS_PARAM_DEBUG_MANUAL_CURRENT: - *value = manual_current; - break; - case CS_PARAM_DEBUG_MANUAL_VOLTAGE: - *value = manual_voltage; - break; - case CS_PARAM_DEBUG_SEEMS_DEAD: - *value = battery_seems_dead; - break; - case CS_PARAM_DEBUG_SEEMS_DISCONNECTED: - *value = battery_seems_disconnected; - break; - case CS_PARAM_DEBUG_BATT_REMOVED: - *value = battery_was_removed; - break; - default: - *value = 0; - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -static enum ec_status -charge_command_charge_state(struct host_cmd_handler_args *args) -{ - const struct ec_params_charge_state *in = args->params; - struct ec_response_charge_state *out = args->response; - uint32_t val; - int rv = EC_RES_SUCCESS; - int chgnum = 0; - - if (args->version > 0) - chgnum = in->chgnum; - - switch (in->cmd) { - - case CHARGE_STATE_CMD_GET_STATE: - out->get_state.ac = curr.ac; - out->get_state.chg_voltage = curr.chg.voltage; - out->get_state.chg_current = curr.chg.current; - out->get_state.chg_input_current = curr.chg.input_current; - out->get_state.batt_state_of_charge = curr.batt.state_of_charge; - args->response_size = sizeof(out->get_state); - break; - - case CHARGE_STATE_CMD_GET_PARAM: - val = 0; - if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE) - && in->get_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN - && in->get_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) { - /* custom profile params */ - rv = charger_profile_override_get_param( - in->get_param.param, &val); - } else if (IS_ENABLED(CONFIG_CHARGE_STATE_DEBUG) - && in->get_param.param >= CS_PARAM_DEBUG_MIN - && in->get_param.param <= CS_PARAM_DEBUG_MAX) { - /* debug params */ - rv = charge_get_charge_state_debug( - in->get_param.param, &val); - } else { - /* standard params */ - switch (in->get_param.param) { - case CS_PARAM_CHG_VOLTAGE: - val = curr.chg.voltage; - break; - case CS_PARAM_CHG_CURRENT: - val = curr.chg.current; - break; - case CS_PARAM_CHG_INPUT_CURRENT: - val = curr.chg.input_current; - break; - case CS_PARAM_CHG_STATUS: - val = curr.chg.status; - break; - case CS_PARAM_CHG_OPTION: - val = curr.chg.option; - break; - case CS_PARAM_LIMIT_POWER: -#ifdef CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW - /* - * LIMIT_POWER status is based on battery level - * and external charger power. - */ - if ((curr.batt.is_present != BP_YES || - curr.batt.state_of_charge < - CONFIG_CHARGER_LIMIT_POWER_THRESH_BAT_PCT) - && charge_manager_get_power_limit_uw() < - CONFIG_CHARGER_LIMIT_POWER_THRESH_CHG_MW - * 1000 && system_is_locked()) - val = 1; - else -#endif - val = 0; - break; - default: - rv = EC_RES_INVALID_PARAM; - } - } - - /* got something */ - out->get_param.value = val; - args->response_size = sizeof(out->get_param); - break; - - case CHARGE_STATE_CMD_SET_PARAM: - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - val = in->set_param.value; - if (IS_ENABLED(CONFIG_CHARGER_PROFILE_OVERRIDE) - && in->set_param.param >= CS_PARAM_CUSTOM_PROFILE_MIN - && in->set_param.param <= CS_PARAM_CUSTOM_PROFILE_MAX) { - /* custom profile params */ - rv = charger_profile_override_set_param( - in->set_param.param, val); - } else { - switch (in->set_param.param) { - case CS_PARAM_CHG_VOLTAGE: - chgstate_set_manual_voltage(val); - break; - case CS_PARAM_CHG_CURRENT: - chgstate_set_manual_current(val); - break; - case CS_PARAM_CHG_INPUT_CURRENT: - if (charger_set_input_current_limit(chgnum, - val)) - rv = EC_RES_ERROR; - break; - case CS_PARAM_CHG_STATUS: - case CS_PARAM_LIMIT_POWER: - /* Can't set this */ - rv = EC_RES_ACCESS_DENIED; - break; - case CS_PARAM_CHG_OPTION: - if (charger_set_option(val)) - rv = EC_RES_ERROR; - break; - default: - rv = EC_RES_INVALID_PARAM; - - } - } - break; - - default: - CPRINTS("EC_CMD_CHARGE_STATE: bad cmd 0x%x", in->cmd); - rv = EC_RES_INVALID_PARAM; - } - - return rv; -} - -DECLARE_HOST_COMMAND(EC_CMD_CHARGE_STATE, charge_command_charge_state, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -/*****************************************************************************/ -/* Console commands */ - -#ifdef CONFIG_CMD_PWR_AVG - -static int command_pwr_avg(int argc, char **argv) -{ - int avg_mv; - int avg_ma; - int avg_mw; - - if (argc != 1) - return EC_ERROR_PARAM_COUNT; - - avg_mv = battery_get_avg_voltage(); - if (avg_mv < 0) - return EC_ERROR_UNKNOWN; - avg_ma = battery_get_avg_current(); - avg_mw = avg_mv * avg_ma / 1000; - - ccprintf("mv = %d\nma = %d\nmw = %d\n", - avg_mv, avg_ma, avg_mw); - return EC_SUCCESS; -} - -DECLARE_CONSOLE_COMMAND(pwr_avg, command_pwr_avg, - NULL, - "Get 1 min power average"); - -#endif /* CONFIG_CMD_PWR_AVG */ - -static int command_chgstate(int argc, char **argv) -{ - int rv; - int val; - char *e; - - if (argc > 1) { - if (!strcasecmp(argv[1], "idle")) { - if (argc <= 2) - return EC_ERROR_PARAM_COUNT; - if (!parse_bool(argv[2], &val)) - return EC_ERROR_PARAM2; - rv = set_chg_ctrl_mode(val ? CHARGE_CONTROL_IDLE : - CHARGE_CONTROL_NORMAL); - if (rv) - return rv; - } else if (!strcasecmp(argv[1], "discharge")) { - if (argc <= 2) - return EC_ERROR_PARAM_COUNT; - if (!parse_bool(argv[2], &val)) - return EC_ERROR_PARAM2; - rv = set_chg_ctrl_mode(val ? CHARGE_CONTROL_DISCHARGE : - CHARGE_CONTROL_NORMAL); - if (rv) - return rv; - } else if (!strcasecmp(argv[1], "debug")) { - if (argc <= 2) - return EC_ERROR_PARAM_COUNT; - if (!parse_bool(argv[2], &debugging)) - return EC_ERROR_PARAM2; - } else if (!strcasecmp(argv[1], "sustain")) { - int lower, upper; - - if (argc <= 3) - return EC_ERROR_PARAM_COUNT; - lower = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - upper = strtoi(argv[3], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - rv = battery_sustainer_set(lower, upper); - if (rv) - return EC_ERROR_INVAL; - } else { - return EC_ERROR_PARAM1; - } - } - - dump_charge_state(); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chgstate, command_chgstate, - "[idle|discharge|debug on|off]" - "\n[sustain <lower> <upper>]", - "Get/set charge state machine status"); - -#ifdef CONFIG_EC_EC_COMM_BATTERY_CLIENT -static int command_chgdualdebug(int argc, char **argv) -{ - int val; - char *e; - - if (argc > 1) { - if (argv[1][0] == 'c') { - if (argc <= 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[2], "auto")) { - val = -1; - } else { - val = strtoi(argv[2], &e, 0); - if (*e || val < 0) - return EC_ERROR_PARAM2; - } - - manual_ac_current_base = val; - charge_wakeup(); - } else if (argv[1][0] == 'd') { - if (argc <= 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[2], "auto")) { - manual_noac_enabled = 0; - } else { - val = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - manual_noac_current_base = val; - manual_noac_enabled = 1; - } - charge_wakeup(); - } else { - return EC_ERROR_PARAM1; - } - } else { - ccprintf("Base/Lid: %d%s/%d mA\n", - prev_current_base, prev_allow_charge_base ? "+" : "", - prev_current_lid); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(chgdualdebug, command_chgdualdebug, - "[charge (auto|<current>)|discharge (auto|<current>)]", - "Manually control dual-battery charging algorithm."); -#endif diff --git a/common/charger.c b/common/charger.c deleted file mode 100644 index 764f8b7ba7..0000000000 --- a/common/charger.c +++ /dev/null @@ -1,712 +0,0 @@ -/* Copyright 2013 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. - * - * Common functions for battery charging. - */ - -#include "battery_smart.h" -#include "charge_state_v2.h" -#include "charger.h" -#include "common.h" -#include "console.h" -#include "dptf.h" -#include "host_command.h" -#include "printf.h" -#include "util.h" -#include "hooks.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHARGER, outstr) -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) - -/* DPTF current limit, -1 = none */ -static int dptf_limit_ma = -1; - -void dptf_set_charging_current_limit(int ma) -{ - dptf_limit_ma = ma >= 0 ? ma : -1; -} - -int dptf_get_charging_current_limit(void) -{ - return dptf_limit_ma; -} - -static void dptf_disable_hook(void) -{ - /* Before get to Sx, EC should take control of charger from DPTF */ - dptf_limit_ma = -1; -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, dptf_disable_hook, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, dptf_disable_hook, HOOK_PRIO_DEFAULT); - -/* - * Boards should override this function if their count may vary during run-time - * due to different DB options. - */ -__overridable uint8_t board_get_charger_chip_count(void) -{ - return CHARGER_NUM; -} - -int charger_closest_voltage(int voltage) -{ - const struct charger_info *info = charger_get_info(); - - /* - * If the requested voltage is non-zero but below our minimum, - * return the minimum. See crosbug.com/p/8662. - */ - if (voltage > 0 && voltage < info->voltage_min) - return info->voltage_min; - - /* Clip to max */ - if (voltage > info->voltage_max) - return info->voltage_max; - - /* Otherwise round down to nearest voltage step */ - return voltage - (voltage % info->voltage_step); -} - -int charger_closest_current(int current) -{ - const struct charger_info * const info = charger_get_info(); - - /* Apply DPTF limit if necessary */ - if (dptf_limit_ma >= 0 && current > dptf_limit_ma) - current = dptf_limit_ma; - - /* - * If the requested current is non-zero but below our minimum, - * return the minimum. See crosbug.com/p/8662. - */ - if (current > 0 && current < info->current_min) - return info->current_min; - - /* Clip to max */ - if (current > info->current_max) - return info->current_max; - - /* Otherwise round down to nearest current step */ - return current - (current % info->current_step); -} - -void charger_get_params(struct charger_params *chg) -{ - int chgnum = 0; - - if (IS_ENABLED(CONFIG_OCPC)) - chgnum = charge_get_active_chg_chip(); - - memset(chg, 0, sizeof(*chg)); - - /* - * Only the primary charger(0) can tightly regulate the current, - * therefore always query the primary charger. - */ - if (charger_get_current(0, &chg->current)) - chg->flags |= CHG_FLAG_BAD_CURRENT; - - if (charger_get_voltage(chgnum, &chg->voltage)) - chg->flags |= CHG_FLAG_BAD_VOLTAGE; - - if (charger_get_input_current_limit(chgnum, &chg->input_current)) - chg->flags |= CHG_FLAG_BAD_INPUT_CURRENT; - - if (charger_get_status(&chg->status)) - chg->flags |= CHG_FLAG_BAD_STATUS; - - if (charger_get_option(&chg->option)) - chg->flags |= CHG_FLAG_BAD_OPTION; -} - -static void print_item_name(const char *name) -{ - ccprintf(" %-8s", name); -} - -static int check_print_error(int rv) -{ - if (rv == EC_SUCCESS) - return 1; - ccputs(rv == EC_ERROR_UNIMPLEMENTED ? "(unsupported)\n" : "(error)\n"); - return 0; -} - -void print_charger_debug(int chgnum) -{ - int d; - const struct charger_info *info = charger_get_info(); - - /* info */ - print_item_name("Name:"); - ccprintf("%s\n", info->name); - - /* option */ - print_item_name("Option:"); - if (check_print_error(charger_get_option(&d))) - ccprintf("%pb (0x%04x)\n", BINARY_VALUE(d, 16), d); - - /* manufacturer id */ - print_item_name("Man id:"); - if (check_print_error(charger_manufacturer_id(&d))) - ccprintf("0x%04x\n", d); - - /* device id */ - print_item_name("Dev id:"); - if (check_print_error(charger_device_id(&d))) - ccprintf("0x%04x\n", d); - - /* charge voltage limit */ - print_item_name("V_batt:"); - if (check_print_error(charger_get_voltage(chgnum, &d))) - ccprintf("%5d (%4d - %5d, %3d)\n", d, info->voltage_min, - info->voltage_max, info->voltage_step); - - /* charge current limit */ - print_item_name("I_batt:"); - if (check_print_error(charger_get_current(chgnum, &d))) - ccprintf("%5d (%4d - %5d, %3d)\n", d, info->current_min, - info->current_max, info->current_step); - - /* input current limit */ - print_item_name("I_in:"); - if (check_print_error(charger_get_input_current_limit(chgnum, &d))) - ccprintf("%5d (%4d - %5d, %3d)\n", d, info->input_current_min, - info->input_current_max, info->input_current_step); - - /* dptf current limit */ - print_item_name("I_dptf:"); - if (dptf_limit_ma >= 0) - ccprintf("%5d\n", dptf_limit_ma); - else - ccputs("disabled\n"); -} - -static int command_charger(int argc, char **argv) -{ - int d; - char *e; - int idx_provided = 0; - int chgnum; - - if (argc == 1) { - print_charger_debug(0); - return EC_SUCCESS; - } - - idx_provided = isdigit((unsigned char)argv[1][0]); - if (idx_provided) - chgnum = atoi(argv[1]); - else - chgnum = 0; - - if ((argc == 2) && idx_provided) { - print_charger_debug(chgnum); - return EC_SUCCESS; - } - - if (strcasecmp(argv[1+idx_provided], "input") == 0) { - d = strtoi(argv[2+idx_provided], &e, 0); - if (*e) - return EC_ERROR_PARAM2+idx_provided; - return charger_set_input_current_limit(chgnum, d); - } else if (strcasecmp(argv[1+idx_provided], "current") == 0) { - d = strtoi(argv[2+idx_provided], &e, 0); - if (*e) - return EC_ERROR_PARAM2+idx_provided; - chgstate_set_manual_current(d); - return charger_set_current(chgnum, d); - } else if (strcasecmp(argv[1+idx_provided], "voltage") == 0) { - d = strtoi(argv[2+idx_provided], &e, 0); - if (*e) - return EC_ERROR_PARAM2+idx_provided; - chgstate_set_manual_voltage(d); - return charger_set_voltage(chgnum, d); - } else if (strcasecmp(argv[1+idx_provided], "dptf") == 0) { - d = strtoi(argv[2+idx_provided], &e, 0); - if (*e) - return EC_ERROR_PARAM2+idx_provided; - dptf_limit_ma = d; - return EC_SUCCESS; - } else { - return EC_ERROR_PARAM1+idx_provided; - } -} - -DECLARE_CONSOLE_COMMAND(charger, command_charger, - "[chgnum] [input | current | voltage | dptf] [newval]", - "Get or set charger param(s)"); - -/* Driver wrapper functions */ - -static void charger_chips_init(void) -{ - int chip; - - for (chip = 0; chip < board_get_charger_chip_count(); chip++) { - if (chg_chips[chip].drv->init) - chg_chips[chip].drv->init(chip); - } -} -DECLARE_HOOK(HOOK_INIT, charger_chips_init, HOOK_PRIO_INIT_I2C + 1); - -enum ec_error_list charger_post_init(void) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->post_init) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->post_init(chgnum); -} - -const struct charger_info *charger_get_info(void) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return NULL; - } - - if (!chg_chips[chgnum].drv->get_info) - return NULL; - - return chg_chips[chgnum].drv->get_info(chgnum); -} - -enum ec_error_list charger_get_status(int *status) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_status) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_status(chgnum, status); -} - -enum ec_error_list charger_set_mode(int mode) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_mode) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_mode(chgnum, mode); -} - -enum ec_error_list charger_enable_otg_power(int chgnum, int enabled) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->enable_otg_power) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->enable_otg_power(chgnum, enabled); -} - -enum ec_error_list charger_set_otg_current_voltage(int chgnum, - int output_current, - int output_voltage) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_otg_current_voltage) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_otg_current_voltage( - chgnum, output_current, output_voltage); -} - -int charger_is_sourcing_otg_power(int port) -{ - int chgnum = 0; - - if (IS_ENABLED(CONFIG_OCPC)) - chgnum = port; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return 0; - } - - if (!chg_chips[chgnum].drv->is_sourcing_otg_power) - return 0; - - return chg_chips[chgnum].drv->is_sourcing_otg_power(chgnum, port); -} - -enum ec_error_list charger_get_actual_current(int chgnum, int *current) -{ - /* Note: chgnum may be -1 if no active port is selected */ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_actual_current) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_actual_current(chgnum, current); -} - -enum ec_error_list charger_get_current(int chgnum, int *current) -{ - /* Note: chgnum may be -1 if no active port is selected */ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_current) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_current(chgnum, current); -} - -enum ec_error_list charger_set_current(int chgnum, int current) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_current) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_current(chgnum, current); -} - -enum ec_error_list charger_get_actual_voltage(int chgnum, int *voltage) -{ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_actual_voltage) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_actual_voltage(chgnum, voltage); -} - -enum ec_error_list charger_get_voltage(int chgnum, int *voltage) -{ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_voltage) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_voltage(chgnum, voltage); -} - -enum ec_error_list charger_set_voltage(int chgnum, int voltage) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_voltage) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_voltage(chgnum, voltage); -} - -enum ec_error_list charger_discharge_on_ac(int enable) -{ - int chgnum; - int rv = EC_ERROR_UNIMPLEMENTED; - - if (IS_ENABLED(CONFIG_CHARGER_DISCHARGE_ON_AC_CUSTOM)) - return board_discharge_on_ac(enable); - - /* - * When discharge on AC is selected, cycle through all chargers to - * enable or disable this feature. - */ - for (chgnum = 0; chgnum < board_get_charger_chip_count(); chgnum++) { - if (chg_chips[chgnum].drv->discharge_on_ac) - rv = chg_chips[chgnum].drv->discharge_on_ac(chgnum, - enable); - } - - return rv; -} - -enum ec_error_list charger_get_vbus_voltage(int port, int *voltage) -{ - int chgnum = 0; - - /* Note: Assumes USBPD port == chgnum on multi-charger systems */ - if (!IS_ENABLED(CONFIG_CHARGER_SINGLE_CHIP)) - chgnum = port; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return 0; - } - - if (!chg_chips[chgnum].drv->get_vbus_voltage) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_vbus_voltage(chgnum, port, voltage); -} - -enum ec_error_list charger_set_input_current_limit(int chgnum, - int input_current) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_input_current_limit) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_input_current_limit(chgnum, - input_current); -} - -enum ec_error_list charger_get_input_current_limit(int chgnum, - int *input_current) -{ - /* Note: may be called with CHARGE_PORT_NONE regularly */ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_input_current_limit) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_input_current_limit(chgnum, - input_current); -} - -enum ec_error_list charger_get_input_current(int chgnum, int *input_current) -{ - if (chgnum < 0) - return EC_ERROR_INVAL; - - if (chgnum >= board_get_charger_chip_count()) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_input_current) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_input_current(chgnum, input_current); -} - -enum ec_error_list charger_manufacturer_id(int *id) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->manufacturer_id) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->manufacturer_id(chgnum, id); -} - -enum ec_error_list charger_device_id(int *id) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->device_id) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->device_id(chgnum, id); -} - -enum ec_error_list charger_get_option(int *option) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->get_option) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->get_option(chgnum, option); -} - -enum ec_error_list charger_set_option(int option) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_option) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_option(chgnum, option); -} - -enum ec_error_list charger_set_hw_ramp(int enable) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (!chg_chips[chgnum].drv->set_hw_ramp) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_hw_ramp(chgnum, enable); -} - -#ifdef CONFIG_CHARGE_RAMP_HW -int chg_ramp_is_stable(void) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return 0; - } - - if (!chg_chips[chgnum].drv->ramp_is_stable) - return 0; - - return chg_chips[chgnum].drv->ramp_is_stable(chgnum); -} - -int chg_ramp_is_detected(void) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return 0; - } - - if (!chg_chips[chgnum].drv->ramp_is_detected) - return 0; - - return chg_chips[chgnum].drv->ramp_is_detected(chgnum); -} - -int chg_ramp_get_current_limit(void) -{ - int chgnum = 0; - - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return 0; - } - - if (!chg_chips[chgnum].drv->ramp_get_current_limit) - return 0; - - return chg_chips[chgnum].drv->ramp_get_current_limit(chgnum); -} -#endif - -enum ec_error_list charger_set_vsys_compensation(int chgnum, - struct ocpc_data *ocpc, - int current_ma, - int voltage_mv) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - /* - * This shouldn't happen as this should only be called on chargers - * that support this. - */ - if (!chg_chips[chgnum].drv->set_vsys_compensation) - return EC_ERROR_UNIMPLEMENTED; - - return chg_chips[chgnum].drv->set_vsys_compensation( - chgnum, ocpc, current_ma, voltage_mv); -} - -enum ec_error_list charger_is_icl_reached(int chgnum, bool *reached) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (chg_chips[chgnum].drv->is_icl_reached) - return chg_chips[chgnum].drv->is_icl_reached(chgnum, reached); - - return EC_ERROR_UNIMPLEMENTED; -} - -enum ec_error_list charger_enable_linear_charge(int chgnum, bool enable) -{ - if ((chgnum < 0) || (chgnum >= board_get_charger_chip_count())) { - CPRINTS("%s(%d) Invalid charger!", __func__, chgnum); - return EC_ERROR_INVAL; - } - - if (chg_chips[chgnum].drv->enable_linear_charge) - return chg_chips[chgnum].drv->enable_linear_charge(chgnum, - enable); - - return EC_ERROR_UNIMPLEMENTED; -} diff --git a/common/charger_profile_override.c b/common/charger_profile_override.c deleted file mode 100644 index 2b691b9a5a..0000000000 --- a/common/charger_profile_override.c +++ /dev/null @@ -1,201 +0,0 @@ -/* Copyright 2016 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. - * - * Charger profile override for fast charging - */ - -#include "charger_profile_override.h" -#include "console.h" -#include "ec_commands.h" -#include "util.h" - -#ifdef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE_TEST -static int fast_charge_test_on; -static int test_flag_temp; -static int test_flag_vtg; -static int test_temp_c; -static int test_vtg_mV = -1; -#endif - -static int fast_charging_allowed = 1; - -int charger_profile_override_common(struct charge_state_data *curr, - const struct fast_charge_params *fast_chg_params, - const struct fast_charge_profile **prev_chg_prof_info, - int batt_vtg_max) -{ - int i, voltage_range; - /* temp in 0.1 deg C */ - int temp_c = curr->batt.temperature - 2731; - int temp_ranges = fast_chg_params->total_temp_ranges; - const struct fast_charge_profile *chg_profile_info = - fast_chg_params->chg_profile_info; - -#ifdef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE_TEST - if (fast_charge_test_on && test_vtg_mV != -1) { - temp_c = TEMPC_TENTHS_OF_DEG(test_temp_c); - curr->batt.voltage = test_vtg_mV; - - if (test_flag_temp) - curr->batt.flags |= BATT_FLAG_BAD_TEMPERATURE; - else - curr->batt.flags &= BATT_FLAG_BAD_TEMPERATURE; - - if (test_flag_vtg) - curr->batt.flags |= BATT_FLAG_BAD_VOLTAGE; - else - curr->batt.flags &= BATT_FLAG_BAD_VOLTAGE; - } -#endif - - /* - * Determine temperature range. - * If temp reading was bad, use last range. - */ - if (!(curr->batt.flags & BATT_FLAG_BAD_TEMPERATURE)) { - while (chg_profile_info && temp_ranges) { - if (temp_c <= chg_profile_info->temp_c) { - *prev_chg_prof_info = chg_profile_info; - break; - } - chg_profile_info++; - temp_ranges--; - } - - /* Invalid charge profile selected */ - if (!chg_profile_info || !temp_ranges) - return -1; - } - - /* - * If the battery voltage reading is bad or the battery voltage is - * greater than or equal to the lower limit or the battery voltage is - * not in the charger profile voltage range, consider battery has high - * voltage range so that we charge at lower current limit. - */ - voltage_range = CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES - 1; - - if (!(curr->batt.flags & BATT_FLAG_BAD_VOLTAGE)) { - for (i = 0; i < CONFIG_CHARGER_PROFILE_VOLTAGE_RANGES - 1; - i++) { - if (curr->batt.voltage < - fast_chg_params->voltage_mV[i]) { - voltage_range = i; - break; - } - } - } - - /* - * If we are not charging or we aren't using fast charging profiles, - * then do not override desired current and voltage. - */ - if (curr->state != ST_CHARGE || !fast_charging_allowed) - return 0; - - /* - * Okay, impose our custom will: - */ - curr->requested_current = - (*prev_chg_prof_info)->current_mA[voltage_range]; - curr->requested_voltage = curr->requested_current ? batt_vtg_max : 0; - -#ifdef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE_TEST - if (fast_charge_test_on) - ccprintf("Fast charge profile i=%dmA, v=%dmV\n", - curr->requested_current, curr->requested_voltage); -#endif - - return 0; -} - -/* Customs options controllable by host command. */ -#define PARAM_FASTCHARGE (CS_PARAM_CUSTOM_PROFILE_MIN + 0) - -enum ec_status charger_profile_override_get_param(uint32_t param, - uint32_t *value) -{ - if (param == PARAM_FASTCHARGE) { - *value = fast_charging_allowed; - return EC_RES_SUCCESS; - } - return EC_RES_INVALID_PARAM; -} - -enum ec_status charger_profile_override_set_param(uint32_t param, - uint32_t value) -{ - if (param == PARAM_FASTCHARGE) { - fast_charging_allowed = value; - return EC_RES_SUCCESS; - } - return EC_RES_INVALID_PARAM; -} - -#ifdef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE -static int command_fastcharge(int argc, char **argv) -{ - if (argc > 1 && !parse_bool(argv[1], &fast_charging_allowed)) - return EC_ERROR_PARAM1; - - ccprintf("fastcharge %s\n", fast_charging_allowed ? "on" : "off"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fastcharge, command_fastcharge, - "[on|off]", - "Get or set fast charging profile"); -#endif - -/* - * Manipulate the temperature and voltage values and check if the correct - * fast charging profile is selected. - */ -#ifdef CONFIG_CMD_CHARGER_PROFILE_OVERRIDE_TEST -static int command_fastcharge_test(int argc, char **argv) -{ - char *e; - int test_on; - - if (argc > 1 && !parse_bool(argv[1], &test_on)) - return EC_ERROR_PARAM2; - - /* Check if only tuurn printf message on / off */ - if (argc == 2) { - fast_charge_test_on = test_on; - test_vtg_mV = -1; - - return EC_SUCCESS; - } - - /* Validate the input parameters */ - if ((test_on && argc != 6) || !test_on) - return EC_ERROR_PARAM_COUNT; - - test_flag_temp = strtoi(argv[2], &e, 0); - if (*e || test_flag_temp > 1 || test_flag_temp < 0) - return EC_ERROR_PARAM3; - - test_flag_vtg = strtoi(argv[3], &e, 0); - if (*e || test_flag_vtg > 1 || test_flag_vtg < 0) - return EC_ERROR_PARAM4; - - test_temp_c = strtoi(argv[4], &e, 0); - if (*e) - return EC_ERROR_PARAM5; - - test_vtg_mV = strtoi(argv[5], &e, 0); - if (*e || test_vtg_mV < 0) { - test_vtg_mV = -1; - return EC_ERROR_PARAM6; - } - - fast_charge_test_on = 1; - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fastchgtest, command_fastcharge_test, - "off | on tempflag[1|0] vtgflag[1|0] temp_c vtg_mV", - "Check if fastcharge profile works"); -#endif diff --git a/common/clz.c b/common/clz.c deleted file mode 100644 index b0b58e76a0..0000000000 --- a/common/clz.c +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright 2014 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. - * - * Software emulation for CLZ instruction - */ - -#include "common.h" - -/** - * Count leading zeros - * - * @param x non null integer. - * @return the number of leading 0-bits in x, - * starting at the most significant bit position. - */ -int __keep __clzsi2(int x) -{ - int r = 0; - - if (!x) - return 32; - if (!(x & 0xffff0000u)) { - x <<= 16; - r += 16; - } - if (!(x & 0xff000000u)) { - x <<= 8; - r += 8; - } - if (!(x & 0xf0000000u)) { - x <<= 4; - r += 4; - } - if (!(x & 0xc0000000u)) { - x <<= 2; - r += 2; - } - if (!(x & 0x80000000u)) { - x <<= 1; - r += 1; - } - return r; -} diff --git a/common/crc.c b/common/crc.c deleted file mode 100644 index 8b45150b67..0000000000 --- a/common/crc.c +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright 2014 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. - */ -/* CRC-32 implementation with USB constants */ - -#include "common.h" - -/* Constants matching USB3 and USB PD definitions */ -#define CRC32_INITIAL 0xFFFFFFFF - -/* Pre-computed values for polynom 0x04C11DB7 */ -static const uint32_t crc32_tab[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -static uint32_t _crc32_hash(uint32_t crc, const void *buf, int size) -{ - const uint8_t *p; - - p = (const uint8_t *)buf; - - while (size--) { - crc ^= *p++; - crc = crc32_tab[crc & 0xFF] ^ (crc >> 8); - } - - return crc; -} - -void crc32_ctx_init(uint32_t *crc) -{ - *crc = CRC32_INITIAL; -} - -void crc32_ctx_hash(uint32_t *crc, const void *buf, int size) -{ - *crc = _crc32_hash(*crc, buf, size); -} - -void crc32_ctx_hash32(uint32_t *crc, uint32_t val) -{ - *crc = _crc32_hash(*crc, &val, sizeof(val)); -} - -void crc32_ctx_hash16(uint32_t *crc, uint16_t val) -{ - *crc = _crc32_hash(*crc, &val, sizeof(val)); -} - -void crc32_ctx_hash8(uint32_t *crc, uint8_t val) -{ - *crc = _crc32_hash(*crc, &val, sizeof(val)); -} - -uint32_t crc32_ctx_result(uint32_t *crc) -{ - return *crc ^ 0xFFFFFFFF; -} - -/* Accumulator for the CRC */ -static uint32_t crc_; - -void crc32_init(void) -{ - crc32_ctx_init(&crc_); -} - -void crc32_hash(const void *buf, int size) -{ - crc32_ctx_hash(&crc_, buf, size); -} - -void crc32_hash32(uint32_t val) -{ - crc32_ctx_hash32(&crc_, val); -} - -void crc32_hash16(uint16_t val) -{ - crc32_ctx_hash16(&crc_, val); -} - -uint32_t crc32_result(void) -{ - return crc32_ctx_result(&crc_); -} diff --git a/common/crc8.c b/common/crc8.c deleted file mode 100644 index 8098fa74eb..0000000000 --- a/common/crc8.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2014 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 "common.h" -#include "crc8.h" - -inline uint8_t cros_crc8(const uint8_t *data, int len) -{ - return cros_crc8_arg(data, len, 0); -} - -uint8_t cros_crc8_arg(const uint8_t *data, int len, uint8_t previous_crc) -{ - unsigned crc = previous_crc << 8; - int i, j; - - for (j = len; j; j--, data++) { - crc ^= (*data << 8); - for (i = 8; i; i--) { - if (crc & 0x8000) - crc ^= (0x1070 << 3); - crc <<= 1; - } - } - - return (uint8_t)(crc >> 8); -} diff --git a/common/ctz.c b/common/ctz.c deleted file mode 100644 index bb6f69624e..0000000000 --- a/common/ctz.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright 2017 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. - * - * Software emulation for CTZ instruction - */ - -#include "common.h" - -/** - * Count trailing zeros - * - * @param x non null integer. - * @return the number of trailing 0-bits in x, - * starting at the least significant bit position. - * - * Using a de Brujin sequence, as documented here: - * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup - */ -int __keep __ctzsi2(int x) -{ - static const uint8_t MulDeBruijnBitPos[32] = { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - return MulDeBruijnBitPos[((uint32_t)((x & -x) * 0x077CB531U)) >> 27]; -} diff --git a/common/curve25519-generic.c b/common/curve25519-generic.c deleted file mode 120000 index 3218a877a2..0000000000 --- a/common/curve25519-generic.c +++ /dev/null @@ -1 +0,0 @@ -../third_party/boringssl/common/curve25519-generic.c
\ No newline at end of file diff --git a/common/curve25519.c b/common/curve25519.c deleted file mode 120000 index aa9bebe86e..0000000000 --- a/common/curve25519.c +++ /dev/null @@ -1 +0,0 @@ -../third_party/boringssl/common/curve25519.c
\ No newline at end of file diff --git a/common/device_event.c b/common/device_event.c deleted file mode 100644 index f7944ae930..0000000000 --- a/common/device_event.c +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Device event commands for Chrome EC */ - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "host_command.h" -#include "lpc.h" -#include "mkbp_event.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_EVENTS, outstr) -#define CPRINTS(format, args...) cprints(CC_EVENTS, format, ## args) - -static uint32_t device_current_events; -static uint32_t device_enabled_events; - -uint32_t device_get_current_events(void) -{ - return device_current_events; -} - -static uint32_t device_get_and_clear_events(void) -{ - return atomic_clear(&device_current_events); -} - -static uint32_t device_get_enabled_events(void) -{ - return device_enabled_events; -} - -void device_set_events(uint32_t mask) -{ - /* Ignore events that are not enabled */ - mask &= device_enabled_events; - - if ((device_current_events & mask) != mask) { - CPRINTS("device event set 0x%08x", mask); - } else { - /* - * We are here because there is no flag change (1->1, 0->0). - * For 0->0, we shouldn't notify the host because the flag is - * disabled. For 1->1, it's most likely redundant but we still - * need to notify the host in case the host didn't have a - * chance to read the flags. Otherwise, the flag would never be - * consumed because the host would never be notified. - */ - if (!mask) - return; - } - - atomic_or(&device_current_events, mask); - - /* Signal host that a device event is pending */ - host_set_single_event(EC_HOST_EVENT_DEVICE); -} - -void device_clear_events(uint32_t mask) -{ - /* Only print if something's about to change */ - if (device_current_events & mask) - CPRINTS("device event clear 0x%08x", mask); - - atomic_clear_bits(&device_current_events, mask); -} - -static void device_set_enabled_events(uint32_t mask) -{ - if ((device_enabled_events & mask) != mask) - CPRINTS("device enabled events set 0x%08x", mask); - - device_enabled_events = mask; -} - -void device_enable_event(enum ec_device_event event) -{ - atomic_or(&device_enabled_events, EC_DEVICE_EVENT_MASK(event)); -} - -/*****************************************************************************/ -/* Console commands */ - -#ifdef CONFIG_CMD_DEVICE_EVENT -static int command_device_event(int argc, char **argv) -{ - /* Handle sub-commands */ - if (argc == 3) { - char *e; - int i = strtoi(argv[2], &e, 0); - - if (*e) - return EC_ERROR_PARAM2; - else if (!strcasecmp(argv[1], "set")) - device_set_events(i); - else if (!strcasecmp(argv[1], "clear")) - device_clear_events(i); - else if (!strcasecmp(argv[1], "enable")) - device_set_enabled_events(i); - else - return EC_ERROR_PARAM1; - } - - ccprintf("Enabled Events: 0x%08x\n", device_get_enabled_events()); - ccprintf("Current Events: 0x%08x\n", device_get_current_events()); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(deviceevent, command_device_event, - "[set | clear | enable] [mask]", - "Print / set device event state"); -#endif - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status device_event_cmd(struct host_cmd_handler_args *args) -{ - const struct ec_params_device_event *p = args->params; - struct ec_response_device_event *r = args->response; - - switch (p->param) { - case EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS: - r->event_mask = device_get_and_clear_events(); - break; - case EC_DEVICE_EVENT_PARAM_GET_ENABLED_EVENTS: - r->event_mask = device_get_enabled_events(); - break; - case EC_DEVICE_EVENT_PARAM_SET_ENABLED_EVENTS: - device_set_enabled_events(p->event_mask); - r->event_mask = device_get_enabled_events(); - break; - default: - return EC_RES_INVALID_PARAM; - } - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_DEVICE_EVENT, device_event_cmd, EC_VER_MASK(0)); diff --git a/common/device_state.c b/common/device_state.c deleted file mode 100644 index 0ba94d6115..0000000000 --- a/common/device_state.c +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright 2016 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 "console.h" -#include "device_state.h" -#include "hooks.h" - -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) - -/** - * Return text description for a state - * - * @param state State - * @return String describing that state - */ -static const char *state_desc(enum device_state state) -{ - return state == DEVICE_STATE_ON ? "on" : - state == DEVICE_STATE_OFF ? "off" : "unknown"; -} - -enum device_state device_get_state(enum device_type device) -{ - return device_states[device].state; -} - -int device_set_state(enum device_type device, enum device_state state) -{ - struct device_config *dc = device_states + device; - - /* - * It'd be handy for debugging if we could print to the console when - * device_set_state() is called. But unfortunately, it'll be called a - * LOT when debouncing UART activity on DETECT_EC or DETECT_AP. So - * only print when the last known state changes below. - */ - - dc->state = state; - - if (state != DEVICE_STATE_UNKNOWN && dc->last_known_state != state) { - dc->last_known_state = state; - CPRINTS("DEV %s -> %s", dc->name, state_desc(state)); - return 1; - } - - return 0; -} - -/** - * Periodic check of device states. - * - * The board does all the work. - * - * Note that device states can change outside of this context as well, for - * example, from a GPIO interrupt handler. - */ -static void check_device_state(void) -{ - int i; - - for (i = 0; i < DEVICE_COUNT; i++) - board_update_device_state(i); -} -DECLARE_HOOK(HOOK_SECOND, check_device_state, HOOK_PRIO_DEFAULT); - -static int command_devices(int argc, char **argv) -{ - const struct device_config *dc = device_states; - int i; - - ccprintf("Device State LastKnown\n"); - - for (i = 0; i < DEVICE_COUNT; i++, dc++) - ccprintf("%-9s %-7s %s\n", dc->name, state_desc(dc->state), - state_desc(dc->last_known_state)); - - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(devices, command_devices, - "", - "Get the device states"); diff --git a/common/dps.c b/common/dps.c deleted file mode 100644 index 235f4d4e08..0000000000 --- a/common/dps.c +++ /dev/null @@ -1,639 +0,0 @@ -/* 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. - * - * Dynamic PDO Selection. - */ - -#include <stdint.h> - -#include "adc.h" -#include "dps.h" -#include "atomic.h" -#include "battery.h" -#include "console.h" -#include "charger.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "charge_state_v2.h" -#include "math_util.h" -#include "task.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "util.h" -#include "usb_pe_sm.h" - - -#define K_MORE_PWR 96 -#define K_LESS_PWR 93 -#define K_SAMPLE 1 -#define K_WINDOW 3 -#define T_REQUEST_STABLE_TIME (10 * SECOND) -#define T_NEXT_CHECK_TIME (5 * SECOND) - -#define DPS_FLAG_DISABLED BIT(0) -#define DPS_FLAG_NO_SRCCAP BIT(1) -#define DPS_FLAG_WAITING BIT(2) -#define DPS_FLAG_SAMPLED BIT(3) -#define DPS_FLAG_NEED_MORE_PWR BIT(4) - -#define DPS_FLAG_STOP_EVENTS (DPS_FLAG_DISABLED | \ - DPS_FLAG_NO_SRCCAP) -#define DPS_FLAG_ALL GENMASK(31, 0) - -#define MAX_MOVING_AVG_WINDOW 5 - -BUILD_ASSERT(K_MORE_PWR > K_LESS_PWR && 100 >= K_MORE_PWR && 100 >= K_LESS_PWR); - -/* lock for updating timeout value */ -static mutex_t dps_lock; -static timestamp_t timeout; -static bool is_enabled = true; -static int debug_level; -static bool fake_enabled; -static int fake_mv, fake_ma; -static int dynamic_mv; -static int dps_port = CHARGE_PORT_NONE; -static uint32_t flag; - -#define CPRINTF(format, args...) cprintf(CC_USBPD, "DPS " format, ##args) -#define CPRINTS(format, args...) cprints(CC_USBPD, "DPS " format, ##args) - -__overridable struct dps_config_t dps_config = { - .k_less_pwr = K_LESS_PWR, - .k_more_pwr = K_MORE_PWR, - .k_sample = K_SAMPLE, - .k_window = K_WINDOW, - .t_stable = T_REQUEST_STABLE_TIME, - .t_check = T_NEXT_CHECK_TIME, - .is_more_efficient = NULL, -}; - -int dps_get_dynamic_voltage(void) -{ - return dynamic_mv; -} - -int dps_get_charge_port(void) -{ - return dps_port; -} - -bool dps_is_enabled(void) -{ - return is_enabled; -} - -static void dps_enable(bool en) -{ - bool prev_en = is_enabled; - - is_enabled = en; - - if (is_enabled && !prev_en) - task_wake(TASK_ID_DPS); -} - -static void update_timeout(int us) -{ - timestamp_t new_timeout; - - new_timeout.val = get_time().val + us; - - mutex_lock(&dps_lock); - if (new_timeout.val > timeout.val) - timeout = new_timeout; - mutex_unlock(&dps_lock); -} - -/* - * DPS reset. - */ -static void dps_reset(void) -{ - dynamic_mv = PD_MAX_VOLTAGE_MV; - dps_port = CHARGE_PORT_NONE; -} - -/* - * DPS initialization. - */ -static void dps_init(void) -{ - dps_reset(); - - if (dps_config.k_window > MAX_MOVING_AVG_WINDOW) { - dps_config.k_window = MAX_MOVING_AVG_WINDOW; - CPRINTS("ERR:WIN"); - } - - if (dps_config.k_less_pwr > 100 || - dps_config.k_more_pwr > 100 || - dps_config.k_more_pwr <= dps_config.k_less_pwr) { - dps_config.k_less_pwr = K_LESS_PWR; - dps_config.k_more_pwr = K_MORE_PWR; - CPRINTS("ERR:COEF"); - } -} - -static bool is_near_limit(int val, int limit) -{ - return val >= (limit * dps_config.k_more_pwr / 100); -} - -bool is_more_efficient(int curr_mv, int prev_mv, int batt_mv, int batt_mw, - int input_mw) -{ - if (dps_config.is_more_efficient) - return dps_config.is_more_efficient(curr_mv, prev_mv, batt_mv, - batt_mw, input_mw); - - return ABS(curr_mv - batt_mv) < ABS(prev_mv - batt_mv); -} - -/* - * Get the input power of the active port. - * - * input_power = vbus * input_current - * - * @param vbus: VBUS in mV - * @param input_curr: input current in mA - * - * @return input_power of the result of vbus * input_curr in mW - */ -static int get_desired_input_power(int *vbus, int *input_current) -{ - int active_port; - int charger_id; - enum ec_error_list rv; - - active_port = charge_manager_get_active_charge_port(); - - if (active_port == CHARGE_PORT_NONE) - return 0; - - charger_id = charge_get_active_chg_chip(); - - if (fake_enabled) { - *vbus = fake_mv; - *input_current = fake_ma; - return fake_mv * fake_ma / 1000; - } - - rv = charger_get_input_current(charger_id, input_current); - if (rv) - return 0; - - *vbus = charge_manager_get_vbus_voltage(active_port); - - return (*vbus) * (*input_current) / 1000; -} - -/* - * Get the most efficient PDO voltage for the battery of the charging port - * - * | W\Batt | 1S(3.7V) | 2S(7.4V) | 3S(11.1V) | 4S(14.8V) | - * -------------------------------------------------------- - * | 0-15W | 5V | 9V | 12V | 15V | - * | 15-27W | 9V | 9V | 12V | 15V | - * | 27-36W | 12V | 12V | 12V | 15V | - * | 36-45W | 15V | 15V | 15V | 15V | - * | 45-60W | 20V | 20V | 20V | 20V | - * - * - * @return 0 if error occurs, else battery efficient voltage in mV - */ -int get_efficient_voltage(void) -{ - int eff_mv = 0; - int batt_mv; - int batt_pwr; - int input_pwr, vbus, input_curr; - const struct batt_params *batt = charger_current_battery_params(); - - input_pwr = get_desired_input_power(&vbus, &input_curr); - - if (!input_pwr) - return 0; - - if (battery_design_voltage(&batt_mv)) - return 0; - - batt_pwr = batt->current * batt->voltage / 1000; - - for (int i = 0; i < board_get_usb_pd_port_count(); ++i) { - const int cnt = pd_get_src_cap_cnt(i); - const uint32_t *src_caps = pd_get_src_caps(i); - - for (int j = 0; j < cnt; ++j) { - int ma, mv, unused; - - pd_extract_pdo_power(src_caps[j], &ma, &mv, &unused); - /* - * If the eff_mv is not picked, or we have more - * efficient voltage (less voltage diff) - */ - if (eff_mv == 0 || - is_more_efficient(mv, eff_mv, batt_mv, batt_pwr, - input_pwr)) - eff_mv = mv; - } - } - - return eff_mv; -} - -struct pdo_candidate { - int port; - int mv; - int mw; -}; - -#define UPDATE_CANDIDATE(new_port, new_mv, new_mw) \ - do { \ - cand->port = new_port; \ - cand->mv = new_mv; \ - cand->mw = new_mw; \ - } while (0) - -#define CLEAR_AND_RETURN() \ - do { \ - moving_avg_count = 0; \ - return false; \ - } while (0) - -/* - * Evaluate the system power if a new PD power request is needed. - * - * @param struct pdo_candidate: The candidate PDO. (Return value) - * @return true if a new power request, or false otherwise. - */ -static bool has_new_power_request(struct pdo_candidate *cand) -{ - int vbus, input_curr, input_pwr; - int input_pwr_avg = 0, input_curr_avg = 0; - int batt_pwr, batt_mv; - int max_mv = pd_get_max_voltage(); - int req_pwr, req_ma, req_mv; - int input_curr_limit; - int active_port = charge_manager_get_active_charge_port(); - int charger_id; - static int input_pwrs[MAX_MOVING_AVG_WINDOW]; - static int input_currs[MAX_MOVING_AVG_WINDOW]; - static int prev_active_port = CHARGE_PORT_NONE; - static int prev_req_mv; - static int moving_avg_count; - const struct batt_params *batt = charger_current_battery_params(); - - /* set a default value in case it early returns. */ - UPDATE_CANDIDATE(CHARGE_PORT_NONE, INT32_MAX, 0); - - if (active_port == CHARGE_PORT_NONE) - CLEAR_AND_RETURN(); - - req_mv = pd_get_requested_voltage(active_port); - req_ma = pd_get_requested_current(active_port); - - if (!req_mv) - CLEAR_AND_RETURN(); - - if (battery_design_voltage(&batt_mv)) - CLEAR_AND_RETURN(); - - /* if last sample is not the same as the current one, reset counting. */ - if (prev_req_mv != req_mv || prev_active_port != active_port) - moving_avg_count = 0; - prev_active_port = active_port; - prev_req_mv = req_mv; - - req_pwr = req_mv * req_ma / 1000; - batt_pwr = batt->current * batt->voltage / 1000; - input_pwr = get_desired_input_power(&vbus, &input_curr); - - if (!input_pwr) - CLEAR_AND_RETURN(); - - /* record moving average */ - input_pwrs[moving_avg_count % dps_config.k_window] = input_pwr; - input_currs[moving_avg_count % dps_config.k_window] = input_curr; - if (++moving_avg_count < dps_config.k_window) - return false; - - for (int i = 0; i < dps_config.k_window; i++) { - input_curr_avg += input_currs[i]; - input_pwr_avg += input_pwrs[i]; - } - input_curr_avg /= dps_config.k_window; - input_pwr_avg /= dps_config.k_window; - - charger_id = charge_get_active_chg_chip(); - - if (!charger_get_input_current_limit(charger_id, &input_curr_limit)) - /* set as last requested mA if we're unable to get the limit. */ - input_curr_limit = req_ma; - - /* - * input power might be insufficient, force it to negotiate a more - * powerful PDO. - */ - if (is_near_limit(input_pwr_avg, req_pwr) || - is_near_limit(input_curr_avg, MIN(req_ma, input_curr_limit))) { - flag |= DPS_FLAG_NEED_MORE_PWR; - if (!fake_enabled) - input_pwr_avg = req_pwr + 1; - } else { - flag &= ~DPS_FLAG_NEED_MORE_PWR; - } - - if (debug_level) - CPRINTS("C%d 0x%x last (%dmW %dmV) input (%dmW %dmV %dmA) " - "avg (%dmW, %dmA)", - active_port, flag, req_pwr, req_mv, input_pwr, vbus, - input_curr, input_pwr_avg, input_curr_avg); - - for (int i = 0; i < board_get_usb_pd_port_count(); ++i) { - const uint32_t * const src_caps = pd_get_src_caps(i); - - for (int j = 0; j < pd_get_src_cap_cnt(i); ++j) { - int ma, mv, unused; - int mw; - bool efficient; - - /* TODO(b:169532537): support augmented PDO. */ - if ((src_caps[j] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - continue; - - pd_extract_pdo_power(src_caps[j], &ma, &mv, &unused); - - if (mv > max_mv) - continue; - - mw = ma * mv / 1000; - efficient = is_more_efficient(mv, cand->mv, batt_mv, - batt_pwr, input_pwr_avg); - - if (flag & DPS_FLAG_NEED_MORE_PWR) { - /* the insufficient case.*/ - if (input_pwr_avg > cand->mw && - (mw > cand->mw || - (mw == cand->mw && efficient))) { - UPDATE_CANDIDATE(i, mv, mw); - } else if (input_pwr_avg <= mw && efficient) { - UPDATE_CANDIDATE(i, mv, mw); - } - } else { - int adjust_pwr = - mw * dps_config.k_less_pwr / 100; - int adjust_cand_mw = - cand->mw * dps_config.k_less_pwr / 100; - - /* Pick if we don't have a candidate yet. */ - if (!cand->mw) { - UPDATE_CANDIDATE(i, mv, mw); - /* - * if the candidate is insufficient, and - * we get one provides more. - */ - } else if ((adjust_cand_mw < input_pwr_avg && - cand->mw < mw) || - /* - * if the candidate is sufficient, - * and we pick a more efficient one. - */ - (adjust_cand_mw >= input_pwr_avg && - adjust_pwr >= input_pwr_avg && - efficient)) { - UPDATE_CANDIDATE(i, mv, mw); - } - } - - - /* - * if the candidate is the same as the current one, pick - * the one at active charge port. - */ - if (mw == cand->mw && mv == cand->mv && - i == active_port) - UPDATE_CANDIDATE(i, mv, mw); - } - } - - if (!cand->mv) - CPRINTS("ERR:CNDMV"); - - return (cand->mv != req_mv); -} - -static bool has_srccap(void) -{ - for (int i = 0; i < board_get_usb_pd_port_count(); ++i) { - if (pd_is_connected(i) && - pd_get_power_role(i) == PD_ROLE_SINK && - pd_get_src_cap_cnt(i) > 0) - return true; - } - return false; -} - -void dps_update_stabilized_time(int port) -{ - update_timeout(dps_config.t_stable); -} - -void dps_task(void *u) -{ - struct pdo_candidate last_cand = {CHARGE_PORT_NONE, 0, 0}; - int sample_count = 0; - - dps_init(); - update_timeout(dps_config.t_check); - - while (1) { - struct pdo_candidate curr_cand = {CHARGE_PORT_NONE, 0, 0}; - timestamp_t now; - - now = get_time(); - if (flag & DPS_FLAG_STOP_EVENTS) { - dps_reset(); - task_wait_event(-1); - /* clear flags after wake up. */ - flag = 0; - update_timeout(dps_config.t_check); - continue; - } else if (now.val < timeout.val) { - flag |= DPS_FLAG_WAITING; - task_wait_event(timeout.val - now.val); - flag &= ~DPS_FLAG_WAITING; - } - - if (!is_enabled) { - flag |= DPS_FLAG_DISABLED; - continue; - } - - if (!has_srccap()) { - flag |= DPS_FLAG_NO_SRCCAP; - continue; - } - - if (!has_new_power_request(&curr_cand)) { - sample_count = 0; - flag &= ~DPS_FLAG_SAMPLED; - } else { - if (last_cand.port == curr_cand.port && - last_cand.mv == curr_cand.mv && - last_cand.mw == curr_cand.mw) - sample_count++; - else - sample_count = 1; - flag |= DPS_FLAG_SAMPLED; - } - - if (sample_count == dps_config.k_sample) { - dynamic_mv = curr_cand.mv; - dps_port = curr_cand.port; - pd_dpm_request(dps_port, - DPM_REQUEST_NEW_POWER_LEVEL); - sample_count = 0; - flag &= ~(DPS_FLAG_SAMPLED | DPS_FLAG_NEED_MORE_PWR); - } - - last_cand.port = curr_cand.port; - last_cand.mv = curr_cand.mv; - last_cand.mw = curr_cand.mw; - - update_timeout(dps_config.t_check); - } -} - -static int command_dps(int argc, char **argv) -{ - int port = charge_manager_get_active_charge_port(); - int input_pwr, vbus, input_curr; - int holder; - - if (argc == 1) { - uint32_t last_ma = 0, last_mv = 0; - int batt_mv; - - ccprintf("flag=0x%x k_more=%d k_less=%d k_sample=%d k_win=%d\n", - flag, dps_config.k_more_pwr, dps_config.k_less_pwr, - dps_config.k_sample, dps_config.k_window); - ccprintf("t_stable=%d t_check=%d\n", - dps_config.t_stable / SECOND, - dps_config.t_check / SECOND); - if (!is_enabled) { - ccprintf("DPS Disabled\n"); - return EC_SUCCESS; - } - - if (port == CHARGE_PORT_NONE) { - ccprintf("No charger attached\n"); - return EC_SUCCESS; - } - - battery_design_voltage(&batt_mv); - input_pwr = get_desired_input_power(&vbus, &input_curr); - if (!(flag & DPS_FLAG_NO_SRCCAP)) { - last_mv = pd_get_requested_voltage(port); - last_ma = pd_get_requested_current(port); - } - ccprintf("C%d DPS Enabled\n" - "Requested: %dmV/%dmA\n" - "Measured: %dmV/%dmA/%dmW\n" - "Efficient: %dmV\n" - "Batt: %dmv\n" - "PDMaxMV: %dmV\n", - port, last_mv, last_ma, - vbus, input_curr, input_pwr, - get_efficient_voltage(), - batt_mv, - pd_get_max_voltage()); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "en")) { - dps_enable(true); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "dis")) { - dps_enable(false); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "fakepwr")) { - if (argc == 2) { - ccprintf("%sabled %dmV/%dmA\n", - fake_enabled ? "en" : "dis", fake_mv, fake_ma); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[2], "dis")) { - fake_enabled = false; - return EC_SUCCESS; - } - - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - holder = atoi(argv[2]); - if (holder <= 0) - return EC_ERROR_PARAM2; - fake_mv = holder; - - holder = atoi(argv[3]); - if (holder <= 0) - return EC_ERROR_PARAM3; - fake_ma = holder; - - fake_enabled = true; - return EC_SUCCESS; - } - - if (argc != 3) - return EC_ERROR_PARAM2; - - if (!strcasecmp(argv[1], "debug")) { - debug_level = atoi(argv[2]); - } else if (!strcasecmp(argv[1], "setkmore")) { - holder = atoi(argv[2]); - if (holder > 100 || holder <= 0 || - holder < dps_config.k_less_pwr) - return EC_ERROR_PARAM2; - dps_config.k_more_pwr = holder; - } else if (!strcasecmp(argv[1], "setkless")) { - holder = atoi(argv[2]); - if (holder > 100 || holder <= 0 || - holder > dps_config.k_more_pwr) - return EC_ERROR_PARAM2; - dps_config.k_less_pwr = holder; - } else if (!strcasecmp(argv[1], "setksample")) { - holder = atoi(argv[2]); - if (holder <= 0) - return EC_ERROR_PARAM2; - dps_config.k_sample = holder; - } else if (!strcasecmp(argv[1], "setkwin")) { - holder = atoi(argv[2]); - if (holder <= 0 || holder > MAX_MOVING_AVG_WINDOW) - return EC_ERROR_PARAM2; - dps_config.k_window = holder; - } else if (!strcasecmp(argv[1], "settcheck")) { - holder = atoi(argv[2]); - if (holder <= 0) - return EC_ERROR_PARAM2; - dps_config.t_check = holder * SECOND; - } else if (!strcasecmp(argv[1], "settstable")) { - holder = atoi(argv[2]); - if (holder <= 0) - return EC_ERROR_PARAM2; - dps_config.t_stable = holder * SECOND; - } else { - return EC_ERROR_PARAM1; - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(dps, command_dps, - "en|dis|debug <int>\n" - "\t\t set(kmore|kless|ksample|kwindow) <int>\n" - "\t\t set(tstable|tcheck) <int>\n" - "\t\t fakepwr [dis|<mV> <mA>]", - "Print/set Dynamic PDO Selection state."); diff --git a/common/dptf.c b/common/dptf.c deleted file mode 100644 index 33a42ba5af..0000000000 --- a/common/dptf.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright 2016 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 "atomic.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "dptf.h" -#include "hooks.h" -#include "host_command.h" -#include "temp_sensor.h" -#include "util.h" - -#ifdef CONFIG_ZEPHYR -#include "temp_sensor/temp_sensor.h" -#endif - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_DPTF, outstr) -#define CPRINTS(format, args...) cprints(CC_DPTF, format, ## args) - -/*****************************************************************************/ -/* DPTF temperature thresholds */ - -static struct { - int temp; /* degrees K, negative for disabled */ - cond_t over; /* watch for crossings */ -} dptf_threshold[TEMP_SENSOR_COUNT][DPTF_THRESHOLDS_PER_SENSOR]; - -static void dptf_init(void) -{ - int id, t; - - for (id = 0; id < TEMP_SENSOR_COUNT; id++) - for (t = 0; t < DPTF_THRESHOLDS_PER_SENSOR; t++) { - dptf_threshold[id][t].temp = -1; - cond_init(&dptf_threshold[id][t].over, 0); - } - -} -DECLARE_HOOK(HOOK_INIT, dptf_init, HOOK_PRIO_DEFAULT); - -/* Keep track of which triggered sensor thresholds the AP has seen */ -static uint32_t dptf_seen; - -int dptf_query_next_sensor_event(void) -{ - int id; - - for (id = 0; id < TEMP_SENSOR_COUNT; id++) - if (dptf_seen & BIT(id)) { /* atomic? */ - atomic_clear_bits(&dptf_seen, BIT(id)); - return id; - } - - return -1; -} - -/* Return true if any threshold transition occurs. */ -static int dptf_check_temp_threshold(int sensor_id, int temp) -{ - int tripped = 0; - int max, i; - - if (sensor_id >= TEMP_SENSOR_COUNT) { - CPRINTS("DPTF: Invalid sensor ID"); - return 0; - } - - for (i = 0; i < DPTF_THRESHOLDS_PER_SENSOR; i++) { - - max = dptf_threshold[sensor_id][i].temp; - if (max < 0) /* disabled? */ - continue; - - if (temp >= max) - cond_set_true(&dptf_threshold[sensor_id][i].over); - else if (temp <= max - DPTF_THRESHOLD_HYSTERESIS) - cond_set_false(&dptf_threshold[sensor_id][i].over); - - if (cond_went_true(&dptf_threshold[sensor_id][i].over)) { - CPRINTS("DPTF over threshold [%d][%d", - sensor_id, i); - atomic_or(&dptf_seen, BIT(sensor_id)); - tripped = 1; - } - if (cond_went_false(&dptf_threshold[sensor_id][i].over)) { - CPRINTS("DPTF under threshold [%d][%d", - sensor_id, i); - atomic_or(&dptf_seen, BIT(sensor_id)); - tripped = 1; - } - } - - return tripped; -} - -void dptf_set_temp_threshold(int sensor_id, int temp, int idx, int enable) -{ - CPRINTS("DPTF sensor %d, threshold %d C, index %d, %sabled", - sensor_id, K_TO_C(temp), idx, enable ? "en" : "dis"); - - if ((sensor_id >= TEMP_SENSOR_COUNT) || - (idx >= DPTF_THRESHOLDS_PER_SENSOR)) { - CPRINTS("DPTF: Invalid sensor ID"); - return; - } - - if (enable) { - /* Don't update threshold condition if already enabled */ - if (dptf_threshold[sensor_id][idx].temp == -1) - cond_init(&dptf_threshold[sensor_id][idx].over, 0); - dptf_threshold[sensor_id][idx].temp = temp; - atomic_clear_bits(&dptf_seen, BIT(sensor_id)); - } else { - dptf_threshold[sensor_id][idx].temp = -1; - } -} - -/*****************************************************************************/ -/* EC-specific thermal controls */ - -test_mockable_static void smi_sensor_failure_warning(void) -{ - CPRINTS("can't read any temp sensors!"); - host_set_single_event(EC_HOST_EVENT_THERMAL); -} - -static void thermal_control_dptf(void) -{ - int i, t, rv; - int dptf_tripped; - int num_sensors_read; - - dptf_tripped = 0; - num_sensors_read = 0; - - /* go through all the sensors */ - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - rv = temp_sensor_read(i, &t); - if (rv != EC_SUCCESS) - continue; - else - num_sensors_read++; - /* and check the dptf thresholds */ - dptf_tripped |= dptf_check_temp_threshold(i, t); - } - - if (!num_sensors_read) { - /* - * Trigger a SMI event if we can't read any sensors. - * - * In theory we could do something more elaborate like forcing - * the system to shut down if no sensors are available after - * several retries. This is a very unlikely scenario - - * particularly on LM4-based boards, since the LM4 has its own - * internal temp sensor. It's most likely to occur during - * bringup of a new board, where we haven't debugged the I2C - * bus to the sensors; forcing a shutdown in that case would - * merely hamper board bringup. - */ - if (!chipset_in_state(CHIPSET_STATE_HARD_OFF)) - smi_sensor_failure_warning(); - } - - /* Don't forget to signal any DPTF thresholds */ - if (dptf_tripped) - host_set_single_event(EC_HOST_EVENT_THERMAL_THRESHOLD); -} - -/* Wait until after the sensors have been read */ -DECLARE_HOOK(HOOK_SECOND, thermal_control_dptf, HOOK_PRIO_TEMP_SENSOR_DONE); - -/*****************************************************************************/ -/* Console commands */ - -static int command_dptftemp(int argc, char **argv) -{ - int id, t; - int temp, trig; - - ccprintf("sensor thresh0 thresh1\n"); - for (id = 0; id < TEMP_SENSOR_COUNT; id++) { - ccprintf(" %2d", id); - for (t = 0; t < DPTF_THRESHOLDS_PER_SENSOR; t++) { - temp = dptf_threshold[id][t].temp; - trig = cond_is_true(&dptf_threshold[id][t].over); - if (temp < 0) - ccprintf(" --- "); - else - ccprintf(" %3d%c", temp, - trig ? '*' : ' '); - } - ccprintf(" %s\n", temp_sensors[id].name); - } - - ccprintf("AP seen mask: 0x%08x\n", dptf_seen); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(dptftemp, command_dptftemp, - NULL, - "Print DPTF thermal parameters (degrees Kelvin)"); diff --git a/common/ec.libsharedobjs.ld b/common/ec.libsharedobjs.ld deleted file mode 100644 index adf5081640..0000000000 --- a/common/ec.libsharedobjs.ld +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright 2015 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. - */ - -SECTIONS -{ - .roshared : { KEEP(*(.roshared*)) } - /* - * Save the .ARM.atrributes section to make the linker not complain - * about conflicting CPU architectures when linking with the RW objs. - * This section will be discarded by the main EC linker script. - */ - .ARM.attributes : { KEEP(*(.ARM.*)) } -} diff --git a/common/ec_ec_comm_client.c b/common/ec_ec_comm_client.c deleted file mode 100644 index c92433af8c..0000000000 --- a/common/ec_ec_comm_client.c +++ /dev/null @@ -1,371 +0,0 @@ -/* Copyright 2017 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. - * - * EC-EC communication, functions and definitions for client. - */ - -#include "battery.h" -#include "common.h" -#include "console.h" -#include "crc8.h" -#include "ec_commands.h" -#include "ec_ec_comm_client.h" -#include "timer.h" -#include "uart.h" -#include "util.h" - -/* Console output macros */ -#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) - -/* - * TODO(b:65697962): The packed structures below do not play well if we force EC - * host commands structures to be aligned on 32-bit boundary. There are ways to - * fix that, possibly requiring copying data around, or modifying - * uart_alt_pad_write_read API to write the actual server response to a separate - * buffer. - */ -#ifdef CONFIG_HOSTCMD_ALIGNED -#error "Cannot define CONFIG_HOSTCMD_ALIGNED with EC-EC communication client." -#endif - -#define EC_EC_HOSTCMD_VERSION 4 - -/* Print extra debugging information */ -#undef EXTRA_DEBUG - -/* - * During early debugging, we would like to check that the error rate does - * grow out of control. - */ -#define DEBUG_EC_COMM_STATS -#ifdef DEBUG_EC_COMM_STATS -struct { - int total; - int errtimeout; - int errbusy; - int errunknown; - int errdatacrc; - int errcrc; - int errinval; -} comm_stats; - -#define INCR_COMM_STATS(var) (comm_stats.var++) -#else -#define INCR_COMM_STATS(var) -#endif - -/** - * Write a command on the EC-EC communication UART channel. - * - * @param command One of EC_CMD_*. - * @param data Packed structure with this layout: - * struct { - * struct { - * struct ec_host_request4 head; - * struct ec_params_* param; - * uint8_t crc8; - * } req; - * struct { - * struct ec_host_response4 head; - * struct ec_response_* info; - * uint8_t crc8; - * } resp; - * } __packed data; - * - * Where req is the request to be transmitted (head and crc8 are computed by - * this function), and resp is the response to be received (head integrity and - * crc8 are verified by this function). - * - * This format is required as the EC-EC UART is half-duplex, and all the - * transmitted data is received back, i.e. the client writes req, then reads - * req, followed by resp. - * - * When a command does not take parameters, param/crc8 must be omitted in - * tx structure. The same applies to rx structure if the response does not - * include a payload: info/crc8 must be omitted. - * - * @param req_len size of req.param (0 if no parameter is passed). - * @param resp_len size of resp.info (0 if no information is returned). - * @param timeout_us timeout in microseconds for the transaction to complete. - * - * @return - * - EC_SUCCESS on success. - * - EC_ERROR_TIMEOUT when remote end times out replying. - * - EC_ERROR_BUSY when UART is busy and cannot transmit currently. - * - EC_ERROR_CRC when the header or data CRC is invalid. - * - EC_ERROR_INVAL when the received header is invalid. - * - EC_ERROR_UNKNOWN on other error. - */ -static int write_command(uint16_t command, - uint8_t *data, int req_len, int resp_len, - int timeout_us) -{ - /* Sequence number. */ - static uint8_t cur_seq; - int ret; - int hascrc, response_seq; - - struct ec_host_request4 *request_header = (void *)data; - /* Request (TX) length is header + (data + crc8), response follows. */ - int tx_length = - sizeof(*request_header) + ((req_len > 0) ? (req_len + 1) : 0); - - struct ec_host_response4 *response_header = - (void *)&data[tx_length]; - /* RX length is TX length + response from server. */ - int rx_length = tx_length + - sizeof(*request_header) + ((resp_len > 0) ? (resp_len + 1) : 0); - - /* - * Make sure there is a gap between each command, so that the server - * can recover its state machine after each command. - * - * TODO(b:65697962): We can be much smarter than this, and record the - * last transaction time instead of just sleeping blindly. - */ - usleep(10*MSEC); - -#ifdef DEBUG_EC_COMM_STATS - if ((comm_stats.total % 128) == 0) { - CPRINTF("UART %d (T%dB%d,U%dC%dD%dI%d)\n", comm_stats.total, - comm_stats.errtimeout, comm_stats.errbusy, - comm_stats.errunknown, comm_stats.errcrc, - comm_stats.errdatacrc, comm_stats.errinval); - } -#endif - - cur_seq = (cur_seq + 1) & - (EC_PACKET4_0_SEQ_NUM_MASK >> EC_PACKET4_0_SEQ_NUM_SHIFT); - - memset(request_header, 0, sizeof(*request_header)); - /* fields0: leave seq_dup and is_response as 0. */ - request_header->fields0 = - EC_EC_HOSTCMD_VERSION | /* version */ - (cur_seq << EC_PACKET4_0_SEQ_NUM_SHIFT); /* seq_num */ - /* fields1: leave command_version as 0. */ - if (req_len > 0) - request_header->fields1 |= EC_PACKET4_1_DATA_CRC_PRESENT_MASK; - request_header->command = command; - request_header->data_len = req_len; - request_header->header_crc = - cros_crc8((uint8_t *)request_header, sizeof(*request_header)-1); - if (req_len > 0) - data[sizeof(*request_header) + req_len] = - cros_crc8(&data[sizeof(*request_header)], req_len); - - ret = uart_alt_pad_write_read((void *)data, tx_length, - (void *)data, rx_length, timeout_us); - - INCR_COMM_STATS(total); - -#ifdef EXTRA_DEBUG - CPRINTF("EC-EC ret=%d/%d\n", ret, rx_length); -#endif - - if (ret != rx_length) { - if (ret == -EC_ERROR_TIMEOUT) { - INCR_COMM_STATS(errtimeout); - return EC_ERROR_TIMEOUT; - } - - if (ret == -EC_ERROR_BUSY) { - INCR_COMM_STATS(errbusy); - return EC_ERROR_BUSY; - } - - INCR_COMM_STATS(errunknown); - return EC_ERROR_UNKNOWN; - } - - if (response_header->header_crc != - cros_crc8((uint8_t *)response_header, - sizeof(*response_header) - 1)) { - INCR_COMM_STATS(errcrc); - return EC_ERROR_CRC; - } - - hascrc = response_header->fields1 & EC_PACKET4_1_DATA_CRC_PRESENT_MASK; - response_seq = (response_header->fields0 & EC_PACKET4_0_SEQ_NUM_MASK) >> - EC_PACKET4_0_SEQ_NUM_SHIFT; - - /* - * Validate received header. - * Note that we _require_ data crc to be present if there is data to be - * read back, else we would not know how many bytes to read exactly. - */ - if ((response_header->fields0 & EC_PACKET4_0_STRUCT_VERSION_MASK) - != EC_EC_HOSTCMD_VERSION || - !(response_header->fields0 & - EC_PACKET4_0_IS_RESPONSE_MASK) || - response_seq != cur_seq || - (response_header->data_len > 0 && !hascrc) || - response_header->data_len != resp_len) { - INCR_COMM_STATS(errinval); - return EC_ERROR_INVAL; - } - - /* Check data CRC. */ - if (hascrc && - data[rx_length - 1] != - cros_crc8(&data[tx_length + sizeof(*request_header)], - resp_len)) { - INCR_COMM_STATS(errdatacrc); - return EC_ERROR_CRC; - } - - return EC_SUCCESS; -} - -/** - * handle error from write_command - * - * @param ret is return value from write_command - * @param request_result is data.resp.head.result (response result value) - * - * @return EC_RES_ERROR if ret is not EC_SUCCESS, else request_result. - */ -static int handle_error(const char *func, int ret, int request_result) -{ - if (ret != EC_SUCCESS) { - /* Do not print busy errors as they just spam the console. */ - if (ret != EC_ERROR_BUSY) - CPRINTF("%s: tx error %d\n", func, ret); - return EC_RES_ERROR; - } - - if (request_result != EC_RES_SUCCESS) - CPRINTF("%s: cmd error %d\n", func, ret); - - return request_result; -} - -#ifdef CONFIG_EC_EC_COMM_BATTERY -int ec_ec_client_base_get_dynamic_info(void) -{ - int ret; - struct { - struct { - struct ec_host_request4 head; - struct ec_params_battery_dynamic_info param; - uint8_t crc8; - } req; - struct { - struct ec_host_response4 head; - struct ec_response_battery_dynamic_info info; - uint8_t crc8; - } resp; - } __packed data; - - data.req.param.index = 0; - - ret = write_command(EC_CMD_BATTERY_GET_DYNAMIC, - (void *)&data, sizeof(data.req.param), - sizeof(data.resp.info), 15 * MSEC); - ret = handle_error(__func__, ret, data.resp.head.result); - if (ret != EC_RES_SUCCESS) - return ret; - -#ifdef EXTRA_DEBUG - CPRINTF("V: %d mV\n", data.resp.info.actual_voltage); - CPRINTF("I: %d mA\n", data.resp.info.actual_current); - CPRINTF("Remaining: %d mAh\n", data.resp.info.remaining_capacity); - CPRINTF("Cap-full: %d mAh\n", data.resp.info.full_capacity); - CPRINTF("Flags: %04x\n", data.resp.info.flags); - CPRINTF("V-desired: %d mV\n", data.resp.info.desired_voltage); - CPRINTF("I-desired: %d mA\n", data.resp.info.desired_current); -#endif - - memcpy(&battery_dynamic[BATT_IDX_BASE], &data.resp.info, - sizeof(battery_dynamic[BATT_IDX_BASE])); - return EC_RES_SUCCESS; -} - -int ec_ec_client_base_get_static_info(void) -{ - int ret; - struct { - struct { - struct ec_host_request4 head; - struct ec_params_battery_static_info param; - uint8_t crc8; - } req; - struct { - struct ec_host_response4 head; - struct ec_response_battery_static_info info; - uint8_t crc8; - } resp; - } __packed data; - - data.req.param.index = 0; - - ret = write_command(EC_CMD_BATTERY_GET_STATIC, - (void *)&data, sizeof(data.req.param), - sizeof(data.resp.info), 15 * MSEC); - ret = handle_error(__func__, ret, data.resp.head.result); - if (ret != EC_RES_SUCCESS) - return ret; - -#ifdef EXTRA_DEBUG - CPRINTF("Cap-design: %d mAh\n", data.resp.info.design_capacity); - CPRINTF("V-design: %d mV\n", data.resp.info.design_voltage); - CPRINTF("Manuf: %s\n", data.resp.info.manufacturer); - CPRINTF("Model: %s\n", data.resp.info.model); - CPRINTF("Serial: %s\n", data.resp.info.serial); - CPRINTF("Type: %s\n", data.resp.info.type); - CPRINTF("C-count: %d\n", data.resp.info.cycle_count); -#endif - - memcpy(&battery_static[BATT_IDX_BASE], &data.resp.info, - sizeof(battery_static[BATT_IDX_BASE])); - return EC_RES_SUCCESS; -} - -int ec_ec_client_base_charge_control(int max_current, - int otg_voltage, - int allow_charging) -{ - int ret; - struct { - struct { - struct ec_host_request4 head; - struct ec_params_charger_control ctrl; - uint8_t crc8; - } req; - struct { - struct ec_host_response4 head; - } resp; - } __packed data; - - data.req.ctrl.allow_charging = allow_charging; - data.req.ctrl.max_current = max_current; - data.req.ctrl.otg_voltage = otg_voltage; - - ret = write_command(EC_CMD_CHARGER_CONTROL, - (void *)&data, sizeof(data.req.ctrl), 0, 30 * MSEC); - - return handle_error(__func__, ret, data.resp.head.result); -} - -int ec_ec_client_hibernate(void) -{ - int ret; - struct { - struct { - struct ec_host_request4 head; - struct ec_params_reboot_ec param; - } req; - struct { - struct ec_host_response4 head; - } resp; - } __packed data; - - data.req.param.cmd = EC_REBOOT_HIBERNATE; - data.req.param.flags = 0; - - ret = write_command(EC_CMD_REBOOT_EC, - (void *)&data, sizeof(data.req.param), 0, 30 * MSEC); - - return handle_error(__func__, ret, data.resp.head.result); -} -#endif /* CONFIG_EC_EC_COMM_BATTERY */ diff --git a/common/ec_ec_comm_server.c b/common/ec_ec_comm_server.c deleted file mode 100644 index 23b5fee139..0000000000 --- a/common/ec_ec_comm_server.c +++ /dev/null @@ -1,328 +0,0 @@ -/* Copyright 2017 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. - * - * EC-EC communication, task and functions for server. - */ - -#include "common.h" -#include "battery.h" -#include "charge_state_v2.h" -#include "console.h" -#include "crc8.h" -#include "ec_commands.h" -#include "ec_ec_comm_server.h" -#include "extpower.h" -#include "hwtimer.h" -#include "hooks.h" -#include "queue.h" -#include "queue_policies.h" -#include "system.h" -#include "task.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) - -/* Print extra debugging information */ -#undef EXTRA_DEBUG - -/* Set if the client allows the server to charge the battery. */ -static int charging_allowed; - -/* - * Our command parameter buffer must be big enough to fit any command - * parameter, and crc byte. - */ -#define LARGEST_PARAMS_SIZE 8 - -BUILD_ASSERT(LARGEST_PARAMS_SIZE >= - sizeof(struct ec_params_battery_static_info)); -BUILD_ASSERT(LARGEST_PARAMS_SIZE >= - sizeof(struct ec_params_battery_dynamic_info)); -BUILD_ASSERT(LARGEST_PARAMS_SIZE >= - sizeof(struct ec_params_charger_control)); - -#define COMMAND_BUFFER_PARAMS_SIZE (LARGEST_PARAMS_SIZE + 1) - -/* - * Maximum time needed to read a full command, commands are at most 17 bytes, so - * should not take more than 2ms to be sent at 115200 bps. - */ -#define COMMAND_TIMEOUT_US (5 * MSEC) - - -void ec_ec_comm_server_written(struct consumer const *consumer, size_t count) -{ - task_wake(TASK_ID_ECCOMM); -} - -/* - * Discard all data from the input queue. - * - * Note that we always sleep for 1ms after clearing the queue, to make sure - * that we give enough time for the next byte to arrive. - */ -static void discard_queue(void) -{ - do { - queue_advance_head(&ec_ec_comm_server_input, - queue_count(&ec_ec_comm_server_input)); - usleep(1 * MSEC); - } while (queue_count(&ec_ec_comm_server_input) > 0); -} - -/* Write response to client. */ -static void write_response(uint16_t res, int seq, const void *data, int len) -{ - struct ec_host_response4 header; - uint8_t crc; - - header.fields0 = - 4 | /* version */ - EC_PACKET4_0_IS_RESPONSE_MASK | /* is_response */ - (seq << EC_PACKET4_0_SEQ_NUM_SHIFT); /* seq_num */ - /* Set data_crc_present if there is data */ - header.fields1 = (len > 0) ? EC_PACKET4_1_DATA_CRC_PRESENT_MASK : 0; - header.result = res; - header.data_len = len; - header.reserved = 0; - header.header_crc = - cros_crc8((uint8_t *)&header, sizeof(header)-1); - QUEUE_ADD_UNITS(&ec_ec_comm_server_output, - (uint8_t *)&header, sizeof(header)); - - if (len > 0) { - QUEUE_ADD_UNITS(&ec_ec_comm_server_output, data, len); - crc = cros_crc8(data, len); - QUEUE_ADD_UNITS(&ec_ec_comm_server_output, &crc, sizeof(crc)); - } -} - -/* - * Read len bytes into buffer. Waiting up to COMMAND_TIMEOUT_US after start. - * - * Returns EC_SUCCESS or EC_ERROR_TIMEOUT. - */ -static int read_data(void *buffer, size_t len, uint32_t start) -{ - uint32_t delta; - - while (queue_count(&ec_ec_comm_server_input) < len) { - delta = __hw_clock_source_read() - start; - if (delta >= COMMAND_TIMEOUT_US) - return EC_ERROR_TIMEOUT; - - /* Every incoming byte wakes the task. */ - task_wait_event(COMMAND_TIMEOUT_US - delta); - } - - /* Fetch header */ - QUEUE_REMOVE_UNITS(&ec_ec_comm_server_input, buffer, len); - - return EC_SUCCESS; -} - -static void handle_cmd_reboot_ec( - const struct ec_params_reboot_ec *params, - int data_len, int seq) -{ - int ret = EC_RES_SUCCESS; - - if (data_len != sizeof(*params)) { - ret = EC_RES_INVALID_COMMAND; - goto out; - } - - /* Only handle hibernate */ - if (params->cmd != EC_REBOOT_HIBERNATE) { - ret = EC_RES_INVALID_PARAM; - goto out; - } - - CPRINTS("Hibernating..."); - - system_hibernate(0, 0); - /* We should not be able to write back the response. */ - -out: - write_response(ret, seq, NULL, 0); -} - -#ifdef CONFIG_EC_EC_COMM_BATTERY -static void handle_cmd_charger_control( - const struct ec_params_charger_control *params, - int data_len, int seq) -{ - int ret = EC_RES_SUCCESS; - int prev_charging_allowed = charging_allowed; - - if (data_len != sizeof(*params)) { - ret = EC_RES_INVALID_COMMAND; - goto out; - } - - if (params->max_current >= 0) { - charge_set_output_current_limit(CHARGER_SOLO, 0, 0); - charge_set_input_current_limit( - MIN(MAX_CURRENT_MA, params->max_current), 0); - charging_allowed = params->allow_charging; - } else { - if (-params->max_current > MAX_OTG_CURRENT_MA || - params->otg_voltage > MAX_OTG_VOLTAGE_MV) { - ret = EC_RES_INVALID_PARAM; - goto out; - } - - /* Reset input current to minimum. */ - charge_set_input_current_limit(CONFIG_CHARGER_INPUT_CURRENT, 0); - /* Setup and enable "OTG". */ - charge_set_output_current_limit(CHARGER_SOLO, - -params->max_current, - params->otg_voltage); - charging_allowed = 0; - } - - if (prev_charging_allowed != charging_allowed) - hook_notify(HOOK_AC_CHANGE); - -out: - write_response(ret, seq, NULL, 0); -} - -/* - * On dual-battery server, we use the charging allowed signal from client to - * indicate whether external power is present. - * - * In most cases, this actually matches the external power status of the client - * (server battery charging when AC is connected, or discharging when server - * battery still has enough capacity), with one exception: when we do client to - * server battery charging (in this case the "external" power is the client). - */ -int extpower_is_present(void) -{ - return charging_allowed; -} -#endif - -void ec_ec_comm_server_task(void *u) -{ - struct ec_host_request4 header; - /* - * If CONFIG_HOSTCMD_ALIGNED is set, it is important that params is - * aligned on a 32-bit boundary. - */ - uint8_t __aligned(4) params[COMMAND_BUFFER_PARAMS_SIZE]; - unsigned int len, seq = 0, hascrc, cmdver; - uint32_t start; - - while (1) { - task_wait_event(-1); - - if (queue_count(&ec_ec_comm_server_input) == 0) - continue; - - /* We got some data, start timeout counter. */ - start = __hw_clock_source_read(); - - /* Wait for whole header to be available and read it. */ - if (read_data(&header, sizeof(header), start)) { - CPRINTS("%s timeout (header)", __func__); - goto discard; - } - -#ifdef EXTRA_DEBUG - CPRINTS("%s f0=%02x f1=%02x cmd=%02x, length=%d", __func__, - header.fields0, header.fields1, - header.command, header.data_len); -#endif - - /* Ignore response (we wrote that ourselves) */ - if (header.fields0 & EC_PACKET4_0_IS_RESPONSE_MASK) - goto discard; - - /* Validate version and crc. */ - if ((header.fields0 & EC_PACKET4_0_STRUCT_VERSION_MASK) != 4 || - header.header_crc != - cros_crc8((uint8_t *)&header, sizeof(header) - 1)) { - CPRINTS("%s header/crc error", __func__); - goto discard; - } - - len = header.data_len; - hascrc = header.fields1 & EC_PACKET4_1_DATA_CRC_PRESENT_MASK; - if (hascrc) - len += 1; - - /* - * Ignore commands that are too long to fit in our buffer. - */ - if (len > sizeof(params)) { - CPRINTS("%s len error (%d)", __func__, len); - /* Discard the data first, then write error back. */ - discard_queue(); - write_response(EC_RES_OVERFLOW, seq, NULL, 0); - goto discard; - } - - seq = (header.fields0 & EC_PACKET4_0_SEQ_NUM_MASK) >> - EC_PACKET4_0_SEQ_NUM_SHIFT; - - cmdver = header.fields1 & EC_PACKET4_1_COMMAND_VERSION_MASK; - - /* Wait for the rest of the data to be available and read it. */ - if (read_data(params, len, start)) { - CPRINTS("%s timeout (data)", __func__); - goto discard; - } - - /* Check data CRC */ - if (hascrc && params[len-1] != cros_crc8(params, len-1)) { - CPRINTS("%s data crc error", __func__); - write_response(EC_RES_INVALID_CHECKSUM, seq, NULL, 0); - goto discard; - } - - /* For now, all commands have version 0. */ - if (cmdver != 0) { - CPRINTS("%s bad command version", __func__); - write_response(EC_RES_INVALID_VERSION, seq, NULL, 0); - continue; - } - - switch (header.command) { -#ifdef CONFIG_EC_EC_COMM_BATTERY - case EC_CMD_BATTERY_GET_STATIC: - /* Note that we ignore the battery index parameter. */ - write_response(EC_RES_SUCCESS, seq, - &battery_static[BATT_IDX_MAIN], - sizeof(battery_static[BATT_IDX_MAIN])); - break; - case EC_CMD_BATTERY_GET_DYNAMIC: - /* Note that we ignore the battery index parameter. */ - write_response(EC_RES_SUCCESS, seq, - &battery_dynamic[BATT_IDX_MAIN], - sizeof(battery_dynamic[BATT_IDX_MAIN])); - break; - case EC_CMD_CHARGER_CONTROL: - handle_cmd_charger_control((void *)params, - header.data_len, seq); - break; -#endif - case EC_CMD_REBOOT_EC: - handle_cmd_reboot_ec((void *)params, - header.data_len, seq); - break; - default: - write_response(EC_RES_INVALID_COMMAND, seq, - NULL, 0); - } - - continue; -discard: - /* - * Some error occurred: discard all data in the queue. - */ - discard_queue(); - } -} diff --git a/common/espi.c b/common/espi.c deleted file mode 100644 index 0a747d3bda..0000000000 --- a/common/espi.c +++ /dev/null @@ -1,57 +0,0 @@ -/* Copyright 2017 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. - */ - -/* eSPI common functionality for Chrome EC */ - -#include "common.h" -#include "gpio.h" -#include "registers.h" -#include "espi.h" -#include "timer.h" -#include "util.h" - - -const char *espi_vw_names[] = { - "VW_SLP_S3_L", - "VW_SLP_S4_L", - "VW_SLP_S5_L", - "VW_SUS_STAT_L", - "VW_PLTRST_L", - "VW_OOB_RST_WARN", - "VW_OOB_RST_ACK", - "VW_WAKE_L", - "VW_PME_L", - "VW_ERROR_FATAL", - "VW_ERROR_NON_FATAL", - /* Merge bit 3/0 into one signal. Need to set them simultaneously */ - "VW_PERIPHERAL_BTLD_STATUS_DONE", - "VW_SCI_L", - "VW_SMI_L", - "VW_RCIN_L", - "VW_HOST_RST_ACK", - "VW_HOST_RST_WARN", - "VW_SUS_ACK", - "VW_SUS_WARN_L", - "VW_SUS_PWRDN_ACK_L", - "VW_SLP_A_L", - "VW_SLP_LAN", - "VW_SLP_WLAN", -}; -BUILD_ASSERT(ARRAY_SIZE(espi_vw_names) == VW_SIGNAL_COUNT); - - -const char *espi_vw_get_wire_name(enum espi_vw_signal signal) -{ - if (espi_signal_is_vw(signal)) - return espi_vw_names[signal - VW_SIGNAL_START]; - - return NULL; -} - - -int espi_signal_is_vw(int signal) -{ - return ((signal >= VW_SIGNAL_START) && (signal < VW_SIGNAL_END)); -} diff --git a/common/event_log.c b/common/event_log.c deleted file mode 100644 index 95e44413bc..0000000000 --- a/common/event_log.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright 2017 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 "common.h" -#include "console.h" -#include "event_log.h" -#include "hooks.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Event log FIFO */ -#define UNIT_SIZE sizeof(struct event_log_entry) -#define UNIT_COUNT (CONFIG_EVENT_LOG_SIZE/UNIT_SIZE) -#define UNIT_COUNT_MASK (UNIT_COUNT - 1) -static struct event_log_entry __bss_slow log_events[UNIT_COUNT]; -BUILD_ASSERT(POWER_OF_TWO(UNIT_COUNT)); - -/* - * The FIFO pointers are defined as following : - * "log_head" is the next available event to dequeue. - * "log_tail" is marking the end of the FIFO content (after last committed - * event) - * "log_tail_next" is the next available spot to enqueue events. - * The pointers are not wrapped until they are used, so we don't need an extra - * entry to disambiguate between full and empty FIFO. - * - * For concurrency, several tasks might try to enqueue events in parallel with - * log_add_event(). Only one task is dequeuing events (host commands, VDM, - * TPM command handler). When the FIFO is full, log_add_event() will discard - * the oldest events, so "log_head" is incremented/decremented in a critical - * section since it is accessed from both log_add_event() and - * log_dequeue_event(). log_tail_next is also protected as several writers can - * race to add an event to the queue. - * When a writer is done adding its event, it is updating log_tail, - * so the event can be consumed by log_dequeue_event(). - */ -static size_t log_head; -static size_t log_tail; -static size_t log_tail_next; - -/* Size of one FIFO entry */ -#define ENTRY_SIZE(payload_sz) (1+DIV_ROUND_UP((payload_sz), UNIT_SIZE)) - -void log_add_event(uint8_t type, uint8_t size, uint16_t data, - void *payload, uint32_t timestamp) -{ - struct event_log_entry *r; - size_t payload_size = EVENT_LOG_SIZE(size); - size_t total_size = ENTRY_SIZE(payload_size); - size_t current_tail, first; - uint32_t lock_key; - - /* --- critical section : reserve queue space --- */ - lock_key = irq_lock(); - current_tail = log_tail_next; - log_tail_next = current_tail + total_size; - irq_unlock(lock_key); - /* --- end of critical section --- */ - - /* Out of space : discard the oldest entry */ - while ((UNIT_COUNT - (current_tail - log_head)) < total_size) { - struct event_log_entry *oldest; - /* --- critical section : atomically free-up space --- */ - lock_key = irq_lock(); - oldest = log_events + (log_head & UNIT_COUNT_MASK); - log_head += ENTRY_SIZE(EVENT_LOG_SIZE(oldest->size)); - irq_unlock(lock_key); - /* --- end of critical section --- */ - } - - r = log_events + (current_tail & UNIT_COUNT_MASK); - - r->timestamp = timestamp; - r->type = type; - r->size = size; - r->data = data; - /* copy the payload into the FIFO */ - first = MIN(total_size - 1, (UNIT_COUNT - - (current_tail & UNIT_COUNT_MASK)) - 1); - if (first) - memcpy(r->payload, payload, first * UNIT_SIZE); - if (first < total_size - 1) - memcpy(log_events, ((uint8_t *)payload) + first * UNIT_SIZE, - (total_size - first) * UNIT_SIZE); - /* mark the entry available in the queue if nobody is behind us */ - if (current_tail == log_tail) - log_tail = log_tail_next; -} - -int log_dequeue_event(struct event_log_entry *r) -{ - uint32_t now = get_time().val >> EVENT_LOG_TIMESTAMP_SHIFT; - unsigned int total_size, first; - struct event_log_entry *entry; - size_t current_head; - uint32_t lock_key; - -retry: - current_head = log_head; - /* The log FIFO is empty */ - if (log_tail == current_head) { - memset(r, 0, UNIT_SIZE); - r->type = EVENT_LOG_NO_ENTRY; - return UNIT_SIZE; - } - - entry = log_events + (current_head & UNIT_COUNT_MASK); - total_size = ENTRY_SIZE(EVENT_LOG_SIZE(entry->size)); - first = MIN(total_size, UNIT_COUNT - (current_head & UNIT_COUNT_MASK)); - memcpy(r, entry, first * UNIT_SIZE); - if (first < total_size) - memcpy(r + first, log_events, (total_size-first) * UNIT_SIZE); - - /* --- critical section : remove the entry from the queue --- */ - lock_key = irq_lock(); - if (log_head != current_head) { /* our entry was thrown away */ - irq_unlock(lock_key); - goto retry; - } - log_head += total_size; - irq_unlock(lock_key); - /* --- end of critical section --- */ - - /* fixup the timestamp : number of milliseconds in the past */ - r->timestamp = now - r->timestamp; - - return total_size * UNIT_SIZE; -} - -#ifdef CONFIG_CMD_DLOG -/* - * Display TPM event logs. - */ -static int command_dlog(int argc, char **argv) -{ - size_t log_cur; - const uint8_t * const log_events_end = - (uint8_t *)&log_events[UNIT_COUNT]; - - if (argc > 1) { - if (!strcasecmp(argv[1], "clear")) { - interrupt_disable(); - log_head = log_tail = log_tail_next = 0; - interrupt_enable(); - - return EC_SUCCESS; - } - /* Too many parameters */ - return EC_ERROR_PARAM1; - } - - ccprintf(" TIMESTAMP | TYPE | DATA | SIZE | PAYLOAD\n"); - log_cur = log_head; - while (log_cur != log_tail) { - struct event_log_entry *r; - uint8_t *payload; - uint32_t payload_bytes; - - r = &log_events[log_cur & UNIT_COUNT_MASK]; - payload_bytes = EVENT_LOG_SIZE(r->size); - log_cur += ENTRY_SIZE(payload_bytes); - - ccprintf("%10d %4d 0x%04X %4d ", r->timestamp, r->type, - r->data, payload_bytes); - - /* display payload if exists */ - payload = r->payload; - while (payload_bytes--) { - if (payload >= log_events_end) - payload = (uint8_t *)&log_events[0]; - - ccprintf("%02X", *payload); - payload++; - } - ccprintf("\n"); - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(dlog, - command_dlog, - "[clear]", - "Display/clear TPM event logs"); -#endif diff --git a/common/extpower_common.c b/common/extpower_common.c deleted file mode 100644 index 9021b77626..0000000000 --- a/common/extpower_common.c +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2020 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 "extpower.h" -#include "hooks.h" -#include "host_command.h" - -__overridable void board_check_extpower(void) -{ -} - -void extpower_handle_update(int is_present) -{ - uint8_t *memmap_batt_flags; - - hook_notify(HOOK_AC_CHANGE); - memmap_batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); - - /* Forward notification to host */ - if (is_present) { - *memmap_batt_flags |= EC_BATT_FLAG_AC_PRESENT; - host_set_single_event(EC_HOST_EVENT_AC_CONNECTED); - } else { - *memmap_batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; - host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED); - } -} diff --git a/common/extpower_gpio.c b/common/extpower_gpio.c deleted file mode 100644 index 4cdcb834f8..0000000000 --- a/common/extpower_gpio.c +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Pure GPIO-based external power detection */ - -#include "common.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "timer.h" - -static int debounced_extpower_presence; - -int extpower_is_present(void) -{ - return debounced_extpower_presence; -} - -/** - * Deferred function to handle external power change - */ -static void extpower_deferred(void) -{ - int extpower_presence = gpio_get_level(GPIO_AC_PRESENT); - - if (extpower_presence == debounced_extpower_presence) - return; - - debounced_extpower_presence = extpower_presence; - extpower_handle_update(extpower_presence); - -} -DECLARE_DEFERRED(extpower_deferred); - -void extpower_interrupt(enum gpio_signal signal) -{ - /* Trigger deferred notification of external power change */ - hook_call_deferred(&extpower_deferred_data, - CONFIG_EXTPOWER_DEBOUNCE_MS * MSEC); -} - -static void extpower_init(void) -{ - uint8_t *memmap_batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); - - debounced_extpower_presence = gpio_get_level(GPIO_AC_PRESENT); - - /* Initialize the memory-mapped AC_PRESENT flag */ - if (debounced_extpower_presence) - *memmap_batt_flags |= EC_BATT_FLAG_AC_PRESENT; - else - *memmap_batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; - - /* Enable interrupts, now that we've initialized */ - gpio_enable_interrupt(GPIO_AC_PRESENT); -} -DECLARE_HOOK(HOOK_INIT, extpower_init, HOOK_PRIO_INIT_EXTPOWER); diff --git a/common/fan.c b/common/fan.c deleted file mode 100644 index 636bec04f9..0000000000 --- a/common/fan.c +++ /dev/null @@ -1,622 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Basic Chrome OS fan control */ - -#include "assert.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "fan.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "printf.h" -#include "system.h" -#include "util.h" - -/* True if we're listening to the thermal control task. False if we're setting - * things manually. */ -static int thermal_control_enabled[CONFIG_FANS]; - -int is_thermal_control_enabled(int idx) -{ - return thermal_control_enabled[idx]; -} - -#ifdef CONFIG_FAN_UPDATE_PERIOD -/* Should we ignore the fans for a while? */ -static int fan_update_counter[CONFIG_FANS]; -#endif - -/* - * Number of fans. - * - * Use fan_get_count and fan_set_count to access it. It should be set only - * before HOOK_INIT/HOOK_PRIO_DEFAULT. - */ -static int fan_count = CONFIG_FANS; - -int fan_get_count(void) -{ - return fan_count; -} - -void fan_set_count(int count) -{ - /* You can only decrease the count. */ - assert(count <= CONFIG_FANS); - fan_count = count; -} - -#ifndef CONFIG_FAN_RPM_CUSTOM -/* This is the default implementation. It's only called over [0,100]. - * Convert the percentage to a target RPM. We can't simply scale all - * the way down to zero because most fans won't turn that slowly, so - * we'll map [1,100] => [FAN_MIN,FAN_MAX], and [0] => "off". -*/ -int fan_percent_to_rpm(int fan, int pct) -{ - int rpm, max, min; - - if (!pct) { - rpm = 0; - } else { - min = fans[fan].rpm->rpm_min; - max = fans[fan].rpm->rpm_max; - rpm = ((pct - 1) * max + (100 - pct) * min) / 99; - } - - return rpm; -} -#endif /* CONFIG_FAN_RPM_CUSTOM */ - -/* The thermal task will only call this function with pct in [0,100]. */ -test_mockable void fan_set_percent_needed(int fan, int pct) -{ - int actual_rpm, new_rpm; - - if (!is_thermal_control_enabled(fan)) - return; - -#ifdef CONFIG_FAN_UPDATE_PERIOD - /* Only set each fan every so often, to avoid rapid changes. */ - fan_update_counter[fan] %= CONFIG_FAN_UPDATE_PERIOD; - if (fan_update_counter[fan]++) - return; -#endif - - new_rpm = fan_percent_to_rpm(fan, pct); - actual_rpm = fan_get_rpm_actual(FAN_CH(fan)); - - /* If we want to turn and the fans are currently significantly below - * the minimum turning speed, we should turn at least as fast as the - * necessary start speed instead. */ - if (new_rpm && - actual_rpm < fans[fan].rpm->rpm_min * 9 / 10 && - new_rpm < fans[fan].rpm->rpm_start) - new_rpm = fans[fan].rpm->rpm_start; - - fan_set_rpm_target(FAN_CH(fan), new_rpm); -} - -static void set_enabled(int fan, int enable) -{ - fan_set_enabled(FAN_CH(fan), enable); - - if (fans[fan].conf->enable_gpio >= 0) - gpio_set_level(fans[fan].conf->enable_gpio, enable); -} - -test_export_static void set_thermal_control_enabled(int fan, int enable) -{ - thermal_control_enabled[fan] = enable; - - /* If controlling the fan, need it in RPM-control mode */ - if (enable) - fan_set_rpm_mode(FAN_CH(fan), 1); -} - -static void set_duty_cycle(int fan, int percent) -{ - /* Move the fan to manual control */ - fan_set_rpm_mode(FAN_CH(fan), 0); - - /* enable the fan when non-zero duty */ - set_enabled(fan, (percent > 0) ? 1 : 0); - - /* Disable thermal engine automatic fan control. */ - set_thermal_control_enabled(fan, 0); - - /* Set the duty cycle */ - fan_set_duty(FAN_CH(fan), percent); -} - -/*****************************************************************************/ -/* Console commands */ - -static int cc_fanauto(int argc, char **argv) -{ - char *e; - int fan = 0; - - if (fan_count > 1) { - if (argc < 2) { - ccprintf("fan number is required as the first arg\n"); - return EC_ERROR_PARAM_COUNT; - } - fan = strtoi(argv[1], &e, 0); - if (*e || fan >= fan_count) - return EC_ERROR_PARAM1; - argc--; - argv++; - } - - set_thermal_control_enabled(fan, 1); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fanauto, cc_fanauto, - "{fan}", - "Enable thermal fan control"); - -/* Return 0 for off, 1 for on, -1 for unknown */ -static int is_powered(int fan) -{ - int is_pgood = -1; - - /* If we have an enable output, see if it's on or off. */ - if (fans[fan].conf->enable_gpio >= 0) - is_pgood = gpio_get_level(fans[fan].conf->enable_gpio); - /* If we have a pgood input, it overrides any enable output. */ - if (fans[fan].conf->pgood_gpio >= 0) - is_pgood = gpio_get_level(fans[fan].conf->pgood_gpio); - - return is_pgood; -} - -static int cc_faninfo(int argc, char **argv) -{ - static const char * const human_status[] = { - "not spinning", "changing", "locked", "frustrated" - }; - int tmp, is_pgood; - int fan; - char leader[20] = ""; - for (fan = 0; fan < fan_count; fan++) { - if (fan_count > 1) - snprintf(leader, sizeof(leader), "Fan %d ", fan); - if (fan) - ccprintf("\n"); - ccprintf("%sActual: %4d rpm\n", leader, - fan_get_rpm_actual(FAN_CH(fan))); - ccprintf("%sTarget: %4d rpm\n", leader, - fan_get_rpm_target(FAN_CH(fan))); - ccprintf("%sDuty: %d%%\n", leader, - fan_get_duty(FAN_CH(fan))); - tmp = fan_get_status(FAN_CH(fan)); - ccprintf("%sStatus: %d (%s)\n", leader, - tmp, human_status[tmp]); - ccprintf("%sMode: %s\n", leader, - fan_get_rpm_mode(FAN_CH(fan)) ? "rpm" : "duty"); - ccprintf("%sAuto: %s\n", leader, - is_thermal_control_enabled(fan) ? "yes" : "no"); - ccprintf("%sEnable: %s\n", leader, - fan_get_enabled(FAN_CH(fan)) ? "yes" : "no"); - is_pgood = is_powered(fan); - if (is_pgood >= 0) - ccprintf("%sPower: %s\n", leader, - is_pgood ? "yes" : "no"); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(faninfo, cc_faninfo, - NULL, - "Print fan info"); - -static int cc_fanset(int argc, char **argv) -{ - const char *rpm_str; - int rpm; - char *e; - int fan = 0; - - if (fan_count == 0) { - ccprintf("Fan count is zero\n"); - return EC_ERROR_INVAL; - } - - if (fan_count > 1) { - if (argc < 3) { - ccprintf("fan number is required as the first arg\n"); - return EC_ERROR_PARAM_COUNT; - } - } - - if (argc == 3) { - fan = strtoi(argv[1], &e, 0); - if (*e || fan >= fan_count) - return EC_ERROR_PARAM1; - rpm_str = argv[2]; - } else if (argc == 2) { - rpm_str = argv[1]; - } else { - return EC_ERROR_PARAM_COUNT; - } - - rpm = strtoi(rpm_str, &e, 0); - if (*e == '%') { /* Wait, that's a percentage */ - ccprintf("Fan rpm given as %d%%\n", rpm); - if (rpm < 0) - rpm = 0; - else if (rpm > 100) - rpm = 100; - rpm = fan_percent_to_rpm(fan, rpm); - } else if (*e) { - return EC_ERROR_PARAM1; - } - - /* Move the fan to automatic control */ - fan_set_rpm_mode(FAN_CH(fan), 1); - - /* enable the fan when non-zero rpm */ - set_enabled(fan, (rpm > 0) ? 1 : 0); - - /* Disable thermal engine automatic fan control. */ - set_thermal_control_enabled(fan, 0); - - fan_set_rpm_target(FAN_CH(fan), rpm); - - ccprintf("Setting fan %d rpm target to %d\n", fan, rpm); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fanset, cc_fanset, - "[fan] (rpm | pct%)", - "Set fan speed"); - -static int cc_fanduty(int argc, char **argv) -{ - const char *percent_str; - int percent = 0; - char *e; - int fan = 0; - - if (fan_count == 0) { - ccprintf("Fan count is zero\n"); - return EC_ERROR_INVAL; - } - - if (fan_count > 1) { - if (argc < 3) { - ccprintf("fan number is required as the first arg\n"); - return EC_ERROR_PARAM_COUNT; - } - } - - if (argc == 3) { - fan = strtoi(argv[1], &e, 0); - if (*e || fan >= fan_count) - return EC_ERROR_PARAM1; - percent_str = argv[2]; - } else if (argc == 2) { - percent_str = argv[1]; - } else { - return EC_ERROR_PARAM_COUNT; - } - - percent = strtoi(percent_str, &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - ccprintf("Setting fan %d duty cycle to %d%%\n", fan, percent); - set_duty_cycle(fan, percent); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fanduty, cc_fanduty, - "[fan] percent", - "Set fan duty cycle"); - -/*****************************************************************************/ -/* DPTF interface functions */ - -/* 0-100% if in duty mode. -1 if not */ -int dptf_get_fan_duty_target(void) -{ - int fan = 0; /* TODO(crosbug.com/p/23803) */ - - if (fan_count == 0) - return -1; - - if (is_thermal_control_enabled(fan) || fan_get_rpm_mode(FAN_CH(fan))) - return -1; - - return fan_get_duty(FAN_CH(fan)); -} - -/* 0-100% sets duty, out of range means let the EC drive */ -void dptf_set_fan_duty_target(int pct) -{ - int fan; - - if (pct < 0 || pct > 100) { - /* TODO(crosbug.com/p/23803) */ - for (fan = 0; fan < fan_count; fan++) - set_thermal_control_enabled(fan, 1); - } else { - /* TODO(crosbug.com/p/23803) */ - for (fan = 0; fan < fan_count; fan++) - set_duty_cycle(fan, pct); - } -} - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -hc_pwm_get_fan_target_rpm(struct host_cmd_handler_args *args) -{ - struct ec_response_pwm_get_fan_rpm *r = args->response; - - if (fan_count == 0) - return EC_RES_ERROR; - - /* TODO(crosbug.com/p/23803) */ - r->rpm = fan_get_rpm_target(FAN_CH(0)); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_FAN_TARGET_RPM, - hc_pwm_get_fan_target_rpm, - EC_VER_MASK(0)); - -static enum ec_status -hc_pwm_set_fan_target_rpm(struct host_cmd_handler_args *args) -{ - const struct ec_params_pwm_set_fan_target_rpm_v1 *p_v1 = args->params; - const struct ec_params_pwm_set_fan_target_rpm_v0 *p_v0 = args->params; - int fan; - - if (args->version == 0) { - for (fan = 0; fan < fan_count; fan++) { - /* enable the fan if rpm is non-zero */ - set_enabled(fan, (p_v0->rpm > 0) ? 1 : 0); - - set_thermal_control_enabled(fan, 0); - fan_set_rpm_mode(FAN_CH(fan), 1); - fan_set_rpm_target(FAN_CH(fan), p_v0->rpm); - } - - return EC_RES_SUCCESS; - } - - fan = p_v1->fan_idx; - if (fan >= fan_count) - return EC_RES_ERROR; - - /* enable the fan if rpm is non-zero */ - set_enabled(fan, (p_v1->rpm > 0) ? 1 :0); - - set_thermal_control_enabled(fan, 0); - fan_set_rpm_mode(FAN_CH(fan), 1); - fan_set_rpm_target(FAN_CH(fan), p_v1->rpm); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_TARGET_RPM, - hc_pwm_set_fan_target_rpm, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static enum ec_status hc_pwm_set_fan_duty(struct host_cmd_handler_args *args) -{ - const struct ec_params_pwm_set_fan_duty_v1 *p_v1 = args->params; - const struct ec_params_pwm_set_fan_duty_v0 *p_v0 = args->params; - int fan; - - if (args->version == 0) { - for (fan = 0; fan < fan_count; fan++) - set_duty_cycle(fan, p_v0->percent); - - return EC_RES_SUCCESS; - } - - fan = p_v1->fan_idx; - if (fan >= fan_count) - return EC_RES_ERROR; - - set_duty_cycle(fan, p_v1->percent); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_FAN_DUTY, - hc_pwm_set_fan_duty, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static enum ec_status -hc_thermal_auto_fan_ctrl(struct host_cmd_handler_args *args) -{ - int fan; - const struct ec_params_auto_fan_ctrl_v1 *p_v1 = args->params; - - if (args->version == 0) { - for (fan = 0; fan < fan_count; fan++) - set_thermal_control_enabled(fan, 1); - - return EC_RES_SUCCESS; - } - - fan = p_v1->fan_idx; - if (fan >= fan_count) - return EC_RES_ERROR; - - set_thermal_control_enabled(fan, 1); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_THERMAL_AUTO_FAN_CTRL, - hc_thermal_auto_fan_ctrl, - EC_VER_MASK(0)|EC_VER_MASK(1)); - - -/*****************************************************************************/ -/* Hooks */ - -/* We only have a limited number of memory-mapped slots to report fan speed to - * the AP. If we have more fans than that, some will be inaccessible. But - * if we're using that many fans, we probably have bigger problems. - */ -BUILD_ASSERT(CONFIG_FANS <= EC_FAN_SPEED_ENTRIES); - -#define PWMFAN_SYSJUMP_TAG 0x5046 /* "PF" */ -#define PWM_HOOK_VERSION 1 -/* Saved PWM state across sysjumps */ -struct pwm_fan_state { - /* TODO(crosbug.com/p/23530): Still treating all fans as one. */ - uint16_t rpm; - uint8_t flag; /* FAN_STATE_FLAG_* */ -}; - -/* For struct pwm_fan_state.flag */ -#define FAN_STATE_FLAG_ENABLED BIT(0) -#define FAN_STATE_FLAG_THERMAL BIT(1) - -static void pwm_fan_init(void) -{ - const struct pwm_fan_state *prev; - struct pwm_fan_state state; - uint16_t *mapped; - int version, size; - int i; - int fan; - - if (fan_count == 0) - return; - - for (fan = 0; fan < fan_count; fan++) - fan_channel_setup(FAN_CH(fan), fans[fan].conf->flags); - - /* Restore previous state. */ - prev = (const struct pwm_fan_state *) - system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size); - if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { - memcpy(&state, prev, sizeof(state)); - } else { - memset(&state, 0, sizeof(state)); - } - - for (fan = 0; fan < fan_count; fan++) { - fan_set_enabled(FAN_CH(fan), - state.flag & FAN_STATE_FLAG_ENABLED); - fan_set_rpm_target(FAN_CH(fan), state.rpm); - set_thermal_control_enabled( - fan, state.flag & FAN_STATE_FLAG_THERMAL); - } - - /* Initialize memory-mapped data */ - mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN); - for (i = 0; i < EC_FAN_SPEED_ENTRIES; i++) - mapped[i] = EC_FAN_SPEED_NOT_PRESENT; -} -DECLARE_HOOK(HOOK_INIT, pwm_fan_init, HOOK_PRIO_DEFAULT); - -static void pwm_fan_second(void) -{ - uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_FAN); - uint16_t rpm; - int stalled = 0; - int fan; - - for (fan = 0; fan < fan_count; fan++) { - if (fan_is_stalled(FAN_CH(fan))) { - rpm = EC_FAN_SPEED_STALLED; - stalled = 1; - cprints(CC_PWM, "Fan %d stalled!", fan); - } else { - rpm = fan_get_rpm_actual(FAN_CH(fan)); - } - - mapped[fan] = rpm; - } - - /* - * Issue warning. As we have thermal shutdown - * protection, issuing warning here should be enough. - */ - if (stalled) - host_set_single_event(EC_HOST_EVENT_THERMAL); -} -DECLARE_HOOK(HOOK_SECOND, pwm_fan_second, HOOK_PRIO_DEFAULT); - -static void pwm_fan_preserve_state(void) -{ - struct pwm_fan_state state = {0}; - int fan = 0; - - if (fan_count == 0) - return; - - /* TODO(crosbug.com/p/23530): Still treating all fans as one. */ - if (fan_get_enabled(FAN_CH(fan))) - state.flag |= FAN_STATE_FLAG_ENABLED; - if (is_thermal_control_enabled(fan)) - state.flag |= FAN_STATE_FLAG_THERMAL; - state.rpm = fan_get_rpm_target(FAN_CH(fan)); - - system_add_jump_tag(PWMFAN_SYSJUMP_TAG, PWM_HOOK_VERSION, - sizeof(state), &state); -} -DECLARE_HOOK(HOOK_SYSJUMP, pwm_fan_preserve_state, HOOK_PRIO_DEFAULT); - -static void pwm_fan_control(int enable) -{ - int fan; - - /* TODO(crosbug.com/p/23530): Still treating all fans as one. */ - for (fan = 0; fan < fan_count; fan++) { - set_thermal_control_enabled(fan, enable); - fan_set_rpm_target(FAN_CH(fan), enable ? - fan_percent_to_rpm(FAN_CH(fan), CONFIG_FAN_INIT_SPEED) : - 0); - set_enabled(fan, enable); - } -} - -static void pwm_fan_stop(void) -{ - /* - * There is no need to cool CPU in S3 or S5. We currently don't - * have fans for battery or charger chip. Battery systems will - * control charge current based on its own temperature readings. - * Thus, we do not need to keep fans running in S3 or S5. - * - * Even with a fan on charging system, it's questionable to run - * a fan in S3/S5. Under an extreme heat condition, spinning a - * fan would create more heat as it draws current from a - * battery and heat would come from ambient air instead of CPU. - * - * Thermal control may be already disabled if DPTF is used. - */ - pwm_fan_control(0); /* crosbug.com/p/8097 */ -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_stop, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_stop, HOOK_PRIO_DEFAULT); - -static void pwm_fan_start(void) -{ - /* - * Even if the DPTF is enabled, enable thermal control here. - * Upon booting to S0, if needed AP will disable/throttle it using - * host commands. - */ - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ON)) - pwm_fan_control(1); -} -/* On Fizz, CHIPSET_RESUME isn't triggered when AP warm resets. - * So we hook CHIPSET_RESET instead. - */ -DECLARE_HOOK(HOOK_CHIPSET_RESET, pwm_fan_start, HOOK_PRIO_FIRST); -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pwm_fan_start, HOOK_PRIO_DEFAULT); diff --git a/common/flash.c b/common/flash.c deleted file mode 100644 index c8f58a82af..0000000000 --- a/common/flash.c +++ /dev/null @@ -1,1562 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Flash memory module for Chrome EC - common functions */ - -#include "common.h" -#include "console.h" -#include "cros_board_info.h" -#include "flash.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "otp.h" -#include "rwsig.h" -#include "shared_mem.h" -#include "system.h" -#include "util.h" -#include "vboot_hash.h" - -/* - * Contents of erased flash, as a 32-bit value. Most platforms erase flash - * bits to 1. - */ -#ifndef CONFIG_FLASH_ERASED_VALUE32 -#define CONFIG_FLASH_ERASED_VALUE32 (-1U) -#endif - -#ifdef CONFIG_FLASH_PSTATE - -/* - * If flash isn't mapped to the EC's address space, it's probably SPI, and - * should be using SPI write protect, not PSTATE. - */ -#if !defined(CONFIG_INTERNAL_STORAGE) || !defined(CONFIG_MAPPED_STORAGE) -#error "PSTATE should only be used with internal mem-mapped flash." -#endif - -#ifdef CONFIG_FLASH_PSTATE_BANK -/* Persistent protection state - emulates a SPI status register for flashrom */ -/* NOTE: It's not expected that RO and RW will support - * differing PSTATE versions. */ -#define PERSIST_STATE_VERSION 3 /* Expected persist_state.version */ - -/* Flags for persist_state.flags */ -/* Protect persist state and RO firmware at boot */ -#define PERSIST_FLAG_PROTECT_RO 0x02 -#define PSTATE_VALID_FLAGS BIT(0) -#define PSTATE_VALID_SERIALNO BIT(1) -#define PSTATE_VALID_MAC_ADDR BIT(2) - -struct persist_state { - uint8_t version; /* Version of this struct */ - uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */ - uint8_t valid_fields; /* Flags for valid data. */ - uint8_t reserved; /* Reserved; set 0 */ -#ifdef CONFIG_SERIALNO_LEN - uint8_t serialno[CONFIG_SERIALNO_LEN]; /* Serial number. */ -#endif /* CONFIG_SERIALNO_LEN */ -#ifdef CONFIG_MAC_ADDR_LEN - uint8_t mac_addr[CONFIG_MAC_ADDR_LEN]; -#endif /* CONFIG_MAC_ADDR_LEN */ -#if !defined(CONFIG_SERIALNO_LEN) && !defined(CONFIG_MAC_ADDR_LEN) - uint8_t padding[4 % CONFIG_FLASH_WRITE_SIZE]; -#endif -}; - -/* written with flash_physical_write, need to respect alignment constraints */ -#ifndef CHIP_FAMILY_STM32L /* STM32L1xx is somewhat lying to us */ -BUILD_ASSERT(sizeof(struct persist_state) % CONFIG_FLASH_WRITE_SIZE == 0); -#endif - -BUILD_ASSERT(sizeof(struct persist_state) <= CONFIG_FW_PSTATE_SIZE); - -#else /* !CONFIG_FLASH_PSTATE_BANK */ - -/* - * Flags for write protect state depend on the erased value of flash. The - * locked value must be the same as the unlocked value with one or more bits - * transitioned away from the erased state. That way, it is possible to - * rewrite the data in-place to set the lock. - * - * STM32F0x can only write 0x0000 to a non-erased half-word, which means - * PSTATE_MAGIC_LOCKED isn't quite as pretty. That's ok; the only thing - * we actually need to detect is PSTATE_MAGIC_UNLOCKED, since that's the - * only value we'll ever alter, and the only value which causes us not to - * lock the flash at boot. - */ -#if (CONFIG_FLASH_ERASED_VALUE32 == -1U) -#define PSTATE_MAGIC_UNLOCKED 0x4f4e5057 /* "WPNO" */ -#define PSTATE_MAGIC_LOCKED 0x00000000 /* "" */ -#elif (CONFIG_FLASH_ERASED_VALUE32 == 0) -#define PSTATE_MAGIC_UNLOCKED 0x4f4e5057 /* "WPNO" */ -#define PSTATE_MAGIC_LOCKED 0x5f5f5057 /* "WP__" */ -#else -/* What kind of wacky flash doesn't erase all bits to 1 or 0? */ -#error "PSTATE needs magic values for this flash architecture." -#endif - -/* - * Rewriting the write protect flag in place currently requires a minimum write - * size <= the size of the flag value. - * - * We could work around this on chips with larger minimum write size by reading - * the write block containing the flag into RAM, changing it to the locked - * value, and then rewriting that block. But we should only pay for that - * complexity when we run across another chip which needs it. - */ -#if (CONFIG_FLASH_WRITE_SIZE > 4) -#error "Non-bank-based PSTATE requires flash write size <= 32 bits." -#endif - -const uint32_t pstate_data __attribute__((section(".rodata.pstate"))) = -#ifdef CONFIG_FLASH_PSTATE_LOCKED - PSTATE_MAGIC_LOCKED; -#else - PSTATE_MAGIC_UNLOCKED; -#endif - -#endif /* !CONFIG_FLASH_PSTATE_BANK */ -#endif /* CONFIG_FLASH_PSTATE */ - -#ifdef CONFIG_FLASH_MULTIPLE_REGION -const struct ec_flash_bank *flash_bank_info(int bank) -{ - int i; - for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) { - if (bank < flash_bank_array[i].count) - return &flash_bank_array[i]; - bank -= flash_bank_array[i].count; - } - - return NULL; -} - -int crec_flash_bank_size(int bank) -{ - int rv; - const struct ec_flash_bank *info = flash_bank_info(bank); - - if (!info) - return -1; - - rv = BIT(info->size_exp); - ASSERT(rv > 0); - return rv; -} - -int crec_flash_bank_erase_size(int bank) -{ - int rv; - const struct ec_flash_bank *info = flash_bank_info(bank); - - if (!info) - return -1; - - rv = BIT(info->erase_size_exp); - ASSERT(rv > 0); - return rv; -} - -int crec_flash_bank_index(int offset) -{ - int bank_offset = 0, i; - - if (offset == 0) - return bank_offset; - - for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) { - int all_sector_size = flash_bank_array[i].count << - flash_bank_array[i].size_exp; - if (offset >= all_sector_size) { - offset -= all_sector_size; - bank_offset += flash_bank_array[i].count; - continue; - } - if (offset & ((1 << flash_bank_array[i].size_exp) - 1)) - return -1; - return bank_offset + (offset >> flash_bank_array[i].size_exp); - } - if (offset != 0) - return -1; - return bank_offset; -} - -int crec_flash_bank_count(int offset, int size) -{ - int begin = crec_flash_bank_index(offset); - int end = crec_flash_bank_index(offset + size); - - if (begin == -1 || end == -1) - return -1; - return end - begin; -} - -int crec_flash_bank_start_offset(int bank) -{ - int i; - int offset; - int bank_size; - - if (bank < 0) - return -1; - - offset = 0; - for (i = 0; i < bank; i++) { - bank_size = crec_flash_bank_size(i); - if (bank_size < 0) - return -1; - offset += bank_size; - } - - return offset; -} - -#endif /* CONFIG_FLASH_MULTIPLE_REGION */ - -static int flash_range_ok(int offset, int size_req, int align) -{ - if (offset < 0 || size_req < 0 || - offset > CONFIG_FLASH_SIZE_BYTES || - size_req > CONFIG_FLASH_SIZE_BYTES || - offset + size_req > CONFIG_FLASH_SIZE_BYTES || - (offset | size_req) & (align - 1)) - return 0; /* Invalid range */ - - return 1; -} - -#ifdef CONFIG_MAPPED_STORAGE -/** - * Get the physical memory address of a flash offset - * - * This is used for direct flash access. We assume that the flash is - * contiguous from this start address through to the end of the usable - * flash. - * - * @param offset Flash offset to get address of - * @param dataptrp Returns pointer to memory address of flash offset - * @return pointer to flash memory offset, if ok, else NULL - */ -static const char *flash_physical_dataptr(int offset) -{ - return (char *)((uintptr_t)CONFIG_MAPPED_STORAGE_BASE + offset); -} - -int crec_flash_dataptr(int offset, int size_req, int align, const char **ptrp) -{ - if (!flash_range_ok(offset, size_req, align)) - return -1; /* Invalid range */ - if (ptrp) - *ptrp = flash_physical_dataptr(offset); - - return CONFIG_FLASH_SIZE_BYTES - offset; -} -#endif - -#ifdef CONFIG_FLASH_PSTATE -#ifdef CONFIG_FLASH_PSTATE_BANK - -/** - * Read and return persistent state flags (EC_FLASH_PROTECT_*) - */ -static uint32_t flash_read_pstate(void) -{ - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - if ((pstate->version == PERSIST_STATE_VERSION) && - (pstate->valid_fields & PSTATE_VALID_FLAGS) && - (pstate->flags & PERSIST_FLAG_PROTECT_RO)) { - /* Lock flag is known to be set */ - return EC_FLASH_PROTECT_RO_AT_BOOT; - } else { -#ifdef CONFIG_WP_ALWAYS - return PERSIST_FLAG_PROTECT_RO; -#else - return 0; -#endif - } -} - -/** - * Write persistent state after erasing. - * - * @param pstate New data to set in pstate. NOT memory mapped - * old pstate as it will be erased. - * @return EC_SUCCESS, or nonzero if error. - */ -static int flash_write_pstate_data(struct persist_state *newpstate) -{ - int rv; - - /* Erase pstate */ - rv = crec_flash_physical_erase(CONFIG_FW_PSTATE_OFF, - CONFIG_FW_PSTATE_SIZE); - if (rv) - return rv; - - /* - * Note that if we lose power in here, we'll lose the pstate contents. - * That's ok, because it's only possible to write the pstate before - * it's protected. - */ - - /* Write the updated pstate */ - return crec_flash_physical_write(CONFIG_FW_PSTATE_OFF, - sizeof(*newpstate), - (const char *)newpstate); -} - - - -/** - * Validate and Init persistent state datastructure. - * - * @param pstate A pstate data structure. Will be valid at complete. - * @return EC_SUCCESS, or nonzero if error. - */ -static int validate_pstate_struct(struct persist_state *pstate) -{ - if (pstate->version != PERSIST_STATE_VERSION) { - memset(pstate, 0, sizeof(*pstate)); - pstate->version = PERSIST_STATE_VERSION; - } - - return EC_SUCCESS; -} - -/** - * Write persistent state from pstate, erasing if necessary. - * - * @param flags New flash write protect flags to set in pstate. - * @return EC_SUCCESS, or nonzero if error. - */ -static int flash_write_pstate(uint32_t flags) -{ - struct persist_state newpstate; - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - /* Only check the flags we write to pstate */ - flags &= EC_FLASH_PROTECT_RO_AT_BOOT; - - /* Check if pstate has actually changed */ - if (flags == flash_read_pstate()) - return EC_SUCCESS; - - /* Cache the old copy for read/modify/write. */ - memcpy(&newpstate, pstate, sizeof(newpstate)); - validate_pstate_struct(&newpstate); - - if (flags & EC_FLASH_PROTECT_RO_AT_BOOT) - newpstate.flags |= PERSIST_FLAG_PROTECT_RO; - else - newpstate.flags &= ~PERSIST_FLAG_PROTECT_RO; - newpstate.valid_fields |= PSTATE_VALID_FLAGS; - - return flash_write_pstate_data(&newpstate); -} - -#ifdef CONFIG_SERIALNO_LEN -/** - * Read and return persistent serial number. - */ -const char *crec_flash_read_pstate_serial(void) -{ - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - if ((pstate->version == PERSIST_STATE_VERSION) && - (pstate->valid_fields & PSTATE_VALID_SERIALNO)) { - return (const char *)(pstate->serialno); - } - - return NULL; -} - -/** - * Write persistent serial number to pstate, erasing if necessary. - * - * @param serialno New ascii serial number to set in pstate. - * @return EC_SUCCESS, or nonzero if error. - */ -int crec_flash_write_pstate_serial(const char *serialno) -{ - int length; - struct persist_state newpstate; - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - /* Check that this is OK */ - if (!serialno) - return EC_ERROR_INVAL; - - length = strnlen(serialno, sizeof(newpstate.serialno)); - if (length >= sizeof(newpstate.serialno)) { - return EC_ERROR_INVAL; - } - - /* Cache the old copy for read/modify/write. */ - memcpy(&newpstate, pstate, sizeof(newpstate)); - validate_pstate_struct(&newpstate); - - /* - * Erase any prior data and copy the string. The length was verified to - * be shorter than the buffer so a null terminator always remains. - */ - memset(newpstate.serialno, '\0', sizeof(newpstate.serialno)); - memcpy(newpstate.serialno, serialno, length); - - newpstate.valid_fields |= PSTATE_VALID_SERIALNO; - - return flash_write_pstate_data(&newpstate); -} - -#endif /* CONFIG_SERIALNO_LEN */ - -#ifdef CONFIG_MAC_ADDR_LEN - -/** - * Read and return persistent MAC address. - */ -const char *crec_flash_read_pstate_mac_addr(void) -{ - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - if ((pstate->version == PERSIST_STATE_VERSION) && - (pstate->valid_fields & PSTATE_VALID_MAC_ADDR)) { - return (const char *)(pstate->mac_addr); - } - - return NULL; -} - -/** - * Write persistent MAC Addr to pstate, erasing if necessary. - * - * @param mac_addr New ascii MAC address to set in pstate. - * @return EC_SUCCESS, or nonzero if error. - */ -int crec_flash_write_pstate_mac_addr(const char *mac_addr) -{ - int length; - struct persist_state newpstate; - const struct persist_state *pstate = - (const struct persist_state *) - flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); - - /* Check that this is OK, data is valid and fits in the region. */ - if (!mac_addr) { - return EC_ERROR_INVAL; - } - - /* - * This will perform validation of the mac address before storing it. - * The MAC address format is '12:34:56:78:90:AB', a 17 character long - * string containing pairs of hex digits, each pair delimited by a ':'. - */ - length = strnlen(mac_addr, sizeof(newpstate.mac_addr)); - if (length != 17) { - return EC_ERROR_INVAL; - } - for (int i = 0; i < 17; i++) { - if (i % 3 != 2) { - /* Verify the remaining characters are hex digits. */ - if ((mac_addr[i] < '0' || '9' < mac_addr[i]) && - (mac_addr[i] < 'A' || 'F' < mac_addr[i]) && - (mac_addr[i] < 'a' || 'f' < mac_addr[i])) { - return EC_ERROR_INVAL; - } - } else { - /* Every 3rd character is a ':' */ - if (mac_addr[i] != ':') { - return EC_ERROR_INVAL; - } - } - } - - /* Cache the old copy for read/modify/write. */ - memcpy(&newpstate, pstate, sizeof(newpstate)); - validate_pstate_struct(&newpstate); - - /* - * Erase any prior data and copy the string. The length was verified to - * be shorter than the buffer so a null terminator always remains. - */ - memset(newpstate.mac_addr, '\0', sizeof(newpstate.mac_addr)); - memcpy(newpstate.mac_addr, mac_addr, length); - - newpstate.valid_fields |= PSTATE_VALID_MAC_ADDR; - - return flash_write_pstate_data(&newpstate); -} - -#endif /* CONFIG_MAC_ADDR_LEN */ - -#else /* !CONFIG_FLASH_PSTATE_BANK */ - -/** - * Return the address of the pstate data in EC-RO. - */ -static const uintptr_t get_pstate_addr(void) -{ - uintptr_t addr = (uintptr_t)&pstate_data; - - /* Always use the pstate data in RO, even if we're RW */ - if (system_is_in_rw()) - addr += CONFIG_RO_MEM_OFF - CONFIG_RW_MEM_OFF; - - return addr; -} - -/** - * Read and return persistent state flags (EC_FLASH_PROTECT_*) - */ -static uint32_t flash_read_pstate(void) -{ - /* Check for the unlocked magic value */ - if (*(const uint32_t *)get_pstate_addr() == PSTATE_MAGIC_UNLOCKED) - return 0; - - /* Anything else is locked */ - return EC_FLASH_PROTECT_RO_AT_BOOT; -} - -/** - * Write persistent state from pstate, erasing if necessary. - * - * @param flags New flash write protect flags to set in pstate. - * @return EC_SUCCESS, or nonzero if error. - */ -static int flash_write_pstate(uint32_t flags) -{ - const uint32_t new_pstate = PSTATE_MAGIC_LOCKED; - - /* Only check the flags we write to pstate */ - flags &= EC_FLASH_PROTECT_RO_AT_BOOT; - - /* Check if pstate has actually changed */ - if (flags == flash_read_pstate()) - return EC_SUCCESS; - - /* We can only set the protect flag, not clear it */ - if (!(flags & EC_FLASH_PROTECT_RO_AT_BOOT)) - return EC_ERROR_ACCESS_DENIED; - - /* - * Write a new pstate. We can overwrite the existing value, because - * we're only moving bits from the erased state to the unerased state. - */ - return crec_flash_physical_write(get_pstate_addr() - - CONFIG_PROGRAM_MEMORY_BASE, - sizeof(new_pstate), - (const char *)&new_pstate); -} - -#endif /* !CONFIG_FLASH_PSTATE_BANK */ -#endif /* CONFIG_FLASH_PSTATE */ - -int crec_flash_is_erased(uint32_t offset, int size) -{ - const uint32_t *ptr; - -#ifdef CONFIG_MAPPED_STORAGE - /* Use pointer directly to flash */ - if (crec_flash_dataptr(offset, size, sizeof(uint32_t), - (const char **)&ptr) < 0) - return 0; - - crec_flash_lock_mapped_storage(1); - for (size /= sizeof(uint32_t); size > 0; size--, ptr++) - if (*ptr != CONFIG_FLASH_ERASED_VALUE32) { - crec_flash_lock_mapped_storage(0); - return 0; - } - - crec_flash_lock_mapped_storage(0); -#else - /* Read flash a chunk at a time */ - uint32_t buf[8]; - int bsize; - - while (size) { - bsize = MIN(size, sizeof(buf)); - - if (crec_flash_read(offset, bsize, (char *)buf)) - return 0; - - size -= bsize; - offset += bsize; - - ptr = buf; - for (bsize /= sizeof(uint32_t); bsize > 0; bsize--, ptr++) - if (*ptr != CONFIG_FLASH_ERASED_VALUE32) - return 0; - - } -#endif - - return 1; -} - -int crec_flash_read(int offset, int size, char *data) -{ -#ifdef CONFIG_MAPPED_STORAGE - const char *src; - - if (crec_flash_dataptr(offset, size, 1, &src) < 0) - return EC_ERROR_INVAL; - - crec_flash_lock_mapped_storage(1); - memcpy(data, src, size); - crec_flash_lock_mapped_storage(0); - return EC_SUCCESS; -#else - return crec_flash_physical_read(offset, size, data); -#endif -} - -static void flash_abort_or_invalidate_hash(int offset, int size) -{ -#ifdef CONFIG_VBOOT_HASH - if (vboot_hash_in_progress()) { - /* Abort hash calculation when flash update is in progress. */ - vboot_hash_abort(); - return; - } - -#ifdef CONFIG_EXTERNAL_STORAGE - /* - * If EC executes in RAM and is currently in RW, we keep the current - * hash. On the next hash check, AP will catch hash mismatch between the - * flash copy and the RAM copy, then take necessary actions. - */ - if (system_is_in_rw()) - return; -#endif - - /* If EC executes in place, we need to invalidate the cached hash. */ - vboot_hash_invalidate(offset, size); -#endif - -#ifdef HAS_TASK_RWSIG - /* - * If RW flash has been written to, make sure we do not automatically - * jump to RW after the timeout. - */ - if ((offset >= CONFIG_RW_MEM_OFF && - offset < (CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE)) || - ((offset + size) > CONFIG_RW_MEM_OFF && - (offset + size) <= (CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE)) || - (offset < CONFIG_RW_MEM_OFF && - (offset + size) > (CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE))) - rwsig_abort(); -#endif -} - -int crec_flash_write(int offset, int size, const char *data) -{ - if (!flash_range_ok(offset, size, CONFIG_FLASH_WRITE_SIZE)) - return EC_ERROR_INVAL; /* Invalid range */ - - flash_abort_or_invalidate_hash(offset, size); - - return crec_flash_physical_write(offset, size, data); -} - -int crec_flash_erase(int offset, int size) -{ -#ifndef CONFIG_FLASH_MULTIPLE_REGION - if (!flash_range_ok(offset, size, CONFIG_FLASH_ERASE_SIZE)) - return EC_ERROR_INVAL; /* Invalid range */ -#endif - - flash_abort_or_invalidate_hash(offset, size); - - return crec_flash_physical_erase(offset, size); -} - -int crec_flash_protect_at_boot(uint32_t new_flags) -{ -#ifdef CONFIG_FLASH_PSTATE - uint32_t new_pstate_flags = new_flags & EC_FLASH_PROTECT_RO_AT_BOOT; - - /* Read the current persist state from flash */ - if (flash_read_pstate() != new_pstate_flags) { - /* Need to update pstate */ - int rv; - -#ifdef CONFIG_FLASH_PSTATE_BANK - /* Fail if write protect block is already locked */ - if (crec_flash_physical_get_protect(PSTATE_BANK)) - return EC_ERROR_ACCESS_DENIED; -#endif - - /* Write the desired flags */ - rv = flash_write_pstate(new_pstate_flags); - if (rv) - return rv; - } - -#ifdef CONFIG_FLASH_PROTECT_NEXT_BOOT - /* - * Try updating at-boot protection state, if on a platform where write - * protection only changes after a reboot. Otherwise we wouldn't - * update it until after the next reboot, and we'd need to reboot - * again. Ignore errors, because the protection registers might - * already be locked this boot, and we'll still apply the correct state - * again on the next boot. - * - * This assumes PSTATE immediately follows RO, which it does on - * all STM32 platforms (which are the only ones with this config). - */ - crec_flash_physical_protect_at_boot(new_flags); -#endif - - return EC_SUCCESS; -#else - return crec_flash_physical_protect_at_boot(new_flags); -#endif -} - -uint32_t crec_flash_get_protect(void) -{ - uint32_t flags = 0; - int i; - /* Region protection status */ - int not_protected[FLASH_REGION_COUNT] = {0}; -#ifdef CONFIG_ROLLBACK - /* Flags that must be set to set ALL_NOW flag. */ - const uint32_t all_flags = EC_FLASH_PROTECT_RO_NOW | - EC_FLASH_PROTECT_RW_NOW | - EC_FLASH_PROTECT_ROLLBACK_NOW; -#else - const uint32_t all_flags = EC_FLASH_PROTECT_RO_NOW | - EC_FLASH_PROTECT_RW_NOW; -#endif - - /* Read write protect GPIO */ -#ifdef CONFIG_WP_ALWAYS - flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; -#elif defined(CONFIG_WP_ACTIVE_HIGH) - if (gpio_get_level(GPIO_WP)) - flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; -#else - if (!gpio_get_level(GPIO_WP_L)) - flags |= EC_FLASH_PROTECT_GPIO_ASSERTED; -#endif - -#ifdef CONFIG_FLASH_PSTATE - /* Read persistent state of RO-at-boot flag */ - flags |= flash_read_pstate(); -#endif - - /* Scan flash protection */ - for (i = 0; i < PHYSICAL_BANKS; i++) { - int is_ro = (i >= WP_BANK_OFFSET && - i < WP_BANK_OFFSET + WP_BANK_COUNT); - enum flash_region region = is_ro ? FLASH_REGION_RO : - FLASH_REGION_RW; - int bank_flag = is_ro ? EC_FLASH_PROTECT_RO_NOW : - EC_FLASH_PROTECT_RW_NOW; - -#ifdef CONFIG_ROLLBACK - if (i >= ROLLBACK_BANK_OFFSET && - i < ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT) { - region = FLASH_REGION_ROLLBACK; - bank_flag = EC_FLASH_PROTECT_ROLLBACK_NOW; - } -#endif - - if (crec_flash_physical_get_protect(i)) { - /* At least one bank in the region is protected */ - flags |= bank_flag; - if (not_protected[region]) - flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; - } else { - /* At least one bank in the region is NOT protected */ - not_protected[region] = 1; - if (flags & bank_flag) - flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; - } - } - - if ((flags & all_flags) == all_flags) - flags |= EC_FLASH_PROTECT_ALL_NOW; - - /* - * If the RW or ROLLBACK banks are protected but the RO banks aren't, - * that's inconsistent. - * - * Note that we check this before adding in the physical flags below, - * since some chips can also protect ALL_NOW for the current boot by - * locking up the flash program-erase registers. - */ - if ((flags & all_flags) && !(flags & EC_FLASH_PROTECT_RO_NOW)) - flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; - -#ifndef CONFIG_FLASH_PROTECT_RW - /* RW flag was used for intermediate computations, clear it now. */ - flags &= ~EC_FLASH_PROTECT_RW_NOW; -#endif - - /* Add in flags from physical layer */ - return flags | crec_flash_physical_get_protect_flags(); -} - -/* - * Request a flash protection flags change for |mask| flash protect flags - * to |flags| state. - * - * Order of flag processing: - * 1. Clear/Set RO_AT_BOOT + Clear *_AT_BOOT flags + Commit *_AT_BOOT flags. - * 2. Return if RO_AT_BOOT and HW-WP are not asserted. - * 3. Set remaining *_AT_BOOT flags + Commit *_AT_BOOT flags. - * 4. Commit RO_NOW. - * 5. Commit ALL_NOW. - */ -int crec_flash_set_protect(uint32_t mask, uint32_t flags) -{ - int retval = EC_SUCCESS; - int rv; - int old_flags_at_boot = crec_flash_get_protect() & - (EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RW_AT_BOOT | - EC_FLASH_PROTECT_ROLLBACK_AT_BOOT | - EC_FLASH_PROTECT_ALL_AT_BOOT); - int new_flags_at_boot = old_flags_at_boot; - - /* Sanitize input flags */ - flags = flags & mask; - - /* - * Process flags we can set. Track the most recent error, but process - * all flags before returning. - */ - - /* - * AT_BOOT flags are trickier than NOW flags, as they can be set - * when HW write protection is disabled and can be unset without - * a reboot. - * - * If we are only setting/clearing RO_AT_BOOT, things are simple. - * Setting ALL_AT_BOOT is processed only if HW write protection is - * enabled and RO_AT_BOOT is set, so it's also simple. - * - * The most tricky one is when we want to clear ALL_AT_BOOT. We need - * to determine whether to clear protection for the entire flash or - * leave RO protected. There are two cases that we want to keep RO - * protected: - * A. RO_AT_BOOT was already set before flash_set_protect() is - * called. - * B. RO_AT_BOOT was not set, but it's requested to be set by - * the caller of flash_set_protect(). - */ - - /* 1.a - Clear RO_AT_BOOT. */ - new_flags_at_boot &= ~(mask & EC_FLASH_PROTECT_RO_AT_BOOT); - /* 1.b - Set RO_AT_BOOT. */ - new_flags_at_boot |= flags & EC_FLASH_PROTECT_RO_AT_BOOT; - - /* 1.c - Clear ALL_AT_BOOT. */ - if ((mask & EC_FLASH_PROTECT_ALL_AT_BOOT) && - !(flags & EC_FLASH_PROTECT_ALL_AT_BOOT)) { - new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT; - /* Must also clear RW/ROLLBACK. */ -#ifdef CONFIG_FLASH_PROTECT_RW - new_flags_at_boot &= ~EC_FLASH_PROTECT_RW_AT_BOOT; -#endif -#ifdef CONFIG_ROLLBACK - new_flags_at_boot &= ~EC_FLASH_PROTECT_ROLLBACK_AT_BOOT; -#endif - } - - /* 1.d - Clear RW_AT_BOOT. */ -#ifdef CONFIG_FLASH_PROTECT_RW - if ((mask & EC_FLASH_PROTECT_RW_AT_BOOT) && - !(flags & EC_FLASH_PROTECT_RW_AT_BOOT)) { - new_flags_at_boot &= ~EC_FLASH_PROTECT_RW_AT_BOOT; - /* Must also clear ALL (otherwise nothing will happen). */ - new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT; - } -#endif - - /* 1.e - Clear ROLLBACK_AT_BOOT. */ -#ifdef CONFIG_ROLLBACK - if ((mask & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) && - !(flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT)) { - new_flags_at_boot &= ~EC_FLASH_PROTECT_ROLLBACK_AT_BOOT; - /* Must also remove ALL (otherwise nothing will happen). */ - new_flags_at_boot &= ~EC_FLASH_PROTECT_ALL_AT_BOOT; - } -#endif - - /* 1.f - Commit *_AT_BOOT "clears" (and RO "set" 1.b). */ - if (new_flags_at_boot != old_flags_at_boot) { - rv = crec_flash_protect_at_boot(new_flags_at_boot); - if (rv) - retval = rv; - old_flags_at_boot = new_flags_at_boot; - } - - /* 2 - Return if RO_AT_BOOT and HW-WP are not asserted. - * - * All subsequent flags only work if write protect is enabled (that is, - * hardware WP flag) *and* RO is protected at boot (software WP flag). - */ - if ((~crec_flash_get_protect()) & (EC_FLASH_PROTECT_GPIO_ASSERTED | - EC_FLASH_PROTECT_RO_AT_BOOT)) - return retval; - - /* - * 3.a - Set ALL_AT_BOOT. - * - * The case where ALL/RW/ROLLBACK_AT_BOOT is cleared is already covered - * above, so we do not need to mask it out. - */ - new_flags_at_boot |= flags & EC_FLASH_PROTECT_ALL_AT_BOOT; - - /* 3.b - Set RW_AT_BOOT. */ -#ifdef CONFIG_FLASH_PROTECT_RW - new_flags_at_boot |= flags & EC_FLASH_PROTECT_RW_AT_BOOT; -#endif - - /* 3.c - Set ROLLBACK_AT_BOOT. */ -#ifdef CONFIG_ROLLBACK - new_flags_at_boot |= flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT; -#endif - - /* 3.d - Commit *_AT_BOOT "sets". */ - if (new_flags_at_boot != old_flags_at_boot) { - rv = crec_flash_protect_at_boot(new_flags_at_boot); - if (rv) - retval = rv; - } - - /* 4 - Commit RO_NOW. */ - if (flags & EC_FLASH_PROTECT_RO_NOW) { - rv = crec_flash_physical_protect_now(0); - if (rv) - retval = rv; - - /* - * Latch the CBI EEPROM WP immediately if HW WP is asserted and - * we're now protecting the RO region with SW WP. - */ - if (IS_ENABLED(CONFIG_EEPROM_CBI_WP) && - (EC_FLASH_PROTECT_GPIO_ASSERTED & - crec_flash_get_protect())) - cbi_latch_eeprom_wp(); - } - - /* 5 - Commit ALL_NOW. */ - if (flags & EC_FLASH_PROTECT_ALL_NOW) { - rv = crec_flash_physical_protect_now(1); - if (rv) - retval = rv; - } - - return retval; -} - -#ifdef CONFIG_FLASH_DEFERRED_ERASE -static volatile enum ec_status erase_rc = EC_RES_SUCCESS; -static struct ec_params_flash_erase_v1 erase_info; - -static void flash_erase_deferred(void) -{ - erase_rc = EC_RES_BUSY; - if (crec_flash_erase(erase_info.params.offset, erase_info.params.size)) - erase_rc = EC_RES_ERROR; - else - erase_rc = EC_RES_SUCCESS; -} -DECLARE_DEFERRED(flash_erase_deferred); -#endif - -/*****************************************************************************/ -/* Console commands */ - -#ifdef CONFIG_CMD_FLASHINFO -static int command_flash_info(int argc, char **argv) -{ - int i, flags; - - ccprintf("Usable: %4d KB\n", CONFIG_FLASH_SIZE_BYTES / 1024); - ccprintf("Write: %4d B (ideal %d B)\n", CONFIG_FLASH_WRITE_SIZE, - CONFIG_FLASH_WRITE_IDEAL_SIZE); -#ifdef CONFIG_FLASH_MULTIPLE_REGION - ccprintf("Regions:\n"); - for (i = 0; i < ARRAY_SIZE(flash_bank_array); i++) { - ccprintf(" %d region%s:\n", - flash_bank_array[i].count, - (flash_bank_array[i].count == 1 ? "" : "s")); - ccprintf(" Erase: %4d B (to %d-bits)\n", - 1 << flash_bank_array[i].erase_size_exp, - CONFIG_FLASH_ERASED_VALUE32 ? 1 : 0); - ccprintf(" Size/Protect: %4d B\n", - 1 << flash_bank_array[i].size_exp); - } -#else - ccprintf("Erase: %4d B (to %d-bits)\n", CONFIG_FLASH_ERASE_SIZE, - CONFIG_FLASH_ERASED_VALUE32 ? 1 : 0); - ccprintf("Protect: %4d B\n", CONFIG_FLASH_BANK_SIZE); -#endif - flags = crec_flash_get_protect(); - ccprintf("Flags: "); - if (flags & EC_FLASH_PROTECT_GPIO_ASSERTED) - ccputs(" wp_gpio_asserted"); - if (flags & EC_FLASH_PROTECT_RO_AT_BOOT) - ccputs(" ro_at_boot"); - if (flags & EC_FLASH_PROTECT_ALL_AT_BOOT) - ccputs(" all_at_boot"); - if (flags & EC_FLASH_PROTECT_RO_NOW) - ccputs(" ro_now"); - if (flags & EC_FLASH_PROTECT_ALL_NOW) - ccputs(" all_now"); -#ifdef CONFIG_FLASH_PROTECT_RW - if (flags & EC_FLASH_PROTECT_RW_AT_BOOT) - ccputs(" rw_at_boot"); - if (flags & EC_FLASH_PROTECT_RW_NOW) - ccputs(" rw_now"); -#endif - if (flags & EC_FLASH_PROTECT_ERROR_STUCK) - ccputs(" STUCK"); - if (flags & EC_FLASH_PROTECT_ERROR_INCONSISTENT) - ccputs(" INCONSISTENT"); -#ifdef CONFIG_ROLLBACK - if (flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) - ccputs(" rollback_at_boot"); - if (flags & EC_FLASH_PROTECT_ROLLBACK_NOW) - ccputs(" rollback_now"); -#endif - ccputs("\n"); - - ccputs("Protected now:"); - for (i = 0; i < PHYSICAL_BANKS; i++) { - if (!(i & 31)) - ccputs("\n "); - else if (!(i & 7)) - ccputs(" "); - ccputs(crec_flash_physical_get_protect(i) ? "Y" : "."); - } - ccputs("\n"); - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(flashinfo, command_flash_info, - NULL, - "Print flash info"); -#endif /* CONFIG_CMD_FLASHINFO */ - -#ifdef CONFIG_CMD_FLASH -static int command_flash_erase(int argc, char **argv) -{ - int offset = -1; - int size = -1; - int rv; - - if (crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) - return EC_ERROR_ACCESS_DENIED; - - rv = parse_offset_size(argc, argv, 1, &offset, &size); - if (rv) - return rv; - - ccprintf("Erasing %d bytes at 0x%x...\n", size, offset); - return crec_flash_erase(offset, size); -} -DECLARE_CONSOLE_COMMAND(flasherase, command_flash_erase, - "offset size", - "Erase flash"); - -static int command_flash_write(int argc, char **argv) -{ - int offset = -1; - int size = -1; - int rv; - char *data; - int i; - - if (crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) - return EC_ERROR_ACCESS_DENIED; - - rv = parse_offset_size(argc, argv, 1, &offset, &size); - if (rv) - return rv; - - if (size > shared_mem_size()) - size = shared_mem_size(); - - /* Acquire the shared memory buffer */ - rv = shared_mem_acquire(size, &data); - if (rv) { - ccputs("Can't get shared mem\n"); - return rv; - } - - /* Fill the data buffer with a pattern */ - for (i = 0; i < size; i++) - data[i] = i; - - ccprintf("Writing %d bytes to 0x%x...\n", size, offset); - rv = crec_flash_write(offset, size, data); - - /* Free the buffer */ - shared_mem_release(data); - - return rv; -} -DECLARE_CONSOLE_COMMAND(flashwrite, command_flash_write, - "offset size", - "Write pattern to flash"); - -static int command_flash_read(int argc, char **argv) -{ - int offset = -1; - int size = 256; - int rv; - char *data; - int i; - - rv = parse_offset_size(argc, argv, 1, &offset, &size); - if (rv) - return rv; - - if (size > shared_mem_size()) - size = shared_mem_size(); - - /* Acquire the shared memory buffer */ - rv = shared_mem_acquire(size, &data); - if (rv) { - ccputs("Can't get shared mem\n"); - return rv; - } - - /* Read the data */ - if (crec_flash_read(offset, size, data)) { - shared_mem_release(data); - return EC_ERROR_INVAL; - } - - /* Dump it */ - for (i = 0; i < size; i++) { - if ((offset + i) % 16) { - ccprintf(" %02x", data[i]); - } else { - ccprintf("\n%08x: %02x", offset + i, data[i]); - cflush(); - } - } - ccprintf("\n"); - - /* Free the buffer */ - shared_mem_release(data); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(flashread, command_flash_read, - "offset [size]", - "Read flash"); -#endif - -#ifdef CONFIG_CMD_FLASH_WP -static int command_flash_wp(int argc, char **argv) -{ - int val; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "now")) - return crec_flash_set_protect(EC_FLASH_PROTECT_ALL_NOW, -1); - - if (!strcasecmp(argv[1], "all")) - return crec_flash_set_protect(EC_FLASH_PROTECT_ALL_AT_BOOT, -1); - - if (!strcasecmp(argv[1], "noall")) - return crec_flash_set_protect(EC_FLASH_PROTECT_ALL_AT_BOOT, 0); - -#ifdef CONFIG_FLASH_PROTECT_RW - if (!strcasecmp(argv[1], "rw")) - return crec_flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, -1); - - if (!strcasecmp(argv[1], "norw")) - return crec_flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, 0); -#endif - -#ifdef CONFIG_ROLLBACK - if (!strcasecmp(argv[1], "rb")) - return crec_flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_AT_BOOT, - -1); - - if (!strcasecmp(argv[1], "norb")) - return crec_flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_AT_BOOT, - 0); -#endif - - /* Do this last, since anything starting with 'n' means "no" */ - if (parse_bool(argv[1], &val)) - return crec_flash_set_protect(EC_FLASH_PROTECT_RO_AT_BOOT, - val ? -1 : 0); - - return EC_ERROR_PARAM1; -} -DECLARE_CONSOLE_COMMAND(flashwp, command_flash_wp, - "<BOOLEAN> | now | all | noall" -#ifdef CONFIG_FLASH_PROTECT_RW - " | rw | norw" -#endif -#ifdef CONFIG_ROLLBACK - " | rb | norb" -#endif - , "Modify flash write protect"); -#endif /* CONFIG_CMD_FLASH_WP */ - -/*****************************************************************************/ -/* Host commands */ - -/* - * All internal EC code assumes that offsets are provided relative to - * physical address zero of storage. In some cases, the region of storage - * belonging to the EC is not physical address zero - a non-zero fmap_base - * indicates so. Since fmap_base is not yet handled correctly by external - * code, we must perform the adjustment in our host command handlers - - * adjust all offsets so they are relative to the beginning of the storage - * region belonging to the EC. TODO(crbug.com/529365): Handle fmap_base - * correctly in flashrom, dump_fmap, etc. and remove EC_FLASH_REGION_START. - */ -#define EC_FLASH_REGION_START MIN(CONFIG_EC_PROTECTED_STORAGE_OFF, \ - CONFIG_EC_WRITABLE_STORAGE_OFF) - -static enum ec_status flash_command_get_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_info_2 *p_2 = args->params; - struct ec_response_flash_info_2 *r_2 = args->response; -#ifdef CONFIG_FLASH_MULTIPLE_REGION - int banks_size = ARRAY_SIZE(flash_bank_array); - const struct ec_flash_bank *banks = flash_bank_array; -#else - struct ec_response_flash_info_1 *r_1 = args->response; -#if CONFIG_FLASH_BANK_SIZE < CONFIG_FLASH_ERASE_SIZE -#error "Flash: Bank size expected bigger or equal to erase size." -#endif - struct ec_flash_bank single_bank = { - .count = CONFIG_FLASH_SIZE_BYTES / CONFIG_FLASH_BANK_SIZE, - .size_exp = __fls(CONFIG_FLASH_BANK_SIZE), - .write_size_exp = __fls(CONFIG_FLASH_WRITE_SIZE), - .erase_size_exp = __fls(CONFIG_FLASH_ERASE_SIZE), - .protect_size_exp = __fls(CONFIG_FLASH_BANK_SIZE), - }; - int banks_size = 1; - const struct ec_flash_bank *banks = &single_bank; -#endif - int banks_len; - int ideal_size; - - /* - * Compute the ideal amount of data for the host to send us, - * based on the maximum response size and the ideal write size. - */ - ideal_size = (args->response_max - - sizeof(struct ec_params_flash_write)) & - ~(CONFIG_FLASH_WRITE_IDEAL_SIZE - 1); - /* - * If we can't get at least one ideal block, then just want - * as high a multiple of the minimum write size as possible. - */ - if (!ideal_size) - ideal_size = (args->response_max - - sizeof(struct ec_params_flash_write)) & - ~(CONFIG_FLASH_WRITE_SIZE - 1); - - - if (args->version >= 2) { - args->response_size = sizeof(struct ec_response_flash_info_2); - r_2->flash_size = - CONFIG_FLASH_SIZE_BYTES - EC_FLASH_REGION_START; -#if (CONFIG_FLASH_ERASED_VALUE32 == 0) - r_2->flags = EC_FLASH_INFO_ERASE_TO_0; -#else - r_2->flags = 0; -#endif -#ifdef CONFIG_FLASH_SELECT_REQUIRED - r_2->flags |= EC_FLASH_INFO_SELECT_REQUIRED; -#endif - r_2->write_ideal_size = ideal_size; - r_2->num_banks_total = banks_size; - r_2->num_banks_desc = MIN(banks_size, p_2->num_banks_desc); - banks_len = r_2->num_banks_desc * sizeof(struct ec_flash_bank); - memcpy(r_2->banks, banks, banks_len); - args->response_size += banks_len; - return EC_RES_SUCCESS; - } -#ifdef CONFIG_FLASH_MULTIPLE_REGION - return EC_RES_INVALID_PARAM; -#else - r_1->flash_size = CONFIG_FLASH_SIZE_BYTES - EC_FLASH_REGION_START; - r_1->flags = 0; - r_1->write_block_size = CONFIG_FLASH_WRITE_SIZE; - r_1->erase_block_size = CONFIG_FLASH_ERASE_SIZE; - r_1->protect_block_size = CONFIG_FLASH_BANK_SIZE; - if (args->version == 0) { - /* Only version 0 fields returned */ - args->response_size = sizeof(struct ec_response_flash_info); - } else { - args->response_size = sizeof(struct ec_response_flash_info_1); - /* Fill in full version 1 struct */ - r_1->write_ideal_size = ideal_size; -#if (CONFIG_FLASH_ERASED_VALUE32 == 0) - r_1->flags |= EC_FLASH_INFO_ERASE_TO_0; -#endif -#ifdef CONFIG_FLASH_SELECT_REQUIRED - r_1->flags |= EC_FLASH_INFO_SELECT_REQUIRED; -#endif - } - return EC_RES_SUCCESS; -#endif /* CONFIG_FLASH_MULTIPLE_REGION */ -} -#ifdef CONFIG_FLASH_MULTIPLE_REGION -#define FLASH_INFO_VER EC_VER_MASK(2) -#else -#define FLASH_INFO_VER (EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2)) -#endif -DECLARE_HOST_COMMAND(EC_CMD_FLASH_INFO, - flash_command_get_info, FLASH_INFO_VER); - - -static enum ec_status flash_command_read(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_read *p = args->params; - uint32_t offset = p->offset + EC_FLASH_REGION_START; - - if (p->size > args->response_max) - return EC_RES_OVERFLOW; - - if (crec_flash_read(offset, p->size, args->response)) - return EC_RES_ERROR; - - args->response_size = p->size; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_READ, - flash_command_read, - EC_VER_MASK(0)); - -/** - * Flash write command - * - * Version 0 and 1 are equivalent from the EC-side; the only difference is - * that the host can only send 64 bytes of data at a time in version 0. - */ -static enum ec_status flash_command_write(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_write *p = args->params; - uint32_t offset = p->offset + EC_FLASH_REGION_START; - - if (crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) - return EC_RES_ACCESS_DENIED; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#ifdef CONFIG_INTERNAL_STORAGE - if (system_unsafe_to_overwrite(offset, p->size)) - return EC_RES_ACCESS_DENIED; -#endif - - if (crec_flash_write(offset, p->size, (const uint8_t *)(p + 1))) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_WRITE, - flash_command_write, - EC_VER_MASK(0) | EC_VER_MASK(EC_VER_FLASH_WRITE)); - -#ifndef CONFIG_FLASH_MULTIPLE_REGION -/* - * Make sure our image sizes are a multiple of flash block erase size so that - * the host can erase the entire image. - * Note that host (flashrom/depthcharge) does not erase/program the - * EC_FLASH_REGION_RO region, it only queries this region. - */ -BUILD_ASSERT(CONFIG_WP_STORAGE_SIZE % CONFIG_FLASH_ERASE_SIZE == 0); -BUILD_ASSERT(CONFIG_EC_WRITABLE_STORAGE_SIZE % CONFIG_FLASH_ERASE_SIZE == 0); - -#endif - -static enum ec_status flash_command_erase(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_erase *p = args->params; - int rc = EC_RES_SUCCESS, cmd = FLASH_ERASE_SECTOR; - uint32_t offset; -#ifdef CONFIG_FLASH_DEFERRED_ERASE - const struct ec_params_flash_erase_v1 *p_1 = args->params; - - if (args->version > 0) { - cmd = p_1->cmd; - p = &p_1->params; - } -#endif - offset = p->offset + EC_FLASH_REGION_START; - - if (crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) - return EC_RES_ACCESS_DENIED; - -#ifdef CONFIG_INTERNAL_STORAGE - if (system_unsafe_to_overwrite(offset, p->size)) - return EC_RES_ACCESS_DENIED; -#endif - - switch (cmd) { - case FLASH_ERASE_SECTOR: -#if defined(HAS_TASK_HOSTCMD) && defined(CONFIG_HOST_COMMAND_STATUS) - args->result = EC_RES_IN_PROGRESS; - host_send_response(args); -#endif - if (crec_flash_erase(offset, p->size)) - return EC_RES_ERROR; - - break; -#ifdef CONFIG_FLASH_DEFERRED_ERASE - case FLASH_ERASE_SECTOR_ASYNC: - rc = erase_rc; - if (rc == EC_RES_SUCCESS) { - memcpy(&erase_info, p_1, sizeof(*p_1)); - hook_call_deferred(&flash_erase_deferred_data, - 100 * MSEC); - } else { - /* - * Not our job to return the result of - * the previous command. - */ - rc = EC_RES_BUSY; - } - break; - case FLASH_ERASE_GET_RESULT: - rc = erase_rc; - if (rc != EC_RES_BUSY) - /* Ready for another command */ - erase_rc = EC_RES_SUCCESS; - break; -#endif - default: - rc = EC_RES_INVALID_PARAM; - } - return rc; -} - - -DECLARE_HOST_COMMAND(EC_CMD_FLASH_ERASE, flash_command_erase, - EC_VER_MASK(0) -#ifdef CONFIG_FLASH_DEFERRED_ERASE - | EC_VER_MASK(1) -#endif - ); - -static enum ec_status flash_command_protect(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_protect *p = args->params; - struct ec_response_flash_protect *r = args->response; - - /* - * Handle requesting new flags. Note that we ignore the return code - * from flash_set_protect(), since errors will be visible to the caller - * via the flags in the response. (If we returned error, the caller - * wouldn't get the response.) - */ - if (p->mask) - crec_flash_set_protect(p->mask, p->flags); - - /* - * Retrieve the current flags. The caller can use this to determine - * which of the requested flags could be set. This is cleaner than - * simply returning error, because it provides information to the - * caller about the actual result. - */ - r->flags = crec_flash_get_protect(); - - /* Indicate which flags are valid on this platform */ - r->valid_flags = - EC_FLASH_PROTECT_GPIO_ASSERTED | - EC_FLASH_PROTECT_ERROR_STUCK | - EC_FLASH_PROTECT_ERROR_INCONSISTENT | - crec_flash_physical_get_valid_flags(); - r->writable_flags = crec_flash_physical_get_writable_flags(r->flags); - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} - -/* - * TODO(crbug.com/239197) : Adding both versions to the version mask is a - * temporary workaround for a problem in the cros_ec driver. Drop - * EC_VER_MASK(0) once cros_ec driver can send the correct version. - */ -DECLARE_HOST_COMMAND(EC_CMD_FLASH_PROTECT, - flash_command_protect, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static enum ec_status -flash_command_region_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_region_info *p = args->params; - struct ec_response_flash_region_info *r = args->response; - - switch (p->region) { - case EC_FLASH_REGION_RO: - r->offset = CONFIG_EC_PROTECTED_STORAGE_OFF + - CONFIG_RO_STORAGE_OFF - - EC_FLASH_REGION_START; - r->size = EC_FLASH_REGION_RO_SIZE; - break; - case EC_FLASH_REGION_ACTIVE: - r->offset = flash_get_rw_offset(system_get_active_copy()) - - EC_FLASH_REGION_START; - r->size = CONFIG_EC_WRITABLE_STORAGE_SIZE; - break; - case EC_FLASH_REGION_WP_RO: - r->offset = CONFIG_WP_STORAGE_OFF - - EC_FLASH_REGION_START; - r->size = CONFIG_WP_STORAGE_SIZE; - break; - case EC_FLASH_REGION_UPDATE: - r->offset = flash_get_rw_offset(system_get_update_copy()) - - EC_FLASH_REGION_START; - r->size = CONFIG_EC_WRITABLE_STORAGE_SIZE; - break; - default: - return EC_RES_INVALID_PARAM; - } - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_REGION_INFO, - flash_command_region_info, - EC_VER_MASK(EC_VER_FLASH_REGION_INFO)); - - -#ifdef CONFIG_FLASH_SELECT_REQUIRED - -static enum ec_status flash_command_select(struct host_cmd_handler_args *args) -{ - const struct ec_params_flash_select *p = args->params; - - return crec_board_flash_select(p->select); -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_SELECT, - flash_command_select, - EC_VER_MASK(0)); - -#endif /* CONFIG_FLASH_SELECT_REQUIRED */ diff --git a/common/fmap.c b/common/fmap.c deleted file mode 100644 index 47fa75f0e9..0000000000 --- a/common/fmap.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2012 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 <stddef.h> - -#include "common.h" -#include "cros_version.h" -#include "rwsig.h" -#include "util.h" - -/* - * FMAP structs. - * See https://chromium.googlesource.com/chromiumos/third_party/flashmap/+/master/lib/fmap.h - */ -#define FMAP_NAMELEN 32 -#define FMAP_SIGNATURE "__FMAP__" -#define FMAP_SIGNATURE_SIZE 8 -#define FMAP_VER_MAJOR 1 -#define FMAP_VER_MINOR 0 - -/* - * For address containing CONFIG_PROGRAM_MEMORY_BASE (symbols in *.RO.lds.S and - * variable), this computes the offset to the start of the image on flash. - */ -#define RELATIVE_RO(addr) ((addr) - CONFIG_PROGRAM_MEMORY_BASE - \ - CONFIG_RO_MEM_OFF) - -/* - * All internal EC code assumes that offsets are provided relative to - * physical address zero of storage. In some cases, the region of storage - * belonging to the EC is not physical address zero - a non-zero fmap_base - * indicates so. Since fmap_base is not yet handled correctly by external - * code, we must perform the adjustment in our host command handlers - - * adjust all offsets so they are relative to the beginning of the storage - * region belonging to the EC. TODO(crbug.com/529365): Handle fmap_base - * correctly in flashrom, dump_fmap, etc. and remove EC_FLASH_REGION_START. - */ -#if CONFIG_EC_WRITABLE_STORAGE_OFF < CONFIG_EC_PROTECTED_STORAGE_OFF -#define FMAP_REGION_START CONFIG_EC_WRITABLE_STORAGE_OFF -#else -#define FMAP_REGION_START CONFIG_EC_PROTECTED_STORAGE_OFF -#endif - -struct fmap_header { - char fmap_signature[FMAP_SIGNATURE_SIZE]; - uint8_t fmap_ver_major; - uint8_t fmap_ver_minor; - uint64_t fmap_base; - uint32_t fmap_size; - char fmap_name[FMAP_NAMELEN]; - uint16_t fmap_nareas; -} __packed; - -#define FMAP_AREA_STATIC BIT(0) /* can be checksummed */ -#define FMAP_AREA_COMPRESSED BIT(1) /* may be compressed */ -#define FMAP_AREA_RO BIT(2) /* writes may fail */ - -struct fmap_area_header { - uint32_t area_offset; - uint32_t area_size; - char area_name[FMAP_NAMELEN]; - uint16_t area_flags; -} __packed; - -#ifdef CONFIG_RWSIG_TYPE_RWSIG -#define NUM_EC_FMAP_AREAS_RWSIG 2 -#else -#define NUM_EC_FMAP_AREAS_RWSIG 0 -#endif - -#ifdef CONFIG_ROLLBACK -#define NUM_EC_FMAP_AREAS_ROLLBACK 1 -#else -#define NUM_EC_FMAP_AREAS_ROLLBACK 0 -#endif -#ifdef CONFIG_RW_B -# ifdef CONFIG_RWSIG_TYPE_RWSIG -# define NUM_EC_FMAP_AREAS_RW_B 2 -# else -# define NUM_EC_FMAP_AREAS_RW_B 1 -# endif -#else -#define NUM_EC_FMAP_AREAS_RW_B 0 -#endif - -#define NUM_EC_FMAP_AREAS (7 + \ - NUM_EC_FMAP_AREAS_RWSIG + \ - NUM_EC_FMAP_AREAS_ROLLBACK + \ - NUM_EC_FMAP_AREAS_RW_B) - -const struct _ec_fmap { - struct fmap_header header; - struct fmap_area_header area[NUM_EC_FMAP_AREAS]; -} ec_fmap __keep __attribute__((section(".google"))) = { - /* Header */ - { - .fmap_signature = {'_', '_', 'F', 'M', 'A', 'P', '_', '_'}, - .fmap_ver_major = FMAP_VER_MAJOR, - .fmap_ver_minor = FMAP_VER_MINOR, - .fmap_base = CONFIG_PROGRAM_MEMORY_BASE, - .fmap_size = CONFIG_FLASH_SIZE_BYTES, - /* Used to distinguish the EC FMAP from other FMAPs */ - .fmap_name = "EC_FMAP", - .fmap_nareas = NUM_EC_FMAP_AREAS, - }, - - { - /* RO Firmware */ - { - /* - * Range of RO firmware to be updated. EC_RO - * section includes the bootloader section - * because it may need to be updated/paired - * with a different RO. Verified in factory - * finalization by hash. Should not have - * volatile data (ex, calibration results). - */ - .area_name = "EC_RO", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START, - .area_size = CONFIG_RO_SIZE + CONFIG_RO_STORAGE_OFF, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, - { - /* (Optional) RO firmware code. */ - .area_name = "FR_MAIN", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RO_STORAGE_OFF, - .area_size = CONFIG_RO_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, - { - /* - * RO firmware version ID. Must be NULL terminated - * ASCII, and padded with \0. - */ - .area_name = "RO_FRID", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RO_STORAGE_OFF + - RELATIVE_RO((uint32_t)__image_data_offset) + - offsetof(struct image_data, version), - .area_size = sizeof(current_image_data.version), - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, - - /* Other RO stuff: FMAP, WP, KEYS, etc. */ - { - .area_name = "FMAP", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RO_STORAGE_OFF + - RELATIVE_RO((uint32_t)&ec_fmap), - .area_size = sizeof(ec_fmap), - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, - { - /* - * The range for write protection, for factory - * finalization. Should include (may be identical to) - * EC_RO and aligned to hardware specification. - */ - .area_name = "WP_RO", - .area_offset = CONFIG_WP_STORAGE_OFF - - FMAP_REGION_START, - .area_size = CONFIG_WP_STORAGE_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, -#ifdef CONFIG_RWSIG_TYPE_RWSIG - { - /* RO public key address, for RW verification */ - .area_name = "KEY_RO", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RO_PUBKEY_ADDR - - CONFIG_PROGRAM_MEMORY_BASE, - .area_size = CONFIG_RO_PUBKEY_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, -#endif - - /* RW Firmware */ - { - /* The range of RW firmware to be auto-updated. */ - .area_name = "EC_RW", - .area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_STORAGE_OFF, - .area_size = CONFIG_RW_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, - { - /* - * RW firmware version ID. Must be NULL terminated - * ASCII, and padded with \0. - * TODO: Get the relative offset of - * __image_data_offset within our RW image to - * accommodate image asymmetry. - */ - .area_name = "RW_FWID", - .area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_STORAGE_OFF + - RELATIVE_RO((uint32_t)__image_data_offset) + - offsetof(struct image_data, version), - .area_size = sizeof(current_image_data.version), - .area_flags = FMAP_AREA_STATIC, - }, -#ifdef CONFIG_ROLLBACK - { - /* - * RW rollback version, 32-bit unsigned integer. - * TODO: Get the relative offset of - * __image_data_offset within our RW image to - * accommodate image asymmetry. - */ - .area_name = "RW_RBVER", - .area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_STORAGE_OFF + - RELATIVE_RO((uint32_t)__image_data_offset) + - offsetof(struct image_data, rollback_version), - .area_size = sizeof( - current_image_data.rollback_version), - .area_flags = FMAP_AREA_STATIC, - }, -#endif -#ifdef CONFIG_RWSIG_TYPE_RWSIG - { - /* RW image signature */ - .area_name = "SIG_RW", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_SIG_ADDR - - CONFIG_PROGRAM_MEMORY_BASE, - .area_size = CONFIG_RW_SIG_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, -#endif -#ifdef CONFIG_RW_B - /* RW Firmware */ - { - /* The range of RW firmware to be auto-updated. */ - .area_name = "EC_RW_B", - .area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_STORAGE_OFF + - CONFIG_RW_SIZE, - .area_size = CONFIG_RW_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, -#ifdef CONFIG_RWSIG_TYPE_RWSIG - { - /* RW_B image signature */ - .area_name = "SIG_RW_B", - .area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF - - FMAP_REGION_START + CONFIG_RW_B_SIG_ADDR - - CONFIG_PROGRAM_MEMORY_BASE, - .area_size = CONFIG_RW_SIG_SIZE, - .area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO, - }, -#endif -#endif - } -}; diff --git a/common/fpsensor/OWNERS b/common/fpsensor/OWNERS deleted file mode 100644 index 395f722670..0000000000 --- a/common/fpsensor/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -# Fingerprint Sensor - -# Don't inherit owners from elsewhere in the manifest -set noparent - -hesling@chromium.org -jora@google.com -josienordrum@google.com -tomhughes@chromium.org -yichengli@chromium.org diff --git a/common/fpsensor/fpsensor.c b/common/fpsensor/fpsensor.c deleted file mode 100644 index 25010c7db8..0000000000 --- a/common/fpsensor/fpsensor.c +++ /dev/null @@ -1,887 +0,0 @@ -/* Copyright 2017 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 "atomic.h" -#include "clock.h" -#include "common.h" -#include "console.h" -#include "cryptoc/util.h" -#include "ec_commands.h" -#include "fpsensor.h" -#include "fpsensor_crypto.h" -#include "fpsensor_detect.h" -#include "fpsensor_private.h" -#include "fpsensor_state.h" -#include "gpio.h" -#include "host_command.h" -#include "link_defs.h" -#include "mkbp_event.h" -#include "overflow.h" -#include "spi.h" -#include "system.h" -#include "task.h" -#include "trng.h" -#include "util.h" -#include "watchdog.h" - -#if !defined(CONFIG_RNG) -#error "fpsensor requires RNG" -#endif - -#if defined(SECTION_IS_RO) -#error "fpsensor code should not be in RO image." -#endif - -/* Ready to encrypt a template. */ -static timestamp_t encryption_deadline; - -/* raw image offset inside the acquired frame */ -#ifndef FP_SENSOR_IMAGE_OFFSET -#define FP_SENSOR_IMAGE_OFFSET 0 -#endif - -#define FP_MODE_ANY_CAPTURE (FP_MODE_CAPTURE | FP_MODE_ENROLL_IMAGE | \ - FP_MODE_MATCH) -#define FP_MODE_ANY_DETECT_FINGER (FP_MODE_FINGER_DOWN | FP_MODE_FINGER_UP | \ - FP_MODE_ANY_CAPTURE) -#define FP_MODE_ANY_WAIT_IRQ (FP_MODE_FINGER_DOWN | FP_MODE_ANY_CAPTURE) - -/* Delay between 2 s of the sensor to detect finger removal */ -#define FINGER_POLLING_DELAY (100*MSEC) - -/* Timing statistics. */ -static uint32_t capture_time_us; -static uint32_t matching_time_us; -static uint32_t overall_time_us; -static timestamp_t overall_t0; -static uint8_t timestamps_invalid; - -BUILD_ASSERT(sizeof(struct ec_fp_template_encryption_metadata) % 4 == 0); - -/* Interrupt line from the fingerprint sensor */ -void fps_event(enum gpio_signal signal) -{ - task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_SENSOR_IRQ); -} - -static void send_mkbp_event(uint32_t event) -{ - atomic_or(&fp_events, event); - mkbp_send_event(EC_MKBP_EVENT_FINGERPRINT); -} - -static inline int is_raw_capture(uint32_t mode) -{ - int capture_type = FP_CAPTURE_TYPE(mode); - - return (capture_type == FP_CAPTURE_VENDOR_FORMAT - || capture_type == FP_CAPTURE_QUALITY_TEST); -} - -#ifdef HAVE_FP_PRIVATE_DRIVER -static inline int is_test_capture(uint32_t mode) -{ - int capture_type = FP_CAPTURE_TYPE(mode); - - return (mode & FP_MODE_CAPTURE) - && (capture_type == FP_CAPTURE_PATTERN0 - || capture_type == FP_CAPTURE_PATTERN1 - || capture_type == FP_CAPTURE_RESET_TEST); -} - -/* - * contains the bit FP_MODE_ENROLL_SESSION if a finger enrollment is on-going. - * It is used to detect the ENROLL_SESSION transition when sensor_mode is - * updated by the host. - */ -static uint32_t enroll_session; - -static uint32_t fp_process_enroll(void) -{ - int percent = 0; - int res; - - if (template_newly_enrolled != FP_NO_SUCH_TEMPLATE) - CPRINTS("Warning: previously enrolled template has not been " - "read yet."); - - /* begin/continue enrollment */ - CPRINTS("[%d]Enrolling ...", templ_valid); - res = fp_finger_enroll(fp_buffer, &percent); - CPRINTS("[%d]Enroll =>%d (%d%%)", templ_valid, res, percent); - if (res < 0) - return EC_MKBP_FP_ENROLL - | EC_MKBP_FP_ERRCODE(EC_MKBP_FP_ERR_ENROLL_INTERNAL); - templ_dirty |= BIT(templ_valid); - if (percent == 100) { - res = fp_enrollment_finish(fp_template[templ_valid]); - if (res) { - res = EC_MKBP_FP_ERR_ENROLL_INTERNAL; - } else { - template_newly_enrolled = templ_valid; - fp_enable_positive_match_secret(templ_valid, - &positive_match_secret_state); - templ_valid++; - } - sensor_mode &= ~FP_MODE_ENROLL_SESSION; - enroll_session &= ~FP_MODE_ENROLL_SESSION; - } - return EC_MKBP_FP_ENROLL | EC_MKBP_FP_ERRCODE(res) - | (percent << EC_MKBP_FP_ENROLL_PROGRESS_OFFSET); -} - -static bool fp_match_success(int match_result) -{ - if (match_result == EC_MKBP_FP_ERR_MATCH_YES || - match_result == EC_MKBP_FP_ERR_MATCH_YES_UPDATED || - match_result == EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED) { - return true; - } - - return false; -} - -static uint32_t fp_process_match(void) -{ - timestamp_t t0 = get_time(); - int res = -1; - uint32_t updated = 0; - int32_t fgr = FP_NO_SUCH_TEMPLATE; - - /* match finger against current templates */ - fp_disable_positive_match_secret(&positive_match_secret_state); - CPRINTS("Matching/%d ...", templ_valid); - if (templ_valid) { - res = fp_finger_match(fp_template[0], templ_valid, fp_buffer, - &fgr, &updated); - CPRINTS("Match =>%d (finger %d)", res, fgr); - - if (fp_match_success(res)) { - /* - * Match succeded! Let's check if template number - * is valid. If it is not valid, overwrite result - * with EC_MKBP_FP_ERR_MATCH_NO_INTERNAL. - */ - if (fgr >= 0 && fgr < FP_MAX_FINGER_COUNT) { - fp_enable_positive_match_secret(fgr, - &positive_match_secret_state); - } else { - res = EC_MKBP_FP_ERR_MATCH_NO_INTERNAL; - } - } else if (res < 0) { - /* - * Negative result means that there is a problem with - * code responsible for matching. Overwrite it with - * MATCH_NO_INTERNAL to let upper layers know what - * happened. - */ - res = EC_MKBP_FP_ERR_MATCH_NO_INTERNAL; - } - - if (res == EC_MKBP_FP_ERR_MATCH_YES_UPDATED) - templ_dirty |= updated; - } else { - CPRINTS("No enrolled templates"); - res = EC_MKBP_FP_ERR_MATCH_NO_TEMPLATES; - } - - if (!fp_match_success(res)) - timestamps_invalid |= FPSTATS_MATCHING_INV; - - matching_time_us = time_since32(t0); - return EC_MKBP_FP_MATCH | EC_MKBP_FP_ERRCODE(res) - | ((fgr << EC_MKBP_FP_MATCH_IDX_OFFSET) & EC_MKBP_FP_MATCH_IDX_MASK); -} - -static void fp_process_finger(void) -{ - timestamp_t t0 = get_time(); - int res = fp_sensor_acquire_image_with_mode(fp_buffer, - FP_CAPTURE_TYPE(sensor_mode)); - capture_time_us = time_since32(t0); - if (!res) { - uint32_t evt = EC_MKBP_FP_IMAGE_READY; - - /* Clean up SPI before clocking up to avoid hang on the dsb - * in dma_go. Ignore the return value to let the WDT reboot - * the MCU (and avoid getting trapped in the loop). - * b/112781659 */ - res = spi_transaction_flush(&spi_devices[0]); - if (res) - CPRINTS("Failed to flush SPI: 0x%x", res); - /* we need CPU power to do the computations */ - clock_enable_module(MODULE_FAST_CPU, 1); - - if (sensor_mode & FP_MODE_ENROLL_IMAGE) - evt = fp_process_enroll(); - else if (sensor_mode & FP_MODE_MATCH) - evt = fp_process_match(); - - sensor_mode &= ~FP_MODE_ANY_CAPTURE; - overall_time_us = time_since32(overall_t0); - send_mkbp_event(evt); - - /* go back to lower power mode */ - clock_enable_module(MODULE_FAST_CPU, 0); - } else { - timestamps_invalid |= FPSTATS_CAPTURE_INV; - } -} -#endif /* HAVE_FP_PRIVATE_DRIVER */ - -void fp_task(void) -{ - int timeout_us = -1; - - CPRINTS("FP_SENSOR_SEL: %s", - fp_sensor_type_to_str(get_fp_sensor_type())); - -#ifdef HAVE_FP_PRIVATE_DRIVER - /* Reset and initialize the sensor IC */ - fp_sensor_init(); - - while (1) { - uint32_t evt; - enum finger_state st = FINGER_NONE; - - /* Wait for a sensor IRQ or a new mode configuration */ - evt = task_wait_event(timeout_us); - - if (evt & TASK_EVENT_UPDATE_CONFIG) { - uint32_t mode = sensor_mode; - - gpio_disable_interrupt(GPIO_FPS_INT); - if ((mode ^ enroll_session) & FP_MODE_ENROLL_SESSION) { - if (mode & FP_MODE_ENROLL_SESSION) { - if (fp_enrollment_begin()) - sensor_mode &= - ~FP_MODE_ENROLL_SESSION; - } else { - fp_enrollment_finish(NULL); - } - enroll_session = - sensor_mode & FP_MODE_ENROLL_SESSION; - } - if (is_test_capture(mode)) { - fp_sensor_acquire_image_with_mode(fp_buffer, - FP_CAPTURE_TYPE(mode)); - sensor_mode &= ~FP_MODE_CAPTURE; - send_mkbp_event(EC_MKBP_FP_IMAGE_READY); - continue; - } else if (sensor_mode & FP_MODE_ANY_DETECT_FINGER) { - /* wait for a finger on the sensor */ - fp_sensor_configure_detect(); - } - if (sensor_mode & FP_MODE_DEEPSLEEP) - /* Shutdown the sensor */ - fp_sensor_low_power(); - if (sensor_mode & FP_MODE_FINGER_UP) - /* Poll the sensor to detect finger removal */ - timeout_us = FINGER_POLLING_DELAY; - else - timeout_us = -1; - if (mode & FP_MODE_ANY_WAIT_IRQ) { - gpio_enable_interrupt(GPIO_FPS_INT); - } else if (mode & FP_MODE_RESET_SENSOR) { - fp_reset_and_clear_context(); - sensor_mode &= ~FP_MODE_RESET_SENSOR; - } else if (mode & FP_MODE_SENSOR_MAINTENANCE) { - fp_maintenance(); - sensor_mode &= ~FP_MODE_SENSOR_MAINTENANCE; - } else { - fp_sensor_low_power(); - } - } else if (evt & (TASK_EVENT_SENSOR_IRQ | TASK_EVENT_TIMER)) { - overall_t0 = get_time(); - timestamps_invalid = 0; - gpio_disable_interrupt(GPIO_FPS_INT); - if (sensor_mode & FP_MODE_ANY_DETECT_FINGER) { - st = fp_sensor_finger_status(); - if (st == FINGER_PRESENT && - sensor_mode & FP_MODE_FINGER_DOWN) { - CPRINTS("Finger!"); - sensor_mode &= ~FP_MODE_FINGER_DOWN; - send_mkbp_event(EC_MKBP_FP_FINGER_DOWN); - } - if (st == FINGER_NONE && - sensor_mode & FP_MODE_FINGER_UP) { - sensor_mode &= ~FP_MODE_FINGER_UP; - timeout_us = -1; - send_mkbp_event(EC_MKBP_FP_FINGER_UP); - } - } - - if (st == FINGER_PRESENT && - sensor_mode & FP_MODE_ANY_CAPTURE) - fp_process_finger(); - - if (sensor_mode & FP_MODE_ANY_WAIT_IRQ) { - fp_sensor_configure_detect(); - gpio_enable_interrupt(GPIO_FPS_INT); - } else { - fp_sensor_low_power(); - } - } - } -#else /* !HAVE_FP_PRIVATE_DRIVER */ - while (1) { - uint32_t evt = task_wait_event(timeout_us); - - send_mkbp_event(evt); - } -#endif /* !HAVE_FP_PRIVATE_DRIVER */ -} - -static enum ec_status fp_command_passthru(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_passthru *params = args->params; - void *out = args->response; - int rc; - int ret = EC_RES_SUCCESS; - - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - if (params->len > args->params_size + - offsetof(struct ec_params_fp_passthru, data) || - params->len > args->response_max) - return EC_RES_INVALID_PARAM; - - rc = spi_transaction_async(&spi_devices[0], params->data, - params->len, out, SPI_READBACK_ALL); - if (params->flags & EC_FP_FLAG_NOT_COMPLETE) - rc |= spi_transaction_wait(&spi_devices[0]); - else - rc |= spi_transaction_flush(&spi_devices[0]); - - if (rc == EC_ERROR_TIMEOUT) - ret = EC_RES_TIMEOUT; - else if (rc) - ret = EC_RES_ERROR; - - args->response_size = params->len; - return ret; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_PASSTHRU, fp_command_passthru, EC_VER_MASK(0)); - -static enum ec_status fp_command_info(struct host_cmd_handler_args *args) -{ - struct ec_response_fp_info *r = args->response; - -#ifdef HAVE_FP_PRIVATE_DRIVER - if (fp_sensor_get_info(r) < 0) -#endif - return EC_RES_UNAVAILABLE; - - r->template_size = FP_ALGORITHM_ENCRYPTED_TEMPLATE_SIZE; - r->template_max = FP_MAX_FINGER_COUNT; - r->template_valid = templ_valid; - r->template_dirty = templ_dirty; - r->template_version = FP_TEMPLATE_FORMAT_VERSION; - - /* V1 is identical to V0 with more information appended */ - args->response_size = args->version ? sizeof(*r) : - sizeof(struct ec_response_fp_info_v0); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_INFO, fp_command_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -BUILD_ASSERT(FP_CONTEXT_NONCE_BYTES == 12); - -int validate_fp_buffer_offset(const uint32_t buffer_size, const uint32_t offset, - const uint32_t size) -{ - uint32_t bytes_requested; - - if (check_add_overflow(size, offset, &bytes_requested)) - return EC_ERROR_OVERFLOW; - - if (bytes_requested > buffer_size) - return EC_ERROR_INVAL; - - return EC_SUCCESS; -} - -static enum ec_status fp_command_frame(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_frame *params = args->params; - void *out = args->response; - uint32_t idx = FP_FRAME_GET_BUFFER_INDEX(params->offset); - uint32_t offset = params->offset & FP_FRAME_OFFSET_MASK; - uint32_t size = params->size; - uint32_t fgr; - uint8_t key[SBP_ENC_KEY_LEN]; - struct ec_fp_template_encryption_metadata *enc_info; - int ret; - - if (size > args->response_max) - return EC_RES_INVALID_PARAM; - - if (idx == FP_FRAME_INDEX_RAW_IMAGE) { - /* The host requested a frame. */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - if (!is_raw_capture(sensor_mode)) - offset += FP_SENSOR_IMAGE_OFFSET; - - ret = validate_fp_buffer_offset(sizeof(fp_buffer), offset, - size); - if (ret != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - memcpy(out, fp_buffer + offset, size); - args->response_size = size; - return EC_RES_SUCCESS; - } - - /* The host requested a template. */ - - /* Templates are numbered from 1 in this host request. */ - fgr = idx - FP_FRAME_INDEX_TEMPLATE; - - if (fgr >= FP_MAX_FINGER_COUNT) - return EC_RES_INVALID_PARAM; - if (fgr >= templ_valid) - return EC_RES_UNAVAILABLE; - ret = validate_fp_buffer_offset(sizeof(fp_enc_buffer), offset, size); - if (ret != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - if (!offset) { - /* Host has requested the first chunk, do the encryption. */ - timestamp_t now = get_time(); - /* Encrypted template is after the metadata. */ - uint8_t *encrypted_template = fp_enc_buffer + sizeof(*enc_info); - /* Positive match salt is after the template. */ - uint8_t *positive_match_salt = - encrypted_template + sizeof(fp_template[0]); - size_t encrypted_blob_size = sizeof(fp_template[0]) + - sizeof(fp_positive_match_salt[0]); - - /* b/114160734: Not more than 1 encrypted message per second. */ - if (!timestamp_expired(encryption_deadline, &now)) - return EC_RES_BUSY; - encryption_deadline.val = now.val + (1 * SECOND); - - memset(fp_enc_buffer, 0, sizeof(fp_enc_buffer)); - /* - * The beginning of the buffer contains nonce, encryption_salt - * and tag. - */ - enc_info = (void *)fp_enc_buffer; - enc_info->struct_version = FP_TEMPLATE_FORMAT_VERSION; - init_trng(); - rand_bytes(enc_info->nonce, FP_CONTEXT_NONCE_BYTES); - rand_bytes(enc_info->encryption_salt, - FP_CONTEXT_ENCRYPTION_SALT_BYTES); - exit_trng(); - - if (fgr == template_newly_enrolled) { - /* - * Newly enrolled templates need new positive match - * salt, new positive match secret and new validation - * value. - */ - template_newly_enrolled = FP_NO_SUCH_TEMPLATE; - init_trng(); - rand_bytes(fp_positive_match_salt[fgr], - FP_POSITIVE_MATCH_SALT_BYTES); - exit_trng(); - } - - ret = derive_encryption_key(key, enc_info->encryption_salt); - if (ret != EC_SUCCESS) { - CPRINTS("fgr%d: Failed to derive key", fgr); - return EC_RES_UNAVAILABLE; - } - - /* - * Copy the payload to |fp_enc_buffer| where it will be - * encrypted in-place. - */ - memcpy(encrypted_template, fp_template[fgr], - sizeof(fp_template[0])); - memcpy(positive_match_salt, fp_positive_match_salt[fgr], - sizeof(fp_positive_match_salt[0])); - - /* Encrypt the secret blob in-place. */ - ret = aes_gcm_encrypt(key, SBP_ENC_KEY_LEN, encrypted_template, - encrypted_template, - encrypted_blob_size, - enc_info->nonce, FP_CONTEXT_NONCE_BYTES, - enc_info->tag, FP_CONTEXT_TAG_BYTES); - always_memset(key, 0, sizeof(key)); - if (ret != EC_SUCCESS) { - CPRINTS("fgr%d: Failed to encrypt template", fgr); - return EC_RES_UNAVAILABLE; - } - templ_dirty &= ~BIT(fgr); - } - memcpy(out, fp_enc_buffer + offset, size); - args->response_size = size; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_FRAME, fp_command_frame, EC_VER_MASK(0)); - -static enum ec_status fp_command_stats(struct host_cmd_handler_args *args) -{ - struct ec_response_fp_stats *r = args->response; - - r->capture_time_us = capture_time_us; - r->matching_time_us = matching_time_us; - r->overall_time_us = overall_time_us; - r->overall_t0.lo = overall_t0.le.lo; - r->overall_t0.hi = overall_t0.le.hi; - r->timestamps_invalid = timestamps_invalid; - /* - * Note that this is set to FP_NO_SUCH_TEMPLATE when positive match - * secret is read/disabled, and we are not using this field in biod. - */ - r->template_matched = positive_match_secret_state.template_matched; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_STATS, fp_command_stats, EC_VER_MASK(0)); - -static bool template_needs_validation_value( - struct ec_fp_template_encryption_metadata *enc_info) -{ - return enc_info->struct_version == 3 - && FP_TEMPLATE_FORMAT_VERSION == 4; -} - -static int validate_template_format( - struct ec_fp_template_encryption_metadata *enc_info) -{ - if (template_needs_validation_value(enc_info)) - /* The host requested migration to v4. */ - return EC_RES_SUCCESS; - - if (enc_info->struct_version != FP_TEMPLATE_FORMAT_VERSION) { - CPRINTS("Invalid template format %d", enc_info->struct_version); - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} - -static enum ec_status fp_command_template(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_template *params = args->params; - uint32_t size = params->size & ~FP_TEMPLATE_COMMIT; - int xfer_complete = params->size & FP_TEMPLATE_COMMIT; - uint32_t offset = params->offset; - uint32_t idx = templ_valid; - uint8_t key[SBP_ENC_KEY_LEN]; - struct ec_fp_template_encryption_metadata *enc_info; - int ret; - - /* Can we store one more template ? */ - if (idx >= FP_MAX_FINGER_COUNT) - return EC_RES_OVERFLOW; - - if (args->params_size != - size + offsetof(struct ec_params_fp_template, data)) - return EC_RES_INVALID_PARAM; - ret = validate_fp_buffer_offset(sizeof(fp_enc_buffer), offset, size); - if (ret != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - memcpy(&fp_enc_buffer[offset], params->data, size); - - if (xfer_complete) { - /* Encrypted template is after the metadata. */ - uint8_t *encrypted_template = fp_enc_buffer + sizeof(*enc_info); - /* Positive match salt is after the template. */ - uint8_t *positive_match_salt = - encrypted_template + sizeof(fp_template[0]); - size_t encrypted_blob_size; - - /* - * The complete encrypted template has been received, start - * decryption. - */ - fp_clear_finger_context(idx); - /* - * The beginning of the buffer contains nonce, encryption_salt - * and tag. - */ - enc_info = (void *)fp_enc_buffer; - ret = validate_template_format(enc_info); - if (ret != EC_RES_SUCCESS) { - CPRINTS("fgr%d: Template format not supported", idx); - return EC_RES_INVALID_PARAM; - } - - if (enc_info->struct_version <= 3) { - encrypted_blob_size = sizeof(fp_template[0]); - } else { - encrypted_blob_size = - sizeof(fp_template[0]) + - sizeof(fp_positive_match_salt[0]); - } - - ret = derive_encryption_key(key, enc_info->encryption_salt); - if (ret != EC_SUCCESS) { - CPRINTS("fgr%d: Failed to derive key", idx); - return EC_RES_UNAVAILABLE; - } - - /* Decrypt the secret blob in-place. */ - ret = aes_gcm_decrypt(key, SBP_ENC_KEY_LEN, encrypted_template, - encrypted_template, - encrypted_blob_size, - enc_info->nonce, FP_CONTEXT_NONCE_BYTES, - enc_info->tag, FP_CONTEXT_TAG_BYTES); - always_memset(key, 0, sizeof(key)); - if (ret != EC_SUCCESS) { - CPRINTS("fgr%d: Failed to decipher template", idx); - /* Don't leave bad data in the template buffer */ - fp_clear_finger_context(idx); - return EC_RES_UNAVAILABLE; - } - memcpy(fp_template[idx], encrypted_template, - sizeof(fp_template[0])); - if (template_needs_validation_value(enc_info)) { - CPRINTS("fgr%d: Generating positive match salt.", idx); - init_trng(); - rand_bytes(positive_match_salt, - FP_POSITIVE_MATCH_SALT_BYTES); - exit_trng(); - } - if (bytes_are_trivial(positive_match_salt, - sizeof(fp_positive_match_salt[0]))) { - CPRINTS("fgr%d: Trivial positive match salt.", idx); - always_memset(fp_template[idx], 0, - sizeof(fp_template[0])); - return EC_RES_INVALID_PARAM; - } - memcpy(fp_positive_match_salt[idx], positive_match_salt, - sizeof(fp_positive_match_salt[0])); - - templ_valid++; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_TEMPLATE, fp_command_template, EC_VER_MASK(0)); - -#ifdef CONFIG_CMD_FPSENSOR_DEBUG -/* --- Debug console commands --- */ - -/* - * Send the current Fingerprint buffer to the host - * it is formatted as an 8-bpp PGM ASCII file. - * - * In addition, it prepends a short Z-Modem download signature, - * which triggers automatically your preferred viewer if you configure it - * properly in "File transfer protocols" in the Minicom options menu. - * (as triggered by Ctrl-A O) - * +--------------------------------------------------------------------------+ - * | Name Program Name U/D FullScr IO-Red. Multi | - * | A zmodem /usr/bin/sz -vv -b Y U N Y Y | - * [...] - * | L pgm /usr/bin/display_pgm N D N Y N | - * | M Zmodem download string activates... L | - * - * My /usr/bin/display_pgm looks like this: - * #!/bin/sh - * TMPF=$(mktemp) - * ascii-xfr -rdv ${TMPF} - * display ${TMPF} - * - * Alternative (if you're using screen as your terminal): - * - * From *outside* the chroot: - * - * Install ascii-xfr: sudo apt-get install minicom - * Install imagemagick: sudo apt-get install imagemagick - * - * Add the following to your ${HOME}/.screenrc: - * - * zmodem catch - * zmodem recvcmd '!!! bash -c "ascii-xfr -rdv /tmp/finger.pgm && display /tmp/finger.pgm"' - * - * From *outside the chroot*, use screen to connect to UART console: - * - * sudo screen -c ${HOME}/.screenrc /dev/pts/NN 115200 - * - */ -static void upload_pgm_image(uint8_t *frame) -{ - int x, y; - uint8_t *ptr = frame; - - /* fake Z-modem ZRQINIT signature */ - CPRINTF("#IGNORE for ZModem\r**\030B00"); - msleep(2000); /* let the download program start */ - /* Print 8-bpp PGM ASCII header */ - CPRINTF("P2\n%d %d\n255\n", FP_SENSOR_RES_X, FP_SENSOR_RES_Y); - - for (y = 0; y < FP_SENSOR_RES_Y; y++) { - watchdog_reload(); - for (x = 0; x < FP_SENSOR_RES_X; x++, ptr++) - CPRINTF("%d ", *ptr); - CPRINTF("\n"); - cflush(); - } - - CPRINTF("\x04"); /* End Of Transmission */ -} - -static enum ec_error_list fp_console_action(uint32_t mode) -{ - int tries = 200; - uint32_t mode_output = 0; - int rc = 0; - - if (!(sensor_mode & FP_MODE_RESET_SENSOR)) - CPRINTS("Waiting for finger ..."); - - rc = fp_set_sensor_mode(mode, &mode_output); - - if (rc != EC_RES_SUCCESS) { - /* - * EC host command errors do not directly map to console command - * errors. - */ - return EC_ERROR_UNKNOWN; - } - - while (tries--) { - if (!(sensor_mode & FP_MODE_ANY_CAPTURE)) { - CPRINTS("done (events:%x)", fp_events); - return 0; - } - usleep(100 * MSEC); - } - return EC_ERROR_TIMEOUT; -} - -int command_fpcapture(int argc, char **argv) -{ - int capture_type = FP_CAPTURE_SIMPLE_IMAGE; - uint32_t mode; - enum ec_error_list rc; - - /* - * TODO(b/142944002): Remove this redundant check for system_is_locked - * once we have unit-tests/integration-tests in place. - */ - if (system_is_locked()) - return EC_ERROR_ACCESS_DENIED; - - if (argc >= 2) { - char *e; - - capture_type = strtoi(argv[1], &e, 0); - if (*e || capture_type < 0) - return EC_ERROR_PARAM1; - } - mode = FP_MODE_CAPTURE | ((capture_type << FP_MODE_CAPTURE_TYPE_SHIFT) - & FP_MODE_CAPTURE_TYPE_MASK); - - rc = fp_console_action(mode); - if (rc == EC_SUCCESS) - upload_pgm_image(fp_buffer + FP_SENSOR_IMAGE_OFFSET); - - return rc; -} -DECLARE_CONSOLE_COMMAND_FLAGS(fpcapture, command_fpcapture, NULL, - "Capture fingerprint in PGM format", - CMD_FLAG_RESTRICTED); - -int command_fpenroll(int argc, char **argv) -{ - enum ec_error_list rc; - int percent = 0; - uint32_t event; - static const char * const enroll_str[] = {"OK", "Low Quality", - "Immobile", "Low Coverage"}; - - /* - * TODO(b/142944002): Remove this redundant check for system_is_locked - * once we have unit-tests/integration-tests in place. - */ - if (system_is_locked()) - return EC_ERROR_ACCESS_DENIED; - - do { - int tries = 1000; - - rc = fp_console_action(FP_MODE_ENROLL_SESSION | - FP_MODE_ENROLL_IMAGE); - if (rc != EC_SUCCESS) - break; - event = atomic_clear(&fp_events); - percent = EC_MKBP_FP_ENROLL_PROGRESS(event); - CPRINTS("Enroll capture: %s (%d%%)", - enroll_str[EC_MKBP_FP_ERRCODE(event) & 3], percent); - /* wait for finger release between captures */ - sensor_mode = FP_MODE_ENROLL_SESSION | FP_MODE_FINGER_UP; - task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG); - while (tries-- && sensor_mode & FP_MODE_FINGER_UP) - usleep(20 * MSEC); - } while (percent < 100); - sensor_mode = 0; /* reset FP_MODE_ENROLL_SESSION */ - task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG); - - return rc; -} -DECLARE_CONSOLE_COMMAND_FLAGS(fpenroll, command_fpenroll, NULL, - "Enroll a new fingerprint", - CMD_FLAG_RESTRICTED); - - -int command_fpmatch(int argc, char **argv) -{ - enum ec_error_list rc = fp_console_action(FP_MODE_MATCH); - uint32_t event = atomic_clear(&fp_events); - - if (rc == EC_SUCCESS && event & EC_MKBP_FP_MATCH) { - uint32_t errcode = EC_MKBP_FP_ERRCODE(event); - - CPRINTS("Match: %s (%d)", - errcode & EC_MKBP_FP_ERR_MATCH_YES ? "YES" : "NO", - errcode); - } - - return rc; -} -DECLARE_CONSOLE_COMMAND(fpmatch, command_fpmatch, NULL, - "Run match algorithm against finger"); - -int command_fpclear(int argc, char **argv) -{ - /* - * We intentionally run this on the fp_task so that we use the - * same code path as host commands. - */ - enum ec_error_list rc = fp_console_action(FP_MODE_RESET_SENSOR); - - if (rc < 0) - CPRINTS("Failed to clear fingerprint context: %d", rc); - - atomic_clear(&fp_events); - - return rc; -} -DECLARE_CONSOLE_COMMAND(fpclear, command_fpclear, NULL, - "Clear fingerprint sensor context"); - -int command_fpmaintenance(int argc, char **argv) -{ -#ifdef HAVE_FP_PRIVATE_DRIVER - return fp_maintenance(); -#else - return EC_SUCCESS; -#endif /* #ifdef HAVE_FP_PRIVATE_DRIVER */ -} -DECLARE_CONSOLE_COMMAND(fpmaintenance, command_fpmaintenance, NULL, - "Run fingerprint sensor maintenance"); - -#endif /* CONFIG_CMD_FPSENSOR_DEBUG */ diff --git a/common/fpsensor/fpsensor_crypto.c b/common/fpsensor/fpsensor_crypto.c deleted file mode 100644 index 73d7aca681..0000000000 --- a/common/fpsensor/fpsensor_crypto.c +++ /dev/null @@ -1,286 +0,0 @@ -/* 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. - */ -#include <stdbool.h> - -#include "aes.h" -#include "aes-gcm.h" -#include "cryptoc/util.h" -#include "fpsensor_crypto.h" -#include "fpsensor_private.h" -#include "fpsensor_state.h" -#include "rollback.h" - -#if !defined(CONFIG_AES) || !defined(CONFIG_AES_GCM) || \ - !defined(CONFIG_ROLLBACK_SECRET_SIZE) -#error "fpsensor requires AES, AES_GCM and ROLLBACK_SECRET_SIZE" -#endif - -static int get_ikm(uint8_t *ikm) -{ - int ret; - - if (!fp_tpm_seed_is_set()) { - CPRINTS("Seed hasn't been set."); - return EC_ERROR_ACCESS_DENIED; - } - - /* - * The first CONFIG_ROLLBACK_SECRET_SIZE bytes of IKM are read from the - * anti-rollback blocks. - */ - ret = rollback_get_secret(ikm); - if (ret != EC_SUCCESS) { - CPRINTS("Failed to read rollback secret: %d", ret); - return EC_ERROR_HW_INTERNAL; - } - /* - * IKM is the concatenation of the rollback secret and the seed from - * the TPM. - */ - memcpy(ikm + CONFIG_ROLLBACK_SECRET_SIZE, tpm_seed, sizeof(tpm_seed)); - - return EC_SUCCESS; -} - -static void hkdf_extract(uint8_t *prk, const uint8_t *salt, size_t salt_size, - const uint8_t *ikm, size_t ikm_size) -{ - /* - * Derive a key with the "extract" step of HKDF - * https://tools.ietf.org/html/rfc5869#section-2.2 - */ - hmac_SHA256(prk, salt, salt_size, ikm, ikm_size); -} - -static int hkdf_expand_one_step(uint8_t *out_key, size_t out_key_size, - uint8_t *prk, size_t prk_size, - uint8_t *info, size_t info_size) -{ - uint8_t key_buf[SHA256_DIGEST_SIZE]; - uint8_t message_buf[SHA256_DIGEST_SIZE + 1]; - - if (out_key_size > SHA256_DIGEST_SIZE) { - CPRINTS("Deriving key material longer than SHA256_DIGEST_SIZE " - "requires more steps of HKDF expand."); - return EC_ERROR_INVAL; - } - - if (info_size > SHA256_DIGEST_SIZE) { - CPRINTS("Info size too big for HKDF."); - return EC_ERROR_INVAL; - } - - memcpy(message_buf, info, info_size); - /* 1 step, set the counter byte to 1. */ - message_buf[info_size] = 0x01; - hmac_SHA256(key_buf, prk, prk_size, message_buf, info_size + 1); - - memcpy(out_key, key_buf, out_key_size); - always_memset(key_buf, 0, sizeof(key_buf)); - - return EC_SUCCESS; -} - -int hkdf_expand(uint8_t *out_key, size_t L, const uint8_t *prk, - size_t prk_size, const uint8_t *info, size_t info_size) -{ - /* - * "Expand" step of HKDF. - * https://tools.ietf.org/html/rfc5869#section-2.3 - */ -#define HASH_LEN SHA256_DIGEST_SIZE - uint8_t count = 1; - const uint8_t *T = out_key; - size_t T_len = 0; - uint8_t T_buffer[HASH_LEN]; - /* Number of blocks. */ - const uint32_t N = DIV_ROUND_UP(L, HASH_LEN); - uint8_t info_buffer[HASH_LEN + HKDF_MAX_INFO_SIZE + sizeof(count)]; - bool arguments_valid = false; - - if (out_key == NULL || L == 0) - CPRINTS("HKDF expand: output buffer not valid."); - else if (prk == NULL) - CPRINTS("HKDF expand: prk is NULL."); - else if (info == NULL && info_size > 0) - CPRINTS("HKDF expand: info is NULL but info size is not zero."); - else if (info_size > HKDF_MAX_INFO_SIZE) - CPRINTF("HKDF expand: info size larger than %d bytes.\n", - HKDF_MAX_INFO_SIZE); - else if (N > HKDF_SHA256_MAX_BLOCK_COUNT) - CPRINTS("HKDF expand: output key size too large."); - else - arguments_valid = true; - - if (!arguments_valid) - return EC_ERROR_INVAL; - - while (L > 0) { - const size_t block_size = L < HASH_LEN ? L : HASH_LEN; - - memcpy(info_buffer, T, T_len); - memcpy(info_buffer + T_len, info, info_size); - info_buffer[T_len + info_size] = count; - hmac_SHA256(T_buffer, prk, prk_size, info_buffer, - T_len + info_size + sizeof(count)); - memcpy(out_key, T_buffer, block_size); - - T += T_len; - T_len = HASH_LEN; - count++; - out_key += block_size; - L -= block_size; - } - always_memset(T_buffer, 0, sizeof(T_buffer)); - always_memset(info_buffer, 0, sizeof(info_buffer)); - return EC_SUCCESS; -#undef HASH_LEN -} - -int derive_positive_match_secret(uint8_t *output, - const uint8_t *input_positive_match_salt) -{ - int ret; - uint8_t ikm[CONFIG_ROLLBACK_SECRET_SIZE + sizeof(tpm_seed)]; - uint8_t prk[SHA256_DIGEST_SIZE]; - static const char info_prefix[] = "positive_match_secret for user "; - uint8_t info[sizeof(info_prefix) - 1 + sizeof(user_id)]; - - if (bytes_are_trivial(input_positive_match_salt, - FP_POSITIVE_MATCH_SALT_BYTES)) { - CPRINTS("Failed to derive positive match secret: " - "salt bytes are trivial."); - return EC_ERROR_INVAL; - } - - ret = get_ikm(ikm); - if (ret != EC_SUCCESS) { - CPRINTS("Failed to get IKM: %d", ret); - return ret; - } - - /* "Extract" step of HKDF. */ - hkdf_extract(prk, input_positive_match_salt, - FP_POSITIVE_MATCH_SALT_BYTES, ikm, sizeof(ikm)); - always_memset(ikm, 0, sizeof(ikm)); - - memcpy(info, info_prefix, strlen(info_prefix)); - memcpy(info + strlen(info_prefix), user_id, sizeof(user_id)); - - /* "Expand" step of HKDF. */ - ret = hkdf_expand(output, FP_POSITIVE_MATCH_SECRET_BYTES, prk, - sizeof(prk), info, sizeof(info)); - always_memset(prk, 0, sizeof(prk)); - - /* Check that secret is not full of 0x00 or 0xff. */ - if (bytes_are_trivial(output, FP_POSITIVE_MATCH_SECRET_BYTES)) { - CPRINTS("Failed to derive positive match secret: " - "derived secret bytes are trivial."); - ret = EC_ERROR_HW_INTERNAL; - } - return ret; -} - -int derive_encryption_key(uint8_t *out_key, const uint8_t *salt) -{ - int ret; - uint8_t ikm[CONFIG_ROLLBACK_SECRET_SIZE + sizeof(tpm_seed)]; - uint8_t prk[SHA256_DIGEST_SIZE]; - - BUILD_ASSERT(SBP_ENC_KEY_LEN <= SHA256_DIGEST_SIZE); - BUILD_ASSERT(SBP_ENC_KEY_LEN <= CONFIG_ROLLBACK_SECRET_SIZE); - BUILD_ASSERT(sizeof(user_id) == SHA256_DIGEST_SIZE); - - ret = get_ikm(ikm); - if (ret != EC_SUCCESS) { - CPRINTS("Failed to get IKM: %d", ret); - return ret; - } - - /* "Extract step of HKDF. */ - hkdf_extract(prk, salt, FP_CONTEXT_ENCRYPTION_SALT_BYTES, ikm, - sizeof(ikm)); - always_memset(ikm, 0, sizeof(ikm)); - - /* - * Only 1 "expand" step of HKDF since the size of the "info" context - * (user_id in our case) is exactly SHA256_DIGEST_SIZE. - * https://tools.ietf.org/html/rfc5869#section-2.3 - */ - ret = hkdf_expand_one_step(out_key, SBP_ENC_KEY_LEN, prk, sizeof(prk), - (uint8_t *)user_id, sizeof(user_id)); - always_memset(prk, 0, sizeof(prk)); - - return ret; -} - -int aes_gcm_encrypt(const uint8_t *key, int key_size, - const uint8_t *plaintext, - uint8_t *ciphertext, int text_size, - const uint8_t *nonce, int nonce_size, - uint8_t *tag, int tag_size) -{ - int res; - AES_KEY aes_key; - GCM128_CONTEXT ctx; - - if (nonce_size != FP_CONTEXT_NONCE_BYTES) { - CPRINTS("Invalid nonce size %d bytes", nonce_size); - return EC_ERROR_INVAL; - } - - res = AES_set_encrypt_key(key, 8 * key_size, &aes_key); - if (res) { - CPRINTS("Failed to set encryption key: %d", res); - return EC_ERROR_UNKNOWN; - } - CRYPTO_gcm128_init(&ctx, &aes_key, (block128_f)AES_encrypt, 0); - CRYPTO_gcm128_setiv(&ctx, &aes_key, nonce, nonce_size); - /* CRYPTO functions return 1 on success, 0 on error. */ - res = CRYPTO_gcm128_encrypt(&ctx, &aes_key, plaintext, ciphertext, - text_size); - if (!res) { - CPRINTS("Failed to encrypt: %d", res); - return EC_ERROR_UNKNOWN; - } - CRYPTO_gcm128_tag(&ctx, tag, tag_size); - return EC_SUCCESS; -} - -int aes_gcm_decrypt(const uint8_t *key, int key_size, uint8_t *plaintext, - const uint8_t *ciphertext, int text_size, - const uint8_t *nonce, int nonce_size, - const uint8_t *tag, int tag_size) -{ - int res; - AES_KEY aes_key; - GCM128_CONTEXT ctx; - - if (nonce_size != FP_CONTEXT_NONCE_BYTES) { - CPRINTS("Invalid nonce size %d bytes", nonce_size); - return EC_ERROR_INVAL; - } - - res = AES_set_encrypt_key(key, 8 * key_size, &aes_key); - if (res) { - CPRINTS("Failed to set decryption key: %d", res); - return EC_ERROR_UNKNOWN; - } - CRYPTO_gcm128_init(&ctx, &aes_key, (block128_f)AES_encrypt, 0); - CRYPTO_gcm128_setiv(&ctx, &aes_key, nonce, nonce_size); - /* CRYPTO functions return 1 on success, 0 on error. */ - res = CRYPTO_gcm128_decrypt(&ctx, &aes_key, ciphertext, plaintext, - text_size); - if (!res) { - CPRINTS("Failed to decrypt: %d", res); - return EC_ERROR_UNKNOWN; - } - res = CRYPTO_gcm128_finish(&ctx, tag, tag_size); - if (!res) { - CPRINTS("Found incorrect tag: %d", res); - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} diff --git a/common/fpsensor/fpsensor_private.h b/common/fpsensor/fpsensor_private.h deleted file mode 100644 index a42049dece..0000000000 --- a/common/fpsensor/fpsensor_private.h +++ /dev/null @@ -1,19 +0,0 @@ -/* 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. - */ - -/* Internal header file for common/fpsensor directory */ - -#ifndef __CROS_EC_FPSENSOR_PRIVATE_H -#define __CROS_EC_FPSENSOR_PRIVATE_H - -#include <stdint.h> - -#define CPRINTF(format, args...) cprintf(CC_FP, format, ## args) -#define CPRINTS(format, args...) cprints(CC_FP, format, ## args) - -int validate_fp_buffer_offset(uint32_t buffer_size, uint32_t offset, - uint32_t size); - -#endif /* __CROS_EC_FPSENSOR_PRIVATE_H */ diff --git a/common/fpsensor/fpsensor_state.c b/common/fpsensor/fpsensor_state.c deleted file mode 100644 index db64110b56..0000000000 --- a/common/fpsensor/fpsensor_state.c +++ /dev/null @@ -1,313 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "cryptoc/util.h" -#include "ec_commands.h" -#include "fpsensor.h" -#include "fpsensor_crypto.h" -#include "fpsensor_private.h" -#include "fpsensor_state.h" -#include "host_command.h" -#include "system.h" -#include "task.h" -#include "util.h" - -/* Last acquired frame (aligned as it is used by arbitrary binary libraries) */ -uint8_t fp_buffer[FP_SENSOR_IMAGE_SIZE] FP_FRAME_SECTION __aligned(4); -/* Fingers templates for the current user */ -uint8_t fp_template[FP_MAX_FINGER_COUNT][FP_ALGORITHM_TEMPLATE_SIZE] - FP_TEMPLATE_SECTION; -/* Encryption/decryption buffer */ -/* TODO: On-the-fly encryption/decryption without a dedicated buffer */ -/* - * Store the encryption metadata at the beginning of the buffer containing the - * ciphered data. - */ -uint8_t fp_enc_buffer[FP_ALGORITHM_ENCRYPTED_TEMPLATE_SIZE] - FP_TEMPLATE_SECTION; -/* Salt used in derivation of positive match secret. */ -uint8_t fp_positive_match_salt - [FP_MAX_FINGER_COUNT][FP_POSITIVE_MATCH_SALT_BYTES]; - -struct positive_match_secret_state positive_match_secret_state = { - .template_matched = FP_NO_SUCH_TEMPLATE, - .readable = false, - .deadline.val = 0, -}; - -/* Index of the last enrolled but not retrieved template. */ -int8_t template_newly_enrolled = FP_NO_SUCH_TEMPLATE; -/* Number of used templates */ -uint32_t templ_valid; -/* Bitmap of the templates with local modifications */ -uint32_t templ_dirty; -/* Current user ID */ -uint32_t user_id[FP_CONTEXT_USERID_WORDS]; -/* Part of the IKM used to derive encryption keys received from the TPM. */ -uint8_t tpm_seed[FP_CONTEXT_TPM_BYTES]; -/* Status of the FP encryption engine. */ -static uint32_t fp_encryption_status; - -uint32_t fp_events; - -uint32_t sensor_mode; - -void fp_task_simulate(void) -{ - int timeout_us = -1; - - while (1) - task_wait_event(timeout_us); -} - -void fp_clear_finger_context(int idx) -{ - always_memset(fp_template[idx], 0, sizeof(fp_template[0])); - always_memset(fp_positive_match_salt[idx], 0, - sizeof(fp_positive_match_salt[0])); -} - -/** - * @warning |fp_buffer| contains data used by the matching algorithm that must - * be released by calling fp_sensor_deinit() first. Call - * fp_reset_and_clear_context instead of calling this directly. - */ -static void _fp_clear_context(void) -{ - int idx; - - templ_valid = 0; - templ_dirty = 0; - always_memset(fp_buffer, 0, sizeof(fp_buffer)); - always_memset(fp_enc_buffer, 0, sizeof(fp_enc_buffer)); - always_memset(user_id, 0, sizeof(user_id)); - fp_disable_positive_match_secret(&positive_match_secret_state); - for (idx = 0; idx < FP_MAX_FINGER_COUNT; idx++) - fp_clear_finger_context(idx); -} - -void fp_reset_and_clear_context(void) -{ - if (fp_sensor_deinit() != EC_SUCCESS) - CPRINTS("Failed to deinit sensor"); - _fp_clear_context(); - if (fp_sensor_init() != EC_SUCCESS) - CPRINTS("Failed to init sensor"); -} - -int fp_get_next_event(uint8_t *out) -{ - uint32_t event_out = atomic_clear(&fp_events); - - memcpy(out, &event_out, sizeof(event_out)); - - return sizeof(event_out); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_FINGERPRINT, fp_get_next_event); - -static enum ec_status fp_command_tpm_seed(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_seed *params = args->params; - - if (params->struct_version != FP_TEMPLATE_FORMAT_VERSION) { - CPRINTS("Invalid seed format %d", params->struct_version); - return EC_RES_INVALID_PARAM; - } - - if (fp_encryption_status & FP_ENC_STATUS_SEED_SET) { - CPRINTS("Seed has already been set."); - return EC_RES_ACCESS_DENIED; - } - memcpy(tpm_seed, params->seed, sizeof(tpm_seed)); - fp_encryption_status |= FP_ENC_STATUS_SEED_SET; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_SEED, fp_command_tpm_seed, EC_VER_MASK(0)); - -int fp_tpm_seed_is_set(void) -{ - return fp_encryption_status & FP_ENC_STATUS_SEED_SET; -} - -static enum ec_status -fp_command_encryption_status(struct host_cmd_handler_args *args) -{ - struct ec_response_fp_encryption_status *r = args->response; - - r->valid_flags = FP_ENC_STATUS_SEED_SET; - r->status = fp_encryption_status; - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_ENC_STATUS, fp_command_encryption_status, - EC_VER_MASK(0)); - -static int validate_fp_mode(const uint32_t mode) -{ - uint32_t capture_type = FP_CAPTURE_TYPE(mode); - uint32_t algo_mode = mode & ~FP_MODE_CAPTURE_TYPE_MASK; - uint32_t cur_mode = sensor_mode; - - if (capture_type >= FP_CAPTURE_TYPE_MAX) - return EC_ERROR_INVAL; - - if (algo_mode & ~FP_VALID_MODES) - return EC_ERROR_INVAL; - - if ((mode & FP_MODE_ENROLL_SESSION) && - templ_valid >= FP_MAX_FINGER_COUNT) { - CPRINTS("Maximum number of fingers already enrolled: %d", - FP_MAX_FINGER_COUNT); - return EC_ERROR_INVAL; - } - - /* Don't allow sensor reset if any other mode is - * set (including FP_MODE_RESET_SENSOR itself). - */ - if (mode & FP_MODE_RESET_SENSOR) { - if (cur_mode & FP_VALID_MODES) - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -int fp_set_sensor_mode(uint32_t mode, uint32_t *mode_output) -{ - int ret; - - if (mode_output == NULL) - return EC_RES_INVALID_PARAM; - - ret = validate_fp_mode(mode); - if (ret != EC_SUCCESS) { - CPRINTS("Invalid FP mode 0x%x", mode); - return EC_RES_INVALID_PARAM; - } - - if (!(mode & FP_MODE_DONT_CHANGE)) { - sensor_mode = mode; - task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG); - } - - *mode_output = sensor_mode; - return EC_RES_SUCCESS; -} - -static enum ec_status fp_command_mode(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_mode *p = args->params; - struct ec_response_fp_mode *r = args->response; - - int ret = fp_set_sensor_mode(p->mode, &r->mode); - - if (ret == EC_RES_SUCCESS) - args->response_size = sizeof(*r); - - return ret; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_MODE, fp_command_mode, EC_VER_MASK(0)); - -static enum ec_status fp_command_context(struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_context_v1 *p = args->params; - uint32_t mode_output; - - switch (p->action) { - case FP_CONTEXT_ASYNC: - if (sensor_mode & FP_MODE_RESET_SENSOR) - return EC_RES_BUSY; - - /** - * Trigger a call to fp_reset_and_clear_context() by - * requesting a reset. Since that function triggers a call to - * fp_sensor_open(), this must be asynchronous because - * fp_sensor_open() can take ~175 ms. See http://b/137288498. - */ - return fp_set_sensor_mode(FP_MODE_RESET_SENSOR, &mode_output); - - case FP_CONTEXT_GET_RESULT: - if (sensor_mode & FP_MODE_RESET_SENSOR) - return EC_RES_BUSY; - - memcpy(user_id, p->userid, sizeof(user_id)); - return EC_RES_SUCCESS; - } - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_CONTEXT, fp_command_context, EC_VER_MASK(1)); - -int fp_enable_positive_match_secret(uint32_t fgr, - struct positive_match_secret_state *state) -{ - timestamp_t now; - - if (state->readable) { - CPRINTS("Error: positive match secret already readable."); - fp_disable_positive_match_secret(state); - return EC_ERROR_UNKNOWN; - } - - now = get_time(); - state->template_matched = fgr; - state->readable = true; - state->deadline.val = now.val + (5 * SECOND); - return EC_SUCCESS; -} - -void fp_disable_positive_match_secret( - struct positive_match_secret_state *state) -{ - state->template_matched = FP_NO_SUCH_TEMPLATE; - state->readable = false; - state->deadline.val = 0; -} - -static enum ec_status fp_command_read_match_secret( - struct host_cmd_handler_args *args) -{ - const struct ec_params_fp_read_match_secret *params = args->params; - struct ec_response_fp_read_match_secret *response = args->response; - int8_t fgr = params->fgr; - timestamp_t now = get_time(); - struct positive_match_secret_state state_copy - = positive_match_secret_state; - - fp_disable_positive_match_secret(&positive_match_secret_state); - - if (fgr < 0 || fgr >= FP_MAX_FINGER_COUNT) { - CPRINTS("Invalid finger number %d", fgr); - return EC_RES_INVALID_PARAM; - } - if (timestamp_expired(state_copy.deadline, &now)) { - CPRINTS("Reading positive match secret disallowed: " - "deadline has passed."); - return EC_RES_TIMEOUT; - } - if (fgr != state_copy.template_matched || !state_copy.readable) { - CPRINTS("Positive match secret for finger %d is not meant to " - "be read now.", fgr); - return EC_RES_ACCESS_DENIED; - } - - if (derive_positive_match_secret(response->positive_match_secret, - fp_positive_match_salt[fgr]) - != EC_SUCCESS) { - CPRINTS("Failed to derive positive match secret for finger %d", - fgr); - /* Keep the template and encryption salt. */ - return EC_RES_ERROR; - } - CPRINTS("Derived positive match secret for finger %d", fgr); - args->response_size = sizeof(*response); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FP_READ_MATCH_SECRET, fp_command_read_match_secret, - EC_VER_MASK(0)); diff --git a/common/gesture.c b/common/gesture.c deleted file mode 100644 index 88d79448a5..0000000000 --- a/common/gesture.c +++ /dev/null @@ -1,335 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Board specific gesture recognition */ - -#include "accelgyro.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "gesture.h" -#include "lid_switch.h" -#include "lightbar.h" -#include "motion_sense.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_GESTURE, outstr) -#define CPRINTS(format, args...) cprints(CC_GESTURE, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_GESTURE, format, ## args) - - -/* - * Double tap detection parameters - * Double tap works by looking for two isolated Z-axis accelerometer impulses - * preceded and followed by relatively calm periods of accelerometer motion. - * - * Define an outer and inner window. The inner window specifies how - * long the tap impulse is expected to last. The outer window specifies the - * period before the initial tap impluse and after the final tap impulse for - * which to check for relatively calm periods. In between the two impulses - * there is a minimum and maximum interstice time allowed. - */ -#define OUTER_WINDOW \ - (CONFIG_GESTURE_TAP_OUTER_WINDOW_T / \ - CONFIG_GESTURE_SAMPLING_INTERVAL_MS) -#define INNER_WINDOW \ - (CONFIG_GESTURE_TAP_INNER_WINDOW_T / \ - CONFIG_GESTURE_SAMPLING_INTERVAL_MS) -#define MIN_INTERSTICE \ - (CONFIG_GESTURE_TAP_MIN_INTERSTICE_T / \ - CONFIG_GESTURE_SAMPLING_INTERVAL_MS) -#define MAX_INTERSTICE \ - (CONFIG_GESTURE_TAP_MAX_INTERSTICE_T / \ - CONFIG_GESTURE_SAMPLING_INTERVAL_MS) -#define MAX_WINDOW OUTER_WINDOW - -/* State machine states for detecting double tap */ -enum tap_states { - /* Look for calm before the storm */ - TAP_IDLE, - /* Record first Z impulse */ - TAP_IMPULSE_1, - - /* Eye of the storm, expect Z motion to drop and then suddenly spike */ - TAP_INTERSTICE_DROP, - TAP_INTERSTICE_RISE, - - /* Record second Z impulse */ - TAP_IMPULSE_2, - /* Should be quiet after the storm */ - TAP_AFTER_EVENT -}; - -/* Tap sensor to use */ -static struct motion_sensor_t *sensor = -&motion_sensors[CONFIG_GESTURE_TAP_SENSOR]; - -/* Tap state information */ -static int history_z[MAX_WINDOW]; /* Changes in Z */ -static int history_xy[MAX_WINDOW]; /* Changes in X and Y */ -static int state, history_idx; -static int history_initialized, history_init_index; -static int tap_debug; - -/* Tap detection flag */ -static int tap_detection; - -/* - * TODO(crosbug.com/p/33102): Cleanup this function: break into multiple - * functions and generalize so it can be used for other boards. - */ -static int gesture_tap_for_battery(void) -{ - /* Current and previous accel x,y,z */ - int x, y, z; - static int x_p, y_p, z_p; - - /* Number of iterations in this state */ - static int state_cnt; - - /* - * Running sums of data diffs for inner and outer windows. - * Z data kept separate from X and Y data - */ - static int sum_z_inner, sum_z_outer, sum_xy_inner, sum_xy_outer; - - /* Total variation in each signal, normalized for window size */ - int delta_z_outer, delta_z_inner, delta_xy_outer, delta_xy_inner; - - /* Max variation seen during tap event and state cnts since max */ - static int delta_z_inner_max; - static int cnts_since_max; - - /* Interstice Z motion thresholds */ - static int z_drop_thresh, z_rise_thresh; - - int history_idx_inner, state_p; - int ret = 0; - - /* Get data */ - x = sensor->xyz[0]; - y = sensor->xyz[1]; - z = sensor->xyz[2]; - - /* - * Calculate history of change in Z sensor and keeping - * running sums for the past. - */ - history_idx_inner = history_idx - INNER_WINDOW; - if (history_idx_inner < 0) - history_idx_inner += MAX_WINDOW; - sum_z_inner -= history_z[history_idx_inner]; - sum_z_outer -= history_z[history_idx]; - history_z[history_idx] = ABS(z - z_p); - sum_z_inner += history_z[history_idx]; - sum_z_outer += history_z[history_idx]; - - /* - * Calculate history of change in X and Y sensors combined - * and keep a running sum of the change over the past. - */ - sum_xy_inner -= history_xy[history_idx_inner]; - sum_xy_outer -= history_xy[history_idx]; - history_xy[history_idx] = ABS(x - x_p) + ABS(y - y_p); - sum_xy_inner += history_xy[history_idx]; - sum_xy_outer += history_xy[history_idx]; - - /* Increment history index */ - history_idx = (history_idx == MAX_WINDOW - 1) ? 0 : (history_idx + 1); - - /* Store previous X, Y, Z data */ - x_p = x; - y_p = y; - z_p = z; - - /* - * Ignore data until we fill history buffer and wrap around. If - * detection is paused, history_init_index will store the index - * when paused, so that when re-started, we will wait until we - * wrap around again. - */ - if (history_idx == history_init_index) - history_initialized = 1; - if (!history_initialized) - return 0; - - /* - * Normalize data based on window size and isolate outer and inner - * window data. - */ - delta_z_outer = (sum_z_outer - sum_z_inner) * 1000 / - (OUTER_WINDOW - INNER_WINDOW); - delta_z_inner = sum_z_inner * 1000 / INNER_WINDOW; - delta_xy_outer = (sum_xy_outer - sum_xy_inner) * 1000 / - (OUTER_WINDOW - INNER_WINDOW); - delta_xy_inner = sum_xy_inner * 1000 / INNER_WINDOW; - - state_cnt++; - state_p = state; - - switch (state) { - case TAP_IDLE: - /* Look for a sudden increase in Z movement */ - if (delta_z_inner > 30000 && - delta_z_inner > 13 * delta_z_outer && - delta_z_inner > 1 * delta_xy_inner) { - delta_z_inner_max = delta_z_inner; - state_cnt = 0; - state = TAP_IMPULSE_1; - } - break; - - case TAP_IMPULSE_1: - /* Find the peak inner window of Z movement */ - if (delta_z_inner > delta_z_inner_max) { - delta_z_inner_max = delta_z_inner; - cnts_since_max = state_cnt; - } - - /* After inner window has passed, move to next state */ - if (state_cnt >= INNER_WINDOW) { - state = TAP_INTERSTICE_DROP; - z_drop_thresh = delta_z_inner_max / 12; - z_rise_thresh = delta_z_inner_max / 3; - state_cnt += INNER_WINDOW - cnts_since_max; - } - break; - - case TAP_INTERSTICE_DROP: - /* Check for z motion to go back down first */ - if (delta_z_inner < z_drop_thresh) - state = TAP_INTERSTICE_RISE; - - if (state_cnt > MAX_INTERSTICE) - state = TAP_IDLE; - - break; - - case TAP_INTERSTICE_RISE: - /* Then, check for z motion to go back up */ - if (delta_z_inner > z_rise_thresh) { - if (state_cnt < MIN_INTERSTICE) { - state = TAP_IDLE; - } else { - delta_z_inner_max = delta_z_inner; - state_cnt = 0; - state = TAP_IMPULSE_2; - } - } - - if (state_cnt > MAX_INTERSTICE) - state = TAP_IDLE; - break; - - case TAP_IMPULSE_2: - /* Find the peak inner window of Z movement */ - if (delta_z_inner > delta_z_inner_max) { - delta_z_inner_max = delta_z_inner; - cnts_since_max = state_cnt; - } - - /* After inner window has passed, move to next state */ - if (state_cnt >= INNER_WINDOW) { - state = TAP_AFTER_EVENT; - state_cnt += INNER_WINDOW - cnts_since_max; - } - - case TAP_AFTER_EVENT: - /* Check for small Z movement after the event */ - if (state_cnt < OUTER_WINDOW) - break; - - if (2 * delta_z_inner_max > 3 * delta_z_outer && - delta_z_outer > 1 * delta_xy_outer) - ret = 1; - - state = TAP_IDLE; - break; - } - - /* On state transitions, print debug info */ - if (tap_debug && - (state != state_p || - (state_cnt % 10000 == 9999))) { - /* make sure we don't divide by 0 */ - if (delta_z_outer == 0 || delta_xy_inner == 0) - CPRINTS("tap st %d->%d, error div by 0", - state_p, state); - else - CPRINTS("tap st %d->%d, st_cnt %-3d " - "Z_in:Z_out %-3d, Z_in:XY_in %-3d " - "dZ_in %-8.3d, dZ_in_max %-8.3d, " - "dZ_out %-8.3d", - state_p, state, state_cnt, - delta_z_inner / delta_z_outer, - delta_z_inner / delta_xy_inner, - delta_z_inner, - delta_z_inner_max, - delta_z_outer); - } - - return ret; -} - -static void gesture_chipset_resume(void) -{ - /* disable tap detection */ - tap_detection = 0; -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, gesture_chipset_resume, - GESTURE_HOOK_PRIO); - -static void gesture_chipset_suspend(void) -{ - /* - * Clear tap init and history initialized so that we have to - * record a whole new set of data, and enable tap detection - */ - history_initialized = 0; - history_init_index = history_idx; - state = TAP_IDLE; - tap_detection = 1; -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, gesture_chipset_suspend, - GESTURE_HOOK_PRIO); - -void gesture_calc(uint32_t *event) -{ - /* Only check for gesture if lid is closed and tap detection is on */ - if (!tap_detection || lid_is_open()) - return; - - if (gesture_tap_for_battery()) - *event |= TASK_EVENT_MOTION_ACTIVITY_INTERRUPT( - MOTIONSENSE_ACTIVITY_DOUBLE_TAP); -} - -/*****************************************************************************/ -/* Console commands */ -static int command_tap_info(int argc, char **argv) -{ - int val; - - ccprintf("tap: %s\n", (tap_detection && !lid_is_open()) ? - "on" : "off"); - - if (argc > 1) { - if (!parse_bool(argv[1], &val)) - return EC_ERROR_PARAM1; - tap_debug = val; - } - - ccprintf("debug: %s\n", tap_debug ? "on" : "off"); - ccprintf("odr: %d\n", sensor->drv->get_data_rate(sensor)); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(tapinfo, command_tap_info, - "debug on/off", - "Print tap information"); - diff --git a/common/gyro_cal.c b/common/gyro_cal.c deleted file mode 100644 index 572e401b18..0000000000 --- a/common/gyro_cal.c +++ /dev/null @@ -1,630 +0,0 @@ -/* Copyright 2020 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 "gyro_cal.h" -#include "string.h" -#include <stdbool.h> - -/* - * Maximum gyro bias correction (should be set based on expected max bias - * of the given sensor). [rad/sec] - */ -#define MAX_GYRO_BIAS FLOAT_TO_FP(0.2f) - -static void device_stillness_check(struct gyro_cal *gyro_cal, - uint32_t sample_time_us); - -static void compute_gyro_cal(struct gyro_cal *gyro_cal, - uint32_t calibration_time_us); - -static void check_window(struct gyro_cal *gyro_cal, uint32_t sample_time_us); - -/** Data tracker command enumeration. */ -enum gyro_cal_tracker_command { - /** Resets the local data used for data tracking. */ - DO_RESET = 0, - /** Updates the local tracking data. */ - DO_UPDATE_DATA, - /** Stores intermediate results for later recall. */ - DO_STORE_DATA, - /** Computes and provides the results of the gate function. */ - DO_EVALUATE -}; - -/** - * Reset the gyro_cal's temperature statistics. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - */ -static void gyro_temperature_stats_tracker_reset(struct gyro_cal *gyro_cal); - -/** - * Updates the temperature min/max and mean during the stillness period. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - * @param temperature_kelvin New temperature sample to include. - */ -static void gyro_temperature_stats_tracker_update(struct gyro_cal *gyro_cal, - int temperature_kelvin); - -/** - * Store the tracker data to be used for calculation. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - */ -static void gyro_temperature_stats_tracker_store(struct gyro_cal *gyro_cal); - -/** - * Compute whether or not the temperature values are in range. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - * @return 'true' if the min and max temperature values exceed the - * range set by 'temperature_delta_limit_kelvin'. - */ -static bool gyro_temperature_stats_tracker_eval(struct gyro_cal *gyro_cal); - -/** - * Tracks the minimum and maximum gyroscope stillness window means. - * Returns - * - * @param gyro_cal Pointer to the gyro_cal data structure. - * @param do_this Command enumerator that controls function behavior. - */ -static void gyro_still_mean_tracker_reset(struct gyro_cal *gyro_cal); - -/** - * Compute the min/max window mean values according to 'window_mean_tracker'. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - */ -static void gyro_still_mean_tracker_update(struct gyro_cal *gyro_cal); - -/** - * Store the most recent "stillness" mean data to the gyro_cal data structure. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - */ -static void gyro_still_mean_tracker_store(struct gyro_cal *gyro_cal); - -/** - * Compute whether or not the gyroscope window range is within the valid range. - * - * @param gyro_cal Pointer to the gyro_cal data structure. - * @return 'true' when the difference between gyroscope min and max - * window means are outside the range set by - * 'stillness_mean_delta_limit'. - */ -static bool gyro_still_mean_tracker_eval(struct gyro_cal *gyro_cal); - -void init_gyro_cal(struct gyro_cal *gyro_cal) -{ - gyro_still_mean_tracker_reset(gyro_cal); - gyro_temperature_stats_tracker_reset(gyro_cal); -} - -void gyro_cal_get_bias(struct gyro_cal *gyro_cal, fpv3_t bias, - int *temperature_kelvin, uint32_t *calibration_time_us) -{ - bias[X] = gyro_cal->bias_x; - bias[Y] = gyro_cal->bias_y; - bias[Z] = gyro_cal->bias_z; - *calibration_time_us = gyro_cal->calibration_time_us; - *temperature_kelvin = gyro_cal->bias_temperature_kelvin; -} - -void gyro_cal_set_bias(struct gyro_cal *gyro_cal, fpv3_t bias, - int temperature_kelvin, uint32_t calibration_time_us) -{ - gyro_cal->bias_x = bias[X]; - gyro_cal->bias_y = bias[Y]; - gyro_cal->bias_z = bias[Z]; - gyro_cal->calibration_time_us = calibration_time_us; - gyro_cal->bias_temperature_kelvin = temperature_kelvin; -} - -void gyro_cal_remove_bias(struct gyro_cal *gyro_cal, fpv3_t in, fpv3_t out) -{ - if (gyro_cal->gyro_calibration_enable) { - out[X] = in[X] - gyro_cal->bias_x; - out[Y] = in[Y] - gyro_cal->bias_y; - out[Z] = in[Z] - gyro_cal->bias_z; - } -} - -bool gyro_cal_new_bias_available(struct gyro_cal *gyro_cal) -{ - bool new_gyro_cal_available = (gyro_cal->gyro_calibration_enable && - gyro_cal->new_gyro_cal_available); - - /* Clear the flag. */ - gyro_cal->new_gyro_cal_available = false; - - return new_gyro_cal_available; -} - -void gyro_cal_update_gyro(struct gyro_cal *gyro_cal, uint32_t sample_time_us, - fp_t x, fp_t y, fp_t z, int temperature_kelvin) -{ - /* - * Make sure that a valid window end-time is set, and start the window - * timer. - */ - if (gyro_cal->stillness_win_endtime_us <= 0) { - gyro_cal->stillness_win_endtime_us = - sample_time_us + gyro_cal->window_time_duration_us; - - /* Start the window timer. */ - gyro_cal->gyro_window_start_us = sample_time_us; - } - - /* Update the temperature statistics. */ - gyro_temperature_stats_tracker_update(gyro_cal, temperature_kelvin); - - /* Pass gyro data to stillness detector */ - gyro_still_det_update(&gyro_cal->gyro_stillness_detect, - gyro_cal->stillness_win_endtime_us, - sample_time_us, x, y, z); - - /* - * Perform a device stillness check, set next window end-time, and - * possibly do a gyro bias calibration and stillness detector reset. - */ - device_stillness_check(gyro_cal, sample_time_us); -} - -void gyro_cal_update_mag(struct gyro_cal *gyro_cal, uint32_t sample_time_us, - fp_t x, fp_t y, fp_t z) -{ - /* Pass magnetometer data to stillness detector. */ - gyro_still_det_update(&gyro_cal->mag_stillness_detect, - gyro_cal->stillness_win_endtime_us, - sample_time_us, x, y, z); - - /* Received a magnetometer sample; incorporate it into detection. */ - gyro_cal->using_mag_sensor = true; - - /* - * Perform a device stillness check, set next window end-time, and - * possibly do a gyro bias calibration and stillness detector reset. - */ - device_stillness_check(gyro_cal, sample_time_us); -} - -void gyro_cal_update_accel(struct gyro_cal *gyro_cal, uint32_t sample_time_us, - fp_t x, fp_t y, fp_t z) -{ - /* Pass accelerometer data to stillnesss detector. */ - gyro_still_det_update(&gyro_cal->accel_stillness_detect, - gyro_cal->stillness_win_endtime_us, - sample_time_us, x, y, z); - - /* - * Perform a device stillness check, set next window end-time, and - * possibly do a gyro bias calibration and stillness detector reset. - */ - device_stillness_check(gyro_cal, sample_time_us); -} - -/** - * Handle the case where the device is found to be still. This function should - * be called from device_stillness_check. - * - * @param gyro_cal Pointer to the gyroscope calibration struct. - */ -static void handle_device_is_still(struct gyro_cal *gyro_cal) -{ - /* - * Device is "still" logic: - * If not previously still, then record the start time. - * If stillness period is too long, then do a calibration. - * Otherwise, continue collecting stillness data. - */ - bool stillness_duration_exceeded = false; - - /* - * If device was not previously still, set new start timestamp. - */ - if (!gyro_cal->prev_still) { - /* - * Record the starting timestamp of the current stillness - * window. This enables the calculation of total duration of - * the stillness period. - */ - gyro_cal->start_still_time_us = - gyro_cal->gyro_stillness_detect.window_start_time; - } - - /* - * Check to see if current stillness period exceeds the desired limit. - */ - stillness_duration_exceeded = - gyro_cal->gyro_stillness_detect.last_sample_time >= - (gyro_cal->start_still_time_us + - gyro_cal->max_still_duration_us); - - /* Track the new stillness mean and temperature data. */ - gyro_still_mean_tracker_store(gyro_cal); - gyro_temperature_stats_tracker_store(gyro_cal); - - if (stillness_duration_exceeded) { - /* - * The current stillness has gone too long. Do a calibration - * with the current data and reset. - */ - - /* - * Updates the gyro bias estimate with the current window data - * and resets the stats. - */ - gyro_still_det_reset(&gyro_cal->accel_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->gyro_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->mag_stillness_detect, - /*reset_stats=*/true); - - /* - * Resets the local calculations because the stillness - * period is over. - */ - gyro_still_mean_tracker_reset(gyro_cal); - gyro_temperature_stats_tracker_reset(gyro_cal); - - /* Computes a new gyro offset estimate. */ - compute_gyro_cal( - gyro_cal, - gyro_cal->gyro_stillness_detect.last_sample_time); - - /* - * Update stillness flag. Force the start of a new - * stillness period. - */ - gyro_cal->prev_still = false; - } else { - /* Continue collecting stillness data. */ - - /* Extend the stillness period. */ - gyro_still_det_reset(&gyro_cal->accel_stillness_detect, - /*reset_stats=*/false); - gyro_still_det_reset(&gyro_cal->gyro_stillness_detect, - /*reset_stats=*/false); - gyro_still_det_reset(&gyro_cal->mag_stillness_detect, - /*reset_stats=*/false); - - /* Update the stillness flag. */ - gyro_cal->prev_still = true; - } -} - -static void handle_device_not_still(struct gyro_cal *gyro_cal) -{ - /* Device is NOT still; motion detected. */ - - /* - * If device was previously still and the total stillness - * duration is not "too short", then do a calibration with the - * data accumulated thus far. - */ - bool stillness_duration_too_short = - gyro_cal->gyro_stillness_detect.window_start_time < - (gyro_cal->start_still_time_us + - gyro_cal->min_still_duration_us); - - if (gyro_cal->prev_still && !stillness_duration_too_short) - compute_gyro_cal( - gyro_cal, - gyro_cal->gyro_stillness_detect.window_start_time); - - /* Reset the stillness detectors and the stats. */ - gyro_still_det_reset(&gyro_cal->accel_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->gyro_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->mag_stillness_detect, - /*reset_stats=*/true); - - /* Resets the temperature and sensor mean data. */ - gyro_temperature_stats_tracker_reset(gyro_cal); - gyro_still_mean_tracker_reset(gyro_cal); - - /* Update stillness flag. */ - gyro_cal->prev_still = false; -} - -void device_stillness_check(struct gyro_cal *gyro_cal, uint32_t sample_time_us) -{ - bool min_max_temp_exceeded = false; - bool mean_not_stable = false; - bool device_is_still = false; - fp_t conf_not_rot = INT_TO_FP(0); - fp_t conf_not_accel = INT_TO_FP(0); - fp_t conf_still = INT_TO_FP(0); - - /* Check the window timer. */ - check_window(gyro_cal, sample_time_us); - - /* Is there enough data to do a stillness calculation? */ - if ((!gyro_cal->mag_stillness_detect.stillness_window_ready && - gyro_cal->using_mag_sensor) || - !gyro_cal->accel_stillness_detect.stillness_window_ready || - !gyro_cal->gyro_stillness_detect.stillness_window_ready) - return; /* Not yet, wait for more data. */ - - /* Set the next window end-time for the stillness detectors. */ - gyro_cal->stillness_win_endtime_us = - sample_time_us + gyro_cal->window_time_duration_us; - - /* Update the confidence scores for all sensors. */ - gyro_still_det_compute(&gyro_cal->accel_stillness_detect); - gyro_still_det_compute(&gyro_cal->gyro_stillness_detect); - if (gyro_cal->using_mag_sensor) { - gyro_still_det_compute(&gyro_cal->mag_stillness_detect); - } else { - /* - * Not using magnetometer, force stillness confidence to 100%. - */ - gyro_cal->mag_stillness_detect.stillness_confidence = - INT_TO_FP(1); - } - - /* Updates the mean tracker data. */ - gyro_still_mean_tracker_update(gyro_cal); - - /* - * Determine motion confidence scores (rotation, accelerating, and - * stillness). - */ - conf_not_rot = - fp_mul(gyro_cal->gyro_stillness_detect.stillness_confidence, - gyro_cal->mag_stillness_detect.stillness_confidence); - conf_not_accel = gyro_cal->accel_stillness_detect.stillness_confidence; - conf_still = fp_mul(conf_not_rot, conf_not_accel); - - /* Evaluate the mean and temperature gate functions. */ - mean_not_stable = gyro_still_mean_tracker_eval(gyro_cal); - min_max_temp_exceeded = gyro_temperature_stats_tracker_eval(gyro_cal); - - /* Determines if the device is currently still. */ - device_is_still = (conf_still > gyro_cal->stillness_threshold) && - !mean_not_stable && !min_max_temp_exceeded; - - if (device_is_still) - handle_device_is_still(gyro_cal); - else - handle_device_not_still(gyro_cal); - - /* Reset the window timer after we have processed data. */ - gyro_cal->gyro_window_start_us = sample_time_us; -} - -void compute_gyro_cal(struct gyro_cal *gyro_cal, uint32_t calibration_time_us) -{ - /* Check to see if new calibration values is within acceptable range. */ - if (!(gyro_cal->gyro_stillness_detect.prev_mean[X] < MAX_GYRO_BIAS && - gyro_cal->gyro_stillness_detect.prev_mean[X] > -MAX_GYRO_BIAS && - gyro_cal->gyro_stillness_detect.prev_mean[Y] < MAX_GYRO_BIAS && - gyro_cal->gyro_stillness_detect.prev_mean[Y] > -MAX_GYRO_BIAS && - gyro_cal->gyro_stillness_detect.prev_mean[Z] < MAX_GYRO_BIAS && - gyro_cal->gyro_stillness_detect.prev_mean[Z] > -MAX_GYRO_BIAS)) - /* Outside of range. Ignore, reset, and continue. */ - return; - - /* Record the new gyro bias offset calibration. */ - gyro_cal->bias_x = gyro_cal->gyro_stillness_detect.prev_mean[X]; - gyro_cal->bias_y = gyro_cal->gyro_stillness_detect.prev_mean[Y]; - gyro_cal->bias_z = gyro_cal->gyro_stillness_detect.prev_mean[Z]; - - /* - * Store the calibration temperature (using the mean temperature over - * the "stillness" period). - */ - gyro_cal->bias_temperature_kelvin = gyro_cal->temperature_mean_kelvin; - - /* Store the calibration time stamp. */ - gyro_cal->calibration_time_us = calibration_time_us; - - /* Record the final stillness confidence. */ - gyro_cal->stillness_confidence = fp_mul( - gyro_cal->gyro_stillness_detect.prev_stillness_confidence, - gyro_cal->accel_stillness_detect.prev_stillness_confidence); - gyro_cal->stillness_confidence = fp_mul( - gyro_cal->stillness_confidence, - gyro_cal->mag_stillness_detect.prev_stillness_confidence); - - /* Set flag to indicate a new gyro calibration value is available. */ - gyro_cal->new_gyro_cal_available = true; -} - -void check_window(struct gyro_cal *gyro_cal, uint32_t sample_time_us) -{ - bool window_timeout; - - /* Check for initialization of the window time (=0). */ - if (gyro_cal->gyro_window_start_us <= 0) - return; - - /* - * Checks for the following window timeout conditions: - * i. The current timestamp has exceeded the allowed window duration. - * ii. A timestamp was received that has jumped backwards by more than - * the allowed window duration (e.g., timestamp clock roll-over). - */ - window_timeout = - (sample_time_us > gyro_cal->gyro_window_timeout_duration_us + - gyro_cal->gyro_window_start_us) || - (sample_time_us + gyro_cal->gyro_window_timeout_duration_us < - gyro_cal->gyro_window_start_us); - - /* If a timeout occurred then reset to known good state. */ - if (window_timeout) { - /* Reset stillness detectors and restart data capture. */ - gyro_still_det_reset(&gyro_cal->accel_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->gyro_stillness_detect, - /*reset_stats=*/true); - gyro_still_det_reset(&gyro_cal->mag_stillness_detect, - /*reset_stats=*/true); - - /* Resets the temperature and sensor mean data. */ - gyro_temperature_stats_tracker_reset(gyro_cal); - gyro_still_mean_tracker_reset(gyro_cal); - - /* Resets the stillness window end-time. */ - gyro_cal->stillness_win_endtime_us = 0; - - /* Force stillness confidence to zero. */ - gyro_cal->accel_stillness_detect.prev_stillness_confidence = 0; - gyro_cal->gyro_stillness_detect.prev_stillness_confidence = 0; - gyro_cal->mag_stillness_detect.prev_stillness_confidence = 0; - gyro_cal->stillness_confidence = 0; - gyro_cal->prev_still = false; - - /* - * If there are no magnetometer samples being received then - * operate the calibration algorithm without this sensor. - */ - if (!gyro_cal->mag_stillness_detect.stillness_window_ready && - gyro_cal->using_mag_sensor) { - gyro_cal->using_mag_sensor = false; - } - - /* Assert window timeout flags. */ - gyro_cal->gyro_window_start_us = 0; - } -} - -void gyro_temperature_stats_tracker_reset(struct gyro_cal *gyro_cal) -{ - /* Resets the mean accumulator. */ - gyro_cal->temperature_mean_tracker.num_points = 0; - gyro_cal->temperature_mean_tracker.mean_accumulator = INT_TO_FP(0); - - /* Initializes the min/max temperatures values. */ - gyro_cal->temperature_mean_tracker.temperature_min_kelvin = 0x7fff; - gyro_cal->temperature_mean_tracker.temperature_max_kelvin = 0xffff; -} - -void gyro_temperature_stats_tracker_update(struct gyro_cal *gyro_cal, - int temperature_kelvin) -{ - /* Does the mean accumulation. */ - gyro_cal->temperature_mean_tracker.mean_accumulator += - temperature_kelvin; - gyro_cal->temperature_mean_tracker.num_points++; - - /* Tracks the min, max, and latest temperature values. */ - gyro_cal->temperature_mean_tracker.latest_temperature_kelvin = - temperature_kelvin; - if (gyro_cal->temperature_mean_tracker.temperature_min_kelvin > - temperature_kelvin) { - gyro_cal->temperature_mean_tracker.temperature_min_kelvin = - temperature_kelvin; - } - if (gyro_cal->temperature_mean_tracker.temperature_max_kelvin < - temperature_kelvin) { - gyro_cal->temperature_mean_tracker.temperature_max_kelvin = - temperature_kelvin; - } -} - -void gyro_temperature_stats_tracker_store(struct gyro_cal *gyro_cal) -{ - /* - * Store the most recent temperature statistics data to the - * gyro_cal data structure. This functionality allows previous - * results to be recalled when the device suddenly becomes "not - * still". - */ - if (gyro_cal->temperature_mean_tracker.num_points > 0) - gyro_cal->temperature_mean_kelvin = - gyro_cal->temperature_mean_tracker.mean_accumulator / - gyro_cal->temperature_mean_tracker.num_points; - else - gyro_cal->temperature_mean_kelvin = - gyro_cal->temperature_mean_tracker - .latest_temperature_kelvin; -} - -bool gyro_temperature_stats_tracker_eval(struct gyro_cal *gyro_cal) -{ - bool min_max_temp_exceeded = false; - - /* Determines if the min/max delta exceeded the set limit. */ - if (gyro_cal->temperature_mean_tracker.num_points > 0) { - min_max_temp_exceeded = - (gyro_cal->temperature_mean_tracker - .temperature_max_kelvin - - gyro_cal->temperature_mean_tracker - .temperature_min_kelvin) > - gyro_cal->temperature_delta_limit_kelvin; - } - - return min_max_temp_exceeded; -} - -void gyro_still_mean_tracker_reset(struct gyro_cal *gyro_cal) -{ - size_t i; - - /* Resets the min/max window mean values to a default value. */ - for (i = 0; i < 3; i++) { - gyro_cal->window_mean_tracker.gyro_winmean_min[i] = FLT_MAX; - gyro_cal->window_mean_tracker.gyro_winmean_max[i] = -FLT_MAX; - } -} - -void gyro_still_mean_tracker_update(struct gyro_cal *gyro_cal) -{ - int i; - - /* Computes the min/max window mean values. */ - for (i = 0; i < 3; ++i) { - if (gyro_cal->window_mean_tracker.gyro_winmean_min[i] > - gyro_cal->gyro_stillness_detect.win_mean[i]) { - gyro_cal->window_mean_tracker.gyro_winmean_min[i] = - gyro_cal->gyro_stillness_detect.win_mean[i]; - } - if (gyro_cal->window_mean_tracker.gyro_winmean_max[i] < - gyro_cal->gyro_stillness_detect.win_mean[i]) { - gyro_cal->window_mean_tracker.gyro_winmean_max[i] = - gyro_cal->gyro_stillness_detect.win_mean[i]; - } - } -} - -void gyro_still_mean_tracker_store(struct gyro_cal *gyro_cal) -{ - /* - * Store the most recent "stillness" mean data to the gyro_cal - * data structure. This functionality allows previous results to - * be recalled when the device suddenly becomes "not still". - */ - memcpy(gyro_cal->gyro_winmean_min, - gyro_cal->window_mean_tracker.gyro_winmean_min, - sizeof(gyro_cal->window_mean_tracker.gyro_winmean_min)); - memcpy(gyro_cal->gyro_winmean_max, - gyro_cal->window_mean_tracker.gyro_winmean_max, - sizeof(gyro_cal->window_mean_tracker.gyro_winmean_max)); -} - -bool gyro_still_mean_tracker_eval(struct gyro_cal *gyro_cal) -{ - bool mean_not_stable = false; - size_t i; - - /* - * Performs the stability check and returns the 'true' if the - * difference between min/max window mean value is outside the - * stable range. - */ - for (i = 0; i < 3 && !mean_not_stable; i++) { - mean_not_stable |= - (gyro_cal->window_mean_tracker.gyro_winmean_max[i] - - gyro_cal->window_mean_tracker.gyro_winmean_min[i]) > - gyro_cal->stillness_mean_delta_limit; - } - - return mean_not_stable; -} diff --git a/common/gyro_still_det.c b/common/gyro_still_det.c deleted file mode 100644 index 4574e22e5f..0000000000 --- a/common/gyro_still_det.c +++ /dev/null @@ -1,242 +0,0 @@ -/* Copyright 2020 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 "gyro_still_det.h" -#include "vec3.h" - -/* Enforces the limits of an input value [0,1]. */ -static fp_t gyro_still_det_limit(fp_t value); - -void gyro_still_det_update(struct gyro_still_det *gyro_still_det, - uint32_t stillness_win_endtime, uint32_t sample_time, - fp_t x, fp_t y, fp_t z) -{ - fp_t delta = INT_TO_FP(0); - - /* - * Using the method of the assumed mean to preserve some numerical - * stability while avoiding per-sample divisions that the more - * numerically stable Welford method would afford. - * - * Reference for the numerical method used below to compute the - * online mean and variance statistics: - * 1). en.wikipedia.org/wiki/assumed_mean - */ - - /* Increment the number of samples. */ - gyro_still_det->num_acc_samples++; - - /* Online computation of mean for the running stillness period. */ - gyro_still_det->mean[X] += x; - gyro_still_det->mean[Y] += y; - gyro_still_det->mean[Z] += z; - - /* Is this the first sample of a new window? */ - if (gyro_still_det->start_new_window) { - /* Record the window start time. */ - gyro_still_det->window_start_time = sample_time; - gyro_still_det->start_new_window = false; - - /* Update assumed mean values. */ - gyro_still_det->assumed_mean[X] = x; - gyro_still_det->assumed_mean[Y] = y; - gyro_still_det->assumed_mean[Z] = z; - - /* Reset current window mean and variance. */ - gyro_still_det->num_acc_win_samples = 0; - gyro_still_det->win_mean[X] = INT_TO_FP(0); - gyro_still_det->win_mean[Y] = INT_TO_FP(0); - gyro_still_det->win_mean[Z] = INT_TO_FP(0); - gyro_still_det->acc_var[X] = INT_TO_FP(0); - gyro_still_det->acc_var[Y] = INT_TO_FP(0); - gyro_still_det->acc_var[Z] = INT_TO_FP(0); - } else { - /* - * Check to see if we have enough samples to compute a stillness - * confidence score. - */ - gyro_still_det->stillness_window_ready = - (sample_time >= stillness_win_endtime) && - (gyro_still_det->num_acc_samples > 1); - } - - /* Record the most recent sample time stamp. */ - gyro_still_det->last_sample_time = sample_time; - - /* Online window mean and variance ("one-pass" accumulation). */ - gyro_still_det->num_acc_win_samples++; - - delta = (x - gyro_still_det->assumed_mean[X]); - gyro_still_det->win_mean[X] += delta; - gyro_still_det->acc_var[X] += fp_sq(delta); - - delta = (y - gyro_still_det->assumed_mean[Y]); - gyro_still_det->win_mean[Y] += delta; - gyro_still_det->acc_var[Y] += fp_sq(delta); - - delta = (z - gyro_still_det->assumed_mean[Z]); - gyro_still_det->win_mean[Z] += delta; - gyro_still_det->acc_var[Z] += fp_sq(delta); -} - -fp_t gyro_still_det_compute(struct gyro_still_det *gyro_still_det) -{ - fp_t tmp_denom = INT_TO_FP(1); - fp_t tmp_denom_mean = INT_TO_FP(1); - fp_t tmp; - fp_t upper_var_thresh, lower_var_thresh; - - /* Don't divide by zero (not likely, but a precaution). */ - if (gyro_still_det->num_acc_win_samples > 1) { - tmp_denom = fp_div( - tmp_denom, - INT_TO_FP(gyro_still_det->num_acc_win_samples - 1)); - tmp_denom_mean = - fp_div(tmp_denom_mean, - INT_TO_FP(gyro_still_det->num_acc_win_samples)); - } else { - /* Return zero stillness confidence. */ - gyro_still_det->stillness_confidence = 0; - return gyro_still_det->stillness_confidence; - } - - /* Update the final calculation of window mean and variance. */ - tmp = gyro_still_det->win_mean[X]; - gyro_still_det->win_mean[X] = - fp_mul(gyro_still_det->win_mean[X], tmp_denom_mean); - gyro_still_det->win_var[X] = - fp_mul((gyro_still_det->acc_var[X] - - fp_mul(gyro_still_det->win_mean[X], tmp)), - tmp_denom); - - tmp = gyro_still_det->win_mean[Y]; - gyro_still_det->win_mean[Y] = - fp_mul(gyro_still_det->win_mean[Y], tmp_denom_mean); - gyro_still_det->win_var[Y] = - fp_mul((gyro_still_det->acc_var[Y] - - fp_mul(gyro_still_det->win_mean[Y], tmp)), - tmp_denom); - - tmp = gyro_still_det->win_mean[Z]; - gyro_still_det->win_mean[Z] = - fp_mul(gyro_still_det->win_mean[Z], tmp_denom_mean); - gyro_still_det->win_var[Z] = - fp_mul((gyro_still_det->acc_var[Z] - - fp_mul(gyro_still_det->win_mean[Z], tmp)), - tmp_denom); - - /* Adds the assumed mean value back to the total mean calculation. */ - gyro_still_det->win_mean[X] += gyro_still_det->assumed_mean[X]; - gyro_still_det->win_mean[Y] += gyro_still_det->assumed_mean[Y]; - gyro_still_det->win_mean[Z] += gyro_still_det->assumed_mean[Z]; - - /* Define the variance thresholds. */ - upper_var_thresh = gyro_still_det->var_threshold + - gyro_still_det->confidence_delta; - - lower_var_thresh = gyro_still_det->var_threshold - - gyro_still_det->confidence_delta; - - /* Compute the stillness confidence score. */ - if ((gyro_still_det->win_var[X] > upper_var_thresh) || - (gyro_still_det->win_var[Y] > upper_var_thresh) || - (gyro_still_det->win_var[Z] > upper_var_thresh)) { - /* - * Sensor variance exceeds the upper threshold (i.e., motion - * detected). Set stillness confidence equal to 0. - */ - gyro_still_det->stillness_confidence = 0; - } else if ((gyro_still_det->win_var[X] <= lower_var_thresh) && - (gyro_still_det->win_var[Y] <= lower_var_thresh) && - (gyro_still_det->win_var[Z] <= lower_var_thresh)) { - /* - * Sensor variance is below the lower threshold (i.e. - * stillness detected). - * Set stillness confidence equal to 1. - */ - gyro_still_det->stillness_confidence = INT_TO_FP(1); - } else { - /* - * Motion detection thresholds not exceeded. Compute the - * stillness confidence score. - */ - fp_t var_thresh = gyro_still_det->var_threshold; - fpv3_t limit; - - /* - * Compute the stillness confidence score. - * Each axis score is limited [0,1]. - */ - tmp_denom = fp_div(INT_TO_FP(1), - (upper_var_thresh - lower_var_thresh)); - limit[X] = gyro_still_det_limit( - FLOAT_TO_FP(0.5f) - - fp_mul(gyro_still_det->win_var[X] - var_thresh, - tmp_denom)); - limit[Y] = gyro_still_det_limit( - FLOAT_TO_FP(0.5f) - - fp_mul(gyro_still_det->win_var[Y] - var_thresh, - tmp_denom)); - limit[Z] = gyro_still_det_limit( - FLOAT_TO_FP(0.5f) - - fp_mul(gyro_still_det->win_var[Z] - var_thresh, - tmp_denom)); - - gyro_still_det->stillness_confidence = - fp_mul(limit[X], fp_mul(limit[Y], limit[Z])); - } - - /* Return the stillness confidence. */ - return gyro_still_det->stillness_confidence; -} - -void gyro_still_det_reset(struct gyro_still_det *gyro_still_det, - bool reset_stats) -{ - fp_t tmp_denom = INT_TO_FP(1); - - /* Reset the stillness data ready flag. */ - gyro_still_det->stillness_window_ready = false; - - /* Signal to start capture of next stillness data window. */ - gyro_still_det->start_new_window = true; - - /* Track the stillness confidence (current->previous). */ - gyro_still_det->prev_stillness_confidence = - gyro_still_det->stillness_confidence; - - /* Track changes in the mean estimate. */ - if (gyro_still_det->num_acc_samples > INT_TO_FP(1)) - tmp_denom = - fp_div(INT_TO_FP(1), gyro_still_det->num_acc_samples); - - gyro_still_det->prev_mean[X] = - fp_mul(gyro_still_det->mean[X], tmp_denom); - gyro_still_det->prev_mean[Y] = - fp_mul(gyro_still_det->mean[Y], tmp_denom); - gyro_still_det->prev_mean[Z] = - fp_mul(gyro_still_det->mean[Z], tmp_denom); - - /* Reset the current statistics to zero. */ - if (reset_stats) { - gyro_still_det->num_acc_samples = 0; - gyro_still_det->mean[X] = INT_TO_FP(0); - gyro_still_det->mean[Y] = INT_TO_FP(0); - gyro_still_det->mean[Z] = INT_TO_FP(0); - gyro_still_det->acc_var[X] = INT_TO_FP(0); - gyro_still_det->acc_var[Y] = INT_TO_FP(0); - gyro_still_det->acc_var[Z] = INT_TO_FP(0); - } -} - -fp_t gyro_still_det_limit(fp_t value) -{ - if (value < INT_TO_FP(0)) - value = INT_TO_FP(0); - else if (value > INT_TO_FP(1)) - value = INT_TO_FP(1); - - return value; -} diff --git a/common/host_command_controller.c b/common/host_command_controller.c deleted file mode 100644 index 0d221e44e3..0000000000 --- a/common/host_command_controller.c +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Host command controller module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "host_command.h" -#include "i2c.h" -#include "task.h" -#include "timer.h" -#include "usb_pd.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_HOSTCMD, outstr) -#define CPRINTS(format, args...) cprints(CC_HOSTCMD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_HOSTCMD, format, ## args) - -/* Number of attempts for each PD host command */ -#define PD_HOST_COMMAND_ATTEMPTS 3 - -static struct mutex pd_mutex; - -/** - * Non-task-safe internal version of pd_host_command(). - * - * Do not call this version directly! Use pd_host_command(). - */ -static int pd_host_command_internal(int command, int version, - const void *outdata, int outsize, - void *indata, int insize) -{ - int ret, i; - int resp_len; - struct ec_host_request rq; - struct ec_host_response rs; - static uint8_t req_buf[EC_LPC_HOST_PACKET_SIZE]; - static uint8_t resp_buf[EC_LPC_HOST_PACKET_SIZE]; - uint8_t sum = 0; - const uint8_t *c; - uint8_t *d; - - /* Fail if output size is too big */ - if (outsize + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) - return -EC_RES_REQUEST_TRUNCATED; - - /* Fill in request packet */ - rq.struct_version = EC_HOST_REQUEST_VERSION; - rq.checksum = 0; - rq.command = command; - rq.command_version = version; - rq.reserved = 0; - rq.data_len = outsize; - - /* Copy data and start checksum */ - for (i = 0, c = (const uint8_t *)outdata; i < outsize; i++, c++) { - req_buf[sizeof(rq) + 1 + i] = *c; - sum += *c; - } - - /* Finish checksum */ - for (i = 0, c = (const uint8_t *)&rq; i < sizeof(rq); i++, c++) - sum += *c; - - /* Write checksum field so the entire packet sums to 0 */ - rq.checksum = (uint8_t)(-sum); - - /* Copy header */ - for (i = 0, c = (const uint8_t *)&rq; i < sizeof(rq); i++, c++) - req_buf[1 + i] = *c; - - /* Set command to use protocol v3 */ - req_buf[0] = EC_COMMAND_PROTOCOL_3; - - /* - * Transmit all data and receive 2 bytes for return value and response - * length. - */ - i2c_lock(I2C_PORT_PD_MCU, 1); - i2c_set_timeout(I2C_PORT_PD_MCU, PD_HOST_COMMAND_TIMEOUT_US); - ret = i2c_xfer_unlocked(I2C_PORT_PD_MCU, - CONFIG_USB_PD_I2C_ADDR_FLAGS, - &req_buf[0], outsize + sizeof(rq) + 1, - &resp_buf[0], 2, I2C_XFER_START); - i2c_set_timeout(I2C_PORT_PD_MCU, 0); - if (ret) { - i2c_lock(I2C_PORT_PD_MCU, 0); - CPRINTS("i2c transaction 1 failed: %d", ret); - return -EC_RES_BUS_ERROR; - } - - resp_len = resp_buf[1]; - - if (resp_len > (insize + sizeof(rs))) { - /* Do a read to generate stop condition */ - i2c_xfer_unlocked(I2C_PORT_PD_MCU, - CONFIG_USB_PD_I2C_ADDR_FLAGS, - 0, 0, &resp_buf[2], 1, I2C_XFER_STOP); - i2c_lock(I2C_PORT_PD_MCU, 0); - CPRINTS("response size is too large %d > %d", - resp_len, insize + sizeof(rs)); - return -EC_RES_RESPONSE_TOO_BIG; - } - - /* Receive remaining data */ - ret = i2c_xfer_unlocked(I2C_PORT_PD_MCU, - CONFIG_USB_PD_I2C_ADDR_FLAGS, - 0, 0, - &resp_buf[2], resp_len, I2C_XFER_STOP); - i2c_lock(I2C_PORT_PD_MCU, 0); - if (ret) { - CPRINTS("i2c transaction 2 failed: %d", ret); - return -EC_RES_BUS_ERROR; - } - - /* Check for host command error code */ - ret = resp_buf[0]; - if (ret) { - CPRINTS("command 0x%02x returned error %d", command, ret); - return -ret; - } - - /* Read back response header and start checksum */ - sum = 0; - for (i = 0, d = (uint8_t *)&rs; i < sizeof(rs); i++, d++) { - *d = resp_buf[i + 2]; - sum += *d; - } - - if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { - CPRINTS("PD response version mismatch"); - return -EC_RES_INVALID_RESPONSE; - } - - if (rs.reserved) { - CPRINTS("PD response reserved != 0"); - return -EC_RES_INVALID_RESPONSE; - } - - if (rs.data_len > insize) { - CPRINTS("PD returned too much data"); - return -EC_RES_RESPONSE_TOO_BIG; - } - - /* Read back data and update checksum */ - resp_len -= sizeof(rs); - for (i = 0, d = (uint8_t *)indata; i < resp_len; i++, d++) { - *d = resp_buf[sizeof(rs) + i + 2]; - sum += *d; - } - - - if ((uint8_t)sum) { - CPRINTS("command 0x%02x bad checksum returned: %d", - command, sum); - return -EC_RES_INVALID_CHECKSUM; - } - - /* Return output buffer size */ - return resp_len; -} - -int pd_host_command(int command, int version, - const void *outdata, int outsize, - void *indata, int insize) -{ - int rv; - int tries = 0; - - /* Try multiple times to send host command. */ - for (tries = 0; tries < PD_HOST_COMMAND_ATTEMPTS; tries++) { - /* Acquire mutex */ - mutex_lock(&pd_mutex); - /* Call internal version of host command */ - rv = pd_host_command_internal(command, version, outdata, - outsize, indata, insize); - /* Release mutex */ - mutex_unlock(&pd_mutex); - - /* If host command error due to i2c bus error, try again. */ - if (rv != -EC_RES_BUS_ERROR) - break; - task_wait_event(50*MSEC); - } - - return rv; -} - -static int command_pd_mcu(int argc, char **argv) -{ - char *e; - static char __bss_slow outbuf[128]; - static char __bss_slow inbuf[128]; - int command, version; - int i, ret, tmp; - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - command = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - version = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - for (i = 3; i < argc; i++) { - tmp = strtoi(argv[i], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - outbuf[i-3] = tmp; - } - - ret = pd_host_command(command, version, &outbuf, argc - 3, &inbuf, - sizeof(inbuf)); - - ccprintf("Host command 0x%02x, returned %d\n", command, ret); - for (i = 0; i < ret; i++) - ccprintf("0x%02x\n", inbuf[i]); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pdcmd, command_pd_mcu, - "cmd ver [params]", - "Send PD host command"); - diff --git a/common/host_command_pd.c b/common/host_command_pd.c deleted file mode 100644 index f9b67c8b8d..0000000000 --- a/common/host_command_pd.c +++ /dev/null @@ -1,229 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Host command module for PD MCU */ - -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "host_command.h" -#include "lightbar.h" -#include "panic.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_PD_HOST_CMD, format, ## args) - -#define TASK_EVENT_EXCHANGE_PD_STATUS TASK_EVENT_CUSTOM_BIT(0) -#define TASK_EVENT_HIBERNATING TASK_EVENT_CUSTOM_BIT(1) - -/* Define local option for if we are a TCPM with an off chip TCPC */ -#if defined(CONFIG_USB_POWER_DELIVERY) && !defined(CONFIG_USB_PD_TCPM_STUB) -#define USB_TCPM_WITH_OFF_CHIP_TCPC -#endif - -#ifdef CONFIG_HOSTCMD_PD_CHG_CTRL -/* By default allow 5V charging only for the dead battery case */ -static enum pd_charge_state charge_state = PD_CHARGE_5V; - -#define CHARGE_PORT_UNINITIALIZED -2 -static int charge_port = CHARGE_PORT_UNINITIALIZED; - -int pd_get_active_charge_port(void) -{ - return charge_port; -} -#endif /* CONFIG_HOSTCMD_PD_CHG_CTRL */ - -void host_command_pd_send_status(enum pd_charge_state new_chg_state) -{ -#ifdef CONFIG_HOSTCMD_PD_CHG_CTRL - /* Update PD MCU charge state if necessary */ - if (new_chg_state != PD_CHARGE_NO_CHANGE) - charge_state = new_chg_state; -#endif - /* Wake PD HC task to send status */ - task_set_event(TASK_ID_PDCMD, TASK_EVENT_EXCHANGE_PD_STATUS); -} - -void host_command_pd_request_hibernate(void) -{ - task_set_event(TASK_ID_PDCMD, TASK_EVENT_HIBERNATING); -} - -#ifdef CONFIG_HOSTCMD_PD -static int pd_send_host_command(struct ec_params_pd_status *ec_status, - struct ec_response_pd_status *pd_status) -{ - return pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, - EC_VER_PD_EXCHANGE_STATUS, ec_status, - sizeof(struct ec_params_pd_status), pd_status, - sizeof(struct ec_response_pd_status)); -} - -static void pd_exchange_update_ec_status(struct ec_params_pd_status *ec_status, - uint32_t ec_state) -{ - /* Send PD charge state and battery state of charge */ -#ifdef CONFIG_HOSTCMD_PD_CHG_CTRL - ec_status->charge_state = charge_state; -#endif - if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE) - ec_status->batt_soc = charge_get_percent(); - else - ec_status->batt_soc = -1; - ec_status->status = ec_state; -} - -#ifdef CONFIG_HOSTCMD_PD_PANIC -static void pd_check_panic(struct ec_response_pd_status *pd_status) -{ - static int pd_in_rw; - - /* - * Check if PD MCU is in RW. If PD MCU was in RW, is now in RO, - * AND it did not sysjump to RO, then it must have crashed, and - * therefore we should panic as well. - */ - if (pd_status->status & PD_STATUS_IN_RW) { - pd_in_rw = 1; - } else if (pd_in_rw && - !(pd_status->status & PD_STATUS_JUMPED_TO_IMAGE)) { - panic_printf("PD crash"); - software_panic(PANIC_SW_PD_CRASH, 0); - } -} -#endif /* CONFIG_HOSTCMD_PD_PANIC */ - -#ifdef CONFIG_HOSTCMD_PD_CHG_CTRL -static void pd_check_chg_status(struct ec_response_pd_status *pd_status) -{ - int rv; -#ifdef HAS_TASK_LIGHTBAR - /* - * If charge port has changed, and it was initialized, then show - * battery status on lightbar. - */ - if (pd_status->active_charge_port != charge_port) { - if (charge_port != CHARGE_PORT_UNINITIALIZED) { - charge_port = pd_status->active_charge_port; - lightbar_sequence(LIGHTBAR_TAP); - } else { - charge_port = pd_status->active_charge_port; - } - } -#else - /* Store the active charge port */ - charge_port = pd_status->active_charge_port; -#endif - - /* Set input current limit */ - rv = charge_set_input_current_limit(MAX(pd_status->curr_lim_ma, - CONFIG_CHARGER_INPUT_CURRENT), 0); - if (rv < 0) - CPRINTS("Failed to set input curr limit from PD MCU"); -} -#endif /* CONFIG_HOSTCMD_PD_CHG_CTRL */ -#endif /* CONFIG_HOSTCMD_PD */ - -#ifdef USB_TCPM_WITH_OFF_CHIP_TCPC -static void pd_service_tcpc_ports(uint16_t port_status) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if ((port_status & (PD_STATUS_TCPC_ALERT_0 << i)) && - pd_is_port_enabled(i)) - tcpc_alert(i); - } -} - -static int pd_get_alert(void) -{ -#ifdef CONFIG_HOSTCMD_PD - return !gpio_get_level(GPIO_PD_MCU_INT); -#else - return !!tcpc_get_alert_status(); -#endif -} - -#endif /* USB_TCPM_WITH_OFF_CHIP_TCPC */ - -static void pd_exchange_status(uint32_t ec_state) -{ -#ifdef USB_TCPM_WITH_OFF_CHIP_TCPC - int first_exchange = 1; -#endif - -#ifdef CONFIG_HOSTCMD_PD - struct ec_params_pd_status ec_status; - struct ec_response_pd_status pd_status; - int rv; - - pd_exchange_update_ec_status(&ec_status, ec_state); -#endif - -#ifdef USB_TCPM_WITH_OFF_CHIP_TCPC - /* Loop until the alert gpio is not active */ - do { -#endif - -#ifdef CONFIG_HOSTCMD_PD - rv = pd_send_host_command(&ec_status, &pd_status); - if (rv < 0) { - CPRINTS("Host command to PD MCU failed: %d", rv); - return; - } - -#ifdef CONFIG_HOSTCMD_PD_PANIC - pd_check_panic(&pd_status); -#endif - -#ifdef CONFIG_HOSTCMD_PD_CHG_CTRL - pd_check_chg_status(&pd_status); -#endif -#endif /* CONFIG_HOSTCMD_PD */ - -#ifdef USB_TCPM_WITH_OFF_CHIP_TCPC -#ifdef CONFIG_HOSTCMD_PD - pd_service_tcpc_ports(pd_status.status); -#else - pd_service_tcpc_ports(tcpc_get_alert_status()); -#endif - - if (!first_exchange) - /* Delay to prevent task starvation */ - usleep(5*MSEC); - first_exchange = 0; - } while (pd_get_alert()); -#endif /* USB_TCPM_WITH_OFF_CHIP_TCPC */ -} - -void pd_command_task(void *u) -{ - /* On startup exchange status with the PD */ - pd_exchange_status(0); - - while (1) { - /* Wait for the next command event */ - int evt = task_wait_event(-1); - uint32_t ec_state = 0; - - if (evt & TASK_EVENT_HIBERNATING) - ec_state = EC_STATUS_HIBERNATING; - - /* Process event to send status to PD */ - if ((evt & TASK_EVENT_EXCHANGE_PD_STATUS) || - (evt & TASK_EVENT_HIBERNATING)) - pd_exchange_status(ec_state); - } -} - diff --git a/common/hotword_dsp_api.c b/common/hotword_dsp_api.c deleted file mode 100644 index dc53cd0055..0000000000 --- a/common/hotword_dsp_api.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#include "audio_codec.h" -#include "hotword_dsp_api.h" - -const int kGoogleHotwordRequiredDataAlignment = 4; - -int GoogleHotwordDspInit(void *hotword_memmap) -{ - return 1; -} - -int GoogleHotwordDspProcess(const void *samples, int num_samples, - int *preamble_length_ms) -{ - return 0; -} - -void GoogleHotwordDspReset(void) -{ -} - -int GoogleHotwordDspGetMaximumAudioPreambleMs(void) -{ - return 0; -} - -int GoogleHotwordVersion(void) -{ - return 0; -} diff --git a/common/i2c_bitbang.c b/common/i2c_bitbang.c deleted file mode 100644 index 86d76a8b47..0000000000 --- a/common/i2c_bitbang.c +++ /dev/null @@ -1,363 +0,0 @@ -/* 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. - */ - -#include "console.h" -#include "gpio.h" -#include "i2c_bitbang.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define CPUTS(str) cputs(CC_I2C, str) - -static int started; - -/* TODO: respect i2c_port->kbps setting */ -static void i2c_delay(void) -{ - udelay(5); -} - -/* Number of attempts to unwedge each pin. */ -#define UNWEDGE_SCL_ATTEMPTS 10 -#define UNWEDGE_SDA_ATTEMPTS 3 - -static void i2c_bitbang_unwedge(const struct i2c_port_t *i2c_port) -{ - int i, j; - - gpio_set_level(i2c_port->scl, 1); - /* - * If clock is low, wait for a while in case of clock stretched - * by a peripheral. - */ - if (!gpio_get_level(i2c_port->scl)) { - for (i = 0;; i++) { - if (i >= UNWEDGE_SCL_ATTEMPTS) { - /* - * If we get here, a peripheral is holding the - * clock low and there is nothing we can do. - */ - CPUTS("I2C unwedge failed, SCL is held low\n"); - return; - } - i2c_delay(); - if (gpio_get_level(i2c_port->scl)) - break; - } - } - - if (gpio_get_level(i2c_port->sda)) - return; - - CPUTS("I2C unwedge called with SDA held low\n"); - - /* Keep trying to unwedge the SDA line until we run out of attempts. */ - for (i = 0; i < UNWEDGE_SDA_ATTEMPTS; i++) { - /* Drive the clock high. */ - gpio_set_level(i2c_port->scl, 0); - i2c_delay(); - - /* - * Clock through the problem by clocking out 9 bits. If - * peripheral releases the SDA line, then we can stop clocking - * bits and send a STOP. - */ - for (j = 0; j < 9; j++) { - if (gpio_get_level(i2c_port->sda)) - break; - - gpio_set_level(i2c_port->scl, 0); - i2c_delay(); - gpio_set_level(i2c_port->scl, 1); - i2c_delay(); - } - - /* Take control of SDA line and issue a STOP command. */ - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - /* Check if the bus is unwedged. */ - if (gpio_get_level(i2c_port->sda) && - gpio_get_level(i2c_port->scl)) - break; - } - - if (!gpio_get_level(i2c_port->sda)) - CPUTS("I2C unwedge failed, SDA still low\n"); - if (!gpio_get_level(i2c_port->scl)) - CPUTS("I2C unwedge failed, SCL still low\n"); -} - -static void i2c_stop_cond(const struct i2c_port_t *i2c_port) -{ - int i; - - if (!started) - return; - - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - - /* - * SMBus 3.0, 4.2.5 - * - * the recommendation is that if SMBDAT is still low tTIMEOUT,MAX after - * SMBCLK has gone high at the end of a transaction the controller - * should hold SMBCLK low for at least tTIMEOUT,MAX in an attempt to - * reset the SMBus interface of all of the devices on the bus. - */ - for (i = 0; i < 7000; i++) { - if (gpio_get_level(i2c_port->scl)) - break; - i2c_delay(); - } - i2c_delay(); - - /* SCL is high, set SDA from 0 to 1 */ - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - started = 0; -} - -static int clock_stretching(const struct i2c_port_t *i2c_port) -{ - int i; - - i2c_delay(); - /* 5us * 7000 iterations ~= 35ms */ - for (i = 0; i < 7000; i++) { - if (gpio_get_level(i2c_port->scl)) - return 0; - i2c_delay(); - } - - /* - * SMBus 3.0, Note 3 - * Devices participating in a transfer can abort the transfer in - * progress and release the bus when any single clock low interval - * exceeds the value of tTIMEOUT,MIN(=25ms). - * After the controller in a transaction detects this condition, it must - * generate a stop condition within or after the current data byte in - * the transfer process. - */ - i2c_stop_cond(i2c_port); - CPUTS("clock low timeout\n"); - - return EC_ERROR_TIMEOUT; -} - -static int i2c_start_cond(const struct i2c_port_t *i2c_port) -{ - int err; - - if (started) { - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - - if (gpio_get_level(i2c_port->sda) == 0) { - CPUTS("start_cond: arbitration lost\n"); - started = 0; - return EC_ERROR_UNKNOWN; - } - } - - /* check if bus is idle before starting */ - if (gpio_get_level(i2c_port->scl) == 0 || - gpio_get_level(i2c_port->sda) == 0) - return EC_ERROR_UNKNOWN; - - gpio_set_level(i2c_port->sda, 0); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 0); - started = 1; - - return 0; -} - -static int i2c_write_bit(const struct i2c_port_t *i2c_port, int bit) -{ - int err; - - gpio_set_level(i2c_port->sda, !!bit); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - - if (bit && gpio_get_level(i2c_port->sda) == 0) { - CPUTS("write_bit: arbitration lost\n"); - started = 0; - return EC_ERROR_UNKNOWN; - } - - gpio_set_level(i2c_port->scl, 0); - - return 0; -} - -static int i2c_read_bit(const struct i2c_port_t *i2c_port, int *bit) -{ - int err; - - gpio_set_level(i2c_port->sda, 1); - i2c_delay(); - - gpio_set_level(i2c_port->scl, 1); - err = clock_stretching(i2c_port); - if (err) - return err; - i2c_delay(); - *bit = gpio_get_level(i2c_port->sda); - - gpio_set_level(i2c_port->scl, 0); - - return 0; -} - -static int i2c_write_byte(const struct i2c_port_t *i2c_port, uint8_t byte) -{ - int i, nack, err; - - for (i = 7; i >= 0; i--) { - err = i2c_write_bit(i2c_port, byte & (1 << i)); - if (err) - return err; - } - - err = i2c_read_bit(i2c_port, &nack); - if (err) - return err; - - if (nack) { - /* - * The peripheral device detects an invalid command or invalid - * data. In this case the peripheral device must NACK the - * received byte. The controller upon detection of this - * condition must generate a STOP condition and retry the - * transaction - */ - i2c_stop_cond(i2c_port); - /* return EC_ERROR_BUSY to indicate i2c_xfer() to retry */ - return EC_ERROR_BUSY; - } - return 0; -} - -static int i2c_read_byte(const struct i2c_port_t *i2c_port, uint8_t *byte, - int nack) -{ - int i; - - *byte = 0; - for (i = 0; i < 8; i++) { - int bit = 0, err; - - err = i2c_read_bit(i2c_port, &bit); - if (err) - return err; - *byte = (*byte << 1) | bit; - } - - return i2c_write_bit(i2c_port, nack); -} - -static int i2c_bitbang_xfer(const struct i2c_port_t *i2c_port, - const uint16_t addr_flags, - const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - uint16_t addr_8bit = addr_flags << 1, err = EC_SUCCESS; - int i = 0; - - if (i2c_port->kbps != 100) - CPUTS("warning: bitbang driver only supports 100kbps\n"); - - if (out_size) { - if (flags & I2C_XFER_START) { - err = i2c_start_cond(i2c_port); - if (err) - goto exit; - err = i2c_write_byte(i2c_port, addr_8bit); - if (err) - goto exit; - } - - for (i = 0; i < out_size; i++) { - err = i2c_write_byte(i2c_port, out[i]); - if (err) - goto exit; - } - } - - if (in_size) { - if (flags & I2C_XFER_START) { - err = i2c_start_cond(i2c_port); - if (err) - goto exit; - err = i2c_write_byte(i2c_port, addr_8bit | 1); - if (err) - goto exit; - } - - for (i = 0; i < in_size; i++) { - err = i2c_read_byte(i2c_port, &in[i], - (flags & I2C_XFER_STOP) && (i == in_size - 1)); - if (err) - goto exit; - } - } - - if (flags & I2C_XFER_STOP) - i2c_stop_cond(i2c_port); - -exit: - if (err) { - i2c_bitbang_unwedge(i2c_port); - started = 0; - } - return err; -} - -const struct i2c_drv bitbang_drv = { - .xfer = &i2c_bitbang_xfer -}; - -#ifdef TEST_BUILD -int bitbang_start_cond(const struct i2c_port_t *i2c_port) -{ - return i2c_start_cond(i2c_port); -} - -void bitbang_stop_cond(const struct i2c_port_t *i2c_port) -{ - i2c_stop_cond(i2c_port); -} - -int bitbang_write_byte(const struct i2c_port_t *i2c_port, uint8_t byte) -{ - return i2c_write_byte(i2c_port, byte); -} - -void bitbang_set_started(int val) -{ - started = val; -} -#endif diff --git a/common/i2c_hid_touchpad.c b/common/i2c_hid_touchpad.c deleted file mode 100644 index 29122f83d6..0000000000 --- a/common/i2c_hid_touchpad.c +++ /dev/null @@ -1,797 +0,0 @@ -/* Copyright 2020 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 "i2c_hid_touchpad.h" - -#include "console.h" -#include "hwtimer.h" -#include "util.h" - -/* 2 bytes for length + 1 byte for report ID */ -#define I2C_HID_HEADER_SIZE 3 - -/* Report ID */ -#define REPORT_ID_TOUCH 0x01 -#define REPORT_ID_MOUSE 0x02 -#define REPORT_ID_DEVICE_CAPS 0x0A -#define REPORT_ID_DEVICE_CERT 0x0B -#define REPORT_ID_INPUT_MODE 0x0C -#define REPORT_ID_REPORTING 0x0D - -#define INPUT_MODE_MOUSE 0x00 -#define INPUT_MODE_TOUCH 0x03 - -/* VID/PID/FW version */ -#if !defined(I2C_HID_TOUCHPAD_VENDOR_ID) || \ - !defined(I2C_HID_TOUCHPAD_PRODUCT_ID) || \ - !defined(I2C_HID_TOUCHPAD_FW_VERSION) -#error "Must define touchpad VID/PID/FW version" -#endif -/* - * Touchpad properties - * - * Physical dimensions are in the unit of mms. - */ -#if !defined(I2C_HID_TOUCHPAD_MAX_X) || \ - !defined(I2C_HID_TOUCHPAD_MAX_Y) || \ - !defined(I2C_HID_TOUCHPAD_MAX_PHYSICAL_X) || \ - !defined(I2C_HID_TOUCHPAD_MAX_PHYSICAL_Y) -#error "Must define finger maximum X/Y and physical dimensions" -#endif -/* - * Maximum width/height of the contact (i.e., touch major/minor in Linux MT-B) - * - * According to the Linux's MT protocol, the max value of touch major/minor - * should be sqrt(X^2+Y^2). However, this is rarely implemented by touchpads - * in practice. Touchpads often output major/minor in custom units with very - * different data ranges. It is therefore recommended for the user to check the - * device's spec and set these values manually. - */ -#if !defined(I2C_HID_TOUCHPAD_MAX_WIDTH) || \ - !defined(I2C_HID_TOUCHPAD_MAX_HEIGHT) || \ - !defined(I2C_HID_TOUCHPAD_MAX_PRESSURE) -#error "Must define finger maximum width/height/pressure" -#endif -/* - * The touchpad is expected to provide at least the horizontal/vertical status - * for each contact (if one is wider than its height). This can be computed - * simply as bool(WIDTH>HEIGHT). - */ -#ifndef I2C_HID_TOUCHPAD_MAX_ORIENTATION -#error "Must define finger maximum orientation value" -#endif -/* - * Conversion factor between the finger movement and the mouse cursor movement. - * This is a bit similar to the mouse CPI and is used by mouse reports only. - */ -#if !defined(I2C_HID_TOUCHPAD_MOUSE_SCALE_X) || \ - !defined(I2C_HID_TOUCHPAD_MOUSE_SCALE_Y) -#error "Must define mouse horizontal/vertical scaling factors" -#endif - -/* Helper bit-op macros */ -#define N_BITS(n) \ -( \ - (n) < (1 << 1) ? 1 : \ - (n) < (1 << 2) ? 2 : \ - (n) < (1 << 3) ? 3 : \ - (n) < (1 << 4) ? 4 : \ - (n) < (1 << 5) ? 5 : \ - (n) < (1 << 6) ? 6 : \ - (n) < (1 << 7) ? 7 : \ - (n) < (1 << 8) ? 8 : \ - (n) < (1 << 9) ? 9 : \ - (n) < (1 << 10) ? 10 : \ - (n) < (1 << 11) ? 11 : \ - (n) < (1 << 12) ? 12 : \ - (n) < (1 << 13) ? 13 : \ - (n) < (1 << 14) ? 14 : \ - (n) < (1 << 15) ? 15 : \ - 16 \ -) -/* We would need to pad some bits at the end of each finger struct to match - * the allocation unit's boundary so the array indexing may work correctly. - */ -#define N_VAR_BITS \ -( \ - N_BITS(I2C_HID_TOUCHPAD_MAX_X) + \ - N_BITS(I2C_HID_TOUCHPAD_MAX_Y) + \ - N_BITS(I2C_HID_TOUCHPAD_MAX_WIDTH) + \ - N_BITS(I2C_HID_TOUCHPAD_MAX_HEIGHT) + \ - N_BITS(I2C_HID_TOUCHPAD_MAX_PRESSURE) + \ - N_BITS(I2C_HID_TOUCHPAD_MAX_ORIENTATION) \ -) -#define N_PADDING_BITS ((DIV_ROUND_UP(N_VAR_BITS, 8) * 8) - N_VAR_BITS) -#define N_BITS_ORIENTATION \ - (N_BITS(I2C_HID_TOUCHPAD_MAX_ORIENTATION) + N_PADDING_BITS) -/* Structs for holding input report data - * - * These need to be modified in correspondence with the HID input report - * descriptor below. - * - * The HID usage names differ from the Evdev event names in some cases. For - * example, touch major/minor are put under width/height and orientation is - * called azimuth. - */ -struct finger { - /* - * Whether a finger is intentional or not. This could be used to - * identify unintended contacts or palms but is up to the OS - * explanation. - */ - uint8_t confidence:1; - /* - * Whether a finger is touching the surface (leaving/left finger gets - * 0). - */ - uint8_t tip:1; - /* - * Whether a finger is within the sensor range. For example, hovering - * fingers would have tip=0 and inrange=1. - */ - uint8_t inrange:1; - /* - * Contact id. This is like slot numbers in Linux MT-B. - */ - uint8_t id:5; - uint16_t x:N_BITS(I2C_HID_TOUCHPAD_MAX_X); - uint16_t y:N_BITS(I2C_HID_TOUCHPAD_MAX_Y); - uint16_t width:N_BITS(I2C_HID_TOUCHPAD_MAX_WIDTH); - uint16_t height:N_BITS(I2C_HID_TOUCHPAD_MAX_HEIGHT); - uint16_t pressure:N_BITS(I2C_HID_TOUCHPAD_MAX_PRESSURE); - uint16_t orientation:N_BITS_ORIENTATION; -} __packed; - -struct touch_report { - uint8_t button:1; - uint8_t count:7; - uint16_t timestamp; - struct finger finger[I2C_HID_TOUCHPAD_MAX_FINGERS]; -} __packed; - -struct mouse_report { - uint8_t button1:1; - /* Windows expects at least two button usages in a mouse report. Many - * touchpads on the Chromebook are a single clickable surface, so - * button2 isn't used. That said, we may later report a button2 event if - * necessary. - */ - uint8_t button2:1; - uint8_t unused:6; - int8_t x; - int8_t y; -} __packed; - -/* HID input report descriptor - * - * For a complete reference, please see the following docs on usb.org - * - * 1. Device Class Definition for HID - * 2. HID Usage Tables - */ -static const uint8_t report_desc[] = { - /* Mouse Collection */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x02, /* Usage (Mouse) */ - 0xA1, 0x01, /* Collection (Application) */ - 0x85, REPORT_ID_MOUSE, /* Report ID (Mouse) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xA1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (Button 1) */ - 0x29, 0x02, /* Usage Maximum (Button 2) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x02, /* Report Count (2) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - 0x95, 0x06, /* Report Count (6) */ - 0x81, 0x03, /* Input (Cnst,Var,Abs) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x15, 0x81, /* Logical Minimum (-127) */ - 0x25, 0x7F, /* Logical Maximum (127) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x02, /* Report Count (2) */ - 0x81, 0x06, /* Input (Data,Var,Rel) */ - 0xC0, /* End Collection */ - 0xC0, /* End Collection */ - - /* Touchpad Collection */ - 0x05, 0x0D, /* Usage Page (Digitizer) */ - 0x09, 0x05, /* Usage (Touch Pad) */ - 0xA1, 0x01, /* Collection (Application) */ - 0x85, REPORT_ID_TOUCH, /* Report ID (Touch) */ - - /* Button */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (0x01) */ - 0x29, 0x01, /* Usage Maximum (0x01) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - - /* Contact count */ - 0x05, 0x0D, /* Usage Page (Digitizer) */ - 0x09, 0x54, /* Usage (Contact count) */ - 0x25, I2C_HID_TOUCHPAD_MAX_FINGERS, /* Logical Max. (MAX_FINGERS) */ - 0x75, 0x07, /* Report Size (7) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - - /* Scan time */ - 0x55, 0x0C, /* Unit Exponent (-4) */ - 0x66, 0x01, 0x10, /* Unit (Seconds) */ - 0x47, 0xFF, 0xFF, 0x00, 0x00, /* Physical Maximum (65535) */ - 0x27, 0xFF, 0xFF, 0x00, 0x00, /* Logical Maximum (65535) */ - 0x75, 0x10, /* Report Size (16) */ - 0x95, 0x01, /* Report Count (1) */ - 0x05, 0x0D, /* Usage Page (Digitizers) */ - 0x09, 0x56, /* Usage (Scan Time) */ - 0x81, 0x02, /* Input (Data,Var,Abs) */ - -#define FINGER(FINGER_NUMBER) \ - /* Finger FINGER_NUMBER */ \ - 0x05, 0x0D, /* Usage Page (Digitizer) */ \ - 0x09, 0x22, /* Usage (Finger) */ \ - 0xA1, 0x02, /* Collection (Logical) */ \ - 0x09, 0x47, /* Usage (Confidence) */ \ - 0x09, 0x42, /* Usage (Tip Switch) */ \ - 0x09, 0x32, /* Usage (In Range) */ \ - 0x15, 0x00, /* Logical Minimum (0) */ \ - 0x25, 0x01, /* Logical Maximum (1) */ \ - 0x75, 0x01, /* Report Size (1) */ \ - 0x95, 0x03, /* Report Count (3) */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x09, 0x51, /* Usage (Contact identifier) */ \ - 0x25, 0x1F, /* Logical Maximum (31) */ \ - 0x75, 0x05, /* Report Size (5) */ \ - 0x95, 0x01, /* Report Count (1) */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ \ - 0x09, 0x30, /* Usage (X) */ \ - 0x55, 0x0E, /* Unit Exponent (-2) */ \ - 0x65, 0x11, /* Unit (SI Linear, Length: cm) */ \ - 0x35, 0x00, /* Physical Minimum (0) */ \ - 0x46, I2C_HID_TOUCHPAD_MAX_PHYSICAL_X&0xff, \ - I2C_HID_TOUCHPAD_MAX_PHYSICAL_X>>8, \ - /* Physical Maximum */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_X&0xff, I2C_HID_TOUCHPAD_MAX_X>>8, \ - /* Logical Maximum */ \ - 0x75, N_BITS(I2C_HID_TOUCHPAD_MAX_X), \ - /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x09, 0x31, /* Usage (Y) */ \ - 0x46, I2C_HID_TOUCHPAD_MAX_PHYSICAL_Y&0xff, \ - I2C_HID_TOUCHPAD_MAX_PHYSICAL_Y>>8, \ - /* Physical Maximum */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_Y&0xff, I2C_HID_TOUCHPAD_MAX_Y>>8, \ - /* Logical Maximum */ \ - 0x75, N_BITS(I2C_HID_TOUCHPAD_MAX_Y), \ - /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x05, 0x0D, /* Usage Page (Digitizer) */ \ - 0x09, 0x48, /* Usage (Width) */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_WIDTH&0xff, I2C_HID_TOUCHPAD_MAX_WIDTH>>8, \ - /* Logical Maximum */ \ - 0x75, N_BITS(I2C_HID_TOUCHPAD_MAX_WIDTH), \ - /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x09, 0x49, /* Usage (Height) */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_HEIGHT&0xff, I2C_HID_TOUCHPAD_MAX_HEIGHT>>8,\ - /* Logical Maximum */ \ - 0x75, N_BITS(I2C_HID_TOUCHPAD_MAX_HEIGHT), \ - /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x09, 0x30, /* Usage (Tip pressure) */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_PRESSURE&0xff, \ - I2C_HID_TOUCHPAD_MAX_PRESSURE>>8, \ - /* Logical Maximum */ \ - 0x75, N_BITS(I2C_HID_TOUCHPAD_MAX_PRESSURE), \ - /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0x09, 0x3f, /* Usage (Azimuth Orientation) */ \ - 0x16, 0x00, 0x00, /* Logical Minimum (0) */ \ - 0x26, I2C_HID_TOUCHPAD_MAX_ORIENTATION&0xff, \ - I2C_HID_TOUCHPAD_MAX_ORIENTATION>>8, \ - /* Logical Maximum */ \ - 0x75, N_BITS_ORIENTATION, /* Report Size */ \ - 0x81, 0x02, /* Input (Data,Var,Abs) */ \ - 0xC0, /* End Collection */ - - FINGER(1) - FINGER(2) - FINGER(3) - FINGER(4) - FINGER(5) - -#undef FINGER - - 0x05, 0x0D, /* Usage Page (Digitizer) */ - 0x85, REPORT_ID_DEVICE_CAPS, /* Report ID (Device Capabilities) */ - 0x09, 0x55, /* Usage (Contact Count Maximum) */ - 0x09, 0x59, /* Usage (Pad Type) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x02, /* Report Count (2) */ - 0x25, 0x0F, /* Logical Maximum (15) */ - 0xB1, 0x02, /* Feature (Data,Var,Abs) */ - 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined) */ - 0x85, REPORT_ID_DEVICE_CERT, /* Report ID (Device Certification) */ - 0x09, 0xC5, /* Usage (Vendor Usage 0xC5) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ - 0x75, 0x08, /* Report Size (8) */ - 0x96, 0x00, 0x01, /* Report Count (256) */ - 0xB1, 0x02, /* Feature (Data,Var,Abs) */ - 0xC0, /* End Collection */ - - /* Configuration Collection */ - 0x05, 0x0D, /* Usage Page (Digitizer) */ - 0x09, 0x0E, /* Usage (Configuration) */ - 0xA1, 0x01, /* Collection (Application) */ - 0x85, REPORT_ID_INPUT_MODE, /* Report ID (Input Mode) */ - 0x09, 0x22, /* Usage (Finger) */ - 0xA1, 0x02, /* Collection (Logical) */ - 0x09, 0x52, /* Usage (Input Mode) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x0F, /* Logical Maximum (15) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x01, /* Report Count (1) */ - 0xB1, 0x02, /* Feature (Data,Var,Abs) */ - 0xC0, /* End Collection */ - 0x09, 0x22, /* Usage (Finger) */ - 0xA1, 0x00, /* Collection (Physical) */ - 0x85, REPORT_ID_REPORTING, /* Report ID (Selective Reporting)*/ - 0x09, 0x57, /* Usage (Surface Switch) */ - 0x09, 0x58, /* Usage (Button Switch) */ - 0x75, 0x04, /* Report Size (4) */ - 0x95, 0x02, /* Report Count (2) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0xB1, 0x02, /* Feature (Data,Var,Abs) */ - 0xC0, /* End Collection */ - 0xC0, /* End Collection */ -}; - -static const uint8_t device_caps[] = { - I2C_HID_TOUCHPAD_MAX_FINGERS, /* Contact Count Maximum */ - 0x00, /* Pad Type: Depressible click-pad */ -}; - -/* A 256-byte default blob for the 'device certification status' feature report - * expected by Windows. - */ -static const uint8_t device_cert[] = { - 0xFC, 0x28, 0xFE, 0x84, 0x40, 0xCB, 0x9A, 0x87, - 0x0D, 0xBE, 0x57, 0x3C, 0xB6, 0x70, 0x09, 0x88, - 0x07, 0x97, 0x2D, 0x2B, 0xE3, 0x38, 0x34, 0xB6, - 0x6C, 0xED, 0xB0, 0xF7, 0xE5, 0x9C, 0xF6, 0xC2, - 0x2E, 0x84, 0x1B, 0xE8, 0xB4, 0x51, 0x78, 0x43, - 0x1F, 0x28, 0x4B, 0x7C, 0x2D, 0x53, 0xAF, 0xFC, - 0x47, 0x70, 0x1B, 0x59, 0x6F, 0x74, 0x43, 0xC4, - 0xF3, 0x47, 0x18, 0x53, 0x1A, 0xA2, 0xA1, 0x71, - 0xC7, 0x95, 0x0E, 0x31, 0x55, 0x21, 0xD3, 0xB5, - 0x1E, 0xE9, 0x0C, 0xBA, 0xEC, 0xB8, 0x89, 0x19, - 0x3E, 0xB3, 0xAF, 0x75, 0x81, 0x9D, 0x53, 0xB9, - 0x41, 0x57, 0xF4, 0x6D, 0x39, 0x25, 0x29, 0x7C, - 0x87, 0xD9, 0xB4, 0x98, 0x45, 0x7D, 0xA7, 0x26, - 0x9C, 0x65, 0x3B, 0x85, 0x68, 0x89, 0xD7, 0x3B, - 0xBD, 0xFF, 0x14, 0x67, 0xF2, 0x2B, 0xF0, 0x2A, - 0x41, 0x54, 0xF0, 0xFD, 0x2C, 0x66, 0x7C, 0xF8, - 0xC0, 0x8F, 0x33, 0x13, 0x03, 0xF1, 0xD3, 0xC1, - 0x0B, 0x89, 0xD9, 0x1B, 0x62, 0xCD, 0x51, 0xB7, - 0x80, 0xB8, 0xAF, 0x3A, 0x10, 0xC1, 0x8A, 0x5B, - 0xE8, 0x8A, 0x56, 0xF0, 0x8C, 0xAA, 0xFA, 0x35, - 0xE9, 0x42, 0xC4, 0xD8, 0x55, 0xC3, 0x38, 0xCC, - 0x2B, 0x53, 0x5C, 0x69, 0x52, 0xD5, 0xC8, 0x73, - 0x02, 0x38, 0x7C, 0x73, 0xB6, 0x41, 0xE7, 0xFF, - 0x05, 0xD8, 0x2B, 0x79, 0x9A, 0xE2, 0x34, 0x60, - 0x8F, 0xA3, 0x32, 0x1F, 0x09, 0x78, 0x62, 0xBC, - 0x80, 0xE3, 0x0F, 0xBD, 0x65, 0x20, 0x08, 0x13, - 0xC1, 0xE2, 0xEE, 0x53, 0x2D, 0x86, 0x7E, 0xA7, - 0x5A, 0xC5, 0xD3, 0x7D, 0x98, 0xBE, 0x31, 0x48, - 0x1F, 0xFB, 0xDA, 0xAF, 0xA2, 0xA8, 0x6A, 0x89, - 0xD6, 0xBF, 0xF2, 0xD3, 0x32, 0x2A, 0x9A, 0xE4, - 0xCF, 0x17, 0xB7, 0xB8, 0xF4, 0xE1, 0x33, 0x08, - 0x24, 0x8B, 0xC4, 0x43, 0xA5, 0xE5, 0x24, 0xC2, -}; - -#define MAX_SIZEOF(a, b) (sizeof(a) > sizeof(b) ? sizeof(a) : sizeof(b)) - -static struct i2c_hid_descriptor hid_desc = { - .wHIDDescLength = I2C_HID_DESC_LENGTH, - .bcdVersion = I2C_HID_BCD_VERSION, - .wReportDescLength = sizeof(report_desc), - .wReportDescRegister = I2C_HID_REPORT_DESC_REGISTER, - .wInputRegister = I2C_HID_INPUT_REPORT_REGISTER, - .wMaxInputLength = I2C_HID_HEADER_SIZE + - MAX_SIZEOF(struct touch_report, struct mouse_report), - .wOutputRegister = 0, - .wMaxOutputLength = 0, - .wCommandRegister = I2C_HID_COMMAND_REGISTER, - .wDataRegister = I2C_HID_DATA_REGISTER, - .wVendorID = I2C_HID_TOUCHPAD_VENDOR_ID, - .wProductID = I2C_HID_TOUCHPAD_PRODUCT_ID, - .wVersionID = I2C_HID_TOUCHPAD_FW_VERSION, -}; - -/* - * In I2C HID, the host would request for an input report immediately following - * the protocol initialization. The device is required to respond with exactly - * 2 empty bytes. Furthermore, some hosts may use a single byte SMBUS read to - * check if the device exists on the specified I2C address. - * - * These variables record if such probing/initialization have been done before. - */ -static bool pending_probe; -static bool pending_reset; - -/* Reports (double buffered) */ -#define MAX_REPORT_CNT 2 - -static struct touch_report touch_reports[MAX_REPORT_CNT]; -static struct mouse_report mouse_reports[MAX_REPORT_CNT]; - -/* Current active report buffer index */ -static int report_active_index; - -/* Current input mode */ -static uint8_t input_mode; - -/* - * TODO(b/151693566): Selectively report surface contact and button state in - * input reports based on |reporting.surface_switch| and - * |reporting.button_switch|, respectively. - */ -struct selective_reporting { - uint8_t surface_switch:4; - uint8_t button_switch:4; -} __packed; - -static struct selective_reporting reporting; - -/* Function declarations */ -static int i2c_hid_touchpad_command_process(size_t len, uint8_t *buffer, - void (*send_response)(int len), - uint8_t *data); - -static size_t fill_report(uint8_t *buffer, uint8_t report_id, const void *data, - size_t data_len) -{ - size_t response_len = I2C_HID_HEADER_SIZE + data_len; - - buffer[0] = response_len & 0xFF; - buffer[1] = (response_len >> 8) & 0xFF; - buffer[2] = report_id; - memcpy(buffer + I2C_HID_HEADER_SIZE, data, data_len); - return response_len; -} - -/* - * Extracts report data from |buffer| into |data| for reports from the host. - * - * |buffer| is expected to contain the values written to the command register - * followed by the values written to the data register, upon receiving a - * SET_REPORT command, in the following byte sequence format: - * - * 00 30 - command register address (0x3000) - * xx - report type and ID - * 03 - SET_REPORT - * 00 30 - data register address (0x3000) - * xx xx - length - * xx - report ID - * xx... - report data - * - * Note that command register and data register have the same address. Also, - * any report ID >= 15 requires an extra byte after the SET_REPORT byte, which - * is not supported here as we don't have any report ID >= 15. - * - * In summary, we expect |buffer| contains at least 10 bytes where the report - * data starts at buffer[9]. If |buffer| contains the incorrect number bytes, - * we ignore the report. - */ -static void extract_report(size_t len, const uint8_t *buffer, void *data, - size_t data_len) -{ - if (len != 9 + data_len) { - ccprints("I2C-HID: SET_REPORT buffer length mismatch"); - return; - } - memcpy(data, buffer + 9, data_len); -} - -void i2c_hid_touchpad_init(void) -{ - input_mode = INPUT_MODE_MOUSE; - reporting.surface_switch = 1; - reporting.button_switch = 1; - report_active_index = 0; - - // Respond probing requests for now. - pending_probe = true; - pending_reset = false; -} - -int i2c_hid_touchpad_process(unsigned int len, uint8_t *buffer, - void (*send_response)(int len), uint8_t *data, - int *reg, int *cmd) -{ - size_t response_len; - - if (len == 0) - *reg = I2C_HID_INPUT_REPORT_REGISTER; - else - *reg = UINT16_FROM_BYTE_ARRAY_LE(buffer, 0); - - *cmd = 0; - switch (*reg) { - case I2C_HID_HID_DESC_REGISTER: - memcpy(buffer, &hid_desc, sizeof(hid_desc)); - send_response(sizeof(hid_desc)); - break; - case I2C_HID_REPORT_DESC_REGISTER: - memcpy(buffer, &report_desc, sizeof(report_desc)); - send_response(sizeof(report_desc)); - break; - case I2C_HID_INPUT_REPORT_REGISTER: - // Single-byte read probing. - if (pending_probe) { - buffer[0] = 0; - send_response(1); - break; - } - // Reset protocol: 2 empty bytes. - if (pending_reset) { - pending_reset = false; - buffer[0] = 0; - buffer[1] = 0; - send_response(2); - break; - } - // Common input report requests. - if (input_mode == INPUT_MODE_TOUCH) { - response_len = - fill_report(buffer, REPORT_ID_TOUCH, - &touch_reports[report_active_index], - sizeof(struct touch_report)); - } else { - response_len = - fill_report(buffer, REPORT_ID_MOUSE, - &mouse_reports[report_active_index], - sizeof(struct mouse_report)); - } - send_response(response_len); - break; - case I2C_HID_COMMAND_REGISTER: - *cmd = i2c_hid_touchpad_command_process(len, buffer, - send_response, data); - break; - default: - /* Unknown register has been received. */ - return EC_ERROR_INVAL; - } - /* Unknown command has been received. */ - if (*cmd < 0) - return EC_ERROR_INVAL; - return EC_SUCCESS; -} - -static int i2c_hid_touchpad_command_process(size_t len, uint8_t *buffer, - void (*send_response)(int len), - uint8_t *data) -{ - uint8_t command = buffer[3] & 0x0F; - uint8_t power_state = buffer[2] & 0x03; - uint8_t report_id = buffer[2] & 0x0F; - size_t response_len; - - switch (command) { - case I2C_HID_CMD_RESET: - i2c_hid_touchpad_init(); - // Wait for the 2-bytes I2C read following the protocol reset. - pending_probe = false; - pending_reset = true; - break; - case I2C_HID_CMD_GET_REPORT: - switch (report_id) { - case REPORT_ID_TOUCH: - response_len = - fill_report(buffer, report_id, - &touch_reports[report_active_index], - sizeof(struct touch_report)); - break; - case REPORT_ID_MOUSE: - response_len = - fill_report(buffer, report_id, - &mouse_reports[report_active_index], - sizeof(struct mouse_report)); - break; - case REPORT_ID_DEVICE_CAPS: - response_len = fill_report(buffer, report_id, - &device_caps, - sizeof(device_caps)); - break; - case REPORT_ID_DEVICE_CERT: - response_len = fill_report(buffer, report_id, - &device_cert, - sizeof(device_cert)); - break; - case REPORT_ID_INPUT_MODE: - response_len = fill_report(buffer, report_id, - &input_mode, - sizeof(input_mode)); - break; - case REPORT_ID_REPORTING: - response_len = fill_report(buffer, report_id, - &reporting, - sizeof(reporting)); - break; - default: - response_len = 2; - buffer[0] = response_len; - buffer[1] = 0; - break; - } - send_response(response_len); - break; - case I2C_HID_CMD_SET_REPORT: - switch (report_id) { - case REPORT_ID_INPUT_MODE: - extract_report(len, buffer, &input_mode, - sizeof(input_mode)); - break; - case REPORT_ID_REPORTING: - extract_report(len, buffer, &reporting, - sizeof(reporting)); - break; - default: - break; - } - break; - case I2C_HID_CMD_SET_POWER: - /* - * Return the power setting so the user can actually set the - * touch controller's power state in board level. - */ - *data = power_state; - break; - default: - return -1; - } - return command; -} - -void i2c_hid_compile_report(struct touchpad_event *event) -{ - /* Save report into back buffer */ - struct touch_report *touch = &touch_reports[report_active_index ^ 1]; - struct touch_report *touch_old = &touch_reports[report_active_index]; - struct mouse_report *mouse = &mouse_reports[report_active_index ^ 1]; - int contact_num = 0; - - /* Touch report. */ - memset(touch, 0, sizeof(struct touch_report)); - for (int i = 0; i < I2C_HID_TOUCHPAD_MAX_FINGERS; i++) { - if (event->finger[i].valid) { - /* - * Windows considers any contact with width or height - * greater than 25mm to unintended, and expects the - * confidence value to be cleared for such a contact. - * We, however, haven't seen a touchpad that actually - * forwards that information to us. - * - * TODO(b/151692377): Revisit this once we have met such - * a device. - */ - touch->finger[i].confidence = 1; - touch->finger[i].tip = 1; - touch->finger[i].inrange = 1; - touch->finger[i].x = event->finger[i].x; - touch->finger[i].y = event->finger[i].y; - touch->finger[i].width = event->finger[i].width; - touch->finger[i].height = event->finger[i].height; - touch->finger[i].pressure = event->finger[i].pressure; - if (event->finger[i].is_palm) - touch->finger[i].pressure = - I2C_HID_TOUCHPAD_MAX_PRESSURE; - touch->finger[i].orientation = - event->finger[i].orientation; - contact_num++; - } else if (touch_old->finger[i].tip) { - /* - * When the finger is leaving, we first clear the tip - * bit while retaining the other values. We then clear - * the other values at the next frame where the finger - * has left. - * - * Setting tip to 0 implies that the finger is leaving - * for both CrOS and Windows. A leaving finger would - * never be re-considered by the OS. - */ - - /* - * First, copy old values from the previous report. - * - * This is suggested on Windows although no - * obvious problem has been noticed by not doing - * so. - */ - touch->finger[i] = touch_old->finger[i]; - - /* - * Leaving finger is not a palm by definition. - * - * Not clearing the confidence bit is essential - * for tap-to-click to work on Windows. - */ - touch->finger[i].confidence = 1; - - /* Leaving finger doesn't exist. */ - touch->finger[i].tip = 0; - - /* - * Assume that the leaving finger is not hovering - * either. We would inject one single fake hovering - * finger later if necessary. - */ - touch->finger[i].inrange = 0; - - contact_num++; - } - - /* id is like slot in Linux MT-B so it is fixed every time. */ - touch->finger[i].id = i; - } - - /* Check for hovering activity if there is no contact report. */ - if (!contact_num) { - if (event->hover) { - /* Put a fake finger at slot #0 if hover is detected. */ - touch->finger[0].inrange = 1; - touch->finger[0].x = I2C_HID_TOUCHPAD_MAX_X / 2; - touch->finger[0].y = I2C_HID_TOUCHPAD_MAX_Y / 2; - contact_num++; - } else if (!touch_old->finger[0].tip && - touch_old->finger[0].inrange) { - /* Clear the fake hovering finger for host. */ - contact_num++; - } - } - - /* Fill in finger counts and the button state. */ - touch->count = I2C_HID_TOUCHPAD_MAX_FINGERS; - touch->button = event->button; - - /* - * Windows expects scan time to be in units of 100us. As Windows - * measures the delta of scan times between the first and the current - * report, we simply report the __hw_clock_source_read() value (which - * is in resolution of 1us) divided by 100 as the scan time. - */ - touch->timestamp = __hw_clock_source_read() / 100; - - /* Mouse report. */ - mouse->button1 = touch->button; - if (touch->finger[0].tip == 1 && touch_old->finger[0].tip == 1) { - /* - * The relative X/Y movements in the mouse report are computed - * based on the deltas of absolute X/Y positions between the - * previous and current touch report. The computed deltas need - * to be scaled for a smooth mouse movement. - */ - mouse->x = (touch->finger[0].x - touch_old->finger[0].x) / - I2C_HID_TOUCHPAD_MOUSE_SCALE_X; - mouse->y = (touch->finger[0].y - touch_old->finger[0].y) / - I2C_HID_TOUCHPAD_MOUSE_SCALE_Y; - } else { - mouse->x = 0; - mouse->y = 0; - } - - /* Swap buffer */ - report_active_index ^= 1; -} diff --git a/common/i2c_peripheral.c b/common/i2c_peripheral.c deleted file mode 100644 index 20a4b4b0ae..0000000000 --- a/common/i2c_peripheral.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2017 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. - */ - -/* I2C peripheral cross-platform code for Chrome EC */ - -#include "host_command.h" -#include "i2c.h" -#include "util.h" - -enum ec_status i2c_get_protocol_info(struct host_cmd_handler_args *args) -{ - struct ec_response_get_protocol_info *r = args->response; - - memset(r, 0, sizeof(*r)); - r->protocol_versions = BIT(3); - r->max_request_packet_size = I2C_MAX_HOST_PACKET_SIZE; - r->max_response_packet_size = I2C_MAX_HOST_PACKET_SIZE; - r->flags = 0; - - args->response_size = sizeof(*r); - - return EC_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, - i2c_get_protocol_info, - EC_VER_MASK(0)); diff --git a/common/i2c_trace.c b/common/i2c_trace.c deleted file mode 100644 index 67b8864b22..0000000000 --- a/common/i2c_trace.c +++ /dev/null @@ -1,211 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "console.h" -#include "i2c.h" -#include "stddef.h" -#include "stdbool.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_I2C, outstr) -#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) - -struct i2c_trace_range { - bool enabled; - int port; - int addr_lo; /* Inclusive */ - int addr_hi; /* Inclusive */ -}; - -static struct i2c_trace_range trace_entries[8]; - -void i2c_trace_notify(int port, uint16_t addr_flags, - const uint8_t *out_data, size_t out_size, - const uint8_t *in_data, size_t in_size) -{ - size_t i; - uint16_t addr = I2C_STRIP_FLAGS(addr_flags); - - for (i = 0; i < ARRAY_SIZE(trace_entries); i++) - if (trace_entries[i].enabled - && trace_entries[i].port == port - && trace_entries[i].addr_lo <= addr - && trace_entries[i].addr_hi >= addr) - goto trace_enabled; - return; - -trace_enabled: - CPRINTF("i2c: %d:0x%X ", port, addr); - if (out_size) { - CPRINTF("wr "); - for (i = 0; i < out_size; i++) - CPRINTF("0x%02X ", out_data[i]); - } - if (in_size) { - CPRINTF(" rd "); - for (i = 0; i < in_size; i++) - CPRINTF("0x%02X ", in_data[i]); - } - CPRINTF("\n"); -} - -static int command_i2ctrace_list(void) -{ - size_t i; - const struct i2c_port_t *i2c_port; - - ccprintf("id port address\n"); - ccprintf("-- ---- -------\n"); - - for (i = 0; i < ARRAY_SIZE(trace_entries); i++) { - if (trace_entries[i].enabled) { - i2c_port = get_i2c_port(trace_entries[i].port); - ccprintf("%-2zd %d %-8s 0x%X", - i, - trace_entries[i].port, - i2c_port->name, - trace_entries[i].addr_lo); - if (trace_entries[i].addr_hi - != trace_entries[i].addr_lo) - ccprintf(" to 0x%X", - trace_entries[i].addr_hi); - ccprintf("\n"); - } - } - - return EC_SUCCESS; -} - -static int command_i2ctrace_disable(size_t id) -{ - if (id >= ARRAY_SIZE(trace_entries)) - return EC_ERROR_PARAM2; - - trace_entries[id].enabled = 0; - return EC_SUCCESS; -} - -static int command_i2ctrace_enable(int port, int addr_lo, - int addr_hi) -{ - struct i2c_trace_range *t; - struct i2c_trace_range *new_entry = NULL; - - if (!get_i2c_port(port)) - return EC_ERROR_PARAM2; - - if (addr_lo > addr_hi) - return EC_ERROR_PARAM3; - - /* - * Scan thru existing entries to see if there is one we can - * extend instead of making a new entry - */ - for (t = trace_entries; - t < trace_entries + ARRAY_SIZE(trace_entries); - t++) { - if (t->enabled && t->port == port) { - /* Subset of existing range, do nothing */ - if (t->addr_lo <= addr_lo && - t->addr_hi >= addr_hi) - return EC_SUCCESS; - - /* Extends exising range on both directions, replace */ - if (t->addr_lo >= addr_lo && - t->addr_hi <= addr_hi) { - t->enabled = 0; - return command_i2ctrace_enable( - port, addr_lo, addr_hi); - } - - /* Extends existing range below */ - if (t->addr_lo - 1 <= addr_hi && - t->addr_hi >= addr_hi) { - t->enabled = 0; - return command_i2ctrace_enable( - port, - addr_lo, - t->addr_hi); - } - - /* Extends existing range above */ - if (t->addr_lo <= addr_lo && - t->addr_hi + 1 >= addr_lo) { - t->enabled = 0; - return command_i2ctrace_enable( - port, - t->addr_lo, - addr_hi); - } - } else if (!t->enabled && !new_entry) { - new_entry = t; - } - } - - /* We need to allocate a new entry */ - if (new_entry) { - new_entry->enabled = 1; - new_entry->port = port; - new_entry->addr_lo = addr_lo; - new_entry->addr_hi = addr_hi; - return EC_SUCCESS; - } - - ccprintf("No space to allocate new trace entry. Delete some first.\n"); - return EC_ERROR_MEMORY_ALLOCATION; -} - - -static int command_i2ctrace(int argc, char **argv) -{ - int id_or_port; - int address_low; - int address_high; - char *end; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "list") && argc == 2) - return command_i2ctrace_list(); - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - id_or_port = strtoi(argv[2], &end, 0); - if (*end || id_or_port < 0) - return EC_ERROR_PARAM2; - - if (!strcasecmp(argv[1], "disable") && argc == 3) - return command_i2ctrace_disable(id_or_port); - - if (!strcasecmp(argv[1], "enable")) { - address_low = strtoi(argv[3], &end, 0); - if (*end || address_low < 0) - return EC_ERROR_PARAM3; - - if (argc == 4) { - address_high = address_low; - } else if (argc == 5) { - address_high = strtoi(argv[4], &end, 0); - if (*end || address_high < 0) - return EC_ERROR_PARAM4; - } else { - return EC_ERROR_PARAM_COUNT; - } - - return command_i2ctrace_enable( - id_or_port, address_low, address_high); - } - - return EC_ERROR_PARAM1; -} -DECLARE_CONSOLE_COMMAND(i2ctrace, - command_i2ctrace, - "[list | disable <id> | enable <port> <address> | " - "enable <port> <address-low> <address-high>]", - "Trace I2C transactions"); diff --git a/common/i2c_wedge.c b/common/i2c_wedge.c deleted file mode 100644 index 48bcac090c..0000000000 --- a/common/i2c_wedge.c +++ /dev/null @@ -1,341 +0,0 @@ -/* Copyright 2013 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. - */ - -/* - * Define CONFIG_CMD_I2CWEDGE and I2C_PORT_HOST to enable the 'i2cwedge' - * console command to allow us to bang the bus into a wedged state. For - * example, include the following lines in board/pit/board.h to enable it on - * pit: - * - * #define CONFIG_CMD_I2CWEDGE - * #define I2C_PORT_HOST I2C_PORT_CONTROLLER - * - */ - -#include "console.h" -#include "gpio.h" -#include "i2c.h" -#include "system.h" -#include "timer.h" -#include "util.h" - -/* - * The implementation is based on Wikipedia. - */ - -int i2c_bang_started; - -static void i2c_bang_delay(void) -{ - udelay(5); -} - -static void i2c_bang_start_cond(void) -{ - /* Restart if needed */ - if (i2c_bang_started) { - /* set SDA to 1 */ - i2c_raw_set_sda(I2C_PORT_HOST, 1); - i2c_bang_delay(); - - /* Clock stretching */ - i2c_raw_set_scl(I2C_PORT_HOST, 1); - while (i2c_raw_get_scl(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): TIMEOUT */ - - /* Repeated start setup time, minimum 4.7us */ - i2c_bang_delay(); - } - - i2c_raw_set_sda(I2C_PORT_HOST, 1); - if (i2c_raw_get_sda(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): arbitration_lost */ - - /* SCL is high, set SDA from 1 to 0. */ - i2c_raw_set_sda(I2C_PORT_HOST, 0); - i2c_bang_delay(); - i2c_raw_set_scl(I2C_PORT_HOST, 0); - i2c_bang_started = 1; - - ccputs("BITBANG: send start\n"); -} - -static void i2c_bang_stop_cond(void) -{ - /* set SDA to 0 */ - i2c_raw_set_sda(I2C_PORT_HOST, 0); - i2c_bang_delay(); - - /* Clock stretching */ - i2c_raw_set_scl(I2C_PORT_HOST, 1); - while (i2c_raw_get_scl(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): TIMEOUT */ - - /* Stop bit setup time, minimum 4us */ - i2c_bang_delay(); - - /* SCL is high, set SDA from 0 to 1 */ - i2c_raw_set_sda(I2C_PORT_HOST, 1); - if (i2c_raw_get_sda(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): arbitration_lost */ - - i2c_bang_delay(); - - i2c_bang_started = 0; - ccputs("BITBANG: send stop\n"); -} - -static void i2c_bang_out_bit(int bit) -{ - if (bit) - i2c_raw_set_sda(I2C_PORT_HOST, 1); - else - i2c_raw_set_sda(I2C_PORT_HOST, 0); - - i2c_bang_delay(); - - /* Clock stretching */ - i2c_raw_set_scl(I2C_PORT_HOST, 1); - while (i2c_raw_get_scl(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): TIMEOUT */ - - /* - * SCL is high, now data is valid - * If SDA is high, check that nobody else is driving SDA - */ - i2c_raw_set_sda(I2C_PORT_HOST, 1); - if (bit && i2c_raw_get_sda(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): arbitration_lost */ - - i2c_bang_delay(); - i2c_raw_set_scl(I2C_PORT_HOST, 0); -} - -static int i2c_bang_in_bit(void) -{ - int bit; - - /* Let the peripheral drive data */ - i2c_raw_set_sda(I2C_PORT_HOST, 1); - i2c_bang_delay(); - - /* Clock stretching */ - i2c_raw_set_scl(I2C_PORT_HOST, 1); - while (i2c_raw_get_scl(I2C_PORT_HOST) == 0) - ; /* TODO(crosbug.com/p/26487): TIMEOUT */ - - /* SCL is high, now data is valid */ - bit = i2c_raw_get_sda(I2C_PORT_HOST); - i2c_bang_delay(); - i2c_raw_set_scl(I2C_PORT_HOST, 0); - - return bit; -} - -/* Write a byte to I2C bus. Return 0 if ack by the peripheral. */ -static int i2c_bang_out_byte(int send_start, int send_stop, unsigned char byte) -{ - unsigned bit; - int nack; - int tmp = byte; - - if (send_start) - i2c_bang_start_cond(); - - for (bit = 0; bit < 8; bit++) { - i2c_bang_out_bit((byte & 0x80) != 0); - byte <<= 1; - } - - nack = i2c_bang_in_bit(); - - ccprintf(" write byte: %d ack/nack=%d\n", tmp, nack); - - if (send_stop) - i2c_bang_stop_cond(); - - return nack; -} - -static unsigned char i2c_bang_in_byte(int ack, int send_stop) -{ - unsigned char byte = 0; - int i; - for (i = 0; i < 8; ++i) - byte = (byte << 1) | i2c_bang_in_bit(); - i2c_bang_out_bit(ack != 0); - if (send_stop) - i2c_bang_stop_cond(); - return byte; -} - -static void i2c_bang_init(void) -{ - i2c_bang_started = 0; - - i2c_raw_mode(I2C_PORT_HOST, 1); -} - -static void i2c_bang_xfer(int addr, int reg) -{ - int byte; - - i2c_bang_init(); - - /* State a write command to 'addr' */ - i2c_bang_out_byte(1 /*start*/, 0 /*stop*/, addr); - /* Write 'reg' */ - i2c_bang_out_byte(0 /*start*/, 0 /*stop*/, reg); - - /* Start a read command */ - i2c_bang_out_byte(1 /*start*/, 0 /*stop*/, addr | 1); - - /* Read two bytes */ - byte = i2c_bang_in_byte(0, 0); /* ack and no stop */ - ccprintf(" read byte: %d\n", byte); - byte = i2c_bang_in_byte(1, 1); /* nack and stop */ - ccprintf(" read byte: %d\n", byte); -} - -static void i2c_bang_wedge_write(int addr, int byte, int bit_count, - int reboot) -{ - int i; - - i2c_bang_init(); - - /* State a write command to 'addr' */ - i2c_bang_out_byte(1 /*start*/, 0 /*stop*/, addr); - /* Send a few bits and stop */ - for (i = 0; i < bit_count; ++i) { - i2c_bang_out_bit((byte & 0x80) != 0); - byte <<= 1; - } - ccprintf(" wedged write after %d bits\n", bit_count); - - if (reboot) - system_reset(0); -} - -static void i2c_bang_wedge_read(int addr, int reg, int bit_count, - int reboot) -{ - int i; - - i2c_bang_init(); - - /* State a write command to 'addr' */ - i2c_bang_out_byte(1 /*start*/, 0 /*stop*/, addr); - /* Write 'reg' */ - i2c_bang_out_byte(0 /*start*/, 0 /*stop*/, reg); - - /* Start a read command */ - i2c_bang_out_byte(1 /*start*/, 0 /*stop*/, addr | 1); - - /* Read bit_count bits and stop */ - for (i = 0; i < bit_count; ++i) - i2c_bang_in_bit(); - - ccprintf(" wedged read after %d bits\n", bit_count); - - if (reboot) - system_reset(0); -} - -#define WEDGE_WRITE 1 -#define WEDGE_READ 2 -#define WEDGE_REBOOT 4 - -static int command_i2c_wedge(int argc, char **argv) -{ - int addr, reg, wedge_flag = 0, wedge_bit_count = -1; - char *e; - enum gpio_signal tmp; - - /* Verify that the I2C_PORT_HOST has SDA and SCL pins defined. */ - if (get_sda_from_i2c_port(I2C_PORT_HOST, &tmp) != EC_SUCCESS || - get_scl_from_i2c_port(I2C_PORT_HOST, &tmp) != EC_SUCCESS) { - ccprintf("Cannot wedge bus because no SCL and SDA pins are" - "defined for this port. Check i2c_ports[].\n"); - return EC_SUCCESS; - } - - if (argc < 3) { - ccputs("Usage: i2cwedge addr out_byte " - "[wedge_flag [wedge_bit_count]]\n"); - ccputs(" wedge_flag - (1: wedge out; 2: wedge in;" - " 5: wedge out+reboot; 6: wedge in+reboot)]\n"); - ccputs(" wedge_bit_count - 0 to 8\n"); - return EC_ERROR_UNKNOWN; - } - - addr = strtoi(argv[1], &e, 0); - if (*e) { - ccprintf("Invalid addr %s\n", argv[1]); - return EC_ERROR_INVAL; - } - reg = strtoi(argv[2], &e, 0); - if (*e) { - ccprintf("Invalid out_byte %s\n", argv[2]); - return EC_ERROR_INVAL; - } - if (argc > 3) { - wedge_flag = strtoi(argv[3], &e, 0); - if (*e) { - ccprintf("Invalid wedge_flag %s\n", argv[3]); - return EC_ERROR_INVAL; - } - } - if (argc > 4) { - wedge_bit_count = strtoi(argv[4], &e, 0); - if (*e || wedge_bit_count < 0 || wedge_bit_count > 8) { - ccprintf("Invalid wedge_bit_count %s.\n", argv[4]); - return EC_ERROR_INVAL; - } - } - - i2c_lock(I2C_PORT_HOST, 1); - - if (wedge_flag & WEDGE_WRITE) { - if (wedge_bit_count < 0) - wedge_bit_count = 8; - i2c_bang_wedge_write(addr, reg, wedge_bit_count, - (wedge_flag & WEDGE_REBOOT)); - } else if (wedge_flag & WEDGE_READ) { - if (wedge_bit_count < 0) - wedge_bit_count = 2; - i2c_bang_wedge_read(addr, reg, wedge_bit_count, - (wedge_flag & WEDGE_REBOOT)); - } else { - i2c_bang_xfer(addr, reg); - } - - /* Put it back into normal mode */ - i2c_raw_mode(I2C_PORT_HOST, 0); - - i2c_lock(I2C_PORT_HOST, 0); - - if (wedge_flag & (WEDGE_WRITE | WEDGE_READ)) - ccprintf("I2C bus %d is now wedged. Enjoy.\n", I2C_PORT_HOST); - else - ccprintf("Bit bang xfer complete.\n"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(i2cwedge, command_i2c_wedge, - "i2cwedge addr out_byte " - "[wedge_flag [wedge_bit_count]]", - "Wedge host I2C bus"); - -static int command_i2c_unwedge(int argc, char **argv) -{ - i2c_unwedge(I2C_PORT_HOST); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(i2cunwedge, command_i2c_unwedge, - "", - "Unwedge host I2C bus"); - diff --git a/common/inductive_charging.c b/common/inductive_charging.c deleted file mode 100644 index 793f535afe..0000000000 --- a/common/inductive_charging.c +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Inductive charging control */ - -#include "common.h" -#include "gpio.h" -#include "hooks.h" -#include "inductive_charging.h" -#include "lid_switch.h" -#include "timer.h" - -/* - * The inductive charger is controlled with two signals: - * - BASE_CHG_VDD_EN controls whether the charger is powered. - * - CHARGE_EN controls whether to enable charging. - * Charging status is reported via CHARGE_DONE, but in a tricky way: - * - It's 0 if: - * + The charger is unpowered. (i.e. BASE_CHG_VDD_EN = 0) - * + Or charging is disabled. (i.e. CHARGE_EN = 0) - * + Or the charging current is small enough. - * - Otherwise, it's 1. - */ - -/* Whether we want to process interrupts on CHARGE_DONE or not. */ -static int monitor_charge_done; - -/* - * Start monitoring CHARGE_DONE and fires the interrupt once so that - * we react to the current value. - */ -static void inductive_charging_monitor_charge(void) -{ - monitor_charge_done = 1; - inductive_charging_interrupt(GPIO_CHARGE_DONE); -} -DECLARE_DEFERRED(inductive_charging_monitor_charge); - -void inductive_charging_interrupt(enum gpio_signal signal) -{ - int charger_enabled = gpio_get_level(GPIO_BASE_CHG_VDD_EN); - int charge_done = gpio_get_level(GPIO_CHARGE_DONE); - static int charge_already_done; - - if (!monitor_charge_done && signal == GPIO_CHARGE_DONE) - return; - - if (signal == GPIO_LID_OPEN) { - /* The lid has been opened. Clear all states. */ - charge_done = 0; - charge_already_done = 0; - monitor_charge_done = 0; - } else if (signal == GPIO_CHARGE_DONE) { - /* - * Once we see CHARGE_DONE=1, we ignore any change on - * CHARGE_DONE until the next time the lid is opened. - */ - if (charge_done == 1) - charge_already_done = 1; - else if (charge_already_done) - return; - } - - if (!charger_enabled || charge_done) { - gpio_set_level(GPIO_CHARGE_EN, 0); - } else { - gpio_set_level(GPIO_CHARGE_EN, 1); - /* - * When the charging is just enabled, there might be a - * blip on CHARGE_DONE. Wait for a second before we start - * looking at CHARGE_DONE. - */ - if (!monitor_charge_done) - hook_call_deferred( - &inductive_charging_monitor_charge_data, - SECOND); - } -} - -static void inductive_charging_deferred_update(void) -{ - int lid_open = lid_is_open(); - gpio_set_level(GPIO_BASE_CHG_VDD_EN, !lid_open); - inductive_charging_interrupt(GPIO_LID_OPEN); -} -DECLARE_DEFERRED(inductive_charging_deferred_update); - -static void inductive_charging_lid_update(void) -{ - /* - * When the lid close signal changes, the coils might still be - * unaligned. Delay here to give the coils time to align before - * we try to clear CHARGE_DONE. - */ - hook_call_deferred(&inductive_charging_deferred_update_data, - 5 * SECOND); -} -DECLARE_HOOK(HOOK_LID_CHANGE, inductive_charging_lid_update, HOOK_PRIO_DEFAULT); - -static void inductive_charging_init(void) -{ - gpio_enable_interrupt(GPIO_CHARGE_DONE); - inductive_charging_lid_update(); -} -DECLARE_HOOK(HOOK_INIT, inductive_charging_init, HOOK_PRIO_DEFAULT); diff --git a/common/init_rom.c b/common/init_rom.c deleted file mode 100644 index 320849c008..0000000000 --- a/common/init_rom.c +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Init ROM module for Chrome EC */ - -#include "builtin/assert.h" -#include "common.h" -#include "init_rom.h" -#include "flash.h" -#include "stdbool.h" -#include "stddef.h" - -const void *init_rom_map(const void *addr, int size) -{ - const char *src; - uintptr_t offset; - - /* - * When CONFIG_CHIP_INIT_ROM_REGION isn't enabled, .init_rom objects - * are linked into the .rodata section and directly addressable. - * Return the caller's pointer. - */ - if (!IS_ENABLED(CONFIG_CHIP_INIT_ROM_REGION)) - return addr; - - /* - * When flash isn't memory mapped, caller's must use init_rom_copy() - * to copy .init_rom data into RAM. - */ - if (!IS_ENABLED(CONFIG_MAPPED_STORAGE)) - return NULL; - - /* - * Safe pointer conversion - needed for host tests which can have - * 64-bit pointers. - */ - offset = (uintptr_t)addr; - - ASSERT(offset <= __INT_MAX__); - - /* - * Convert flash offset to memory mapped address - */ - if (crec_flash_dataptr((int)offset, size, 1, &src) < 0) - return NULL; - - /* Once the flash offset is validated, lock the flash for the caller */ - crec_flash_lock_mapped_storage(1); - - return src; -} - -/* - * The addr and size parameters are provided for forward compatibility if - * the flash API is extended to support locking less than the entire flash. - */ -void init_rom_unmap(const void *addr, int size) -{ - if (IS_ENABLED(CONFIG_CHIP_INIT_ROM_REGION)) - crec_flash_lock_mapped_storage(0); -} - -int init_rom_copy(int offset, int size, char *data) -{ - return crec_flash_read(offset, size, data); -} - diff --git a/common/ioexpander.c b/common/ioexpander.c deleted file mode 100644 index ccf3cc7c4a..0000000000 --- a/common/ioexpander.c +++ /dev/null @@ -1,338 +0,0 @@ -/* 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. - */ - -/* IO Expander Controller Common Code */ - -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "ioexpander.h" -#include "system.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_GPIO, format, ## args) -#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) - -static uint8_t last_val[(IOEX_COUNT + 7) / 8]; - -static int last_val_changed(enum ioex_signal signal, int v) -{ - const int i = signal - IOEX_SIGNAL_START; - - ASSERT(signal_is_ioex(signal)); - - if (v && !(last_val[i / 8] & (BIT(i % 8)))) { - last_val[i / 8] |= BIT(i % 8); - return 1; - } else if (!v && last_val[i / 8] & (BIT(i % 8))) { - last_val[i / 8] &= ~(BIT(i % 8)); - return 1; - } else { - return 0; - } -} - -int signal_is_ioex(int signal) -{ - return ((signal >= IOEX_SIGNAL_START) && (signal < IOEX_SIGNAL_END)); -} - -static const struct ioex_info *ioex_get_signal_info(enum ioex_signal signal) -{ - const struct ioex_info *g; - - ASSERT(signal_is_ioex(signal)); - - g = ioex_list + signal - IOEX_SIGNAL_START; - - if (ioex_config[g->ioex].flags & IOEX_FLAGS_DISABLED) { - CPRINTS("ioex %s disabled", g->name); - return NULL; - } - - return g; -} - -static int ioex_is_valid_interrupt_signal(enum ioex_signal signal) -{ - const struct ioexpander_drv *drv; - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return EC_ERROR_BUSY; - - /* Fail if no interrupt handler */ - if (signal - IOEX_SIGNAL_START >= ioex_ih_count) - return EC_ERROR_PARAM1; - - drv = ioex_config[g->ioex].drv; - /* - * Not every IOEX chip can support interrupt, check it before enabling - * the interrupt function - */ - if (drv->enable_interrupt == NULL) { - CPRINTS("IOEX chip port %d doesn't support INT", g->ioex); - return EC_ERROR_UNIMPLEMENTED; - } - - return EC_SUCCESS; -} - -int ioex_enable_interrupt(enum ioex_signal signal) -{ - int rv; - const struct ioex_info *g = ioex_get_signal_info(signal); - const struct ioexpander_drv *drv; - - rv = ioex_is_valid_interrupt_signal(signal); - if (rv != EC_SUCCESS) - return rv; - - drv = ioex_config[g->ioex].drv; - return drv->enable_interrupt(g->ioex, g->port, g->mask, 1); -} - -int ioex_disable_interrupt(enum ioex_signal signal) -{ - int rv; - const struct ioexpander_drv *drv; - const struct ioex_info *g = ioex_get_signal_info(signal); - - rv = ioex_is_valid_interrupt_signal(signal); - if (rv != EC_SUCCESS) - return rv; - - drv = ioex_config[g->ioex].drv; - return drv->enable_interrupt(g->ioex, g->port, g->mask, 0); -} - -int ioex_get_flags(enum ioex_signal signal, int *flags) -{ - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return EC_ERROR_BUSY; - - return ioex_config[g->ioex].drv->get_flags_by_mask(g->ioex, - g->port, g->mask, flags); -} - -int ioex_set_flags(enum ioex_signal signal, int flags) -{ - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return EC_ERROR_BUSY; - - return ioex_config[g->ioex].drv->set_flags_by_mask(g->ioex, - g->port, g->mask, flags); -} - -int ioex_get_level(enum ioex_signal signal, int *val) -{ - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return EC_ERROR_BUSY; - - return ioex_config[g->ioex].drv->get_level(g->ioex, g->port, - g->mask, val); -} - -int ioex_set_level(enum ioex_signal signal, int value) -{ - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return EC_ERROR_BUSY; - - return ioex_config[g->ioex].drv->set_level(g->ioex, g->port, - g->mask, value); -} - -#ifdef CONFIG_IO_EXPANDER_SUPPORT_GET_PORT -int ioex_get_port(int ioex, int port, int *val) -{ - if (ioex_config[ioex].drv->get_port == NULL) - return EC_ERROR_UNIMPLEMENTED; - - return ioex_config[ioex].drv->get_port(ioex, port, val); -} -#endif - -int ioex_init(int ioex) -{ - const struct ioex_info *g = ioex_list; - const struct ioexpander_drv *drv = ioex_config[ioex].drv; - int rv; - int i; - - if (ioex_config[ioex].flags & IOEX_FLAGS_DISABLED) - return EC_ERROR_BUSY; - - if (drv->init != NULL) { - rv = drv->init(ioex); - if (rv != EC_SUCCESS) - return rv; - } - - /* - * Set all IO expander GPIOs to default flags according to the setting - * in gpio.inc - */ - for (i = 0; i < IOEX_COUNT; i++, g++) { - int flags = g->flags; - - if (g->ioex == ioex && g->mask && !(flags & GPIO_DEFAULT)) { - /* Late-sysJump should not set the output levels */ - if (system_jumped_late()) - flags &= ~(GPIO_LOW | GPIO_HIGH); - - drv->set_flags_by_mask(g->ioex, g->port, - g->mask, flags); - } - } - - return EC_SUCCESS; -} - -static void ioex_init_default(void) -{ - int i; - - for (i = 0; i < CONFIG_IO_EXPANDER_PORT_COUNT; i++) - ioex_init(i); -} -DECLARE_HOOK(HOOK_INIT, ioex_init_default, HOOK_PRIO_INIT_I2C + 1); - -const char *ioex_get_name(enum ioex_signal signal) -{ - const struct ioex_info *g = ioex_list + signal - IOEX_SIGNAL_START; - - return g->name; -} - -static void print_ioex_info(enum ioex_signal signal) -{ - int changed, v, val; - int flags = 0; - const struct ioex_info *g = ioex_list + signal - IOEX_SIGNAL_START; - - if (ioex_config[g->ioex].flags & IOEX_FLAGS_DISABLED) { - ccprintf(" DISABLED %s\n", ioex_get_name(signal)); - return; - } - - - v = ioex_get_level(signal, &val); - if (v) { - ccprintf("Fail to get %s level\n", ioex_get_name(signal)); - return; - } - v = ioex_get_flags(signal, &flags); - if (v) { - ccprintf("Fail to get %s flags\n", ioex_get_name(signal)); - return; - } - - changed = last_val_changed(signal, val); - - ccprintf(" %d%c %s%s%s%s%s%s\n", val, - (changed ? '*' : ' '), - (flags & GPIO_INPUT ? "I " : ""), - (flags & GPIO_OUTPUT ? "O " : ""), - (flags & GPIO_LOW ? "L " : ""), - (flags & GPIO_HIGH ? "H " : ""), - (flags & GPIO_OPEN_DRAIN ? "ODR " : ""), - ioex_get_name(signal)); - - /* Flush console to avoid truncating output */ - cflush(); -} - -static int ioex_get_default_flags(enum ioex_signal signal) -{ - const struct ioex_info *g = ioex_get_signal_info(signal); - - if (g == NULL) - return 0; - - return g->flags; -} - -/* IO expander commands */ -static enum ioex_signal find_ioex_by_name(const char *name) -{ - enum ioex_signal signal; - - if (!name) - return IOEX_SIGNAL_END; - - for (signal = IOEX_SIGNAL_START; signal < IOEX_SIGNAL_END; signal++) { - if (!strcasecmp(name, ioex_get_name(signal))) - return signal; - } - - return IOEX_SIGNAL_END; -} - -static enum ec_error_list ioex_set(const char *name, int value) -{ - enum ioex_signal signal = find_ioex_by_name(name); - - if (!signal_is_ioex(signal)) - return EC_ERROR_INVAL; - - if (!(ioex_get_default_flags(signal) & GPIO_OUTPUT)) - return EC_ERROR_INVAL; - - return ioex_set_level(signal, value); -} - -static int command_ioex_set(int argc, char **argv) -{ - char *e; - int v; - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - v = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - if (ioex_set(argv[1], v) != EC_SUCCESS) - return EC_ERROR_PARAM1; - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ioexset, command_ioex_set, - "name <0 | 1>", - "Set level of a IO expander IO"); - -static int command_ioex_get(int argc, char **argv) -{ - enum ioex_signal signal; - - /* If a signal is specified, print only that one */ - if (argc == 2) { - signal = find_ioex_by_name(argv[1]); - if (!signal_is_ioex(signal)) - return EC_ERROR_PARAM1; - print_ioex_info(signal); - - return EC_SUCCESS; - } - - /* Otherwise print them all */ - for (signal = IOEX_SIGNAL_START; signal < IOEX_SIGNAL_END; signal++) - print_ioex_info(signal); - - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(ioexget, command_ioex_get, - "[name]", - "Read level of IO expander pin(s)"); - diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c deleted file mode 100644 index 699eaa6687..0000000000 --- a/common/keyboard_8042.c +++ /dev/null @@ -1,1328 +0,0 @@ -/* Copyright 2013 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. - * - * 8042 keyboard protocol - */ - -#include "chipset.h" -#include "button.h" -#include "common.h" -#include "console.h" -#include "device_event.h" -#include "hooks.h" -#include "host_command.h" -#include "i8042_protocol.h" -#include "keyboard_8042_sharedlib.h" -#include "keyboard_config.h" -#include "keyboard_protocol.h" -#include "lightbar.h" -#include "lpc.h" -#include "power_button.h" -#include "queue.h" -#include "shared_mem.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -#ifdef CONFIG_KEYBOARD_DEBUG -#define CPUTS5(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS5(format, args...) cprints(CC_KEYBOARD, format, ## args) -#else -#define CPUTS5(outstr) -#define CPRINTS5(format, args...) -#endif - -/* - * This command needs malloc to work. Could we use this instead? - * - * #define CMD_KEYBOARD_LOG IS_ENABLED(CONFIG_MALLOC) - */ -#ifdef CONFIG_MALLOC -#define CMD_KEYBOARD_LOG 1 -#else -#define CMD_KEYBOARD_LOG 0 -#endif - -static enum { - STATE_NORMAL = 0, - STATE_SCANCODE, - STATE_SETLEDS, - STATE_EX_SETLEDS_1, /* Expect 2-byte parameter */ - STATE_EX_SETLEDS_2, - STATE_WRITE_CMD_BYTE, - STATE_WRITE_OUTPUT_PORT, - STATE_ECHO_MOUSE, - STATE_SETREP, - STATE_SEND_TO_MOUSE, -} data_port_state = STATE_NORMAL; - -enum scancode_set_list { - SCANCODE_GET_SET = 0, - SCANCODE_SET_1, - SCANCODE_SET_2, - SCANCODE_SET_3, - SCANCODE_MAX = SCANCODE_SET_3, -}; - -#define MAX_SCAN_CODE_LEN 4 - -/* Number of bytes host can get behind before we start generating extra IRQs */ -#define KB_TO_HOST_RETRIES 3 - -/* - * Mutex to control write access to the to-host buffer head. Don't need to - * mutex the tail because reads are only done in one place. - */ -static mutex_t to_host_mutex; - -/* Queue command/data to the host */ -enum { - CHAN_KBD = 0, - CHAN_AUX, -}; -struct data_byte { - uint8_t chan; - uint8_t byte; -}; - -static struct queue const to_host = QUEUE_NULL(16, struct data_byte); - -/* Queue command/data from the host */ -enum { - HOST_COMMAND = 0, - HOST_DATA, -}; -struct host_byte { - uint8_t type; - uint8_t byte; -}; - -/* - * The buffer for i8042 command from host. So far the largest command - * we see from kernel is: - * - * d1 -> i8042 (command) # enable A20 in i8042_platform_init() of - * df -> i8042 (parameter) # serio/i8042-x86ia64io.h file. - * ff -> i8042 (command) - * 20 -> i8042 (command) # read CTR - * - * Hence, 5 (actually 4 plus one spare) is large enough, but use 8 for safety. - */ -static struct queue const from_host = QUEUE_NULL(8, struct host_byte); - -/* Queue aux data to the host from interrupt context. */ -static struct queue const aux_to_host_queue = QUEUE_NULL(16, uint8_t); - -static int i8042_keyboard_irq_enabled; -static int i8042_aux_irq_enabled; - -/* i8042 global settings */ -static int keyboard_enabled; /* default the keyboard is disabled. */ -static int aux_chan_enabled; /* default the mouse is disabled. */ -static int keystroke_enabled; /* output keystrokes */ -static uint8_t resend_command[MAX_SCAN_CODE_LEN]; -static uint8_t resend_command_len; -static uint8_t controller_ram_address; -static uint8_t controller_ram[0x20] = { - /* the so called "command byte" */ - I8042_XLATE | I8042_AUX_DIS | I8042_KBD_DIS, - /* 0x01 - 0x1f are controller RAM */ -}; -static uint8_t A20_status; - -/* - * Scancode settings - */ -static enum scancode_set_list scancode_set = SCANCODE_SET_2; - -/* - * Typematic delay, rate and counter variables. - * - * 7 6 5 4 3 2 1 0 - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * |un- | delay | B | D | - * | used| 0 1 | 0 1 | 0 1 1 | - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * Formula: - * the inter-char delay = (2 ** B) * (D + 8) / 240 (sec) - * Default: 500ms delay, 10.9 chars/sec. - */ -#define DEFAULT_TYPEMATIC_VALUE (BIT(5) | BIT(3) | (3 << 0)) -static uint8_t typematic_value_from_host; -static int typematic_first_delay; -static int typematic_inter_delay; -static int typematic_len; /* length of typematic_scan_code */ -static uint8_t typematic_scan_code[MAX_SCAN_CODE_LEN]; -static timestamp_t typematic_deadline; - -#define KB_SYSJUMP_TAG 0x4b42 /* "KB" */ -#define KB_HOOK_VERSION 2 -/* the previous keyboard state before reboot_ec. */ -struct kb_state { - uint8_t codeset; - uint8_t ctlram; - uint8_t keystroke_enabled; -}; - -/*****************************************************************************/ -/* Keyboard event log */ - -/* Log the traffic between EC and host -- for debug only */ -#define MAX_KBLOG 512 /* Max events in keyboard log */ - -struct kblog_t { - /* - * Type: - * - * s = byte enqueued to send to host - * a = aux byte enqueued to send to host - * t = to-host queue tail pointer before type='s' bytes enqueued - * - * d = data byte from host - * c = command byte from host - * - * k = to-host queue head pointer before byte dequeued - * K = byte actually sent to host via LPC - * A = byte actually sent to host via LPC as AUX - * - * x = to_host queue was cleared - * - * The to-host head and tail pointers are logged pre-wrapping to the - * queue size. This means that they continually increment as units - * are dequeued and enqueued respectively. Since only the bottom - * byte of the value is logged they will wrap every 256 units. - */ - uint8_t type; - uint8_t byte; -}; - -static struct kblog_t *kblog_buf; /* Log buffer; NULL if not logging */ -static int kblog_len; /* Current log length */ - -/** - * Add event to keyboard log. - */ -static void kblog_put(char type, uint8_t byte) -{ - if (kblog_buf && kblog_len < MAX_KBLOG) { - kblog_buf[kblog_len].type = type; - kblog_buf[kblog_len].byte = byte; - kblog_len++; - } -} - -/*****************************************************************************/ - -void keyboard_host_write(int data, int is_cmd) -{ - struct host_byte h; - - h.type = is_cmd ? HOST_COMMAND : HOST_DATA; - h.byte = data; - queue_add_unit(&from_host, &h); - task_wake(TASK_ID_KEYPROTO); -} - -/** - * Enable keyboard IRQ generation. - * - * @param enable Enable (!=0) or disable (0) IRQ generation. - */ -static void keyboard_enable_irq(int enable) -{ - CPRINTS("KB IRQ %s", enable ? "enable" : "disable"); - - i8042_keyboard_irq_enabled = enable; - if (enable) - lpc_keyboard_resume_irq(); -} - -/** - * Enable mouse IRQ generation. - * - * @param enable Enable (!=0) or disable (0) IRQ generation. - */ -static void aux_enable_irq(int enable) -{ - CPRINTS("AUX IRQ %s", enable ? "enable" : "disable"); - - i8042_aux_irq_enabled = enable; -} - -/** - * Send a scan code to the host. - * - * The EC lib will push the scan code bytes to host via port 0x60 and assert - * the IBF flag to trigger an interrupt. The EC lib must queue them if the - * host cannot read the previous byte away in time. - * - * @param len Number of bytes to send to the host - * @param to_host Data to send - * @param chan Channel to send data on - */ -static void i8042_send_to_host(int len, const uint8_t *bytes, - uint8_t chan) -{ - int i; - struct data_byte data; - - /* Enqueue output data if there's space */ - mutex_lock(&to_host_mutex); - - for (i = 0; i < len; i++) - kblog_put(chan == CHAN_AUX ? 'a' : 's', bytes[i]); - - if (queue_space(&to_host) >= len) { - kblog_put('t', to_host.state->tail); - for (i = 0; i < len; i++) { - data.chan = chan; - data.byte = bytes[i]; - queue_add_unit(&to_host, &data); - } - } - mutex_unlock(&to_host_mutex); - - /* Wake up the task to move from queue to host */ - task_wake(TASK_ID_KEYPROTO); -} - -/* Change to set 1 if the I8042_XLATE flag is set. */ -static enum scancode_set_list acting_code_set(enum scancode_set_list set) -{ - /* Always generate set 1 if keyboard translation is enabled */ - if (controller_ram[0] & I8042_XLATE) - return SCANCODE_SET_1; - - return set; -} - -static int is_supported_code_set(enum scancode_set_list set) -{ - return (set == SCANCODE_SET_1 || set == SCANCODE_SET_2); -} - -/** - * Return the make or break code bytes for the active scancode set. - * - * @param make_code The make code to generate the make or break code from - * @param pressed Whether the key or button was pressed - * @param code_set The scancode set being used - * @param scan_code An array of bytes to store the make or break code in - * @param len The number of valid bytes to send in scan_code - */ -static void scancode_bytes(uint16_t make_code, int8_t pressed, - enum scancode_set_list code_set, uint8_t *scan_code, - int32_t *len) -{ - *len = 0; - - /* Output the make code (from table) */ - if (make_code >= 0x0100) { - scan_code[(*len)++] = make_code >> 8; - make_code &= 0xff; - } - - switch (code_set) { - case SCANCODE_SET_1: - make_code = scancode_translate_set2_to_1(make_code); - scan_code[(*len)++] = pressed ? make_code : (make_code | 0x80); - break; - - case SCANCODE_SET_2: - if (pressed) { - scan_code[(*len)++] = make_code; - } else { - scan_code[(*len)++] = 0xf0; - scan_code[(*len)++] = make_code; - } - break; - default: - break; - } -} - -static enum ec_error_list matrix_callback(int8_t row, int8_t col, - int8_t pressed, - enum scancode_set_list code_set, - uint8_t *scan_code, int32_t *len) -{ - uint16_t make_code; - - ASSERT(scan_code); - ASSERT(len); - - if (row >= KEYBOARD_ROWS || col >= keyboard_cols) - return EC_ERROR_INVAL; - - make_code = get_scancode_set2(row, col); - -#ifdef CONFIG_KEYBOARD_SCANCODE_CALLBACK - { - enum ec_error_list r = keyboard_scancode_callback( - &make_code, pressed); - if (r != EC_SUCCESS) - return r; - } -#endif - - code_set = acting_code_set(code_set); - if (!is_supported_code_set(code_set)) { - CPRINTS("KB scancode set %d unsupported", code_set); - return EC_ERROR_UNIMPLEMENTED; - } - - if (!make_code) { - CPRINTS("KB scancode %d:%d missing", row, col); - return EC_ERROR_UNIMPLEMENTED; - } - - scancode_bytes(make_code, pressed, code_set, scan_code, len); - return EC_SUCCESS; -} - -/** - * Set typematic delays based on host data byte. - */ -static void set_typematic_delays(uint8_t data) -{ - typematic_value_from_host = data; - typematic_first_delay = MSEC * - (((typematic_value_from_host & 0x60) >> 5) + 1) * 250; - typematic_inter_delay = SECOND * - (1 << ((typematic_value_from_host & 0x18) >> 3)) * - ((typematic_value_from_host & 0x7) + 8) / 240; -} - -static void reset_rate_and_delay(void) -{ - set_typematic_delays(DEFAULT_TYPEMATIC_VALUE); -} - -void keyboard_clear_buffer(void) -{ - CPRINTS("KB Clear Buffer"); - mutex_lock(&to_host_mutex); - kblog_put('x', queue_count(&to_host)); - queue_init(&to_host); - mutex_unlock(&to_host_mutex); - lpc_keyboard_clear_buffer(); -} - -static void keyboard_wakeup(void) -{ - host_set_single_event(EC_HOST_EVENT_KEY_PRESSED); -} - -static void set_typematic_key(const uint8_t *scan_code, int32_t len) -{ - typematic_deadline.val = get_time().val + typematic_first_delay; - memcpy(typematic_scan_code, scan_code, len); - typematic_len = len; -} - -void clear_typematic_key(void) -{ - typematic_len = 0; -} - -void keyboard_state_changed(int row, int col, int is_pressed) -{ - uint8_t scan_code[MAX_SCAN_CODE_LEN]; - int32_t len = 0; - enum ec_error_list ret; - -#ifdef CONFIG_KEYBOARD_DEBUG - char mylabel = get_keycap_label(row, col); - - if (mylabel & KEYCAP_LONG_LABEL_BIT) - CPRINTS("KB (%d,%d)=%d %s", row, col, is_pressed, - get_keycap_long_label(mylabel & KEYCAP_LONG_LABEL_INDEX_BITMASK)); - else - CPRINTS("KB (%d,%d)=%d %c", row, col, is_pressed, mylabel); -#endif - - ret = matrix_callback(row, col, is_pressed, scancode_set, scan_code, - &len); - if (ret == EC_SUCCESS) { - ASSERT(len > 0); - if (keystroke_enabled) - i8042_send_to_host(len, scan_code, CHAN_KBD); - } - - if (is_pressed) { - keyboard_wakeup(); - set_typematic_key(scan_code, len); - task_wake(TASK_ID_KEYPROTO); - } else { - clear_typematic_key(); - } -} - -static void keystroke_enable(int enable) -{ - if (!keystroke_enabled && enable) - CPRINTS("KS enable"); - else if (keystroke_enabled && !enable) - CPRINTS("KS disable"); - - keystroke_enabled = enable; -} - -static void keyboard_enable(int enable) -{ - if (!keyboard_enabled && enable) - CPRINTS("KB enable"); - else if (keyboard_enabled && !enable) - CPRINTS("KB disable"); - - keyboard_enabled = enable; -} - -static void aux_enable(int enable) -{ - if (!aux_chan_enabled && enable) - CPRINTS("AUX enabled"); - else if (aux_chan_enabled && !enable) - CPRINTS("AUX disabled"); - - aux_chan_enabled = enable; -} - -static uint8_t read_ctl_ram(uint8_t addr) -{ - if (addr < ARRAY_SIZE(controller_ram)) - return controller_ram[addr]; - else - return 0; -} - -/** - * Manipulate the controller_ram[]. - * - * Some bits change may trigger internal state change. - */ -static void update_ctl_ram(uint8_t addr, uint8_t data) -{ - uint8_t orig; - - if (addr >= ARRAY_SIZE(controller_ram)) - return; - - orig = controller_ram[addr]; - controller_ram[addr] = data; - CPRINTS5("KB set CTR_RAM(0x%02x)=0x%02x (old:0x%02x)", - addr, data, orig); - - if (addr == 0x00) { - /* Keyboard enable/disable */ - - /* Enable IRQ before enable keyboard (queue chars to host) */ - if (!(orig & I8042_ENIRQ1) && (data & I8042_ENIRQ1)) - keyboard_enable_irq(1); - if (!(orig & I8042_ENIRQ12) && (data & I8042_ENIRQ12)) - aux_enable_irq(1); - - /* Handle the I8042_KBD_DIS bit */ - keyboard_enable(!(data & I8042_KBD_DIS)); - - /* Handle the I8042_AUX_DIS bit */ - aux_enable(!(data & I8042_AUX_DIS)); - - /* - * Disable IRQ after disable keyboard so that every char must - * have informed the host. - */ - if ((orig & I8042_ENIRQ1) && !(data & I8042_ENIRQ1)) - keyboard_enable_irq(0); - if ((orig & I8042_ENIRQ12) && !(data & I8042_ENIRQ12)) - aux_enable_irq(0); - } -} - -/** - * Handle the port 0x60 writes from host. - * - * Returns 1 if the event was handled. - */ -static int handle_mouse_data(uint8_t data, uint8_t *output, int *count) -{ - int out_len = 0; - - switch (data_port_state) { - case STATE_ECHO_MOUSE: - CPRINTS5("STATE_ECHO_MOUSE: 0x%02x", data); - output[out_len++] = data; - data_port_state = STATE_NORMAL; - break; - - case STATE_SEND_TO_MOUSE: - CPRINTS5("STATE_SEND_TO_MOUSE: 0x%02x", data); - send_aux_data_to_device(data); - data_port_state = STATE_NORMAL; - break; - - default: /* STATE_NORMAL */ - return 0; - } - - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - - *count = out_len; - - return 1; -} - -/** - * Handle the port 0x60 writes from host. - * - * This functions returns the number of bytes stored in *output buffer. - */ -static int handle_keyboard_data(uint8_t data, uint8_t *output) -{ - int out_len = 0; - int save_for_resend = 1; - int i; - - switch (data_port_state) { - case STATE_SCANCODE: - CPRINTS5("KB eaten by STATE_SCANCODE: 0x%02x", data); - if (data == SCANCODE_GET_SET) { - output[out_len++] = I8042_RET_ACK; - output[out_len++] = scancode_set; - } else { - scancode_set = data; - CPRINTS("KB scancode set to %d", scancode_set); - output[out_len++] = I8042_RET_ACK; - } - data_port_state = STATE_NORMAL; - break; - - case STATE_SETLEDS: - CPRINTS5("KB eaten by STATE_SETLEDS"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - case STATE_EX_SETLEDS_1: - CPRINTS5("KB eaten by STATE_EX_SETLEDS_1"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_EX_SETLEDS_2; - break; - - case STATE_EX_SETLEDS_2: - CPRINTS5("KB eaten by STATE_EX_SETLEDS_2"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - case STATE_WRITE_CMD_BYTE: - CPRINTS5("KB eaten by STATE_WRITE_CMD_BYTE: 0x%02x", - data); - update_ctl_ram(controller_ram_address, data); - data_port_state = STATE_NORMAL; - break; - - case STATE_WRITE_OUTPUT_PORT: - CPRINTS5("KB eaten by STATE_WRITE_OUTPUT_PORT: 0x%02x", - data); - A20_status = (data & BIT(1)) ? 1 : 0; - data_port_state = STATE_NORMAL; - break; - - case STATE_SETREP: - CPRINTS5("KB eaten by STATE_SETREP: 0x%02x", data); - set_typematic_delays(data); - - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - default: /* STATE_NORMAL */ - switch (data) { - case I8042_CMD_GSCANSET: /* also I8042_CMD_SSCANSET */ - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SCANCODE; - break; - - case I8042_CMD_SETLEDS: - /* Chrome OS doesn't have keyboard LEDs, so ignore */ - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SETLEDS; - break; - - case I8042_CMD_EX_SETLEDS: - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_EX_SETLEDS_1; - break; - - case I8042_CMD_DIAG_ECHO: - output[out_len++] = I8042_RET_ACK; - output[out_len++] = I8042_CMD_DIAG_ECHO; - break; - - case I8042_CMD_GETID: /* fall-thru */ - case I8042_CMD_OK_GETID: - output[out_len++] = I8042_RET_ACK; - output[out_len++] = 0xab; /* Regular keyboards */ - output[out_len++] = 0x83; - break; - - case I8042_CMD_SETREP: - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SETREP; - break; - - case I8042_CMD_ENABLE: - output[out_len++] = I8042_RET_ACK; - keystroke_enable(1); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET_DIS: - output[out_len++] = I8042_RET_ACK; - keystroke_enable(0); - reset_rate_and_delay(); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET_DEF: - output[out_len++] = I8042_RET_ACK; - reset_rate_and_delay(); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET: - reset_rate_and_delay(); - keyboard_clear_buffer(); - output[out_len++] = I8042_RET_ACK; - break; - - case I8042_CMD_RESEND: - save_for_resend = 0; - for (i = 0; i < resend_command_len; ++i) - output[out_len++] = resend_command[i]; - break; - - case 0x60: /* fall-thru */ - case 0x45: - /* U-boot hack. Just ignore; don't reply. */ - break; - - case I8042_CMD_SETALL_MB: /* fall-thru */ - case I8042_CMD_SETALL_MBR: - case I8042_CMD_EX_ENABLE: - default: - output[out_len++] = I8042_RET_NAK; - CPRINTS("KB Unsupported i8042 data 0x%02x", - data); - break; - } - } - - /* For resend, keep output before leaving. */ - if (out_len && save_for_resend) { - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - for (i = 0; i < out_len; ++i) - resend_command[i] = output[i]; - resend_command_len = out_len; - } - - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - return out_len; -} - -/** - * Handle the port 0x64 writes from host. - * - * This functions returns the number of bytes stored in *output buffer. - * BUT those bytes will appear at port 0x60. - */ -static int handle_keyboard_command(uint8_t command, uint8_t *output) -{ - int out_len = 0; - - CPRINTS5("KB recv cmd: 0x%02x", command); - kblog_put('c', command); - - switch (command) { - case I8042_READ_CMD_BYTE: - /* - * Ensure that the keyboard buffer is cleared before adding - * command byte to it. Since the host is asking for command - * byte, sending it buffered key press data can confuse the - * host and result in it taking incorrect action. - */ - keyboard_clear_buffer(); - output[out_len++] = read_ctl_ram(0); - break; - - case I8042_WRITE_CMD_BYTE: - data_port_state = STATE_WRITE_CMD_BYTE; - controller_ram_address = command - 0x60; - break; - - case I8042_DIS_KB: - update_ctl_ram(0, read_ctl_ram(0) | I8042_KBD_DIS); - reset_rate_and_delay(); - typematic_len = 0; /* stop typematic */ - keyboard_clear_buffer(); - break; - - case I8042_ENA_KB: - update_ctl_ram(0, read_ctl_ram(0) & ~I8042_KBD_DIS); - keystroke_enable(1); - keyboard_clear_buffer(); - break; - - case I8042_READ_OUTPUT_PORT: - output[out_len++] = - (lpc_keyboard_input_pending() ? BIT(5) : 0) | - (lpc_keyboard_has_char() ? BIT(4) : 0) | - (A20_status ? BIT(1) : 0) | - 1; /* Main processor in normal mode */ - break; - - case I8042_WRITE_OUTPUT_PORT: - data_port_state = STATE_WRITE_OUTPUT_PORT; - break; - - case I8042_RESET_SELF_TEST: - output[out_len++] = 0x55; /* Self test success */ - break; - - case I8042_TEST_KB_PORT: - output[out_len++] = 0x00; - break; - - case I8042_DIS_MOUSE: - update_ctl_ram(0, read_ctl_ram(0) | I8042_AUX_DIS); - break; - - case I8042_ENA_MOUSE: - update_ctl_ram(0, read_ctl_ram(0) & ~I8042_AUX_DIS); - break; - - case I8042_TEST_MOUSE: - output[out_len++] = 0; /* No error detected */ - break; - - case I8042_ECHO_MOUSE: - data_port_state = STATE_ECHO_MOUSE; - break; - - case I8042_SEND_TO_MOUSE: - data_port_state = STATE_SEND_TO_MOUSE; - break; - - case I8042_SYSTEM_RESET: - chipset_reset(CHIPSET_RESET_KB_SYSRESET); - break; - - default: - if (command >= I8042_READ_CTL_RAM && - command <= I8042_READ_CTL_RAM_END) { - output[out_len++] = read_ctl_ram(command - 0x20); - } else if (command >= I8042_WRITE_CTL_RAM && - command <= I8042_WRITE_CTL_RAM_END) { - data_port_state = STATE_WRITE_CMD_BYTE; - controller_ram_address = command - 0x60; - } else if (command == I8042_DISABLE_A20) { - A20_status = 0; - } else if (command == I8042_ENABLE_A20) { - A20_status = 1; - } else if (command >= I8042_PULSE_START && - command <= I8042_PULSE_END) { - /* Pulse Output Bits, - * b0=0 to reset CPU, see I8042_SYSTEM_RESET above - * b1=0 to disable A20 line - */ - A20_status = command & BIT(1) ? 1 : 0; - } else { - CPRINTS("KB unsupported cmd: 0x%02x", command); - reset_rate_and_delay(); - keyboard_clear_buffer(); - output[out_len++] = I8042_RET_NAK; - data_port_state = STATE_NORMAL; - } - break; - } - - return out_len; -} - -static void i8042_handle_from_host(void) -{ - struct host_byte h; - int ret_len; - uint8_t output[MAX_SCAN_CODE_LEN]; - uint8_t chan = CHAN_KBD; - - while (queue_remove_unit(&from_host, &h)) { - if (h.type == HOST_COMMAND) { - ret_len = handle_keyboard_command(h.byte, output); - } else { - CPRINTS5("KB recv data: 0x%02x", h.byte); - kblog_put('d', h.byte); - - if (IS_ENABLED(CONFIG_8042_AUX) && - handle_mouse_data(h.byte, output, &ret_len)) - chan = CHAN_AUX; - else - ret_len = handle_keyboard_data(h.byte, output); - } - - i8042_send_to_host(ret_len, output, chan); - } -} - -void keyboard_protocol_task(void *u) -{ - int wait = -1; - int retries = 0; - - reset_rate_and_delay(); - - while (1) { - /* Wait for next host read/write */ - task_wait_event(wait); - - while (1) { - timestamp_t t = get_time(); - struct data_byte entry; - - /* Handle typematic */ - if (!typematic_len) { - /* Typematic disabled; wait for enable */ - wait = -1; - } else if (timestamp_expired(typematic_deadline, &t)) { - /* Ready for next typematic keystroke */ - if (keystroke_enabled) - i8042_send_to_host(typematic_len, - typematic_scan_code, - CHAN_KBD); - typematic_deadline.val = t.val + - typematic_inter_delay; - wait = typematic_inter_delay; - } else { - /* Wait for remaining interval */ - wait = typematic_deadline.val - t.val; - } - - /* Handle command/data write from host */ - i8042_handle_from_host(); - - /* Check if we have data to send to host */ - if (queue_is_empty(&to_host)) - break; - - /* Handle data waiting for host */ - if (lpc_keyboard_has_char()) { - /* If interrupts disabled, nothing we can do */ - if (!i8042_keyboard_irq_enabled && - !i8042_aux_irq_enabled) - break; - - /* Give the host a little longer to respond */ - if (++retries < KB_TO_HOST_RETRIES) - break; - - /* - * We keep getting data, but the host keeps - * ignoring us. Fine, we're done waiting. - * Hey, host, are you ever gonna get to this - * data? Send it another interrupt in case it - * somehow missed the first one. - */ - CPRINTS("KB extra IRQ"); - lpc_keyboard_resume_irq(); - retries = 0; - break; - } - - /* Get a char from buffer. */ - kblog_put('k', to_host.state->head); - queue_remove_unit(&to_host, &entry); - - /* Write to host. */ - if (entry.chan == CHAN_AUX && - IS_ENABLED(CONFIG_8042_AUX)) { - kblog_put('A', entry.byte); - lpc_aux_put_char(entry.byte, - i8042_aux_irq_enabled); - } else { - kblog_put('K', entry.byte); - lpc_keyboard_put_char( - entry.byte, i8042_keyboard_irq_enabled); - } - retries = 0; - } - } -} - -static void send_aux_data_to_host_deferred(void) -{ - uint8_t data; - - if (IS_ENABLED(CONFIG_DEVICE_EVENT) && - chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - device_set_single_event(EC_DEVICE_EVENT_TRACKPAD); - - while (!queue_is_empty(&aux_to_host_queue)) { - queue_remove_unit(&aux_to_host_queue, &data); - if (aux_chan_enabled && IS_ENABLED(CONFIG_8042_AUX)) - i8042_send_to_host(1, &data, CHAN_AUX); - else - CPRINTS("AUX Callback ignored"); - } -} -DECLARE_DEFERRED(send_aux_data_to_host_deferred); - -/** - * Send aux data to host from interrupt context. - * - * @param data Aux response to send to host. - */ -void send_aux_data_to_host_interrupt(uint8_t data) -{ - queue_add_unit(&aux_to_host_queue, &data); - hook_call_deferred(&send_aux_data_to_host_deferred_data, 0); -} - -/** - * Handle button changing state. - * - * @param button Type of button that changed - * @param is_pressed Whether the button was pressed or released - */ -test_mockable void keyboard_update_button(enum keyboard_button_type button, - int is_pressed) -{ - uint8_t scan_code[MAX_SCAN_CODE_LEN]; - uint32_t len; - struct button_8042_t button_8042; - enum scancode_set_list code_set; - - /* - * Only send the scan code if main chipset is fully awake and - * keystrokes are enabled. - */ - if (!chipset_in_state(CHIPSET_STATE_ON) || !keystroke_enabled) - return; - - code_set = acting_code_set(scancode_set); - if (!is_supported_code_set(code_set)) - return; - - button_8042 = buttons_8042[button]; - scancode_bytes(button_8042.scancode, is_pressed, code_set, scan_code, - &len); - ASSERT(len > 0); - - if (button_8042.repeat) { - if (is_pressed) - set_typematic_key(scan_code, len); - else - clear_typematic_key(); - } - - if (keystroke_enabled) { - i8042_send_to_host(len, scan_code, CHAN_KBD); - task_wake(TASK_ID_KEYPROTO); - } -} - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_KEYBOARD -static int command_typematic(int argc, char **argv) -{ - int i; - - if (argc == 3) { - typematic_first_delay = strtoi(argv[1], NULL, 0) * MSEC; - typematic_inter_delay = strtoi(argv[2], NULL, 0) * MSEC; - } - - ccprintf("From host: 0x%02x\n", typematic_value_from_host); - ccprintf("First delay: %3d ms\n", typematic_first_delay / 1000); - ccprintf("Inter delay: %3d ms\n", typematic_inter_delay / 1000); - ccprintf("Now: %.6" PRId64 "\n", get_time().val); - ccprintf("Deadline: %.6" PRId64 "\n", typematic_deadline.val); - - ccputs("Repeat scan code: {"); - for (i = 0; i < typematic_len; ++i) - ccprintf("0x%02x, ", typematic_scan_code[i]); - ccputs("}\n"); - return EC_SUCCESS; -} - -static int command_codeset(int argc, char **argv) -{ - if (argc == 2) { - int set = strtoi(argv[1], NULL, 0); - switch (set) { - case SCANCODE_SET_1: /* fall-thru */ - case SCANCODE_SET_2: /* fall-thru */ - scancode_set = set; - break; - default: - return EC_ERROR_PARAM1; - } - } - - ccprintf("Set: %d\n", scancode_set); - ccprintf("I8042_XLATE: %d\n", controller_ram[0] & I8042_XLATE ? 1 : 0); - return EC_SUCCESS; -} - -static int command_controller_ram(int argc, char **argv) -{ - int index; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - index = strtoi(argv[1], NULL, 0); - if (index >= ARRAY_SIZE(controller_ram)) - return EC_ERROR_PARAM1; - - if (argc >= 3) - update_ctl_ram(index, strtoi(argv[2], NULL, 0)); - - ccprintf("%d = 0x%02x\n", index, controller_ram[index]); - return EC_SUCCESS; -} - -static int command_keyboard_log(int argc, char **argv) -{ - int i; - - /* If no args, print log */ - if (argc == 1) { - ccprintf("KBC log (len=%d):\n", kblog_len); - for (i = 0; kblog_buf && i < kblog_len; ++i) { - ccprintf("%c.%02x ", - kblog_buf[i].type, kblog_buf[i].byte); - if ((i & 15) == 15) { - ccputs("\n"); - cflush(); - } - } - ccputs("\n"); - return EC_SUCCESS; - } - - /* Otherwise, enable/disable */ - if (!parse_bool(argv[1], &i)) - return EC_ERROR_PARAM1; - - if (i) { - if (!kblog_buf) { - int rv = SHARED_MEM_ACQUIRE_CHECK( - sizeof(*kblog_buf) * MAX_KBLOG, - (char **)&kblog_buf); - if (rv != EC_SUCCESS) - kblog_buf = NULL; - kblog_len = 0; - return rv; - } - } else { - kblog_len = 0; - if (kblog_buf) - shared_mem_release(kblog_buf); - kblog_buf = NULL; - } - - return EC_SUCCESS; -} - -static int command_keyboard(int argc, char **argv) -{ - int ena; - - if (argc > 1) { - if (!parse_bool(argv[1], &ena)) - return EC_ERROR_PARAM1; - - keyboard_enable(ena); - } - - ccprintf("Enabled: %d\n", keyboard_enabled); - return EC_SUCCESS; -} - -static int command_8042_internal(int argc, char **argv) -{ - int i; - - ccprintf("data_port_state=%d\n", data_port_state); - ccprintf("i8042_keyboard_irq_enabled=%d\n", i8042_keyboard_irq_enabled); - ccprintf("i8042_aux_irq_enabled=%d\n", i8042_aux_irq_enabled); - ccprintf("keyboard_enabled=%d\n", keyboard_enabled); - ccprintf("keystroke_enabled=%d\n", keystroke_enabled); - ccprintf("aux_chan_enabled=%d\n", aux_chan_enabled); - - ccprintf("resend_command[]={"); - for (i = 0; i < resend_command_len; i++) - ccprintf("0x%02x, ", resend_command[i]); - ccprintf("}\n"); - - ccprintf("controller_ram_address=0x%02x\n", controller_ram_address); - ccprintf("A20_status=%d\n", A20_status); - - ccprintf("from_host[]={"); - for (i = 0; i < queue_count(&from_host); ++i) { - struct host_byte entry; - - queue_peek_units(&from_host, &entry, i, 1); - - ccprintf("0x%02x, 0x%02x, ", entry.type, entry.byte); - } - ccprintf("}\n"); - - ccprintf("to_host[]={"); - for (i = 0; i < queue_count(&to_host); ++i) { - struct data_byte entry; - - queue_peek_units(&to_host, &entry, i, 1); - - ccprintf("0x%02x%s, ", entry.byte, - entry.chan == CHAN_AUX ? " aux" : ""); - } - ccprintf("}\n"); - - return EC_SUCCESS; -} - -/* Zephyr only provides these as subcommands*/ -#ifndef CONFIG_ZEPHYR -DECLARE_CONSOLE_COMMAND(typematic, command_typematic, - "[first] [inter]", - "Get/set typematic delays"); -DECLARE_CONSOLE_COMMAND(codeset, command_codeset, - "[set]", - "Get/set keyboard codeset"); -DECLARE_CONSOLE_COMMAND(ctrlram, command_controller_ram, - "index [value]", - "Get/set keyboard controller RAM"); -DECLARE_CONSOLE_COMMAND(kblog, command_keyboard_log, - "[on | off]", - "Print or toggle keyboard event log"); -DECLARE_CONSOLE_COMMAND(kbd, command_keyboard, - "[on | off]", - "Print or toggle keyboard info"); -#endif - -static int command_8042(int argc, char **argv) -{ - if (argc >= 2) { - if (!strcasecmp(argv[1], "internal")) - return command_8042_internal(argc, argv); - else if (!strcasecmp(argv[1], "typematic")) - return command_typematic(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "codeset")) - return command_codeset(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "ctrlram")) - return command_controller_ram(argc - 1, argv + 1); - else if (CMD_KEYBOARD_LOG && !strcasecmp(argv[1], "kblog")) - return command_keyboard_log(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "kbd")) - return command_keyboard(argc - 1, argv + 1); - else - return EC_ERROR_PARAM1; - } else { - char *ctlram_argv[] = {"ctrlram", "0"}; - - ccprintf("\n- Typematic:\n"); - command_typematic(argc, argv); - ccprintf("\n- Codeset:\n"); - command_codeset(argc, argv); - ccprintf("\n- Control RAM:\n"); - command_controller_ram( - sizeof(ctlram_argv) / sizeof(ctlram_argv[0]), - ctlram_argv); - if (CMD_KEYBOARD_LOG) { - ccprintf("\n- Keyboard log:\n"); - command_keyboard_log(argc, argv); - } - ccprintf("\n- Keyboard:\n"); - command_keyboard(argc, argv); - ccprintf("\n- Internal:\n"); - command_8042_internal(argc, argv); - ccprintf("\n"); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(8042, command_8042, - "[internal | typematic | codeset | ctrlram |" - " kblog | kbd]", - "Print 8042 state in one place"); -#endif - - -/*****************************************************************************/ -/* Hooks */ - -/** - * Preserve the states of keyboard controller to keep the initialized states - * between reboot_ec commands. Saving info include: - * - * - code set - * - controller_ram[0]: - * - XLATE - * - KB/TP disabled - * - KB/TP IRQ enabled - */ -static void keyboard_preserve_state(void) -{ - struct kb_state state; - - state.codeset = scancode_set; - state.ctlram = controller_ram[0]; - state.keystroke_enabled = keystroke_enabled; - - system_add_jump_tag(KB_SYSJUMP_TAG, KB_HOOK_VERSION, - sizeof(state), &state); -} -DECLARE_HOOK(HOOK_SYSJUMP, keyboard_preserve_state, HOOK_PRIO_DEFAULT); - -/** - * Restore the keyboard states after reboot_ec command. See above function. - */ -static void keyboard_restore_state(void) -{ - const struct kb_state *prev; - int version, size; - - prev = (const struct kb_state *)system_get_jump_tag(KB_SYSJUMP_TAG, - &version, &size); - if (prev && version == KB_HOOK_VERSION && size == sizeof(*prev)) { - /* Coming back from a sysjump, so restore settings. */ - scancode_set = prev->codeset; - update_ctl_ram(0, prev->ctlram); - keystroke_enabled = prev->keystroke_enabled; - } -} -DECLARE_HOOK(HOOK_INIT, keyboard_restore_state, HOOK_PRIO_DEFAULT); - -#if defined(CONFIG_POWER_BUTTON) && !defined(CONFIG_MKBP_INPUT_DEVICES) -/** - * Handle power button changing state. - */ -static void keyboard_power_button(void) -{ - keyboard_update_button(KEYBOARD_BUTTON_POWER, - power_button_is_pressed()); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, keyboard_power_button, - HOOK_PRIO_DEFAULT); - -#endif /* CONFIG_POWER_BUTTON && !CONFIG_MKBP_INPUT_DEVICES */ - diff --git a/common/keyboard_8042_sharedlib.c b/common/keyboard_8042_sharedlib.c deleted file mode 100644 index 1d024d3f47..0000000000 --- a/common/keyboard_8042_sharedlib.c +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright 2015 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. - * - * Objects which can be shared between RO and RW for 8042 keyboard protocol. - */ - -#include "button.h" -#include "keyboard_8042_sharedlib.h" -#include "keyboard_config.h" -#include "keyboard_protocol.h" -#include "libsharedobjs.h" -#include "util.h" - -#ifndef CONFIG_KEYBOARD_CUSTOMIZATION -/* The standard Chrome OS keyboard matrix table in scan code set 2. */ -static uint16_t scancode_set2[KEYBOARD_COLS_MAX][KEYBOARD_ROWS] = { - {0x0000, 0x0000, 0x0014, 0xe01f, 0xe014, 0xe007, 0x0000, 0x0000}, - {0xe01f, 0x0076, 0x000d, 0x000e, 0x001c, 0x001a, 0x0016, 0x0015}, - {0x0005, 0x000c, 0x0004, 0x0006, 0x0023, 0x0021, 0x0026, 0x0024}, - {0x0032, 0x0034, 0x002c, 0x002e, 0x002b, 0x002a, 0x0025, 0x002d}, - {0x0009, 0x0083, 0x000b, 0x0003, 0x001b, 0x0022, 0x001e, 0x001d}, - {0x0051, 0x0000, 0x005b, 0x0000, 0x0042, 0x0041, 0x003e, 0x0043}, - {0x0031, 0x0033, 0x0035, 0x0036, 0x003b, 0x003a, 0x003d, 0x003c}, - {0x0000, 0x0000, 0x0061, 0x0000, 0x0000, 0x0012, 0x0000, 0x0059}, - {0x0055, 0x0052, 0x0054, 0x004e, 0x004c, 0x004a, 0x0045, 0x004d}, - {0x0000, 0x0001, 0x000a, 0x002f, 0x004b, 0x0049, 0x0046, 0x0044}, - {0xe011, 0x0000, 0x006a, 0x0000, 0x005d, 0x0000, 0x0011, 0x0000}, -#ifndef CONFIG_KEYBOARD_KEYPAD - {0x0000, 0x0066, 0x0000, 0x005d, 0x005a, 0x0029, 0xe072, 0xe075}, - {0x0000, 0x0064, 0x0000, 0x0067, 0x0000, 0x0000, 0xe074, 0xe06b}, -#else - {0x0000, 0x0066, 0xe071, 0x005d, 0x005a, 0x0029, 0xe072, 0xe075}, - {0xe06c, 0x0064, 0xe07d, 0x0067, 0xe069, 0xe07a, 0xe074, 0xe06b}, - {0xe04a, 0x007c, 0x007b, 0x0074, 0x0071, 0x0073, 0x006b, 0x0070}, - {0x006c, 0x0075, 0x007d, 0x0079, 0x007a, 0x0072, 0x0069, 0xe05a}, -#endif -}; - -uint16_t get_scancode_set2(uint8_t row, uint8_t col) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) - return scancode_set2[col][row]; - return 0; -} - -void set_scancode_set2(uint8_t row, uint8_t col, uint16_t val) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) - scancode_set2[col][row] = val; -} - -#endif /* CONFIG_KEYBOARD_CUSTOMIZATION */ - -/* - * The translation table from scan code set 2 to set 1. - * Ref: http://kbd-project.org/docs/scancodes/scancodes-10.html#ss10.3 - * To reduce space, we only keep the translation for 0~127, - * so a real translation need to do 0x83=>0x41 explicitly ( - * see scancode_translate_set2_to_1 below). - */ -SHAREDLIB(const uint8_t scancode_translate_table[128] = { - 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, - 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, - 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, - 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b, - 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, - 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d, - 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, - 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, - 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, - 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, - 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, - 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, - 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, - 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, - 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, - 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, -}); - - -#ifdef CONFIG_KEYBOARD_DEBUG -SHAREDLIB(const -static char * const keycap_long_label[KLLI_MAX & KEYCAP_LONG_LABEL_INDEX_BITMASK] = { - "UNKNOWN", "F1", "F2", "F3", - "F4", "F5", "F6", "F7", - "F8", "F9", "F10", "F11", - "F12", "F13", "F14", "F15", - "L-ALT", "R-ALT", "L-CTR", "R-CTR", - "L-SHT", "R-SHT", "ENTER", "SPACE", - "B-SPC", "TAB", "SEARC", "LEFT", - "RIGHT", "DOWN", "UP", "ESC", -}); - -const char *get_keycap_long_label(uint8_t idx) -{ - if (idx < ARRAY_SIZE(keycap_long_label)) - return keycap_long_label[idx]; - return "UNKNOWN"; -} - -#ifndef CONFIG_KEYBOARD_CUSTOMIZATION -static char keycap_label[KEYBOARD_COLS_MAX][KEYBOARD_ROWS] = { - {KLLI_UNKNO, KLLI_UNKNO, KLLI_L_CTR, KLLI_SEARC, - KLLI_R_CTR, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO}, - {KLLI_F11, KLLI_ESC, KLLI_TAB, '~', - 'a', 'z', '1', 'q'}, - {KLLI_F1, KLLI_F4, KLLI_F3, KLLI_F2, - 'd', 'c', '3', 'e'}, - {'b', 'g', 't', '5', - 'f', 'v', '4', 'r'}, - {KLLI_F10, KLLI_F7, KLLI_F6, KLLI_F5, - 's', 'x', '2', 'w'}, - {KLLI_UNKNO, KLLI_F12, ']', KLLI_F13, - 'k', ',', '8', 'i'}, - {'n', 'h', 'y', '6', - 'j', 'm', '7', 'u'}, - {KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, - KLLI_UNKNO, KLLI_L_SHT, KLLI_UNKNO, KLLI_R_SHT}, - {'=', '\'', '[', '-', - ';', '/', '0', 'p'}, - {KLLI_F14, KLLI_F9, KLLI_F8, KLLI_UNKNO, - '|', '.', '9', 'o'}, - {KLLI_R_ALT, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, - KLLI_UNKNO, KLLI_UNKNO, KLLI_L_ALT, KLLI_UNKNO}, - {KLLI_F15, KLLI_B_SPC, KLLI_UNKNO, '\\', - KLLI_ENTER, KLLI_SPACE, KLLI_DOWN, KLLI_UP}, - {KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, - KLLI_UNKNO, KLLI_UNKNO, KLLI_RIGHT, KLLI_LEFT}, -#ifdef CONFIG_KEYBOARD_KEYPAD - /* TODO: Populate these */ - {KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, - KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO}, - {KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, - KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO, KLLI_UNKNO}, -#endif -}; - -char get_keycap_label(uint8_t row, uint8_t col) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) - return keycap_label[col][row]; - return KLLI_UNKNO; -} - -void set_keycap_label(uint8_t row, uint8_t col, char val) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) - keycap_label[col][row] = val; -} -#endif /* CONFIG_KEYBOARD_CUSTOMIZATION */ -#endif /* CONFIG_KEYBOARD_DEBUG */ - -uint8_t scancode_translate_set2_to_1(uint8_t code) -{ - if (code & 0x80) { - if (code == 0x83) - return 0x41; - return code; - } - return scancode_translate_table[code]; -} - -/* - * Button scan codes. - * Must be in the same order as defined in keyboard_button_type. - */ -SHAREDLIB(const struct button_8042_t buttons_8042[] = { - {SCANCODE_POWER, 0}, - {SCANCODE_VOLUME_DOWN, 1}, - {SCANCODE_VOLUME_UP, 1}, - {SCANCODE_1, 1}, - {SCANCODE_2, 1}, - {SCANCODE_3, 1}, - {SCANCODE_4, 1}, - {SCANCODE_5, 1}, - {SCANCODE_6, 1}, - {SCANCODE_7, 1}, - {SCANCODE_8, 1}, -}); -BUILD_ASSERT(ARRAY_SIZE(buttons_8042) == KEYBOARD_BUTTON_COUNT); diff --git a/common/keyboard_backlight.c b/common/keyboard_backlight.c deleted file mode 100644 index 82312e0776..0000000000 --- a/common/keyboard_backlight.c +++ /dev/null @@ -1,161 +0,0 @@ -/* 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. - */ - -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_backlight.h" -#include "lid_switch.h" -#include "timer.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -static struct kblight_conf kblight; -static int current_percent; - -void __attribute__((weak)) board_kblight_init(void) -{ } - -static int kblight_init(void) -{ - if (!kblight.drv || !kblight.drv->init) - return EC_ERROR_UNIMPLEMENTED; - return kblight.drv->init(); -} - -static void kblight_set_deferred(void) -{ - if (!kblight.drv || !kblight.drv->set) - return; - kblight.drv->set(current_percent); -} -DECLARE_DEFERRED(kblight_set_deferred); - -/* - * APIs - */ -int kblight_set(int percent) -{ - if (percent < 0 || 100 < percent) - return EC_ERROR_INVAL; - current_percent = percent; - /* Need to defer i2c in case it's called from an interrupt handler. */ - hook_call_deferred(&kblight_set_deferred_data, 0); - return EC_SUCCESS; -} - -int kblight_get(void) -{ - return current_percent; -} - -int kblight_enable(int enable) -{ -#ifdef GPIO_EN_KEYBOARD_BACKLIGHT - gpio_set_level(GPIO_EN_KEYBOARD_BACKLIGHT, enable); -#endif - if (!kblight.drv || !kblight.drv->enable) - return -1; - return kblight.drv->enable(enable); -} - -int kblight_register(const struct kblight_drv *drv) -{ - kblight.drv = drv; - CPRINTS("kblight registered"); - return EC_SUCCESS; -} - -/* - * Hooks - */ -static void keyboard_backlight_init(void) -{ - /* Uses PWM by default. Can be customized by board_kblight_init */ -#ifdef CONFIG_PWM_KBLIGHT - kblight_register(&kblight_pwm); -#endif - board_kblight_init(); - if (kblight_init()) - CPRINTS("kblight init failed"); - /* Don't leave kblight enable state undetermined */ - kblight_enable(0); -} -DECLARE_HOOK(HOOK_INIT, keyboard_backlight_init, HOOK_PRIO_DEFAULT); - -static void kblight_suspend(void) -{ - kblight_enable(0); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, kblight_suspend, HOOK_PRIO_DEFAULT); - -static void kblight_resume(void) -{ - if (lid_is_open() && current_percent) { - kblight_enable(1); - kblight_set(current_percent); - } -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, kblight_resume, HOOK_PRIO_DEFAULT); - -static void kblight_lid_change(void) -{ - kblight_enable(lid_is_open() && current_percent); -} -DECLARE_HOOK(HOOK_LID_CHANGE, kblight_lid_change, HOOK_PRIO_DEFAULT); - -/* - * Console and host commands - */ -static int cc_kblight(int argc, char **argv) -{ - if (argc >= 2) { - char *e; - int i = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - if (kblight_set(i)) - return EC_ERROR_PARAM1; - if (kblight_enable(i > 0)) - return EC_ERROR_PARAM1; - } - ccprintf("Keyboard backlight: %d%%\n", kblight_get()); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(kblight, cc_kblight, - "percent", - "Get/set keyboard backlight"); - -enum ec_status hc_get_keyboard_backlight(struct host_cmd_handler_args *args) -{ - struct ec_response_pwm_get_keyboard_backlight *r = args->response; - - r->percent = kblight_get(); - r->enabled = 1; /* Deprecated */ - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT, - hc_get_keyboard_backlight, - EC_VER_MASK(0)); - -enum ec_status hc_set_keyboard_backlight(struct host_cmd_handler_args *args) -{ - const struct ec_params_pwm_set_keyboard_backlight *p = args->params; - - if (kblight_set(p->percent)) - return EC_RES_ERROR; - if (kblight_enable(p->percent > 0)) - return EC_RES_ERROR; - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT, - hc_set_keyboard_backlight, - EC_VER_MASK(0)); diff --git a/common/keyboard_mkbp.c b/common/keyboard_mkbp.c deleted file mode 100644 index d8e9f8d909..0000000000 --- a/common/keyboard_mkbp.c +++ /dev/null @@ -1,224 +0,0 @@ -/* Copyright 2013 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. - * - * MKBP keyboard protocol - */ - -#include "chipset.h" -#include "common.h" -#include "host_command.h" -#include "keyboard_config.h" -#include "keyboard_mkbp.h" -#include "keyboard_protocol.h" -#include "keyboard_raw.h" -#include "keyboard_scan.h" -#include "keyboard_test.h" -#include "mkbp_event.h" -#include "mkbp_fifo.h" -#include "task.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -/* Changes to col,row here need to also be reflected in kernel. - * drivers/input/mkbp.c ... see KEY_BATTERY. - */ -#define BATTERY_KEY_COL 0 -#define BATTERY_KEY_ROW 7 -#define BATTERY_KEY_ROW_MASK BIT(BATTERY_KEY_ROW) - -#ifndef HAS_TASK_KEYSCAN -#error "Task KEYSCAN has to be enabled for MKBP keyboard" -#endif /* !defined(HAS_TASK_KEYSCAN) */ - -/* Config for mkbp protocol; does not include fields from scan config */ -struct ec_mkbp_protocol_config { - uint32_t valid_mask; /* valid fields */ - uint8_t flags; /* some flags (enum mkbp_config_flags) */ - uint8_t valid_flags; /* which flags are valid */ - - /* maximum depth to allow for fifo (0 = no keyscan output) */ - uint8_t fifo_max_depth; -} __packed; - -static struct ec_mkbp_protocol_config config = { - .valid_mask = EC_MKBP_VALID_SCAN_PERIOD | EC_MKBP_VALID_POLL_TIMEOUT | - EC_MKBP_VALID_MIN_POST_SCAN_DELAY | - EC_MKBP_VALID_OUTPUT_SETTLE | EC_MKBP_VALID_DEBOUNCE_DOWN | - EC_MKBP_VALID_DEBOUNCE_UP | EC_MKBP_VALID_FIFO_MAX_DEPTH, - .valid_flags = EC_MKBP_FLAGS_ENABLE, - .flags = EC_MKBP_FLAGS_ENABLE, - .fifo_max_depth = FIFO_DEPTH, -}; - -/*****************************************************************************/ -/* Interface */ - -void keyboard_clear_buffer(void) -{ - mkbp_fifo_clear_keyboard(); -} - -test_mockable int mkbp_keyboard_add(const uint8_t *buffp) -{ - /* - * If the keyboard protocol is not enabled, don't save the state to - * the FIFO or trigger an interrupt. - */ - if (!(config.flags & EC_MKBP_FLAGS_ENABLE)) - return EC_SUCCESS; - - return mkbp_fifo_add((uint8_t)EC_MKBP_EVENT_KEY_MATRIX, buffp); -} - -static int keyboard_get_next_event(uint8_t *out) -{ - return mkbp_fifo_get_next_event(out, EC_MKBP_EVENT_KEY_MATRIX); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_KEY_MATRIX, keyboard_get_next_event); - -void keyboard_send_battery_key(void) -{ - uint8_t state[KEYBOARD_COLS_MAX]; - - /* Copy debounced state and add battery pseudo-key */ - memcpy(state, keyboard_scan_get_state(), sizeof(state)); - state[BATTERY_KEY_COL] ^= BATTERY_KEY_ROW_MASK; - - /* Add to FIFO only if AP is on or else it will wake from suspend */ - if (chipset_in_state(CHIPSET_STATE_ON)) - mkbp_keyboard_add(state); -} - -void clear_typematic_key(void) -{ } - -static void set_keyscan_config(const struct ec_mkbp_config *src, - struct ec_mkbp_protocol_config *dst, - uint32_t valid_mask, uint8_t new_flags) -{ - struct keyboard_scan_config *ksc = keyboard_scan_get_config(); - - if (valid_mask & EC_MKBP_VALID_SCAN_PERIOD) - ksc->scan_period_us = src->scan_period_us; - - if (valid_mask & EC_MKBP_VALID_POLL_TIMEOUT) - ksc->poll_timeout_us = src->poll_timeout_us; - - if (valid_mask & EC_MKBP_VALID_MIN_POST_SCAN_DELAY) { - /* - * Key scanning is high priority, so we should require at - * least 100us min delay here. Setting this to 0 will cause - * watchdog events. Use 200 to be safe. - */ - ksc->min_post_scan_delay_us = - MAX(src->min_post_scan_delay_us, 200); - } - - if (valid_mask & EC_MKBP_VALID_OUTPUT_SETTLE) - ksc->output_settle_us = src->output_settle_us; - - if (valid_mask & EC_MKBP_VALID_DEBOUNCE_DOWN) - ksc->debounce_down_us = src->debounce_down_us; - - if (valid_mask & EC_MKBP_VALID_DEBOUNCE_UP) - ksc->debounce_up_us = src->debounce_up_us; - - /* - * If we just enabled key scanning, kick the task so that it will - * fall out of the task_wait_event() in keyboard_scan_task(). - */ - if ((new_flags & EC_MKBP_FLAGS_ENABLE) && - !(dst->flags & EC_MKBP_FLAGS_ENABLE)) - task_wake(TASK_ID_KEYSCAN); -} - -static void get_keyscan_config(struct ec_mkbp_config *dst) -{ - const struct keyboard_scan_config *ksc = keyboard_scan_get_config(); - - /* Copy fields from keyscan config to mkbp config */ - dst->output_settle_us = ksc->output_settle_us; - dst->debounce_down_us = ksc->debounce_down_us; - dst->debounce_up_us = ksc->debounce_up_us; - dst->scan_period_us = ksc->scan_period_us; - dst->min_post_scan_delay_us = ksc->min_post_scan_delay_us; - dst->poll_timeout_us = ksc->poll_timeout_us; -} - -/** - * Copy keyscan configuration from one place to another according to flags - * - * This is like a structure copy, except that only selected fields are - * copied. - * - * @param src Source config - * @param dst Destination config - * @param valid_mask Bits representing which fields to copy - each bit is - * from enum mkbp_config_valid - * @param valid_flags Bit mask controlling flags to copy. Any 1 bit means - * that the corresponding bit in src->flags is copied - * over to dst->flags - */ -static void keyscan_copy_config(const struct ec_mkbp_config *src, - struct ec_mkbp_protocol_config *dst, - uint32_t valid_mask, uint8_t valid_flags) -{ - uint8_t new_flags; - - if (valid_mask & EC_MKBP_VALID_FIFO_MAX_DEPTH) { - /* Validity check for fifo depth */ - dst->fifo_max_depth = MIN(src->fifo_max_depth, - FIFO_DEPTH); - } - - new_flags = dst->flags & ~valid_flags; - new_flags |= src->flags & valid_flags; - - set_keyscan_config(src, dst, valid_mask, new_flags); - dst->flags = new_flags; -} - -static enum ec_status -host_command_mkbp_set_config(struct host_cmd_handler_args *args) -{ - const struct ec_params_mkbp_set_config *req = args->params; - - keyscan_copy_config(&req->config, &config, - config.valid_mask & req->config.valid_mask, - config.valid_flags & req->config.valid_flags); - - mkbp_fifo_depth_update(config.fifo_max_depth); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_MKBP_SET_CONFIG, - host_command_mkbp_set_config, - EC_VER_MASK(0)); - -static enum ec_status -host_command_mkbp_get_config(struct host_cmd_handler_args *args) -{ - struct ec_response_mkbp_get_config *resp = args->response; - struct ec_mkbp_config *dst = &resp->config; - - memcpy(&resp->config, &config, sizeof(config)); - - /* Copy fields from mkbp protocol config to mkbp config */ - dst->valid_mask = config.valid_mask; - dst->flags = config.flags; - dst->valid_flags = config.valid_flags; - dst->fifo_max_depth = config.fifo_max_depth; - - get_keyscan_config(dst); - - args->response_size = sizeof(*resp); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_MKBP_GET_CONFIG, - host_command_mkbp_get_config, - EC_VER_MASK(0)); diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c deleted file mode 100644 index 6584a55d84..0000000000 --- a/common/keyboard_scan.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Keyboard scanner module for Chrome EC */ - -#include "chipset.h" -#include "clock.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_config.h" -#include "keyboard_protocol.h" -#include "keyboard_raw.h" -#include "keyboard_scan.h" -#include "keyboard_test.h" -#include "lid_switch.h" -#include "switch.h" -#include "system.h" -#include "tablet_mode.h" -#include "task.h" -#include "timer.h" -#include "usb_api.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYSCAN, outstr) -#define CPRINTF(format, args...) cprintf(CC_KEYSCAN, format, ## args) -#define CPRINTS(format, args...) cprints(CC_KEYSCAN, format, ## args) - -#ifdef CONFIG_KEYBOARD_DEBUG -#define CPUTS5(outstr) cputs(CC_KEYSCAN, outstr) -#define CPRINTS5(format, args...) cprints(CC_KEYBOARD, format, ## args) -#else -#define CPUTS5(outstr) -#define CPRINTS5(format, args...) -#endif - -#define SCAN_TIME_COUNT 32 /* Number of last scan times to track */ - -/* If we're waiting for a scan to happen, we'll give it this long */ -#define SCAN_TASK_TIMEOUT_US (100 * MSEC) - -#ifndef CONFIG_KEYBOARD_POST_SCAN_CLOCKS -/* - * Default delay in clocks; this was experimentally determined to be long - * enough to avoid watchdog warnings or I2C errors on a typical notebook - * config on STM32. - */ -#define CONFIG_KEYBOARD_POST_SCAN_CLOCKS 16000 -#endif - -__overridable struct keyboard_scan_config keyscan_config = { -#ifdef CONFIG_KEYBOARD_COL2_INVERTED - /* - * CONFIG_KEYBOARD_COL2_INVERTED is defined for passing the column 2 - * to H1 which inverts the signal. The signal passing through H1 - * adds more delay. Need a larger delay value. Otherwise, pressing - * Refresh key will also trigger T key, which is in the next scanning - * column line. See http://b/156007029. - */ - .output_settle_us = 80, -#else - .output_settle_us = 50, -#endif /* CONFIG_KEYBOARD_COL2_INVERTED */ - .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 = { - 0x1c, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, - 0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* full set */ - }, -}; - -/* Boot key list. Must be in same order as enum boot_key. */ -struct boot_key_entry { - uint8_t mask_index; - uint8_t mask_value; -}; - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -static const struct boot_key_entry boot_key_list[] = { - {KEYBOARD_COL_ESC, KEYBOARD_MASK_ESC}, /* Esc */ - {KEYBOARD_COL_DOWN, KEYBOARD_MASK_DOWN}, /* Down-arrow */ - {KEYBOARD_COL_LEFT_SHIFT, KEYBOARD_MASK_LEFT_SHIFT}, /* Left-Shift */ -}; -static uint32_t boot_key_value = BOOT_KEY_NONE; -#endif - -uint8_t keyboard_cols = KEYBOARD_COLS_MAX; - -/* Debounced key matrix */ -static uint8_t __bss_slow debounced_state[KEYBOARD_COLS_MAX]; -/* Mask of keys being debounced */ -static uint8_t __bss_slow debouncing[KEYBOARD_COLS_MAX]; -/* Keys simulated-pressed */ -static uint8_t __bss_slow simulated_key[KEYBOARD_COLS_MAX]; -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -static uint8_t __bss_slow keyboard_id[KEYBOARD_IDS]; -#endif - -/* Times of last scans */ -static uint32_t __bss_slow scan_time[SCAN_TIME_COUNT]; -/* Current scan_time[] index */ -static int __bss_slow scan_time_index; - -/* Index into scan_time[] when each key started debouncing */ -static uint8_t __bss_slow scan_edge_index[KEYBOARD_COLS_MAX][KEYBOARD_ROWS]; - -/* Minimum delay between keyboard scans based on current clock frequency */ -static uint32_t __bss_slow post_scan_clock_us; - -/* - * Print all keyboard scan state changes? Off by default because it generates - * a lot of debug output, which makes the saved EC console data less useful. - */ -static int __bss_slow print_state_changes; - -/* Must init to 0 for scanning at boot */ -static volatile uint32_t __bss_slow disable_scanning_mask; - -/* Constantly incrementing counter of the number of times we polled */ -static volatile int kbd_polls; - -/* If true, we'll force a keyboard poll */ -static volatile int force_poll; - -static int keyboard_scan_is_enabled(void) -{ - /* NOTE: this is just an instantaneous glimpse of the variable. */ - return !disable_scanning_mask; -} - -void keyboard_scan_enable(int enable, enum kb_scan_disable_masks mask) -{ - /* Access atomically */ - if (enable) { - atomic_clear_bits((uint32_t *)&disable_scanning_mask, mask); - } else { - atomic_or((uint32_t *)&disable_scanning_mask, mask); - clear_typematic_key(); - } - - /* Let the task figure things out */ - task_wake(TASK_ID_KEYSCAN); -} - -/** - * Print the keyboard state. - * - * @param state State array to print - * @param msg Description of state - */ -static void print_state(const uint8_t *state, const char *msg) -{ - int c; - - CPRINTF("[%pT KB %s:", PRINTF_TIMESTAMP_NOW, msg); - for (c = 0; c < keyboard_cols; c++) { - if (state[c]) - CPRINTF(" %02x", state[c]); - else - CPUTS(" --"); - } - CPUTS("]\n"); -} - -/** - * Ensure that the keyboard has been scanned. - * - * Makes sure that we've fully gone through the keyboard scanning loop at - * least once. - */ -static void ensure_keyboard_scanned(int old_polls) -{ - uint64_t start_time; - - start_time = get_time().val; - - /* - * Ensure we see the poll task run. - * - * Note that the poll task is higher priority than ours so we know that - * while we're running it's not partway through a poll. That means that - * if kbd_polls changes we've gone through a whole cycle. - */ - while ((kbd_polls == old_polls) && - (get_time().val - start_time < SCAN_TASK_TIMEOUT_US)) - usleep(keyscan_config.scan_period_us); -} - -/** - * Simulate a keypress. - * - * @param row Row of key - * @param col Column of key - * @param pressed Non-zero if pressed, zero if released - */ -static void simulate_key(int row, int col, int pressed) -{ - int old_polls; - - if ((simulated_key[col] & BIT(row)) == ((pressed ? 1 : 0) << row)) - return; /* No change */ - - simulated_key[col] ^= BIT(row); - - /* Keep track of polls now that we've got keys simulated */ - old_polls = kbd_polls; - - print_state(simulated_key, "simulated "); - - /* Force a poll even though no keys are pressed */ - force_poll = 1; - - /* Wake the task to handle changes in simulated keys */ - task_wake(TASK_ID_KEYSCAN); - - /* - * Make sure that the keyboard task sees the key for long enough. - * That means it needs to have run and for enough time. - */ - ensure_keyboard_scanned(old_polls); - usleep(pressed ? - keyscan_config.debounce_down_us : keyscan_config.debounce_up_us); - ensure_keyboard_scanned(kbd_polls); -} - -/** - * Read the raw keyboard matrix state. - * - * Used in pre-init, so must not make task-switching-dependent calls; udelay() - * is ok because it's a spin-loop. - * - * @param state Destination for new state (must be KEYBOARD_COLS_MAX - * long). - * - * @return 1 if at least one key is pressed, else zero. - */ -static int read_matrix(uint8_t *state) -{ - int c; - int pressed = 0; - - /* 1. Read input pins */ - for (c = 0; c < keyboard_cols; c++) { - /* - * Skip if scanning becomes disabled. Clear the state - * to make sure we don't mix new and old states in the - * same array. - * - * Note, scanning is enabled on boot by default. - */ - if (!keyboard_scan_is_enabled()) { - state[c] = 0; - continue; - } - - /* Select column, then wait a bit for it to settle */ - keyboard_raw_drive_column(c); - udelay(keyscan_config.output_settle_us); - - /* Read the row state */ - state[c] = keyboard_raw_read_rows(); - - /* Use simulated keyscan sequence instead if testing active */ - if (IS_ENABLED(CONFIG_KEYBOARD_TEST)) - state[c] = keyscan_seq_get_scan(c, state[c]); - } - - /* 2. Detect transitional ghost */ - for (c = 0; c < keyboard_cols; c++) { - int c2; - - for (c2 = 0; c2 < c; c2++) { - /* - * If two columns shares at least one key but their - * states are different, maybe the state changed between - * two "keyboard_raw_read_rows"s. If this happened, - * update both columns to the union of them. - * - * Note that in theory we need to rescan from col 0 if - * anything is updated, to make sure the newly added - * bits does not introduce more inconsistency. - * Let's ignore this rare case for now. - */ - if ((state[c] & state[c2]) && (state[c] != state[c2])) { - uint8_t merged = state[c] | state[c2]; - - state[c] = state[c2] = merged; - } - } - } - - /* 3. Fix result */ - for (c = 0; c < keyboard_cols; c++) { - /* Add in simulated keypresses */ - state[c] |= simulated_key[c]; - - /* - * Keep track of what keys appear to be pressed. Even if they - * don't exist in the matrix, they'll keep triggering - * interrupts, so we can't leave scanning mode. - */ - pressed |= state[c]; - - /* Mask off keys that don't exist on the actual keyboard */ - state[c] &= keyscan_config.actual_key_mask[c]; - - } - - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - return pressed ? 1 : 0; -} - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -/** - * Read the raw keyboard IDs state. - * - * Used in pre-init, so must not make task-switching-dependent calls; udelay() - * is ok because it's a spin-loop. - * - * @param id Destination for keyboard id (must be KEYBOARD_IDS long). - * - */ -static void read_matrix_id(uint8_t *id) -{ - int c; - - for (c = 0; c < KEYBOARD_IDS; c++) { - /* Select the ID pin, then wait a bit for it to settle. - * Caveat: If a keyboard maker puts ID pins right after scan - * columns, we can't support variable column size with a single - * image. */ - keyboard_raw_drive_column(KEYBOARD_COLS_MAX + c); - udelay(keyscan_config.output_settle_us); - - /* Read the row state */ - id[c] = keyboard_raw_read_rows(); - - CPRINTS("Keyboard ID%u: 0x%02x", c, id[c]); - } - - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); -} -#endif - -#ifdef CONFIG_KEYBOARD_RUNTIME_KEYS - -static uint8_t key_vol_up_row = KEYBOARD_DEFAULT_ROW_VOL_UP; -static uint8_t key_vol_up_col = KEYBOARD_DEFAULT_COL_VOL_UP; - -void set_vol_up_key(uint8_t row, uint8_t col) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) { - key_vol_up_row = row; - key_vol_up_col = col; - } -} - -/** - * Check special runtime key combinations. - * - * @param state Keyboard state to use when checking keys. - * - * @return 1 if a special key was pressed, 0 if not - */ -static int check_runtime_keys(const uint8_t *state) -{ - int num_press = 0; - int c; - - /* - * All runtime key combos are (right or left ) alt + volume up + (some - * key NOT on the same col as alt or volume up ) - */ - if (state[key_vol_up_col] != KEYBOARD_ROW_TO_MASK(key_vol_up_row)) - return 0; - - if (state[KEYBOARD_COL_RIGHT_ALT] != KEYBOARD_MASK_RIGHT_ALT && - state[KEYBOARD_COL_LEFT_ALT] != KEYBOARD_MASK_LEFT_ALT) - return 0; - - /* - * Count number of columns with keys pressed. We know two columns are - * pressed for volume up and alt, so if only one more key is pressed - * there will be exactly 3 non-zero columns. - */ - for (c = 0; c < keyboard_cols; c++) { - if (state[c]) - num_press++; - } - - if (num_press != 3) - return 0; - - /* Check individual keys */ - if (state[KEYBOARD_COL_KEY_R] == KEYBOARD_MASK_KEY_R) { - /* R = reboot */ - CPRINTS("KB warm reboot"); - keyboard_clear_buffer(); - chipset_reset(CHIPSET_RESET_KB_WARM_REBOOT); - return 1; - } else if (state[KEYBOARD_COL_KEY_H] == KEYBOARD_MASK_KEY_H) { - /* H = hibernate */ - CPRINTS("KB hibernate"); - system_enter_hibernate(0, 0); - return 1; - } - - return 0; -} -#endif /* CONFIG_KEYBOARD_RUNTIME_KEYS */ - -/** - * Check for ghosting in the keyboard state. - * - * Assumes that the state has already been masked with the actual key mask, so - * that coords which don't correspond with actual keys don't trigger ghosting - * detection. - * - * @param state Keyboard state to check. - * - * @return 1 if ghosting detected, else 0. - */ -static int has_ghosting(const uint8_t *state) -{ - int c, c2; - - for (c = 0; c < keyboard_cols; c++) { - if (!state[c]) - continue; - - for (c2 = c + 1; c2 < keyboard_cols; c2++) { - /* - * A little bit of cleverness here. Ghosting happens - * if 2 columns share at least 2 keys. So we OR the - * columns together and then see if more than one bit - * is set. x&(x-1) is non-zero only if x has more than - * one bit set. - */ - uint8_t common = state[c] & state[c2]; - - if (common & (common - 1)) - return 1; - } - } - - return 0; -} - -/* Inform keyboard module if scanning is enabled */ -static void key_state_changed(int row, int col, uint8_t state) -{ - if (!keyboard_scan_is_enabled()) - return; - - /* No-op for protocols that require full keyboard matrix (e.g. MKBP). */ - keyboard_state_changed(row, col, !!(state & BIT(row))); -} - -/** - * Update keyboard state using low-level interface to read keyboard. - * - * @param state Keyboard state to update. - * - * @return 1 if any key is still pressed, 0 if no key is pressed. - */ -static int check_keys_changed(uint8_t *state) -{ - int any_pressed = 0; - int c, i; - int any_change = 0; - static uint8_t __bss_slow new_state[KEYBOARD_COLS_MAX]; - uint32_t tnow = get_time().le.lo; - - /* Save the current scan time */ - if (++scan_time_index >= SCAN_TIME_COUNT) - scan_time_index = 0; - scan_time[scan_time_index] = tnow; - - /* Read the raw key state */ - any_pressed = read_matrix(new_state); - - /* Ignore if so many keys are pressed that we're ghosting. */ - if (has_ghosting(new_state)) - return any_pressed; - - /* Check for changes between previous scan and this one */ - for (c = 0; c < keyboard_cols; c++) { - int diff = new_state[c] ^ state[c]; - - /* Clear debouncing flag, if sufficient time has elapsed. */ - for (i = 0; i < KEYBOARD_ROWS && debouncing[c]; i++) { - if (!(debouncing[c] & BIT(i))) - continue; - if (tnow - scan_time[scan_edge_index[c][i]] < - (state[c] ? keyscan_config.debounce_down_us : - keyscan_config.debounce_up_us)) - continue; /* Not done debouncing */ - debouncing[c] &= ~BIT(i); - - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) - continue; - if (!(diff & BIT(i))) - /* Debounced but no difference. */ - continue; - any_change = 1; - key_state_changed(i, c, new_state[c]); - /* - * This makes state[c] == new_state[c] for row i. - * Thus, when diff is calculated below, it won't - * be asserted (for row i). - */ - state[c] ^= diff & BIT(i); - } - - /* Recognize change in state, unless debounce in effect. */ - diff = (new_state[c] ^ state[c]) & ~debouncing[c]; - if (!diff) - continue; - for (i = 0; i < KEYBOARD_ROWS; i++) { - if (!(diff & BIT(i))) - continue; - scan_edge_index[c][i] = scan_time_index; - - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) { - any_change = 1; - key_state_changed(i, c, new_state[c]); - } - } - - /* For any keyboard events just sent, turn on debouncing. */ - debouncing[c] |= diff; - /* - * Note: In order to "remember" what was last reported - * (up or down), the state bits are only updated if the - * edge was not suppressed due to debouncing. - */ - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) - state[c] ^= diff; - } - - if (any_change) { - -#ifdef CONFIG_KEYBOARD_SUPPRESS_NOISE - /* Suppress keyboard noise */ - keyboard_suppress_noise(); -#endif - - if (print_state_changes) - print_state(state, "state"); - -#ifdef CONFIG_KEYBOARD_PRINT_SCAN_TIMES - /* Print delta times from now back to each previous scan */ - CPRINTF("[%pT kb deltaT", PRINTF_TIMESTAMP_NOW); - for (i = 0; i < SCAN_TIME_COUNT; i++) { - int tnew = scan_time[ - (SCAN_TIME_COUNT + scan_time_index - i) % - SCAN_TIME_COUNT]; - CPRINTF(" %d", tnow - tnew); - } - CPRINTF("]\n"); -#endif - -#ifdef CONFIG_KEYBOARD_RUNTIME_KEYS - /* Swallow special keys */ - if (check_runtime_keys(state)) - return 0; -#endif - -#ifdef CONFIG_KEYBOARD_PROTOCOL_MKBP - mkbp_keyboard_add(state); -#endif - } - - kbd_polls++; - - return any_pressed; -} - -static uint8_t keyboard_mask_refresh; -__overridable uint8_t board_keyboard_row_refresh(void) -{ - if (IS_ENABLED(CONFIG_KEYBOARD_REFRESH_ROW3)) - return 3; - else - return 2; -} - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -/* - * Returns mask of the boot keys that are pressed, with at most the keys used - * for keyboard-controlled reset also pressed. - */ -static uint32_t check_key_list(const uint8_t *state) -{ - uint8_t curr_state[KEYBOARD_COLS_MAX]; - int c; - uint32_t boot_key_mask = BOOT_KEY_NONE; - const struct boot_key_entry *k; - - /* Make copy of current debounced state. */ - memcpy(curr_state, state, sizeof(curr_state)); - -#ifdef KEYBOARD_MASK_PWRBTN - /* - * Check if KSI2 or KSI3 is asserted for all columns due to power - * button hold, and ignore it if so. - */ - for (c = 0; c < keyboard_cols; c++) - if ((keyscan_config.actual_key_mask[c] & KEYBOARD_MASK_PWRBTN) - && !(curr_state[c] & KEYBOARD_MASK_PWRBTN)) - break; - - if (c == keyboard_cols) - for (c = 0; c < keyboard_cols; c++) - curr_state[c] &= ~KEYBOARD_MASK_PWRBTN; -#endif - - curr_state[KEYBOARD_COL_REFRESH] &= ~keyboard_mask_refresh; - - /* Update mask with all boot keys that were pressed. */ - k = boot_key_list; - for (c = 0; c < ARRAY_SIZE(boot_key_list); c++, k++) { - if (curr_state[k->mask_index] & k->mask_value) { - boot_key_mask |= BIT(c); - curr_state[k->mask_index] &= ~k->mask_value; - } - } - - /* If any other key was pressed, ignore all boot keys. */ - for (c = 0; c < keyboard_cols; c++) { - if (curr_state[c]) - return BOOT_KEY_NONE; - } - - CPRINTS("KB boot key mask %x", boot_key_mask); - return boot_key_mask; -} - -/** - * Check what boot key is down, if any. - * - * @param state Keyboard state at boot. - * - * @return the key which is down, or BOOT_KEY_NONE if an unrecognized - * key combination is down or this isn't the right type of boot to look at - * boot keys. - */ -static uint32_t check_boot_key(const uint8_t *state) -{ - /* - * If we jumped to this image, ignore boot keys. This prevents - * re-triggering events in RW firmware that were already processed by - * RO firmware. - */ - if (system_jumped_late()) - return BOOT_KEY_NONE; - - /* If reset was not caused by reset pin, refresh must be held down */ - if (!(system_get_reset_flags() & EC_RESET_FLAG_RESET_PIN) && - !(state[KEYBOARD_COL_REFRESH] & keyboard_mask_refresh)) - return BOOT_KEY_NONE; - - return check_key_list(state); -} -#endif - -static void keyboard_freq_change(void) -{ - post_scan_clock_us = (CONFIG_KEYBOARD_POST_SCAN_CLOCKS * 1000) / - (clock_get_freq() / 1000); -} -DECLARE_HOOK(HOOK_FREQ_CHANGE, keyboard_freq_change, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Interface */ - -struct keyboard_scan_config *keyboard_scan_get_config(void) -{ - return &keyscan_config; -} - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -uint32_t keyboard_scan_get_boot_keys(void) -{ - return boot_key_value; -} -#endif - -const uint8_t *keyboard_scan_get_state(void) -{ - return debounced_state; -} - -void keyboard_scan_init(void) -{ - if (IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE) && - keyscan_config.debounce_down_us != keyscan_config.debounce_up_us) { - /* - * Strict debouncer is prone to keypress reordering if debounce - * durations for down and up are not equal. crbug.com/547131 - */ - CPRINTS("KB WARN: Debounce durations not equal"); - } - - /* Configure refresh key matrix */ - keyboard_mask_refresh = KEYBOARD_ROW_TO_MASK( - board_keyboard_row_refresh()); - - /* Configure GPIO */ - keyboard_raw_init(); - - /* Tri-state the columns */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Initialize raw state */ - read_matrix(debounced_state); - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID - /* Check keyboard ID state */ - read_matrix_id(keyboard_id); -#endif - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS - /* Check for keys held down at boot */ - boot_key_value = check_boot_key(debounced_state); - - /* - * If any key other than Esc or Left_Shift was pressed, do not trigger - * recovery. - */ - if (boot_key_value & ~(BOOT_KEY_ESC | BOOT_KEY_LEFT_SHIFT)) - return; - -#ifdef CONFIG_HOSTCMD_EVENTS - if (boot_key_value & BOOT_KEY_ESC) { - host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY); - /* - * In recovery mode, we should force clamshell mode in order to - * prevent the keyboard from being disabled unintentionally due - * to unstable accel readings. - * - * You get the same effect if motion sensors or a motion sense - * task are disabled in RO. - */ - if (IS_ENABLED(CONFIG_TABLET_MODE)) - tablet_disable(); - if (boot_key_value & BOOT_KEY_LEFT_SHIFT) - host_set_single_event( - EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT); - } -#endif -#endif /* CONFIG_KEYBOARD_BOOT_KEYS */ -} - -void keyboard_scan_task(void *u) -{ - timestamp_t poll_deadline, start; - int wait_time; - uint32_t local_disable_scanning = 0; - - print_state(debounced_state, "init state"); - - keyboard_raw_task_start(); - - /* Set initial clock frequency-based minimum delay between scans */ - keyboard_freq_change(); - - while (1) { - /* Enable all outputs */ - CPRINTS5("KB wait"); - - keyboard_raw_enable_interrupt(1); - - /* Wait for scanning enabled and key pressed. */ - while (1) { - uint32_t new_disable_scanning; - - /* Read it once to get consistent glimpse */ - new_disable_scanning = disable_scanning_mask; - - if (local_disable_scanning != new_disable_scanning) - CPRINTS("KB disable_scanning_mask changed: " - "0x%08x", new_disable_scanning); - - if (!new_disable_scanning) { - /* Enabled now */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); - } else if (!local_disable_scanning) { - /* - * Scanning isn't enabled but it was last time - * we looked. - * - * No race here even though we're basing on a - * glimpse of disable_scanning_mask since if - * someone changes disable_scanning_mask they - * are guaranteed to call task_wake() on us - * afterward so we'll run the loop again. - */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - keyboard_clear_buffer(); - } - - local_disable_scanning = new_disable_scanning; - - /* - * Done waiting if scanning is enabled and a key is - * already pressed. This prevents a race between the - * user pressing a key and enable_interrupt() - * starting to pay attention to edges. - */ - if (!local_disable_scanning && - (keyboard_raw_read_rows() || force_poll)) - break; - else - task_wait_event(-1); - } - - /* We're about to poll, so any existing forces are fulfilled */ - force_poll = 0; - - /* Enter polling mode */ - CPRINTS5("KB poll"); - keyboard_raw_enable_interrupt(0); - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Busy polling keyboard state. */ - while (keyboard_scan_is_enabled()) { - start = get_time(); - - /* Check for keys down */ - if (check_keys_changed(debounced_state)) { - poll_deadline.val = start.val - + keyscan_config.poll_timeout_us; - } else if (timestamp_expired(poll_deadline, &start)) { - break; - } - - /* Delay between scans */ - wait_time = keyscan_config.scan_period_us - - (get_time().val - start.val); - - if (wait_time < keyscan_config.min_post_scan_delay_us) - wait_time = - keyscan_config.min_post_scan_delay_us; - - if (wait_time < post_scan_clock_us) - wait_time = post_scan_clock_us; - - usleep(wait_time); - } - } -} - -#ifdef CONFIG_LID_SWITCH - -static void keyboard_lid_change(void) -{ - if (lid_is_open()) - keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED); - else - keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED); -} -DECLARE_HOOK(HOOK_LID_CHANGE, keyboard_lid_change, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_INIT, keyboard_lid_change, HOOK_PRIO_INIT_LID + 1); - -#endif - -#ifdef CONFIG_USB_SUSPEND -static void keyboard_usb_pm_change(void) -{ - /* - * If USB interface is suspended, and host is not asking us to do remote - * wakeup, we can turn off the key scanning. - */ - if (usb_is_suspended() && !usb_is_remote_wakeup_enabled()) - keyboard_scan_enable(0, KB_SCAN_DISABLE_USB_SUSPENDED); - else - keyboard_scan_enable(1, KB_SCAN_DISABLE_USB_SUSPENDED); -} -DECLARE_HOOK(HOOK_USB_PM_CHANGE, keyboard_usb_pm_change, HOOK_PRIO_DEFAULT); -#endif - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -mkbp_command_simulate_key(struct host_cmd_handler_args *args) -{ - const struct ec_params_mkbp_simulate_key *p = args->params; - - /* Only available on unlocked systems */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - if (p->col >= keyboard_cols || p->row >= KEYBOARD_ROWS) - return EC_RES_INVALID_PARAM; - - simulate_key(p->row, p->col, p->pressed); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_MKBP_SIMULATE_KEY, - mkbp_command_simulate_key, - EC_VER_MASK(0)); - -#ifdef CONFIG_KEYBOARD_FACTORY_TEST - -/* Run keyboard factory testing, scan out KSO/KSI if any shorted. */ -int keyboard_factory_test_scan(void) -{ - int i, j, flags; - uint16_t shorted = 0; - int port, id; - - /* Disable keyboard scan while testing */ - keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED); - flags = gpio_get_default_flags(GPIO_KBD_KSO2); - - /* Set all of KSO/KSI pins to internal pull-up and input */ - for (i = 0; i < keyboard_factory_scan_pins_used; i++) { - - if (keyboard_factory_scan_pins[i][0] < 0) - continue; - - port = keyboard_factory_scan_pins[i][0]; - id = keyboard_factory_scan_pins[i][1]; - - gpio_set_alternate_function(port, 1 << id, - GPIO_ALT_FUNC_NONE); - gpio_set_flags_by_mask(port, 1 << id, - GPIO_INPUT | GPIO_PULL_UP); - } - - /* - * Set start pin to output low, then check other pins - * going to low level, it indicate the two pins are shorted. - */ - for (i = 0; i < keyboard_factory_scan_pins_used; i++) { - - if (keyboard_factory_scan_pins[i][0] < 0) - continue; - - port = keyboard_factory_scan_pins[i][0]; - id = keyboard_factory_scan_pins[i][1]; - - gpio_set_flags_by_mask(port, 1 << id, GPIO_OUT_LOW); - - for (j = 0; j < i; j++) { - - if (keyboard_factory_scan_pins[j][0] < 0) - continue; - - if (keyboard_raw_is_input_low( - keyboard_factory_scan_pins[j][0], - keyboard_factory_scan_pins[j][1])) { - shorted = i << 8 | j; - goto done; - } - } - gpio_set_flags_by_mask(port, 1 << id, - GPIO_INPUT | GPIO_PULL_UP); - } -done: - gpio_config_module(MODULE_KEYBOARD_SCAN, 1); - gpio_set_flags(GPIO_KBD_KSO2, flags); - keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED); - - return shorted; -} - -static enum ec_status keyboard_factory_test(struct host_cmd_handler_args *args) -{ - struct ec_response_keyboard_factory_test *r = args->response; - - /* Only available on unlocked systems */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - if (keyboard_factory_scan_pins_used == 0) - return EC_RES_INVALID_COMMAND; - - r->shorted = keyboard_factory_test_scan(); - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_KEYBOARD_FACTORY_TEST, - keyboard_factory_test, - EC_VER_MASK(0)); -#endif - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -int keyboard_get_keyboard_id(void) -{ - int c; - uint32_t id = 0; - - BUILD_ASSERT(sizeof(id) >= KEYBOARD_IDS); - - for (c = 0; c < KEYBOARD_IDS; c++) { - /* Check ID ghosting if more than one bit in any KSIs was set */ - if (keyboard_id[c] & (keyboard_id[c] - 1)) - /* ID ghosting is found */ - return KEYBOARD_ID_UNREADABLE; - else - id |= keyboard_id[c] << (c * 8); - } - return id; -} -#endif - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_KEYBOARD -static int command_ksstate(int argc, char **argv) -{ - if (argc > 1) { - if (!strcasecmp(argv[1], "force")) { - print_state_changes = 1; - keyboard_scan_enable(1, -1); - } else if (!parse_bool(argv[1], &print_state_changes)) { - return EC_ERROR_PARAM1; - } - } - - print_state(debounced_state, "debounced "); - print_state(debouncing, "debouncing"); - - ccprintf("Keyboard scan disable mask: 0x%08x\n", - disable_scanning_mask); - ccprintf("Keyboard scan state printing %s\n", - print_state_changes ? "on" : "off"); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ksstate, command_ksstate, - "ksstate [on | off | force]", - "Show or toggle printing keyboard scan state"); - -static int command_keyboard_press(int argc, char **argv) -{ - if (argc == 1) { - int i, j; - - ccputs("Simulated keys:\n"); - for (i = 0; i < keyboard_cols; ++i) { - if (simulated_key[i] == 0) - continue; - for (j = 0; j < KEYBOARD_ROWS; ++j) - if (simulated_key[i] & BIT(j)) - ccprintf("\t%d %d\n", i, j); - } - - } else if (argc == 3 || argc == 4) { - int r, c, p; - char *e; - - c = strtoi(argv[1], &e, 0); - if (*e || c < 0 || c >= keyboard_cols) - return EC_ERROR_PARAM1; - - r = strtoi(argv[2], &e, 0); - if (*e || r < 0 || r >= KEYBOARD_ROWS) - return EC_ERROR_PARAM2; - - if (argc == 3) { - /* Simulate a press and release */ - simulate_key(r, c, 1); - simulate_key(r, c, 0); - } else { - p = strtoi(argv[3], &e, 0); - if (*e || p < 0 || p > 1) - return EC_ERROR_PARAM3; - - simulate_key(r, c, p); - } - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(kbpress, command_keyboard_press, - "[col row [0 | 1]]", - "Simulate keypress"); -#endif diff --git a/common/keyboard_test.c b/common/keyboard_test.c deleted file mode 100644 index e7b1dfe501..0000000000 --- a/common/keyboard_test.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2013 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 <common.h> -#include <console.h> -#include <ec_commands.h> -#include <host_command.h> -#include <keyboard_test.h> -#include <task.h> -#include <util.h> - -enum { - KEYSCAN_MAX_LENGTH = 20, - KEYSCAN_SEQ_START_DELAY_US = 10000, -}; - -static uint8_t keyscan_seq_count; -static int8_t keyscan_seq_upto = -1; -static struct keyscan_item keyscan_items[KEYSCAN_MAX_LENGTH]; -struct keyscan_item *keyscan_seq_cur; - -static int keyscan_seq_is_active(void) -{ - return keyscan_seq_upto != -1; -} - -/** - * Get the current item in the keyscan sequence - * - * This looks at the current time, and returns the correct key scan for that - * time. - * - * @return pointer to keyscan item, or NULL if none - */ -static const struct keyscan_item *keyscan_seq_get(void) -{ - struct keyscan_item *ksi; - - if (!keyscan_seq_is_active()) - return NULL; - - ksi = &keyscan_items[keyscan_seq_upto]; - while (keyscan_seq_upto < keyscan_seq_count) { - /* - * If we haven't reached the time for the next one, return - * this one. - */ - if (!timestamp_expired(ksi->abs_time, NULL)) { - /* Yippee, we get to present this one! */ - if (keyscan_seq_cur) - keyscan_seq_cur->done = 1; - return keyscan_seq_cur; - } - - keyscan_seq_cur = ksi; - keyscan_seq_upto++; - ksi++; - } - - ccprints("keyscan_seq done, upto=%d", keyscan_seq_upto); - keyscan_seq_upto = -1; - keyscan_seq_cur = NULL; - return NULL; -} - -uint8_t keyscan_seq_get_scan(int column, uint8_t scan) -{ - const struct keyscan_item *item; - - /* Use simulated keyscan sequence instead if active */ - item = keyscan_seq_get(); - if (item) { - /* OR all columns together */ - if (column == -1) { - int c; - - scan = 0; - for (c = 0; c < keyboard_cols; c++) - scan |= item->scan[c]; - } else { - scan = item->scan[column]; - } - } - - return scan; -} - -int keyscan_seq_next_event_delay(void) -{ - const struct keyscan_item *ksi; - int delay; - - /* - * Make sure we are pointing to the right event. This function will - * return the event that should currently be presented. In fact we - * want to look at the next event to be presented, so we manually - * look that up after calling this function. - */ - ksi = keyscan_seq_get(); - - if (!keyscan_seq_is_active()) - return -1; - - /* Calculate the delay until the event */ - ksi = &keyscan_items[keyscan_seq_upto]; - delay = MAX(ksi->abs_time.val - get_time().val, 0); - - return delay; -} - -static void keyscan_seq_start(void) -{ - timestamp_t start; - int i; - - start = get_time(); - start.val += KEYSCAN_SEQ_START_DELAY_US; - for (i = 0; i < keyscan_seq_count; i++) { - struct keyscan_item *ksi = &keyscan_items[i]; - - ksi->abs_time = start; - ksi->abs_time.val += ksi->time_us; - } - - keyscan_seq_upto = 0; - keyscan_seq_cur = NULL; - task_wake(TASK_ID_KEYSCAN); -} - -static int keyscan_seq_collect(struct ec_params_keyscan_seq_ctrl *req, - struct ec_result_keyscan_seq_ctrl *resp) -{ - struct keyscan_item *ksi; - int start, end; - int i; - - /* Range check the input values */ - start = req->collect.start_item; - end = start + req->collect.num_items; - if (start >= keyscan_seq_count) - end = start; - else - end = MIN(end, keyscan_seq_count); - start = MIN(start, end); - - /* Response plus one byte per item */ - end = MIN(end - start, EC_HOST_PARAM_SIZE - sizeof(*resp)); - resp->collect.num_items = end - start; - - for (i = start, ksi = keyscan_items; i < end; i++, ksi++) - resp->collect.item[i].flags = ksi->done ? - EC_KEYSCAN_SEQ_FLAG_DONE : 0; - - return sizeof(*resp) + resp->collect.num_items; -} - -static enum ec_status keyscan_seq_ctrl(struct host_cmd_handler_args *args) -{ - struct ec_params_keyscan_seq_ctrl req, *msg; - struct keyscan_item *ksi; - - /* For now we must do our own alignment */ - memcpy(&req, args->params, sizeof(req)); - - ccprintf("keyscan %d\n", req.cmd); - switch (req.cmd) { - case EC_KEYSCAN_SEQ_CLEAR: - keyscan_seq_count = 0; - break; - case EC_KEYSCAN_SEQ_ADD: - if (keyscan_seq_count == KEYSCAN_MAX_LENGTH) - return EC_RES_OVERFLOW; - - ksi = &keyscan_items[keyscan_seq_count]; - ksi->time_us = req.add.time_us; - ksi->done = 0; - ksi->abs_time.val = 0; - msg = (struct ec_params_keyscan_seq_ctrl *)args->params; - memcpy(ksi->scan, msg->add.scan, sizeof(ksi->scan)); - keyscan_seq_count++; - break; - case EC_KEYSCAN_SEQ_START: - keyscan_seq_start(); - break; - case EC_KEYSCAN_SEQ_COLLECT: - args->response_size = keyscan_seq_collect(&req, - (struct ec_result_keyscan_seq_ctrl *)args->response); - break; - default: - return EC_RES_INVALID_COMMAND; - } - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_KEYSCAN_SEQ_CTRL, - keyscan_seq_ctrl, - EC_VER_MASK(0)); diff --git a/common/keyboard_vivaldi.c b/common/keyboard_vivaldi.c deleted file mode 100644 index 443b475c82..0000000000 --- a/common/keyboard_vivaldi.c +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Vivali Keyboard code for Chrome EC */ - -#include "keyboard_8042_sharedlib.h" -#include "keyboard_scan.h" -#include "ec_commands.h" -#include <host_command.h> -#include <util.h> -#include <hooks.h> - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -/* - * Row Column info for Top row keys T1 - T15. This has been sourced from - * go/vivaldi-matrix (internal link for vivaldi scan matrix spec). - */ -static const struct key { - uint8_t row; - uint8_t col; -} vivaldi_keys[] = { - {.row = 0, .col = 2}, /* T1 */ - {.row = 3, .col = 2}, /* T2 */ - {.row = 2, .col = 2}, /* T3 */ - {.row = 1, .col = 2}, /* T4 */ - {.row = 3, .col = 4}, /* T5 */ - {.row = 2, .col = 4}, /* T6 */ - {.row = 1, .col = 4}, /* T7 */ - {.row = 2, .col = 9}, /* T8 */ - {.row = 1, .col = 9}, /* T9 */ - {.row = 0, .col = 4}, /* T10 */ - {.row = 0, .col = 1}, /* T11 */ - {.row = 1, .col = 5}, /* T12 */ - {.row = 3, .col = 5}, /* T13 */ - {.row = 0, .col = 9}, /* T14 */ - {.row = 0, .col = 11}, /* T15 */ -}; -BUILD_ASSERT(ARRAY_SIZE(vivaldi_keys) == MAX_TOP_ROW_KEYS); - -/* Scancodes for top row action keys */ -static const uint16_t action_scancodes[] = { - [TK_BACK] = SCANCODE_BACK, - [TK_FORWARD] = SCANCODE_FORWARD, - [TK_REFRESH] = SCANCODE_REFRESH, - [TK_FULLSCREEN] = SCANCODE_FULLSCREEN, - [TK_OVERVIEW] = SCANCODE_OVERVIEW, - [TK_VOL_MUTE] = SCANCODE_VOLUME_MUTE, - [TK_VOL_DOWN] = SCANCODE_VOLUME_DOWN, - [TK_VOL_UP] = SCANCODE_VOLUME_UP, - [TK_PLAY_PAUSE] = SCANCODE_PLAY_PAUSE, - [TK_NEXT_TRACK] = SCANCODE_NEXT_TRACK, - [TK_PREV_TRACK] = SCANCODE_PREV_TRACK, - [TK_SNAPSHOT] = SCANCODE_SNAPSHOT, - [TK_BRIGHTNESS_DOWN] = SCANCODE_BRIGHTNESS_DOWN, - [TK_BRIGHTNESS_UP] = SCANCODE_BRIGHTNESS_UP, - [TK_KBD_BKLIGHT_DOWN] = SCANCODE_KBD_BKLIGHT_DOWN, - [TK_KBD_BKLIGHT_UP] = SCANCODE_KBD_BKLIGHT_UP, - [TK_PRIVACY_SCRN_TOGGLE] = SCANCODE_PRIVACY_SCRN_TOGGLE, - [TK_MICMUTE] = SCANCODE_MICMUTE, - [TK_KBD_BKLIGHT_TOGGLE] = SCANCODE_KBD_BKLIGHT_TOGGLE, -}; - -static const struct ec_response_keybd_config *vivaldi_keybd; - -static enum -ec_status get_vivaldi_keybd_config(struct host_cmd_handler_args *args) -{ - struct ec_response_keybd_config *resp = args->response; - - if (vivaldi_keybd && vivaldi_keybd->num_top_row_keys) { - memcpy(resp, vivaldi_keybd, sizeof(*resp)); - args->response_size = sizeof(*resp); - return EC_RES_SUCCESS; - } - return EC_RES_ERROR; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_KEYBD_CONFIG, get_vivaldi_keybd_config, - EC_VER_MASK(0)); - -#ifdef CONFIG_KEYBOARD_CUSTOMIZATION - -/* - * Boards selecting CONFIG_KEYBOARD_CUSTOMIZATION are likely to not - * want vivaldi code messing with their customized keyboards. - */ -__overridable -const struct ec_response_keybd_config *board_vivaldi_keybd_config(void) -{ - return NULL; -} - -#else - -static const struct ec_response_keybd_config default_keybd = { - /* Default Chromeos keyboard config */ - .num_top_row_keys = 10, - .action_keys = { - TK_BACK, /* T1 */ - TK_FORWARD, /* T2 */ - TK_REFRESH, /* T3 */ - TK_FULLSCREEN, /* T4 */ - TK_OVERVIEW, /* T5 */ - TK_BRIGHTNESS_DOWN, /* T6 */ - TK_BRIGHTNESS_UP, /* T7 */ - TK_VOL_MUTE, /* T8 */ - TK_VOL_DOWN, /* T9 */ - TK_VOL_UP, /* T10 */ - }, - /* No function keys, no numeric keypad, has screenlock key */ - .capabilities = KEYBD_CAP_SCRNLOCK_KEY, -}; - -__overridable -const struct ec_response_keybd_config *board_vivaldi_keybd_config(void) -{ - return &default_keybd; -} - -#endif /* CONFIG_KEYBOARD_CUSTOMIZATION */ - -static void vivaldi_init(void) -{ - uint8_t i; - - /* Allow the boards to change the keyboard config */ - vivaldi_keybd = board_vivaldi_keybd_config(); - - if (!vivaldi_keybd || !vivaldi_keybd->num_top_row_keys) { - CPUTS("VIVALDI keybd disabled on board request"); - return; - } - - CPRINTS("VIVALDI: Num top row keys = %u", - vivaldi_keybd->num_top_row_keys); - - if (vivaldi_keybd->num_top_row_keys > MAX_TOP_ROW_KEYS || - vivaldi_keybd->num_top_row_keys < MIN_TOP_ROW_KEYS) { - CPRINTS("VIVALDI: Error! num_top_row_keys=%u, disabled vivaldi", - vivaldi_keybd->num_top_row_keys); - vivaldi_keybd = NULL; - return; - } - - for (i = 0; i < ARRAY_SIZE(vivaldi_keys); i++) { - - uint8_t row, col, *mask; - enum action_key key; - - row = vivaldi_keys[i].row; - col = vivaldi_keys[i].col; - - if (col >= KEYBOARD_COLS_MAX || row >= KEYBOARD_ROWS) { - CPRINTS("VIVALDI: Bad (row,col) for T-%u: (%u,%u)", - i, row, col); - ASSERT(false); - } - - mask = &keyscan_config.actual_key_mask[col]; - - /* - * Potentially indexing past meaningful data, - * but we bounds check it below. - */ - key = vivaldi_keybd->action_keys[i]; - - if (i < vivaldi_keybd->num_top_row_keys && key != TK_ABSENT) { - - /* Enable the mask */ - *mask |= BIT(row); - - /* Populate the scancode */ - set_scancode_set2(row, col, action_scancodes[key]); - CPRINTS("VIVALDI key-%u (r-%u, c-%u) = scancode-%X", - i, row, col, action_scancodes[key]); - - if (key == TK_VOL_UP) - set_vol_up_key(row, col); - - } - } -} -DECLARE_HOOK(HOOK_INIT, vivaldi_init, HOOK_PRIO_DEFAULT); diff --git a/common/lb_common.c b/common/lb_common.c deleted file mode 100644 index 019e0e254f..0000000000 --- a/common/lb_common.c +++ /dev/null @@ -1,345 +0,0 @@ -/* Copyright 2012 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. - * - * Lightbar IC interface - * - * Here's the API provided by this file. - * - * Looking at it from the outside, the lightbar has four "segments", each of - * which can be independently adjusted to display a unique color such as blue, - * purple, yellow, pinkish-white, etc. Segment 0 is on the left (looking - * straight at it from behind). - * - * The lb_set_rgb() and lb_get_rgb() functions let you specify the color of a - * segment using individual Red, Green, and Blue values in the 0x00 to 0xFF - * range (see https://en.wikipedia.org/wiki/Web_color for background info). - * - * The lb_set_brightness() function provides a simple way to set the intensity, - * over a range of 0x00 (off) to 0xFF (full brightness). It does this by - * scaling each RGB value proportionally. For example, an RGB value of #FF8000 - * appears orange. To make the segment half as bright, you could specify a RGB - * value of #7f4000, or you could leave the RGB value unchanged and just set - * the brightness to 0x80. - * - * That covers most of the lb_* functions found in include/lb_common.h, and - * those functions are what are used to implement the various colors and - * sequences for displaying power state changes and other events. - * - * The internals are a little more messy. - * - * Each segment has three individual color emitters - red, green, and blue. A - * single emitter may consist of 3 to 7 physical LEDs, but they are all wired - * in parallel so there is only one wire that provides current for any one - * color emitter. That makes a total of 12 current control wires for the - * lightbar: four segments, three color emitters per segment. - * - * The ICs that we use each have seven independently adjustable - * current-limiters. We use six of those current limiters (called "Independent - * Sink Controls", or "ISC"s ) from each of two ICs to control the 12 color - * emitters in the lightbar. The ICs are not identical, but they're close - * enough that we can treat them the same. We call the ICs "controller 0" and - * "controller 1". - * - * For no apparent reason, each Chromebook has wired the ICs and the ISCs - * differently, so there are a couple of lookup tables that ensure that when we - * call lb_set_rgb() to make segment 1 yellow, it looks the same on all - * Chromebooks. - * - * Each ISC has a control register to set the amount of current that passes - * through the color emitter control wire. We need to limit the max current so - * that the current through each of the emitter's LEDs doesn't exceed the - * manufacturer's specifications. For example, if a particular LED can't handle - * more than 5 mA, and the emitter is made up of four LEDs in parallel, the - * maxiumum limit for that particular ISC would be 20 mA. - * - * Although the specified maximum currents are usually similar, the three - * different colors of LEDs have different brightnesses. For any given current, - * green LEDs are pretty bright, red LEDS are medium, and blue are fairly dim. - * So we calibrate the max current per ISC differently, depending on which - * color it controls. - * - * First we set one segment to red, one to green, and one to blue, using the - * ISC register to allow the max current per LED that the LED manufacturer - * recommends. Then we adjust the current of the brighter segments downward - * until all three segments appear equally bright to the eye. The MAX_RED, - * MAX_BLUE, and MAX_GREEN values are the ISC control register values at this - * point. This means that if we set all ISCs to their MAX_* values, all - * segments should appear white. - * - * To translate the RGB values passed to lb_set_rgb() into ISC values, we - * perform two transformations. The color value is first scaled according to - * the current brightness setting, and then that intensity is scaled according - * to the MAX_* value for the particular color. The result is the ISC register - * value to use. - * - * To add lightbar support for a new Chromebook, you do the following: - * - * 1. Figure out the segment-to-IC and color-to-ISC mappings so that - * lb_set_rgb() does the same thing as on the other Chromebooks. - * - * 2. Calibrate the MAX_RED, MAX_GREEN, and MAX_BLUE values so that white looks - * white, and solid red, green, and blue all appear to be the same - * brightness. - * - * 3. Use lb_set_rgb() to set the colors to what *should be* the Google colors - * (at maximum brightness). Tweak the RGB values until the colors match, - * then edit common/lightbar.c to set them as the defaults. - * - * 4. Curse because the physical variation between the LEDs prevents you from - * getting everything exactly right: white looks bluish, yellow turns - * orange at lower brightness, segment 3 has a bright spot when displaying - * solid red, etc. Go back to step 2, and repeat until deadline. - */ - -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "i2c.h" -#include "lb_common.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LIGHTBAR, outstr) -#define CPRINTF(format, args...) cprintf(CC_LIGHTBAR, format, ## args) -#define CPRINTS(format, args...) cprints(CC_LIGHTBAR, format, ## args) - -/******************************************************************************/ -/* How to talk to the controller */ -/******************************************************************************/ - -/* Since there's absolutely nothing we can do about it if an I2C access - * isn't working, we're completely ignoring any failures. */ - -static const uint16_t i2c_addr_flags[] = { 0x2A, 0x2B }; - -static inline void controller_write(int ctrl_num, uint8_t reg, uint8_t val) -{ - uint8_t buf[2]; - - buf[0] = reg; - buf[1] = val; - ctrl_num = ctrl_num % ARRAY_SIZE(i2c_addr_flags); - i2c_xfer_unlocked(I2C_PORT_LIGHTBAR, i2c_addr_flags[ctrl_num], - buf, 2, 0, 0, - I2C_XFER_SINGLE); -} - -static inline uint8_t controller_read(int ctrl_num, uint8_t reg) -{ - uint8_t buf[1]; - int rv; - - ctrl_num = ctrl_num % ARRAY_SIZE(i2c_addr_flags); - rv = i2c_xfer_unlocked(I2C_PORT_LIGHTBAR, i2c_addr_flags[ctrl_num], - ®, 1, buf, 1, I2C_XFER_SINGLE); - return rv ? 0 : buf[0]; -} - -/******************************************************************************/ -/* Controller details. We have an ADP8861 and and ADP8863, but we can treat - * them identically for our purposes */ -/******************************************************************************/ - -#ifdef BOARD_BDS -/* We need to limit the total current per ISC to no more than 20mA (5mA per - * color LED, but we have four LEDs in parallel on each ISC). Any more than - * that runs the risk of damaging the LED component. A value of 0x67 is as high - * as we want (assuming Square Law), but the blue LED is the least bright, so - * I've lowered the other colors until they all appear approximately equal - * brightness when full on. That's still pretty bright and a lot of current - * drain on the battery, so we'll probably rarely go that high. */ -#define MAX_RED 0x5c -#define MAX_GREEN 0x30 -#define MAX_BLUE 0x67 -#endif -#ifdef BOARD_HOST -/* For testing only */ -#define MAX_RED 0xff -#define MAX_GREEN 0xff -#define MAX_BLUE 0xff -#endif - -/* How we'd like to see the driver chips initialized. The controllers have some - * auto-cycling capability, but it's not much use for our purposes. For now, - * we'll just control all color changes actively. */ -struct initdata_s { - uint8_t reg; - uint8_t val; -}; - -static const struct initdata_s init_vals[] = { - {0x04, 0x00}, /* no backlight function */ - {0x05, 0x3f}, /* xRGBRGB per chip */ - {0x0f, 0x01}, /* square law looks better */ - {0x10, 0x3f}, /* enable independent LEDs */ - {0x11, 0x00}, /* no auto cycling */ - {0x12, 0x00}, /* no auto cycling */ - {0x13, 0x00}, /* instant fade in/out */ - {0x14, 0x00}, /* not using LED 7 */ - {0x15, 0x00}, /* current for LED 6 (blue) */ - {0x16, 0x00}, /* current for LED 5 (red) */ - {0x17, 0x00}, /* current for LED 4 (green) */ - {0x18, 0x00}, /* current for LED 3 (blue) */ - {0x19, 0x00}, /* current for LED 2 (red) */ - {0x1a, 0x00}, /* current for LED 1 (green) */ -}; - -/* Controller register lookup tables. */ -static const uint8_t led_to_ctrl[] = { 1, 1, 0, 0 }; -#ifdef BOARD_BDS -static const uint8_t led_to_isc[] = { 0x18, 0x15, 0x18, 0x15 }; -#endif -#ifdef BOARD_HOST -/* For testing only */ -static const uint8_t led_to_isc[] = { 0x15, 0x18, 0x15, 0x18 }; -#endif - -/* Scale 0-255 into max value */ -static inline uint8_t scale_abs(int val, int max) -{ - return (val * max)/255; -} - -/* This is the overall brightness control. */ -static int brightness = 0xc0; - -/* So that we can make brightness changes happen instantly, we need to track - * the current values. The values in the controllers aren't very helpful. */ -static uint8_t current[NUM_LEDS][3]; - -/* Scale 0-255 by brightness */ -static inline uint8_t scale(int val, int max) -{ - return scale_abs((val * brightness)/255, max); -} - -/* Helper function to set one LED color and remember it for later */ -static void setrgb(int led, int red, int green, int blue) -{ - int ctrl, bank; - current[led][0] = red; - current[led][1] = green; - current[led][2] = blue; - ctrl = led_to_ctrl[led]; - bank = led_to_isc[led]; - i2c_lock(I2C_PORT_LIGHTBAR, 1); - controller_write(ctrl, bank, scale(blue, MAX_BLUE)); - controller_write(ctrl, bank+1, scale(red, MAX_RED)); - controller_write(ctrl, bank+2, scale(green, MAX_GREEN)); - i2c_lock(I2C_PORT_LIGHTBAR, 0); -} - -/* LEDs are numbered 0-3, RGB values should be in 0-255. - * If you specify too large an LED, it sets them all. */ -void lb_set_rgb(unsigned int led, int red, int green, int blue) -{ - int i; - if (led >= NUM_LEDS) - for (i = 0; i < NUM_LEDS; i++) - setrgb(i, red, green, blue); - else - setrgb(led, red, green, blue); -} - -/* Get current LED values, if the LED number is in range. */ -int lb_get_rgb(unsigned int led, uint8_t *red, uint8_t *green, uint8_t *blue) -{ - if (led < 0 || led >= NUM_LEDS) - return EC_RES_INVALID_PARAM; - - *red = current[led][0]; - *green = current[led][1]; - *blue = current[led][2]; - - return EC_RES_SUCCESS; -} - -/* Change current display brightness (0-255) */ -void lb_set_brightness(unsigned int newval) -{ - int i; - CPRINTS("LB_bright 0x%02x", newval); - brightness = newval; - for (i = 0; i < NUM_LEDS; i++) - setrgb(i, current[i][0], current[i][1], current[i][2]); -} - -/* Get current display brightness (0-255) */ -uint8_t lb_get_brightness(void) -{ - return brightness; -} - -/* Initialize the controller ICs after reset */ -void lb_init(int use_lock) -{ - int i; - - CPRINTF("[%pT LB_init_vals ", PRINTF_TIMESTAMP_NOW); - for (i = 0; i < ARRAY_SIZE(init_vals); i++) { - CPRINTF("%c", '0' + i % 10); - if (use_lock) - i2c_lock(I2C_PORT_LIGHTBAR, 1); - controller_write(0, init_vals[i].reg, init_vals[i].val); - controller_write(1, init_vals[i].reg, init_vals[i].val); - if (use_lock) - i2c_lock(I2C_PORT_LIGHTBAR, 0); - } - CPRINTF("]\n"); - memset(current, 0, sizeof(current)); -} - -/* Just go into standby mode. No register values should change. */ -void lb_off(void) -{ - CPRINTS("LB_off"); - i2c_lock(I2C_PORT_LIGHTBAR, 1); - controller_write(0, 0x01, 0x00); - controller_write(1, 0x01, 0x00); - i2c_lock(I2C_PORT_LIGHTBAR, 0); -} - -/* Come out of standby mode. */ -void lb_on(void) -{ - CPRINTS("LB_on"); - i2c_lock(I2C_PORT_LIGHTBAR, 1); - controller_write(0, 0x01, 0x20); - controller_write(1, 0x01, 0x20); - i2c_lock(I2C_PORT_LIGHTBAR, 0); -} - -static const uint8_t dump_reglist[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a -}; - -/* Helper for host command to dump controller registers */ -void lb_hc_cmd_dump(struct ec_response_lightbar *out) -{ - int i; - uint8_t reg; - - BUILD_ASSERT(ARRAY_SIZE(dump_reglist) == - ARRAY_SIZE(out->dump.vals)); - - for (i = 0; i < ARRAY_SIZE(dump_reglist); i++) { - reg = dump_reglist[i]; - out->dump.vals[i].reg = reg; - i2c_lock(I2C_PORT_LIGHTBAR, 1); - out->dump.vals[i].ic0 = controller_read(0, reg); - out->dump.vals[i].ic1 = controller_read(1, reg); - i2c_lock(I2C_PORT_LIGHTBAR, 0); - } -} - -/* Helper for host command to write controller registers directly */ -void lb_hc_cmd_reg(const struct ec_params_lightbar *in) -{ - i2c_lock(I2C_PORT_LIGHTBAR, 1); - controller_write(in->reg.ctrl, in->reg.reg, in->reg.value); - i2c_lock(I2C_PORT_LIGHTBAR, 0); -} diff --git a/common/led_common.c b/common/led_common.c deleted file mode 100644 index 85879b148f..0000000000 --- a/common/led_common.c +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2013 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. - * - * Common functions for blinking LEDs. - */ - -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "led_common.h" -#include "util.h" - -#define LED_AUTO_CONTROL_FLAG(id) (1 << (id)) - -static uint32_t led_auto_control_flags = ~0x00; - -static int led_is_supported(enum ec_led_id led_id) -{ - int i; - static int supported_leds = -1; - - if (supported_leds == -1) { - supported_leds = 0; - - for (i = 0; i < supported_led_ids_count; i++) - supported_leds |= (1 << supported_led_ids[i]); - } - - return ((1 << (int)led_id) & supported_leds); -} - -void led_auto_control(enum ec_led_id led_id, int enable) -{ - if (enable) - led_auto_control_flags |= LED_AUTO_CONTROL_FLAG(led_id); - else - led_auto_control_flags &= ~LED_AUTO_CONTROL_FLAG(led_id); -} - -int led_auto_control_is_enabled(enum ec_led_id led_id) -{ - if (!led_is_supported(led_id)) - return 0; - - return (led_auto_control_flags & LED_AUTO_CONTROL_FLAG(led_id)) != 0; -} - -static enum ec_status led_command_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_led_control *p = args->params; - struct ec_response_led_control *r = args->response; - int i; - - args->response_size = sizeof(*r); - memset(r->brightness_range, 0, sizeof(r->brightness_range)); - - if (!led_is_supported(p->led_id)) - return EC_RES_INVALID_PARAM; - - led_get_brightness_range(p->led_id, r->brightness_range); - if (p->flags & EC_LED_FLAGS_QUERY) - return EC_RES_SUCCESS; - - for (i = 0; i < EC_LED_COLOR_COUNT; i++) - if (r->brightness_range[i] == 0 && p->brightness[i] != 0) - return EC_RES_INVALID_PARAM; - - if (p->flags & EC_LED_FLAGS_AUTO) { - led_auto_control(p->led_id, 1); - } else { - if (led_set_brightness(p->led_id, p->brightness) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - led_auto_control(p->led_id, 0); - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_LED_CONTROL, led_command_control, EC_VER_MASK(1)); - -__attribute__((weak)) -void led_control(enum ec_led_id led_id, enum ec_led_state state) -{ - /* - * Default weak implementation that does not affect the state of - * LED. Boards can provide their own implementation. - */ -} diff --git a/common/led_onoff_states.c b/common/led_onoff_states.c deleted file mode 100644 index 48886e5de3..0000000000 --- a/common/led_onoff_states.c +++ /dev/null @@ -1,279 +0,0 @@ -/* 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. - * - * Power and battery LED state control - */ - -#include "battery.h" -#include "charge_state.h" -#include "chipset.h" -#include "console.h" -#include "ec_commands.h" -#include "extpower.h" -#include "hooks.h" -#include "led_common.h" -#include "led_onoff_states.h" -#include "system.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_GPIO, format, ## args) - -/* - * In order to support the battery LED being optional (ex. for Chromeboxes), - * set up default battery table, setter, and variables. - */ -__overridable struct led_descriptor - led_bat_state_table[LED_NUM_STATES][LED_NUM_PHASES]; -__overridable const int led_charge_lvl_1; -__overridable const int led_charge_lvl_2; -__overridable void led_set_color_battery(enum ec_led_colors color) -{ -} - -#ifndef CONFIG_CHARGER -/* Include for the sake of compilation */ -int charge_get_percent(void); -#endif - -static int led_get_charge_percent(void) -{ - return DIV_ROUND_NEAREST(charge_get_display_charge(), 10); -} - -static enum led_states led_get_state(void) -{ - int charge_lvl; - enum led_states new_state = LED_NUM_STATES; - - if (!IS_ENABLED(CONFIG_CHARGER)) - return new_state; - - switch (charge_get_state()) { - case PWR_STATE_CHARGE: - /* Get percent charge */ - charge_lvl = led_get_charge_percent(); - /* Determine which charge state to use */ - if (charge_lvl < led_charge_lvl_1) - new_state = STATE_CHARGING_LVL_1; - else if (charge_lvl < led_charge_lvl_2) - new_state = STATE_CHARGING_LVL_2; - else - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - new_state = STATE_CHARGING_FULL_S5; - else - new_state = STATE_CHARGING_FULL_CHARGE; - break; - case PWR_STATE_DISCHARGE_FULL: - if (extpower_is_present()) { - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - new_state = STATE_CHARGING_FULL_S5; - else - new_state = STATE_CHARGING_FULL_CHARGE; - break; - } - /* Intentional fall-through */ - case PWR_STATE_DISCHARGE /* and PWR_STATE_DISCHARGE_FULL */: - if (chipset_in_state(CHIPSET_STATE_ON)) { -#ifdef CONFIG_LED_ONOFF_STATES_BAT_LOW - if (led_get_charge_percent() < - CONFIG_LED_ONOFF_STATES_BAT_LOW) - new_state = STATE_DISCHARGE_S0_BAT_LOW; - else -#endif - new_state = STATE_DISCHARGE_S0; - } else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - new_state = STATE_DISCHARGE_S3; - else - new_state = STATE_DISCHARGE_S5; - break; - case PWR_STATE_ERROR: - new_state = STATE_BATTERY_ERROR; - break; - case PWR_STATE_CHARGE_NEAR_FULL: - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - new_state = STATE_CHARGING_FULL_S5; - else - new_state = STATE_CHARGING_FULL_CHARGE; - break; - case PWR_STATE_IDLE: /* External power connected in IDLE */ - if (charge_get_flags() & CHARGE_FLAG_FORCE_IDLE) - new_state = STATE_FACTORY_TEST; - else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - new_state = STATE_DISCHARGE_S5; - else - new_state = STATE_DISCHARGE_S0; - break; - default: - /* Other states don't alter LED behavior */ - break; - } - - return new_state; -} - -__overridable enum led_states board_led_get_state(enum led_states desired_state) -{ - return desired_state; -} - -static void led_update_battery(void) -{ - static uint8_t ticks, period; - static int led_state = LED_NUM_STATES; - int phase; - enum led_states desired_state = led_get_state(); - - desired_state = board_led_get_state(desired_state); - - /* - * We always need to check the current state since the value could - * have been manually overwritten. If we're in a new valid state, - * update our ticks and period info. If our new state isn't defined, - * continue using the previous one. - */ - if (desired_state != led_state && desired_state < LED_NUM_STATES) { - /* - * Allow optional CHARGING_FULL_S5 state to fall back to - * FULL_CHARGE if not defined. - */ - if (desired_state == STATE_CHARGING_FULL_S5 && - led_bat_state_table[desired_state][LED_PHASE_0].time == 0) - desired_state = STATE_CHARGING_FULL_CHARGE; - - /* State is changing */ - led_state = desired_state; - /* Reset ticks and period when state changes */ - ticks = 0; - - period = led_bat_state_table[led_state][LED_PHASE_0].time + - led_bat_state_table[led_state][LED_PHASE_1].time; - - } - - /* If this state is undefined, turn the LED off */ - if (period == 0) { - CPRINTS("Undefined LED behavior for battery state %d," - "turning off LED", led_state); - led_set_color_battery(LED_OFF); - return; - } - - /* - * Determine which phase of the state table to use. The phase is - * determined if it falls within first phase time duration. - */ - phase = ticks < led_bat_state_table[led_state][LED_PHASE_0].time ? - 0 : 1; - ticks = (ticks + 1) % period; - - /* Set the color for the given state and phase */ - led_set_color_battery(led_bat_state_table[led_state][phase].color); -} - -/* - * In order to support the power LED being optional, set up default power LED - * table and setter - */ -__overridable const struct led_descriptor - led_pwr_state_table[PWR_LED_NUM_STATES][LED_NUM_PHASES]; -__overridable void led_set_color_power(enum ec_led_colors color) -{ -} - -static enum pwr_led_states pwr_led_get_state(void) -{ - if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) { - if (extpower_is_present()) - return PWR_LED_STATE_SUSPEND_AC; - else - return PWR_LED_STATE_SUSPEND_NO_AC; - } else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - if (system_can_boot_ap()) - return PWR_LED_STATE_OFF; - else - return PWR_LED_STATE_OFF_LOW_POWER; - } else if (chipset_in_state(CHIPSET_STATE_ON)) { - return PWR_LED_STATE_ON; - } - - return PWR_LED_NUM_STATES; -} - -static void led_update_power(void) -{ - static uint8_t ticks, period; - static enum pwr_led_states led_state = PWR_LED_NUM_STATES; - int phase; - enum pwr_led_states desired_state = pwr_led_get_state(); - - /* - * If we're in a new valid state, update our ticks and period info. - * Otherwise, continue to use old state - */ - if (desired_state != led_state && desired_state < PWR_LED_NUM_STATES) { - /* - * Allow optional OFF_LOW_POWER state to fall back to - * OFF not defined, as indicated by no specified phase 0 time. - */ - if (desired_state == PWR_LED_STATE_OFF_LOW_POWER && - led_pwr_state_table[desired_state][LED_PHASE_0].time == 0) - desired_state = PWR_LED_STATE_OFF; - - /* State is changing */ - led_state = desired_state; - /* Reset ticks and period when state changes */ - ticks = 0; - - period = led_pwr_state_table[led_state][LED_PHASE_0].time + - led_pwr_state_table[led_state][LED_PHASE_1].time; - - } - - /* If this state is undefined, turn the LED off */ - if (period == 0) { - CPRINTS("Undefined LED behavior for power state %d," - "turning off LED", led_state); - led_set_color_power(LED_OFF); - return; - } - - /* - * Determine which phase of the state table to use. The phase is - * determined if it falls within first phase time duration. - */ - phase = ticks < led_pwr_state_table[led_state][LED_PHASE_0].time ? - 0 : 1; - ticks = (ticks + 1) % period; - - /* Set the color for the given state and phase */ - led_set_color_power(led_pwr_state_table[led_state][phase].color); - -} - -static void led_init(void) -{ - /* If battery LED is enabled, set it to "off" to start with */ - if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) - led_set_color_battery(LED_OFF); - - /* If power LED is enabled, set it to "off" to start with */ - if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) - led_set_color_power(LED_OFF); - -} -DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); - -/* Called by hook task every hook tick (200 msec) */ -static void led_update(void) -{ - /* - * If battery LED is enabled, set its state based on our power and - * charge - */ - if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) - led_update_battery(); - if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) - led_update_power(); -} -DECLARE_HOOK(HOOK_TICK, led_update, HOOK_PRIO_DEFAULT); diff --git a/common/led_policy_std.c b/common/led_policy_std.c deleted file mode 100644 index e9fe4568a2..0000000000 --- a/common/led_policy_std.c +++ /dev/null @@ -1,202 +0,0 @@ -/* Copyright 2015 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. - * - * Standard Battery LED and Power LED control - * This assumes a red/green battery led and a single power led. - */ - -#include "gpio.h" -#include "hooks.h" -#include "battery.h" -#include "charge_state.h" -#include "chipset.h" -#include "led_common.h" -#include "util.h" -#include "lid_switch.h" - -#ifdef CONFIG_LED_BAT_ACTIVE_LOW -#define BAT_LED_ON 0 -#define BAT_LED_OFF 1 -#else -#define BAT_LED_ON 1 -#define BAT_LED_OFF 0 -#endif - -#ifdef CONFIG_LED_POWER_ACTIVE_LOW -#define POWER_LED_ON 0 -#define POWER_LED_OFF 1 -#else -#define POWER_LED_ON 1 -#define POWER_LED_OFF 0 -#endif - -const enum ec_led_id supported_led_ids[] = { - EC_LED_ID_BATTERY_LED, EC_LED_ID_POWER_LED}; - -const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); - -enum led_color { - LED_OFF = 0, - LED_RED, - LED_AMBER, - LED_GREEN, - LED_WHITE, - LED_COLOR_COUNT /* Number of colors, not a color itself */ -}; - -static int bat_led_set_color(enum led_color color) -{ - switch (color) { - case LED_OFF: - gpio_set_level(GPIO_BAT_LED_GREEN, BAT_LED_OFF); - gpio_set_level(GPIO_BAT_LED_RED, BAT_LED_OFF); - break; - case LED_RED: - gpio_set_level(GPIO_BAT_LED_GREEN, BAT_LED_OFF); - gpio_set_level(GPIO_BAT_LED_RED, BAT_LED_ON); - break; - case LED_AMBER: - gpio_set_level(GPIO_BAT_LED_GREEN, BAT_LED_ON); - gpio_set_level(GPIO_BAT_LED_RED, BAT_LED_ON); - break; - case LED_GREEN: - gpio_set_level(GPIO_BAT_LED_GREEN, BAT_LED_ON); - gpio_set_level(GPIO_BAT_LED_RED, BAT_LED_OFF); - break; - default: - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -static int pwr_led_set_color(enum led_color color) -{ - switch (color) { - case LED_OFF: - gpio_set_level(GPIO_POWER_LED, POWER_LED_OFF); - break; - case LED_WHITE: - gpio_set_level(GPIO_POWER_LED, - lid_is_open() ? POWER_LED_ON : POWER_LED_OFF); - break; - default: - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) -{ - switch (led_id) { - case EC_LED_ID_BATTERY_LED: - brightness_range[EC_LED_COLOR_RED] = 1; - brightness_range[EC_LED_COLOR_GREEN] = 1; - break; - case EC_LED_ID_POWER_LED: - brightness_range[EC_LED_COLOR_WHITE] = 1; - break; - default: - /* ignore */ - break; - } -} - -int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) -{ - switch (led_id) { - case EC_LED_ID_BATTERY_LED: - gpio_set_level(GPIO_BAT_LED_RED, - (brightness[EC_LED_COLOR_RED] != 0) ? - BAT_LED_ON : BAT_LED_OFF); - gpio_set_level(GPIO_BAT_LED_GREEN, - (brightness[EC_LED_COLOR_GREEN] != 0) ? - BAT_LED_ON : BAT_LED_OFF); - break; - case EC_LED_ID_POWER_LED: - gpio_set_level(GPIO_POWER_LED, - (brightness[EC_LED_COLOR_WHITE] != 0) ? - POWER_LED_ON : POWER_LED_OFF); - break; - default: - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -#ifdef HAS_TASK_CHIPSET -static void std_led_shutdown(void) -{ - pwr_led_set_color(LED_OFF); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, std_led_shutdown, HOOK_PRIO_DEFAULT); -#endif - -static void std_led_set_power(void) -{ - static int power_second; - - power_second++; - - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - pwr_led_set_color(LED_OFF); - else if (chipset_in_state(CHIPSET_STATE_ON)) - pwr_led_set_color(LED_WHITE); - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - pwr_led_set_color((power_second & 3) ? LED_OFF : LED_WHITE); -} - -static void std_led_set_battery(void) -{ - static int battery_second; - uint32_t chflags = charge_get_flags(); - - battery_second++; - - /* BAT LED behavior: - * Same as the chromeos spec - * Green/Amber for CHARGE_FLAG_FORCE_IDLE - */ - switch (charge_get_state()) { - case PWR_STATE_CHARGE: - bat_led_set_color(LED_AMBER); - break; - case PWR_STATE_DISCHARGE: - if (charge_get_percent() < 3) - bat_led_set_color((battery_second & 1) - ? LED_OFF : LED_AMBER); - else if (charge_get_percent() < 10) - bat_led_set_color((battery_second & 3) - ? LED_OFF : LED_AMBER); - else - bat_led_set_color(LED_OFF); - break; - case PWR_STATE_ERROR: - bat_led_set_color((battery_second & 1) ? LED_OFF : LED_RED); - break; - case PWR_STATE_CHARGE_NEAR_FULL: - bat_led_set_color(LED_GREEN); - break; - case PWR_STATE_IDLE: /* External power connected in IDLE. */ - if (chflags & CHARGE_FLAG_FORCE_IDLE) - bat_led_set_color( - (battery_second & 0x2) ? LED_GREEN : LED_AMBER); - else - bat_led_set_color(LED_GREEN); - break; - default: - /* Other states don't alter LED behavior */ - break; - } -} - -/** * Called by hook task every 1 sec */ -static void led_second(void) -{ - if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) - std_led_set_power(); - if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) - std_led_set_battery(); -} -DECLARE_HOOK(HOOK_SECOND, led_second, HOOK_PRIO_DEFAULT); - diff --git a/common/led_pwm.c b/common/led_pwm.c deleted file mode 100644 index cc946ba522..0000000000 --- a/common/led_pwm.c +++ /dev/null @@ -1,311 +0,0 @@ -/* 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. - */ - -/* PWM LED control to conform to Chrome OS LED behaviour specification. */ - -/* - * This assumes that a single logical LED is shared between both power and - * charging/battery status. If multiple logical LEDs are present, they all - * follow the same patterns. - */ - -#include "battery.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "led_common.h" -#include "led_pwm.h" -#include "pwm.h" -#include "timer.h" -#include "util.h" - -/* Battery percentage thresholds to blink at different rates. */ -#define CRITICAL_LOW_BATTERY_PERCENTAGE 3 -#define LOW_BATTERY_PERCENTAGE 10 - -#define PULSE_TICK (250 * MSEC) - -static uint8_t led_is_pulsing; - -static int get_led_id_color(enum pwm_led_id id, int color) -{ -#ifdef CONFIG_LED_PWM_ACTIVE_CHARGE_PORT_ONLY - int active_chg_port = charge_manager_get_active_charge_port(); - - /* We should always be able to turn off a LED. */ - if (color == -1) - return -1; - - if (led_is_pulsing) - return color; - - /* The inactive charge port LEDs should be off. */ - if ((int)id != active_chg_port) - return -1; -#endif /* CONFIG_LED_PWM_ACTIVE_CHARGE_PORT_ONLY */ - return color; -} - -void set_pwm_led_color(enum pwm_led_id id, int color) -{ - struct pwm_led duty = { 0 }; - const struct pwm_led *led = &pwm_leds[id]; - - if ((id >= CONFIG_LED_PWM_COUNT) || (id < 0) || - (color >= EC_LED_COLOR_COUNT) || (color < -1)) - return; - - if (color != -1) { - duty.ch0 = led_color_map[color].ch0; - duty.ch1 = led_color_map[color].ch1; - duty.ch2 = led_color_map[color].ch2; - } - - if (led->ch0 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->set_duty(led->ch0, duty.ch0); - if (led->ch1 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->set_duty(led->ch1, duty.ch1); - if (led->ch2 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->set_duty(led->ch2, duty.ch2); -} - -static void set_led_color(int color) -{ - /* - * We must check if auto control is enabled since the LEDs may be - * controlled from the AP at anytime. - */ - if ((led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) || - (led_auto_control_is_enabled(EC_LED_ID_LEFT_LED))) - set_pwm_led_color(PWM_LED0, get_led_id_color(PWM_LED0, color)); - -#if CONFIG_LED_PWM_COUNT >= 2 - if (led_auto_control_is_enabled(EC_LED_ID_RIGHT_LED)) - set_pwm_led_color(PWM_LED1, get_led_id_color(PWM_LED1, color)); -#endif /* CONFIG_LED_PWM_COUNT >= 2 */ -} - -static void set_pwm_led_enable(enum pwm_led_id id, int enable) -{ - const struct pwm_led *led = &pwm_leds[id]; - - if ((id >= CONFIG_LED_PWM_COUNT) || (id < 0)) - return; - - if (led->ch0 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->enable(led->ch0, enable); - if (led->ch1 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->enable(led->ch1, enable); - if (led->ch2 != (enum pwm_channel)PWM_LED_NO_CHANNEL) - led->enable(led->ch2, enable); -} - -static void init_leds_off(void) -{ - /* Turn off LEDs such that they are in a known state with zero duty. */ - set_led_color(-1); - - /* Enable pwm modules for each channels of LEDs */ - set_pwm_led_enable(PWM_LED0, 1); - -#if CONFIG_LED_PWM_COUNT >= 2 - set_pwm_led_enable(PWM_LED1, 1); -#endif /* CONFIG_LED_PWM_COUNT >= 2 */ -} -DECLARE_HOOK(HOOK_INIT, init_leds_off, HOOK_PRIO_INIT_PWM + 1); - -static uint8_t pulse_period; -static uint8_t pulse_ontime; -static enum ec_led_colors pulse_color; -static void update_leds(void); -static void pulse_leds_deferred(void); -DECLARE_DEFERRED(pulse_leds_deferred); -static void pulse_leds_deferred(void) -{ - static uint8_t tick_count; - - if (!led_is_pulsing) { - tick_count = 0; - /* - * Since we're not pulsing anymore, turn the colors off in case - * we were in the "on" time. - */ - set_led_color(-1); - /* Then show the desired state. */ - update_leds(); - return; - } - - if (tick_count < pulse_ontime) - set_led_color(pulse_color); - else - set_led_color(-1); - - tick_count = (tick_count + 1) % pulse_period; - hook_call_deferred(&pulse_leds_deferred_data, PULSE_TICK); -} - -static void pulse_leds(enum ec_led_colors color, int ontime, int period) -{ - pulse_color = color; - pulse_ontime = ontime; - pulse_period = period; - led_is_pulsing = 1; - pulse_leds_deferred(); -} - -static int show_charge_state(void) -{ - enum charge_state chg_st = charge_get_state(); - - /* - * The colors listed below are the default, but can be overridden. - * - * Solid Amber == Charging - * Solid Green == Charging (near full) - * Fast Flash Red == Charging error or battery not present - */ - if (chg_st == PWR_STATE_CHARGE) { - led_is_pulsing = 0; - set_led_color(CONFIG_LED_PWM_CHARGE_COLOR); - } else if (chg_st == PWR_STATE_CHARGE_NEAR_FULL || - chg_st == PWR_STATE_DISCHARGE_FULL) { - led_is_pulsing = 0; - set_led_color(CONFIG_LED_PWM_NEAR_FULL_COLOR); - } else if ((battery_is_present() != BP_YES) || - (chg_st == PWR_STATE_ERROR)) { - /* 500 ms period, 50% duty cycle. */ - pulse_leds(CONFIG_LED_PWM_CHARGE_ERROR_COLOR, 1, 2); - } else { - /* Discharging or not charging. */ -#ifdef CONFIG_LED_PWM_CHARGE_STATE_ONLY - /* - * If we only show the charge state, the only reason we - * would pulse the LEDs is if we had an error. If it no longer - * exists, stop pulsing the LEDs. - */ - led_is_pulsing = 0; -#endif /* CONFIG_LED_PWM_CHARGE_STATE_ONLY */ - return 0; - } - return 1; -} - -#ifndef CONFIG_LED_PWM_CHARGE_STATE_ONLY -static int show_battery_state(void) -{ - int batt_percentage = charge_get_percent(); - - /* - * The colors listed below are the default, but can be overridden. - * - * Fast Flash Amber == Critical Battery - * Slow Flash Amber == Low Battery - */ - if (batt_percentage < CRITICAL_LOW_BATTERY_PERCENTAGE) { - /* Flash amber faster (1 second period, 50% duty cycle) */ - pulse_leds(CONFIG_LED_PWM_LOW_BATT_COLOR, 2, 4); - } else if (batt_percentage < LOW_BATTERY_PERCENTAGE) { - /* Flash amber (4 second period, 50% duty cycle) */ - pulse_leds(CONFIG_LED_PWM_LOW_BATT_COLOR, 8, 16); - } else { - /* Sufficient charge, no need to show anything for this. */ - return 0; - } - return 1; -} - -static int show_chipset_state(void) -{ - /* Reflect the SoC state. */ - led_is_pulsing = 0; - if (chipset_in_state(CHIPSET_STATE_ON)) { - /* The LED must be on in the Active state. */ - set_led_color(CONFIG_LED_PWM_SOC_ON_COLOR); - } else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) { - /* The power LED must pulse in the suspend state. */ - pulse_leds(CONFIG_LED_PWM_SOC_SUSPEND_COLOR, 4, 16); - } else { - /* Chipset is off, no need to show anything for this. */ - return 0; - } - return 1; -} -#endif /* CONFIG_LED_PWM_CHARGE_STATE_ONLY */ - -static void update_leds(void) -{ - /* Reflecting the charge state is the highest priority. */ - if (show_charge_state()) - return; - -#ifndef CONFIG_LED_PWM_CHARGE_STATE_ONLY - if (show_battery_state()) - return; - - if (show_chipset_state()) - return; -#endif /* CONFIG_LED_PWM_CHARGE_STATE_ONLY */ - - set_led_color(-1); -} -DECLARE_HOOK(HOOK_TICK, update_leds, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_CMD_LEDTEST -int command_ledtest(int argc, char **argv) -{ - int enable; - int pwm_led_id; - int led_id; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - pwm_led_id = atoi(argv[1]); - if ((pwm_led_id < 0) || (pwm_led_id >= CONFIG_LED_PWM_COUNT)) - return EC_ERROR_PARAM1; - led_id = supported_led_ids[pwm_led_id]; - - if (argc == 2) { - ccprintf("PWM LED %d: led_id=%d, auto_control=%d\n", - pwm_led_id, led_id, - led_auto_control_is_enabled(led_id) != 0); - return EC_SUCCESS; - } - if (!parse_bool(argv[2], &enable)) - return EC_ERROR_PARAM2; - - /* Inverted because this drives auto control. */ - led_auto_control(led_id, !enable); - - if (argc == 4) { - /* Set the color. */ - if (!strncmp(argv[3], "red", 3)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_RED); - else if (!strncmp(argv[3], "green", 5)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_GREEN); - else if (!strncmp(argv[3], "amber", 5)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_AMBER); - else if (!strncmp(argv[3], "blue", 4)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_BLUE); - else if (!strncmp(argv[3], "white", 5)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_WHITE); - else if (!strncmp(argv[3], "yellow", 6)) - set_pwm_led_color(pwm_led_id, EC_LED_COLOR_YELLOW); - else if (!strncmp(argv[3], "off", 3)) - set_pwm_led_color(pwm_led_id, -1); - else - return EC_ERROR_PARAM3; - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ledtest, command_ledtest, - "<pwm led idx> <enable|disable> [color|off]", ""); -#endif /* defined(CONFIG_CMD_LEDTEST) */ diff --git a/common/lid_angle.c b/common/lid_angle.c deleted file mode 100644 index 8a3775b959..0000000000 --- a/common/lid_angle.c +++ /dev/null @@ -1,202 +0,0 @@ -/* Copyright 2014 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. - */ - -/* Lid angle module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "keyboard_scan.h" -#include "lid_angle.h" -#include "lid_switch.h" -#include "math_util.h" -#include "motion_lid.h" -#include "motion_sense.h" -#include "tablet_mode.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LIDANGLE, outstr) -#define CPRINTS(format, args...) cprints(CC_LIDANGLE, format, ## args) - -/* - * Define the number of previous lid angle measurements to keep for determining - * whether to enable or disable peripherals that are only needed for laptop - * mode. These incude keyboard and trackpad. Note, that in order to change the - * enable/disable state of these peripherals, all stored measurements of the - * lid angle buffer must be in the specified range. - */ -#define LID_ANGLE_BUFFER_SIZE 4 - -/* - * Define two variables to determine if wake source peripherals that are only - * applicable for laptop mode should be enabled or disabled in S3 based on the - * current lid angle. Note, the lid angle is bound to [0, 360]. Here are two - * angles, defined such that we segregate the lid angle space into two regions. - * The first region is the region in which we enable peripherals in S3 and is - * when the lid angle CCW of the small_angle and CW of the large_angle. The - * second region is the region in which we disable peripherals in S3 and is when - * the lid angle is CCW of the large_angle and CW of the small_angle. - * - * Note, the most sensical values are small_angle = 0 and large_angle = 180, - * but, the angle measurement is not perfect, and we know that if the angle is - * near 0 and the lid isn't closed, then the lid must be near 360. So, the - * small_angle is set to a small positive value to make sure we don't swap modes - * when the lid is open all the way but is measuring a small positive value. - */ -static int wake_large_angle = 180; -static const int wake_small_angle = 13; - -/* Define hysteresis value to add stability to the flags. */ -#define LID_ANGLE_HYSTERESIS_DEG 2 - -/* Define max and min values for wake_large_angle. */ -#define LID_ANGLE_MIN_LARGE_ANGLE 0 -#define LID_ANGLE_MAX_LARGE_ANGLE 360 - -/** - * Determine if given angle is in region to enable peripherals. - * - * @param ang Some lid angle in degrees [0, 360] - * - * @return true/false - */ -static int lid_in_range_to_enable_peripherals(int ang) -{ - /* - * If the wake large angle is min or max, then this function should - * return false or true respectively, independent of input angle. - */ - if (wake_large_angle == LID_ANGLE_MIN_LARGE_ANGLE) - return 0; - else if (wake_large_angle == LID_ANGLE_MAX_LARGE_ANGLE) - return 1; - - return (ang >= (wake_small_angle + LID_ANGLE_HYSTERESIS_DEG)) && - (ang <= (wake_large_angle - LID_ANGLE_HYSTERESIS_DEG)); -} - -/** - * Determine if given angle is in region to ignore peripherals. - * - * @param ang Some lid angle in degrees [0, 360] - * - * @return true/false - */ -static int lid_in_range_to_ignore_peripherals(int ang) -{ - /* - * If the wake large angle is min or max, then this function should - * return true or false respectively, independent of input angle. - */ - if (wake_large_angle == LID_ANGLE_MIN_LARGE_ANGLE) - return 1; - else if (wake_large_angle == LID_ANGLE_MAX_LARGE_ANGLE) - return 0; - - return (ang <= (wake_small_angle - LID_ANGLE_HYSTERESIS_DEG)) || - (ang >= (wake_large_angle + LID_ANGLE_HYSTERESIS_DEG)); -} - - -int lid_angle_get_wake_angle(void) -{ - return wake_large_angle; -} - -void lid_angle_set_wake_angle(int ang) -{ - if (ang < LID_ANGLE_MIN_LARGE_ANGLE) - ang = LID_ANGLE_MIN_LARGE_ANGLE; - else if (ang > LID_ANGLE_MAX_LARGE_ANGLE) - ang = LID_ANGLE_MAX_LARGE_ANGLE; - - wake_large_angle = ang; -} - -void lid_angle_update(int lid_ang) -{ - static int lidangle_buffer[LID_ANGLE_BUFFER_SIZE]; - static int index; - int i; - int accept = 1, ignore = 1; - - /* Record most recent lid angle in circular buffer. */ - lidangle_buffer[index] = lid_ang; - index = (index == LID_ANGLE_BUFFER_SIZE-1) ? 0 : index+1; - - /* - * Manage whether or not peripherals are enabled based on lid angle - * history. - */ - for (i = 0; i < LID_ANGLE_BUFFER_SIZE; i++) { - /* - * If any lid angle samples are unreliable, then - * don't change peripheral state. - */ - if (lidangle_buffer[i] == LID_ANGLE_UNRELIABLE) - return; - - /* - * Force all elements of the lid angle buffer to be - * in range of one of the conditions in order to change - * to the corresponding peripheral state. - */ - if (!lid_in_range_to_enable_peripherals(lidangle_buffer[i])) - accept = 0; - if (!lid_in_range_to_ignore_peripherals(lidangle_buffer[i])) - ignore = 0; - } - - /* Enable or disable peripherals as necessary. */ - if (accept) - lid_angle_peripheral_enable(1); - else if (ignore && !accept) - lid_angle_peripheral_enable(0); -} - -static void enable_peripherals(void) -{ - /* - * Make sure lid angle is not disabling peripherals when AP is running. - */ - lid_angle_peripheral_enable(1); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, enable_peripherals, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_TABLET_MODE -static void suspend_peripherals(void) -{ - /* - * Make sure peripherals are disabled in S3 in tablet mode. - */ - if (tablet_get_mode()) - lid_angle_peripheral_enable(0); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, suspend_peripherals, HOOK_PRIO_DEFAULT); -#endif /* CONFIG_TABLET_MODE */ - -#ifdef TEST_BUILD -__overridable void lid_angle_peripheral_enable(int enable) -{ -} -#else -__overridable void lid_angle_peripheral_enable(int enable) -{ - int chipset_in_s0 = chipset_in_state(CHIPSET_STATE_ON); - - if (enable) { - keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_ANGLE); - } else { - /* - * Ensure that the chipset is off before disabling the keyboard. - * When the chipset is on, the EC keeps the keyboard enabled and - * the AP decides whether to ignore input devices or not. - */ - if (!chipset_in_s0) - keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_ANGLE); - } -} -#endif /* TEST_BUILD */ diff --git a/common/lightbar.c b/common/lightbar.c deleted file mode 100644 index f80287941d..0000000000 --- a/common/lightbar.c +++ /dev/null @@ -1,2068 +0,0 @@ -/* - * Copyright 2012 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. - * - * LED controls. - */ - -#ifdef LIGHTBAR_SIMULATION -#include "simulation.h" -#else -#include "battery.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "lb_common.h" -#include "lightbar.h" -#include "lid_switch.h" -#include "motion_sense.h" -#include "pwm.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#endif - -/* - * The Link lightbar had no version command, so defaulted to zero. We have - * added a couple of new commands, so we've updated the version. Any - * optional features in the current version should be marked with flags. - */ -#define LIGHTBAR_IMPLEMENTATION_VERSION 1 -#define LIGHTBAR_IMPLEMENTATION_FLAGS 0 - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LIGHTBAR, outstr) -#define CPRINTS(format, args...) cprints(CC_LIGHTBAR, format, ## args) - -#define FP_SCALE 10000 - -/******************************************************************************/ -/* Here's some state that we might want to maintain across sysjumps, just to - * prevent the lightbar from flashing during normal boot as the EC jumps from - * RO to RW. */ -static struct p_state { - /* What patterns are we showing? */ - enum lightbar_sequence cur_seq; - enum lightbar_sequence prev_seq; - - /* Quantized battery charge level: 0=low 1=med 2=high 3=full. */ - int battery_level; - int battery_percent; - - /* It's either charging or discharging. */ - int battery_is_charging; - - /* Is power-on prevented due to battery level? */ - int battery_is_power_on_prevented; - - /* Pattern variables for state S0. */ - uint16_t w0; /* primary phase */ - uint8_t ramp; /* ramp-in for S3->S0 */ - - uint8_t _pad0; /* next item is __packed */ - - /* Tweakable parameters. */ - union { - struct lightbar_params_v1 p; - struct { - struct lightbar_params_v2_timing timing; - struct lightbar_params_v2_tap tap; - struct lightbar_params_v2_oscillation osc; - struct lightbar_params_v2_brightness bright; - struct lightbar_params_v2_thresholds thlds; - struct lightbar_params_v2_colors colors; - } p_v2; - }; -} st; - -/* Each of the parameters must be less than 120 bytes - * (crbug.com/467716) - */ -#define MAX_PARAM_SIZE 120 -BUILD_ASSERT(sizeof(struct lightbar_params_v2_timing) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_tap) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_oscillation) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_brightness) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_thresholds) <= MAX_PARAM_SIZE); -BUILD_ASSERT(sizeof(struct lightbar_params_v2_colors) <= MAX_PARAM_SIZE); -#undef MAX_PARAM_SIZE - -#define PRIMARY_BLUE 4 -#define PRIMARY_RED 5 -#define PRIMARY_YELLOW 6 -#define PRIMARY_GREEN 7 - -static const struct lightbar_params_v1 default_params = { - .google_ramp_up = 2500, - .google_ramp_down = 10000, - .s3s0_ramp_up = 2000, - .s0_tick_delay = { 45000, 30000 }, /* battery, AC */ - .s0a_tick_delay = { 5000, 3000 }, /* battery, AC */ - .s0s3_ramp_down = 2000, - .s3_sleep_for = 5 * SECOND, /* between checks */ - .s3_ramp_up = 2500, - .s3_ramp_down = 10000, - .s5_ramp_up = 2500, - .s5_ramp_down = 10000, - .tap_tick_delay = 5000, /* oscillation step time */ - .tap_gate_delay = 200 * MSEC, /* segment gating delay */ - .tap_display_time = 3 * SECOND, /* total sequence time */ - - /* TODO (crosbug.com/p/36996): remove unused tap_pct_red */ - .tap_pct_red = 14, /* below this is red */ - .tap_pct_green = 94, /* above this is green */ - .tap_seg_min_on = 35, /* min intensity (%) for "on" */ - .tap_seg_max_on = 100, /* max intensity (%) for "on" */ - .tap_seg_osc = 50, /* amplitude for charging osc */ - .tap_idx = {PRIMARY_RED, PRIMARY_YELLOW, PRIMARY_GREEN}, /* color */ - - .osc_min = { 0x60, 0x60 }, /* battery, AC */ - .osc_max = { 0xd0, 0xd0 }, /* battery, AC */ - .w_ofs = {24, 24}, /* phase offset, 256 == 2*PI */ - - .bright_bl_off_fixed = {0xcc, 0xff}, /* backlight off: battery, AC */ - .bright_bl_on_min = {0xcc, 0xff}, /* backlight on: battery, AC */ - .bright_bl_on_max = {0xcc, 0xff}, /* backlight on: battery, AC */ - - .battery_threshold = { 14, 40, 99 }, /* percent, lowest to highest */ - .s0_idx = { - /* battery: 0 = red, other = blue */ - { PRIMARY_RED, PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE }, - /* AC: always blue */ - { PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE, PRIMARY_BLUE } - }, - .s3_idx = { - /* battery: 0 = red, else off */ - { PRIMARY_RED, 0xff, 0xff, 0xff }, - /* AC: do nothing */ - { 0xff, 0xff, 0xff, 0xff } - }, - .s5_idx = PRIMARY_RED, /* flash red */ - .color = { - /* - * These values have been optically calibrated for the - * Samus LEDs to best match the official colors, described at - * https://sites.google.com/a/google.com/brandsite/the-colours - * See crosbug.com/p/33017 before making any changes. - */ - {0x34, 0x70, 0xb4}, /* 0: Google blue */ - {0xbc, 0x50, 0x2c}, /* 1: Google red */ - {0xd0, 0xe0, 0x00}, /* 2: Google yellow */ - {0x50, 0xa0, 0x40}, /* 3: Google green */ - /* These are primary colors */ - {0x00, 0x00, 0xff}, /* 4: full blue */ - {0xff, 0x00, 0x00}, /* 5: full red */ - {0xff, 0xff, 0x00}, /* 6: full yellow */ - {0x00, 0xff, 0x00}, /* 7: full green */ - }, -}; - -#define LB_SYSJUMP_TAG 0x4c42 /* "LB" */ -static void lightbar_preserve_state(void) -{ - system_add_jump_tag(LB_SYSJUMP_TAG, 0, sizeof(st), &st); -} -DECLARE_HOOK(HOOK_SYSJUMP, lightbar_preserve_state, HOOK_PRIO_DEFAULT); - -static void lightbar_restore_state(void) -{ - const uint8_t *old_state = 0; - int size; - - old_state = system_get_jump_tag(LB_SYSJUMP_TAG, 0, &size); - if (old_state && size == sizeof(st)) { - memcpy(&st, old_state, size); - CPRINTS("LB state restored: %d %d - %d %d/%d", - st.cur_seq, st.prev_seq, - st.battery_is_charging, - st.battery_percent, - st.battery_level); - } else { - st.cur_seq = st.prev_seq = LIGHTBAR_S5; - st.battery_percent = 100; - st.battery_level = LB_BATTERY_LEVELS - 1; - st.w0 = 0; - st.ramp = 0; - memcpy(&st.p, &default_params, sizeof(st.p)); - CPRINTS("LB state initialized"); - } -} - -/******************************************************************************/ -/* The patterns are generally dependent on the current battery level and AC - * state. These functions obtain that information, generally by querying the - * power manager task. In demo mode, the keyboard task forces changes to the - * state by calling the demo_* functions directly. */ -/******************************************************************************/ - -#ifdef CONFIG_PWM_KBLIGHT -static int last_backlight_level; -#endif -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING -test_export_static int google_color_id; -#endif - -static int demo_mode = DEMO_MODE_DEFAULT; - -static int quantize_battery_level(int pct) -{ - int i, bl = 0; - for (i = 0; i < LB_BATTERY_LEVELS - 1; i++) - if (pct >= st.p.battery_threshold[i]) - bl++; - return bl; -} - -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING -test_export_static int lux_level_to_google_color(const int lux) -{ - int i; - - if (!lid_is_open()) { - /* The lid shades the light sensor, use full brightness. */ - if (google_color_id != 0) { - google_color_id = 0; - return 1; - } else { - return 0; - } - } - - /* See if we need to decrease brightness */ - for (i = google_color_id; i < lb_brightness_levels_count ; i++) - if (lux >= lb_brightness_levels[i].lux_down) - break; - if (i > google_color_id) { - google_color_id = i; - return 1; - } - /* See if we need to increase brightness */ - for (i = google_color_id; i > 0; i--) - if (lux < lb_brightness_levels[i - 1].lux_up) - break; - if (i < google_color_id) { - google_color_id = i; - return 1; - } - return 0; -} -#endif - -/* - * Update the known state. - * Return 1 if something changes. - */ -static int get_battery_level(void) -{ - int pct = 0; - int bl, change = 0; - - if (demo_mode) - return 0; - -#ifdef HAS_TASK_CHARGER - st.battery_percent = pct = charge_get_percent(); - st.battery_is_charging = (PWR_STATE_DISCHARGE != charge_get_state()); - st.battery_is_power_on_prevented = charge_prevent_power_on(0); -#endif - - /* Find the new battery level */ - bl = quantize_battery_level(pct); - - /* Use some hysteresis to avoid flickering */ - if (bl < st.battery_level || - (bl > st.battery_level - && pct >= (st.p.battery_threshold[st.battery_level] + 1))) { - st.battery_level = bl; - change = 1; - } - -#ifdef CONFIG_PWM_KBLIGHT - /* - * With nothing else to go on, use the keyboard backlight level to * - * set the brightness. In general, if the keyboard backlight - * is OFF (which it is when ambient is bright), use max brightness for - * lightbar. If keyboard backlight is ON, use keyboard backlight - * brightness. That fails if the keyboard backlight is off because - * someone's watching a movie in the dark, of course. Ideally we should - * just let the AP control it directly. - */ - if (pwm_get_enabled(PWM_CH_KBLIGHT)) { - pct = pwm_get_duty(PWM_CH_KBLIGHT); - pct = (255 * pct) / 100; /* 00 - FF */ - if (pct > st.p.bright_bl_on_max[st.battery_is_charging]) - pct = st.p.bright_bl_on_max[st.battery_is_charging]; - else if (pct < st.p.bright_bl_on_min[st.battery_is_charging]) - pct = st.p.bright_bl_on_min[st.battery_is_charging]; - } else - pct = st.p.bright_bl_off_fixed[st.battery_is_charging]; - - if (pct != last_backlight_level) { - last_backlight_level = pct; - lb_set_brightness(pct); - change = 1; - } -#endif -#ifdef CONFIG_ALS_LIGHTBAR_DIMMING - /* Read last value (in lux) collected by the motion sensor. */ - /* Convert lux into brightness percentage */ - if (lux_level_to_google_color(MOTION_SENSE_LUX)) { - memcpy(st.p.color, lb_brightness_levels[google_color_id].color, - sizeof(lb_brightness_levels[google_color_id].color)); - change = 1; - } -#endif - return change; -} - -/* Forcing functions for demo mode, called by the keyboard task. */ - -/* Up/Down keys */ -#define DEMO_CHARGE_STEP 1 -void demo_battery_level(int inc) -{ - if (!demo_mode) - return; - - st.battery_percent += DEMO_CHARGE_STEP * inc; - - if (st.battery_percent > 100) - st.battery_percent = 100; - else if (st.battery_percent < 0) - st.battery_percent = 0; - - st.battery_level = quantize_battery_level(st.battery_percent); - - CPRINTS("LB demo: battery_percent = %d%%, battery_level=%d", - st.battery_percent, st.battery_level); -} - -/* Left/Right keys */ - -void demo_is_charging(int ischarge) -{ - if (!demo_mode) - return; - - st.battery_is_charging = ischarge; - CPRINTS("LB demo: battery_is_charging=%d", - st.battery_is_charging); -} - -/* Bright/Dim keys */ -void demo_brightness(int inc) -{ - int b; - - if (!demo_mode) - return; - - b = lb_get_brightness() + (inc * 16); - if (b > 0xff) - b = 0xff; - else if (b < 0) - b = 0; - lb_set_brightness(b); -} - -/* T key */ -void demo_tap(void) -{ - if (!demo_mode) - return; - lightbar_sequence(LIGHTBAR_TAP); -} - -/******************************************************************************/ -/* Helper functions and data. */ -/******************************************************************************/ - -#define F(x) (x * FP_SCALE) -static const uint16_t _ramp_table[] = { - F(0.000000), F(0.002408), F(0.009607), F(0.021530), F(0.038060), - F(0.059039), F(0.084265), F(0.113495), F(0.146447), F(0.182803), - F(0.222215), F(0.264302), F(0.308658), F(0.354858), F(0.402455), - F(0.450991), F(0.500000), F(0.549009), F(0.597545), F(0.645142), - F(0.691342), F(0.735698), F(0.777785), F(0.817197), F(0.853553), - F(0.886505), F(0.915735), F(0.940961), F(0.961940), F(0.978470), - F(0.990393), F(0.997592), F(1.000000), -}; -#undef F - -/* This function provides a smooth ramp up from 0.0 to 1.0 and back to 0.0, - * for input from 0x00 to 0xff. */ -static inline int cycle_010(uint8_t i) -{ - uint8_t bucket, index; - - if (i == 128) - return FP_SCALE; - else if (i > 128) - i = 256 - i; - - bucket = i >> 2; - index = i & 0x3; - - return _ramp_table[bucket] + - ((_ramp_table[bucket + 1] - _ramp_table[bucket]) * index >> 2); -} - -/******************************************************************************/ -/* Here's where we keep messages waiting to be delivered to the lightbar task. - * If more than one is sent before the task responds, we only want to deliver - * the latest one. */ -static uint32_t pending_msg; -/* And here's the task event that we use to trigger delivery. */ -#define PENDING_MSG TASK_EVENT_CUSTOM_BIT(0) - -/* Interruptible delay. */ -#define WAIT_OR_RET(A) \ - do { \ - uint32_t msg = task_wait_event(A); \ - uint32_t p_msg = pending_msg; \ - if (msg & PENDING_MSG && p_msg != st.cur_seq) \ - return p_msg; \ - } while (0) - -/******************************************************************************/ -/* Here are the preprogrammed sequences. */ -/******************************************************************************/ - -/* Pulse google colors once, off to on to off. */ -static uint32_t pulse_google_colors(void) -{ - int w, i, r, g, b; - int f; - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_down); - } - - return 0; -} - -/* CPU is waking from sleep. */ -static uint32_t sequence_S3S0(void) -{ - int w, r, g, b; - int f, fmin; - int ci; - uint32_t res; - - lb_init(1); - lb_on(); - get_battery_level(); - - res = pulse_google_colors(); - if (res) - return res; - -#ifndef BLUE_PULSING - /* next sequence */ - return LIGHTBAR_S0; -#endif - - /* Ramp up to starting brightness, using S0 colors */ - ci = st.p.s0_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - ci = 0; - - fmin = st.p.osc_min[st.battery_is_charging] * FP_SCALE / 255; - - for (w = 0; w <= 128; w++) { - f = cycle_010(w) * fmin / FP_SCALE; - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3s0_ramp_up); - } - - /* Initial conditions */ - st.w0 = -256; /* start cycle_npn() quietly */ - st.ramp = 0; - - /* Ready for S0 */ - return LIGHTBAR_S0; -} - -#ifdef BLUE_PULSING - -/* This function provides a pulsing oscillation between -0.5 and +0.5. */ -static inline int cycle_npn(uint16_t i) -{ - if ((i / 256) % 4) - return -FP_SCALE / 2; - return cycle_010(i) - FP_SCALE / 2; -} - -/* CPU is fully on */ -static uint32_t sequence_S0(void) -{ - int tick, last_tick; - timestamp_t start, now; - uint8_t r, g, b; - int i, ci; - uint8_t w_ofs; - uint16_t w; - int f, fmin, fmax, base_s0, osc_s0, f_ramp; - - start = get_time(); - tick = last_tick = 0; - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - - while (1) { - now = get_time(); - - /* Only check the battery state every few seconds. The battery - * charging task doesn't update as quickly as we do, and isn't - * always valid for a bit after jumping from RO->RW. */ - tick = (now.le.lo - start.le.lo) / SECOND; - if (tick % 4 == 3 && tick != last_tick) { - get_battery_level(); - last_tick = tick; - } - - /* Calculate the colors */ - ci = st.p.s0_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - ci = 0; - w_ofs = st.p.w_ofs[st.battery_is_charging]; - fmin = st.p.osc_min[st.battery_is_charging] * FP_SCALE / 255; - fmax = st.p.osc_max[st.battery_is_charging] * FP_SCALE / 255; - base_s0 = (fmax + fmin) / 2; - osc_s0 = fmax - fmin; - f_ramp = st.ramp * FP_SCALE / 255; - - for (i = 0; i < NUM_LEDS; i++) { - w = st.w0 - i * w_ofs * f_ramp / FP_SCALE; - f = base_s0 + osc_s0 * cycle_npn(w) / FP_SCALE; - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - - /* Increment the phase */ - if (st.battery_is_charging) - st.w0--; - else - st.w0++; - - /* Continue ramping in if needed */ - if (st.ramp < 0xff) - st.ramp++; - - i = st.p.s0a_tick_delay[st.battery_is_charging]; - WAIT_OR_RET(i); - } - return 0; -} - -#else /* just simple google colors */ - -static uint32_t sequence_S0(void) -{ - int w, i, r, g, b; - int f, change; - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - - /* Ramp up */ - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r * f / FP_SCALE; - g = st.p.color[i].g * f / FP_SCALE; - b = st.p.color[i].b * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.google_ramp_up); - } - - while (1) { - change = get_battery_level(); - - if (change) { - /* Not really low use google colors */ - if (st.battery_level) { - for (i = 0; i < NUM_LEDS; i++) { - r = st.p.color[i].r; - g = st.p.color[i].g; - b = st.p.color[i].b; - lb_set_rgb(i, r, g, b); - } - } else { - r = st.p.color[PRIMARY_RED].r; - g = st.p.color[PRIMARY_RED].g; - b = st.p.color[PRIMARY_RED].b; - lb_set_rgb(4, r, g, b); - } - } - - WAIT_OR_RET(1 * SECOND); - } - return 0; -} - -#endif - -/* CPU is going to sleep. */ -static uint32_t sequence_S0S3(void) -{ - int w, i, r, g, b; - int f; - uint8_t drop[NUM_LEDS][3]; - uint32_t res; - - /* Grab current colors */ - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &drop[i][0], &drop[i][1], &drop[i][2]); - - /* Fade down to black */ - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = drop[i][0] * f / FP_SCALE; - g = drop[i][1] * f / FP_SCALE; - b = drop[i][2] * f / FP_SCALE; - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(st.p.s0s3_ramp_down); - } - - /* pulse once and done */ - res = pulse_google_colors(); - if (res) - return res; - - /* next sequence */ - return LIGHTBAR_S3; -} - -/* CPU is sleeping */ -static uint32_t sequence_S3(void) -{ - int r, g, b; - int w; - int f; - int ci; - - lb_off(); - lb_init(1); - lb_set_rgb(NUM_LEDS, 0, 0, 0); - get_battery_level(); - while (1) { - WAIT_OR_RET(st.p.s3_sleep_for); - - /* only pulse if we've been given a valid color index */ - ci = st.p.s3_idx[st.battery_is_charging][st.battery_level]; - if (ci >= ARRAY_SIZE(st.p.color)) - continue; - - /* pulse once */ - lb_on(); - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - r = st.p.color[ci].r * f / FP_SCALE; - g = st.p.color[ci].g * f / FP_SCALE; - b = st.p.color[ci].b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s3_ramp_down); - } - - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_off(); - } - return 0; -} - - -/* CPU is powering up. We generally boot fast enough that we don't have time - * to do anything interesting in the S3 state, but go straight on to S0. */ -static uint32_t sequence_S5S3(void) -{ - /* The controllers need 100us after power is applied before they'll - * respond. Don't return early, because we still want to initialize the - * lightbar even if another message comes along while we're waiting. */ - usleep(100); - lb_init(1); - lb_set_rgb(NUM_LEDS, 0, 0, 0); - lb_on(); - /* next sequence */ - return LIGHTBAR_S3; -} - -/* Sleep to off. The S3->S5 transition takes about 10msec, so just wait. */ -static uint32_t sequence_S3S5(void) -{ - lb_off(); - /* next sequence */ - return LIGHTBAR_S5; -} - -/* Pulse S5 color to indicate that the battery is so critically low that it - * must charge first before the system can power on. */ -static uint32_t pulse_s5_color(void) -{ - int r, g, b; - int f; - int w; - struct rgb_s *color = &st.p.color[st.p.s5_idx]; - - for (w = 0; w < 128; w += 2) { - f = cycle_010(w); - r = color->r * f / FP_SCALE; - g = color->g * f / FP_SCALE; - b = color->b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s5_ramp_up); - } - for (w = 128; w <= 256; w++) { - f = cycle_010(w); - r = color->r * f / FP_SCALE; - g = color->g * f / FP_SCALE; - b = color->b * f / FP_SCALE; - lb_set_rgb(NUM_LEDS, r, g, b); - WAIT_OR_RET(st.p.s5_ramp_down); - } - - return 0; -} - -/* CPU is off. Pulse the lightbar if a charger is attached and the battery is - * so low that the system cannot power on. Otherwise, the lightbar loses power - * when the CPU is in S5, so there's nothing to do. We'll just wait here until - * the state changes. */ -static uint32_t sequence_S5(void) -{ - int initialized = 0; - uint32_t res = 0; - - get_battery_level(); - while (1) { - if (!st.battery_is_power_on_prevented || - !st.battery_is_charging) - break; - - if (!initialized) { -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Request that lightbar power rails be turned on. */ - if (lb_power(1)) { - lb_set_rgb(NUM_LEDS, 0, 0, 0); - } -#endif - lb_on(); - initialized = 1; - } - - res = pulse_s5_color(); - if (res) - break; - } - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - if (initialized) - /* Suggest that the lightbar power rails can be shut down. */ - lb_power(0); -#endif - lb_off(); - if (!res) - WAIT_OR_RET(-1); - return res; -} - -/* The AP is going to poke at the lightbar directly, so we don't want the EC - * messing with it. We'll just sit here and ignore all other messages until - * we're told to continue (or until we think the AP is shutting down). - */ -static uint32_t sequence_STOP(void) -{ - uint32_t msg; - - do { - msg = task_wait_event(-1); - CPRINTS("LB %s() got pending_msg %d", __func__, pending_msg); - } while (msg != PENDING_MSG || ( - pending_msg != LIGHTBAR_RUN && - pending_msg != LIGHTBAR_S0S3 && - pending_msg != LIGHTBAR_S3 && - pending_msg != LIGHTBAR_S3S5 && - pending_msg != LIGHTBAR_S5)); - return 0; -} - -/* Telling us to run when we're already running should do nothing. */ -static uint32_t sequence_RUN(void) -{ - return 0; -} - -/* We shouldn't come here, but if we do it shouldn't hurt anything. This - * sequence is to indicate an internal error in the lightbar logic, not an - * error with the Chromebook itself. - */ -static uint32_t sequence_ERROR(void) -{ - lb_init(1); - lb_on(); - - lb_set_rgb(0, 255, 255, 255); - lb_set_rgb(1, 255, 0, 255); - lb_set_rgb(2, 0, 255, 255); - lb_set_rgb(3, 255, 255, 255); - - WAIT_OR_RET(10 * SECOND); - return 0; -} - -static const struct { - uint8_t led; - uint8_t r, g, b; - unsigned int delay; -} konami[] = { - - {1, 0xff, 0xff, 0x00, 0}, - {2, 0xff, 0xff, 0x00, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 100000}, - - {1, 0xff, 0xff, 0x00, 0}, - {2, 0xff, 0xff, 0x00, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0x00, 0xff, 0}, - {3, 0x00, 0x00, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0x00, 0xff, 0}, - {3, 0x00, 0x00, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0xff, 0x00, 0x00, 0}, - {1, 0xff, 0x00, 0x00, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {1, 0x00, 0x00, 0x00, 100000}, - - {2, 0x00, 0xff, 0x00, 0}, - {3, 0x00, 0xff, 0x00, 100000}, - {2, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0xff, 0x00, 0x00, 0}, - {1, 0xff, 0x00, 0x00, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {1, 0x00, 0x00, 0x00, 100000}, - - {2, 0x00, 0xff, 0x00, 0}, - {3, 0x00, 0xff, 0x00, 100000}, - {2, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 100000}, - - {0, 0x00, 0xff, 0xff, 0}, - {2, 0x00, 0xff, 0xff, 100000}, - {0, 0x00, 0x00, 0x00, 0}, - {2, 0x00, 0x00, 0x00, 150000}, - - {1, 0xff, 0x00, 0xff, 0}, - {3, 0xff, 0x00, 0xff, 100000}, - {1, 0x00, 0x00, 0x00, 0}, - {3, 0x00, 0x00, 0x00, 250000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, - - {4, 0xff, 0xff, 0xff, 100000}, - {4, 0x00, 0x00, 0x00, 100000}, -}; - -static uint32_t sequence_KONAMI_inner(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(konami); i++) { - lb_set_rgb(konami[i].led, - konami[i].r, konami[i].g, konami[i].b); - if (konami[i].delay) - WAIT_OR_RET(konami[i].delay); - } - - return 0; -} - -static uint32_t sequence_KONAMI(void) -{ - int tmp; - uint32_t r; - - /* First clear all segments */ - lb_set_rgb(NUM_LEDS, 0, 0, 0); - - /* Force brightness to max, then restore it */ - tmp = lb_get_brightness(); - lb_set_brightness(255); - r = sequence_KONAMI_inner(); - lb_set_brightness(tmp); - return r; -} - -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT -/* Returns 0.0 to 1.0 for val in [min, min + ofs] */ -static int range(int val, int min, int ofs) -{ - if (val <= min) - return 0; - if (val >= min+ofs) - return FP_SCALE; - return (val - min) * FP_SCALE / ofs; -} -#endif - -/* Handy constant */ -#define CUT (100 / NUM_LEDS) - -static uint32_t sequence_TAP_inner(int dir) -{ - enum { RED, YELLOW, GREEN } base_color; - timestamp_t start, now; - uint32_t elapsed_time = 0; - int i, l, ci, max_led; - int f_osc, f_mult; - int gi, gr, gate[NUM_LEDS] = {0, 0, 0, 0}; - uint8_t w = 0; -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT - int f_min, f_delta, f_power; - - f_min = st.p.tap_seg_min_on * FP_SCALE / 100; - f_delta = (st.p.tap_seg_max_on - st.p.tap_seg_min_on) * FP_SCALE / 100; -#endif - f_osc = st.p.tap_seg_osc * FP_SCALE / 100; - - get_battery_level(); - - if (st.battery_level == 0) - base_color = RED; - else if (st.battery_percent > st.p.tap_pct_green) - base_color = GREEN; - else - base_color = YELLOW; - - ci = st.p.tap_idx[base_color]; - max_led = st.battery_percent / CUT; - - start = get_time(); - while (1) { - /* Enable the segments gradually */ - gi = elapsed_time / st.p.tap_gate_delay; - gr = elapsed_time % st.p.tap_gate_delay; - if (gi < NUM_LEDS) - gate[gi] = FP_SCALE * gr / st.p.tap_gate_delay; - if (gi && gi <= NUM_LEDS) - gate[gi - 1] = FP_SCALE; - - for (i = 0; i < NUM_LEDS; i++) { - -#ifdef CONFIG_LIGHTBAR_TAP_DIM_LAST_SEGMENT - if (max_led > i) { - f_mult = FP_SCALE; - } else if (max_led < i) { - f_mult = 0; - } else { - switch (base_color) { - case RED: - f_power = range(st.battery_percent, 0, - st.p.battery_threshold[0] - 1); - break; - case YELLOW: - f_power = range(st.battery_percent, - i * CUT, CUT - 1); - break; - case GREEN: - /* green is always full on */ - f_power = FP_SCALE; - } - f_mult = f_min + f_power * f_delta / FP_SCALE; - } -#else - if (max_led >= i) - f_mult = FP_SCALE; - else if (max_led < i) - f_mult = 0; -#endif - - f_mult = f_mult * gate[i] / FP_SCALE; - - /* Pulse when charging and not yet full */ - if (st.battery_is_charging && - st.battery_percent <= st.p.tap_pct_green) { - int scale = (FP_SCALE - - f_osc * cycle_010(w++) / FP_SCALE); - f_mult = f_mult * scale / FP_SCALE; - } - - l = dir ? i : NUM_LEDS - 1 - i; - lb_set_rgb(l, f_mult * st.p.color[ci].r / FP_SCALE, - f_mult * st.p.color[ci].g / FP_SCALE, - f_mult * st.p.color[ci].b / FP_SCALE); - } - - WAIT_OR_RET(st.p.tap_tick_delay); - - /* Return after some time has elapsed */ - now = get_time(); - elapsed_time = now.le.lo - start.le.lo; - if (elapsed_time > st.p.tap_display_time) - break; - } - return 0; -} - -/* Override the tap direction for testing. -1 means ask the PD MCU. */ -static int force_dir = -1; - -/* Return 0 (left or none) or 1 (right) */ -static int get_tap_direction(void) -{ - static int last_dir; - int dir = 0; - - if (force_dir >= 0) - dir = force_dir; -#ifdef HAS_TASK_PDCMD - else - dir = pd_get_active_charge_port(); -#endif - if (dir < 0) - dir = last_dir; - else if (dir != 1) - dir = 0; - - CPRINTS("LB tap direction %d", dir); - last_dir = dir; - return dir; -} - -static uint32_t sequence_TAP(void) -{ - int i; - uint32_t r; - uint8_t br, save[NUM_LEDS][3]; - int dir; - - /* - * There's a lot of unavoidable glitchiness on the AC_PRESENT interrupt - * each time the EC boots, resulting in fights between the TAP sequence - * and the S5S3->S3->S3S0->S0 sequences. This delay prevents the lights - * from flickering without reducing the responsiveness to manual taps. - */ - WAIT_OR_RET(100 * MSEC); - - /* Which direction should the power meter go? */ - dir = get_tap_direction(); - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Request that the lightbar power rails be turned on. */ - if (lb_power(1)) { - lb_set_rgb(NUM_LEDS, 0, 0, 0); - } -#endif - /* First clear all segments */ - lb_set_rgb(NUM_LEDS, 0, 0, 0); - - lb_on(); - - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &save[i][0], &save[i][1], &save[i][2]); - br = lb_get_brightness(); - lb_set_brightness(255); - - r = sequence_TAP_inner(dir); - - lb_set_brightness(br); - for (i = 0; i < NUM_LEDS; i++) - lb_set_rgb(i, save[i][0], save[i][1], save[i][2]); - -#ifdef CONFIG_LIGHTBAR_POWER_RAILS - /* Suggest that the lightbar power rails can be shut down again. */ - lb_power(0); -#endif - return r; -} - -/****************************************************************************/ -/* Lightbar bytecode interpreter: Lightbyte. */ -/****************************************************************************/ - -/* When a program halts, return this. */ -#define PROGRAM_FINISHED 2 - -static struct lightbar_program cur_prog; -static struct lightbar_program next_prog; -static uint8_t pc; - -static uint8_t led_desc[NUM_LEDS][LB_CONT_MAX][3]; -static uint32_t lb_wait_delay; -static uint32_t lb_ramp_delay; -/* Get one byte of data pointed to by the pc and advance - * the pc forward. - */ -static inline uint32_t decode_8(uint8_t *dest) -{ - if (pc >= cur_prog.size) { - CPRINTS("pc 0x%02x out of bounds", pc); - return EC_RES_INVALID_PARAM; - } - *dest = cur_prog.data[pc++]; - return EC_SUCCESS; -} - -/* Get four bytes of data pointed to by the pc and advance - * the pc forward that amount. - */ -static inline uint32_t decode_32(uint32_t *dest) -{ - if (pc >= cur_prog.size - 3) { - CPRINTS("pc 0x%02x near or out of bounds", pc); - return EC_RES_INVALID_PARAM; - } - *dest = cur_prog.data[pc++] << 24; - *dest |= cur_prog.data[pc++] << 16; - *dest |= cur_prog.data[pc++] << 8; - *dest |= cur_prog.data[pc++]; - return EC_SUCCESS; -} - -/* ON - turn on lightbar */ -static uint32_t lightbyte_ON(void) -{ - lb_on(); - return EC_SUCCESS; -} - -/* OFF - turn off lightbar */ -static uint32_t lightbyte_OFF(void) -{ - lb_off(); - return EC_SUCCESS; -} - -/* JUMP xx - jump to immediate location - * Changes the pc to the one-byte immediate argument. - */ -static uint32_t lightbyte_JUMP(void) -{ - return decode_8(&pc); -} - -/* JUMP_BATTERY aa bb - switch on battery level - * If the battery is low, changes pc to aa. - * If the battery is high, changes pc to bb. - * Otherwise, continues execution as normal. - */ -static uint32_t lightbyte_JUMP_BATTERY(void) -{ - uint8_t low_pc, high_pc; - if (decode_8(&low_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&high_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - get_battery_level(); - if (st.battery_level == 0) - pc = low_pc; - else if (st.battery_level == 3) - pc = high_pc; - - return EC_SUCCESS; -} - -/* JUMP_IF_CHARGING xx - conditional jump to location - * Changes the pc to xx if the device is charging. - */ -static uint32_t lightbyte_JUMP_IF_CHARGING(void) -{ - uint8_t charge_pc; - if (decode_8(&charge_pc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - if (st.battery_is_charging) - pc = charge_pc; - - return EC_SUCCESS; -} - -/* SET_WAIT_DELAY xx xx xx xx - set up to yield processor - * Sets the wait delay to the given four-byte immediate, in - * microseconds. Future WAIT instructions will wait for this - * much time. - */ -static uint32_t lightbyte_SET_WAIT_DELAY(void) -{ - return decode_32(&lb_wait_delay); -} - -/* SET_RAMP_DELAY xx xx xx xx - change ramp speed - * This sets the length of time between ramp/cycle steps to - * the four-byte immediate argument, which represents a duration - * in milliseconds. - */ -static uint32_t lightbyte_SET_RAMP_DELAY(void) -{ - return decode_32(&lb_ramp_delay); -} - -/* WAIT - yield processor for some time - * Yields the processor for some amount of time set by the most - * recent SET_WAIT_DELAY instruction. - */ -static uint32_t lightbyte_WAIT(void) -{ - if (lb_wait_delay != 0) - WAIT_OR_RET(lb_wait_delay); - - return EC_SUCCESS; -} - -/* SET_BRIGHTNESS xx - * Sets the current brightness to the given one-byte - * immediate argument. - */ -static uint32_t lightbyte_SET_BRIGHTNESS(void) -{ - uint8_t val; - if (decode_8(&val) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - lb_set_brightness(val); - return EC_SUCCESS; -} - -/* SET_COLOR_SINGLE cc xx - * SET_COLOR_RGB cc rr gg bb - * Stores a color value in the led_desc structure. - * cc is a bit-packed location to perform the action on. - * - * The high four bits are a bitset for which LEDs to operate on. - * LED 0 is the lowest of the four bits. - * - * The next two bits are the control bits. This should be a value - * in lb_control that is not LB_CONT_MAX, and the corresponding - * color will be the one the action is performed on. - * - * The last two bits are the color bits if this instruction is - * SET_COLOR_SINGLE. They correspond to a LB_COL value for the - * channel to set the color for using the next immediate byte. - * In SET_COLOR_RGB, these bits are don't-cares, as there should - * always be three bytes that follow, which correspond to a - * complete RGB specification. - */ -static uint32_t lightbyte_SET_COLOR_SINGLE(void) -{ - - uint8_t packed_loc, led, control, color, value; - int i; - if (decode_8(&packed_loc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&value) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - led = packed_loc >> 4; - control = (packed_loc >> 2) & 0x3; - color = packed_loc & 0x3; - - if (control >= LB_CONT_MAX) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < NUM_LEDS; i++) - if (led & BIT(i)) - led_desc[i][control][color] = value; - - return EC_SUCCESS; -} - -static uint32_t lightbyte_SET_COLOR_RGB(void) -{ - uint8_t packed_loc, r, g, b, led, control; - int i; - - /* gross */ - if (decode_8(&packed_loc) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&r) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&g) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - if (decode_8(&b) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - led = packed_loc >> 4; - control = (packed_loc >> 2) & 0x3; - - if (control >= LB_CONT_MAX) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < NUM_LEDS; i++) - if (led & BIT(i)) { - led_desc[i][control][LB_COL_RED] = r; - led_desc[i][control][LB_COL_GREEN] = g; - led_desc[i][control][LB_COL_BLUE] = b; - } - - return EC_SUCCESS; -} - -/* GET_COLORS - take current colors and push them to the state - * Gets the current state of the LEDs and puts them in COLOR0. - * Good for the beginning of a program if you need to fade in. - */ -static uint32_t lightbyte_GET_COLORS(void) -{ - int i; - for (i = 0; i < NUM_LEDS; i++) - lb_get_rgb(i, &led_desc[i][LB_CONT_COLOR0][LB_COL_RED], - &led_desc[i][LB_CONT_COLOR0][LB_COL_GREEN], - &led_desc[i][LB_CONT_COLOR0][LB_COL_BLUE]); - - return EC_SUCCESS; -} - -/* SWAP_COLORS - swaps beginning and end colors in state - * Exchanges COLOR0 and COLOR1 on all LEDs. - */ -static uint32_t lightbyte_SWAP_COLORS(void) -{ - int i, j, tmp; - for (i = 0; i < NUM_LEDS; i++) - for (j = 0; j < 3; j++) { - tmp = led_desc[i][LB_CONT_COLOR0][j]; - led_desc[i][LB_CONT_COLOR0][j] = - led_desc[i][LB_CONT_COLOR1][j]; - led_desc[i][LB_CONT_COLOR1][j] = tmp; - } - - return EC_SUCCESS; -} - -static inline int get_interp_value(int led, int color, int interp) -{ - int base = led_desc[led][LB_CONT_COLOR0][color]; - int delta = led_desc[led][LB_CONT_COLOR1][color] - base; - return base + (delta * interp / FP_SCALE); -} - -static void set_all_leds(int color) -{ - int i, r, g, b; - for (i = 0; i < NUM_LEDS; i++) { - r = led_desc[i][color][LB_COL_RED]; - g = led_desc[i][color][LB_COL_GREEN]; - b = led_desc[i][color][LB_COL_BLUE]; - lb_set_rgb(i, r, g, b); - } -} - -static uint32_t ramp_all_leds(int stop_at) -{ - int w, i, r, g, b, f; - for (w = 0; w < stop_at; w++) { - f = cycle_010(w); - for (i = 0; i < NUM_LEDS; i++) { - r = get_interp_value(i, LB_COL_RED, f); - g = get_interp_value(i, LB_COL_GREEN, f); - b = get_interp_value(i, LB_COL_BLUE, f); - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(lb_ramp_delay); - } - return EC_SUCCESS; -} - -/* RAMP_ONCE - simple gradient or color set - * If the ramp delay is set to zero, then this sets the color of - * all LEDs to their respective COLOR1. - * If the ramp delay is nonzero, then this sets their color to - * their respective COLOR0, and takes them via interpolation to - * COLOR1, with the delay time passing in between each step. - */ -static uint32_t lightbyte_RAMP_ONCE(void) -{ - /* special case for instantaneous set */ - if (lb_ramp_delay == 0) { - set_all_leds(LB_CONT_COLOR1); - return EC_SUCCESS; - } - - return ramp_all_leds(128); -} - -/* CYCLE_ONCE - simple cycle or color set - * If the ramp delay is zero, then this sets the color of all LEDs - * to their respective COLOR0. - * If the ramp delay is nonzero, this sets the color of all LEDs - * to COLOR0, then performs a ramp (as in RAMP_ONCE) to COLOR1, - * and finally back to COLOR0. - */ -static uint32_t lightbyte_CYCLE_ONCE(void) -{ - /* special case for instantaneous set */ - if (lb_ramp_delay == 0) { - set_all_leds(LB_CONT_COLOR0); - return EC_SUCCESS; - } - - return ramp_all_leds(256); -} - -/* CYCLE - repeating cycle - * Indefinitely ramps from COLOR0 to COLOR1, taking into - * account the PHASE of each component of each color when - * interpolating. (Different LEDs and different color channels - * on a single LED can start at different places in the cycle, - * though they will advance at the same rate.) - * - * If the ramp delay is zero, this instruction will error out. - */ -static uint32_t lightbyte_CYCLE(void) -{ - int w, i, r, g, b; - - /* what does it mean to cycle indefinitely with 0 delay? */ - if (lb_ramp_delay == 0) - return EC_RES_INVALID_PARAM; - - for (w = 0;; w++) { - for (i = 0; i < NUM_LEDS; i++) { - r = get_interp_value(i, LB_COL_RED, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_RED])); - g = get_interp_value(i, LB_COL_GREEN, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_GREEN])); - b = get_interp_value(i, LB_COL_BLUE, - cycle_010((w & 0xff) + - led_desc[i][LB_CONT_PHASE][LB_COL_BLUE])); - lb_set_rgb(i, r, g, b); - } - WAIT_OR_RET(lb_ramp_delay); - } - return EC_SUCCESS; -} - -/* HALT - return with success - * Show's over. Go back to what you were doing before. - */ -static uint32_t lightbyte_HALT(void) -{ - return PROGRAM_FINISHED; -} - -#undef GET_INTERP_VALUE - -#define OP(NAME, BYTES, MNEMONIC) NAME, -#include "lightbar_opcode_list.h" -enum lightbyte_opcode { - LIGHTBAR_OPCODE_TABLE - MAX_OPCODE -}; -#undef OP - -#define OP(NAME, BYTES, MNEMONIC) lightbyte_ ## NAME, -#include "lightbar_opcode_list.h" -static uint32_t (*lightbyte_dispatch[])(void) = { - LIGHTBAR_OPCODE_TABLE -}; -#undef OP - -#define OP(NAME, BYTES, MNEMONIC) MNEMONIC, -#include "lightbar_opcode_list.h" -static const char * const lightbyte_names[] = { - LIGHTBAR_OPCODE_TABLE -}; -#undef OP - -static uint32_t sequence_PROGRAM(void) -{ - uint8_t saved_brightness; - uint8_t next_inst; - uint32_t rc; - uint8_t old_pc; - - /* load next program */ - memcpy(&cur_prog, &next_prog, sizeof(struct lightbar_program)); - - /* reset program state */ - saved_brightness = lb_get_brightness(); - pc = 0; - memset(led_desc, 0, sizeof(led_desc)); - lb_wait_delay = 0; - lb_ramp_delay = 0; - - lb_on(); - lb_set_brightness(255); - - /* decode-execute loop */ - for (;;) { - old_pc = pc; - if (decode_8(&next_inst) != EC_SUCCESS) - return EC_RES_INVALID_PARAM; - - if (next_inst >= MAX_OPCODE) { - CPRINTS("LB PROGRAM pc: 0x%02x, " - "found invalid opcode 0x%02x", - old_pc, next_inst); - lb_set_brightness(saved_brightness); - return EC_RES_INVALID_PARAM; - } else { - CPRINTS("LB PROGRAM pc: 0x%02x, opcode 0x%02x -> %s", - old_pc, next_inst, lightbyte_names[next_inst]); - rc = lightbyte_dispatch[next_inst](); - if (rc) { - lb_set_brightness(saved_brightness); - return rc; - } - } - - /* yield processor in case we are stuck in a tight loop */ - WAIT_OR_RET(100); - } -} - -/****************************************************************************/ -/* The main lightbar task. It just cycles between various pretty patterns. */ -/****************************************************************************/ - -/* Distinguish "normal" sequences from one-shot sequences */ -static inline int is_normal_sequence(enum lightbar_sequence seq) -{ - return (seq >= LIGHTBAR_S5 && seq <= LIGHTBAR_S3S5); -} - -/* Link each sequence with a command to invoke it. */ -struct lightbar_cmd_t { - const char * const string; - uint32_t (*sequence)(void); -}; - -#define LBMSG(state) { #state, sequence_##state } -#include "lightbar_msg_list.h" -static struct lightbar_cmd_t lightbar_cmds[] = { - LIGHTBAR_MSG_LIST -}; -#undef LBMSG - -void lightbar_task(void) -{ - uint32_t next_seq; - - CPRINTS("LB task starting"); - - lightbar_restore_state(); - - while (1) { - CPRINTS("LB running cur_seq %d %s. prev_seq %d %s", - st.cur_seq, lightbar_cmds[st.cur_seq].string, - st.prev_seq, lightbar_cmds[st.prev_seq].string); - next_seq = lightbar_cmds[st.cur_seq].sequence(); - if (next_seq) { - CPRINTS("LB cur_seq %d %s returned pending msg %d %s", - st.cur_seq, lightbar_cmds[st.cur_seq].string, - next_seq, lightbar_cmds[next_seq].string); - if (st.cur_seq != next_seq) { - if (is_normal_sequence(st.cur_seq)) - st.prev_seq = st.cur_seq; - st.cur_seq = next_seq; - } - } else { - CPRINTS("LB cur_seq %d %s returned value 0", - st.cur_seq, lightbar_cmds[st.cur_seq].string); - switch (st.cur_seq) { - case LIGHTBAR_S5S3: - st.cur_seq = LIGHTBAR_S3; - break; - case LIGHTBAR_S3S0: - st.cur_seq = LIGHTBAR_S0; - break; - case LIGHTBAR_S0S3: - st.cur_seq = LIGHTBAR_S3; - break; - case LIGHTBAR_S3S5: - st.cur_seq = LIGHTBAR_S5; - break; - case LIGHTBAR_STOP: - case LIGHTBAR_RUN: - case LIGHTBAR_ERROR: - case LIGHTBAR_KONAMI: - case LIGHTBAR_TAP: - case LIGHTBAR_PROGRAM: - st.cur_seq = st.prev_seq; - default: - break; - } - } - } -} - -/* Function to request a preset sequence from the lightbar task. */ -void lightbar_sequence_f(enum lightbar_sequence num, const char *f) -{ - if (num > 0 && num < LIGHTBAR_NUM_SEQUENCES) { - CPRINTS("LB %s() requests %d %s", f, num, - lightbar_cmds[num].string); - pending_msg = num; - task_set_event(TASK_ID_LIGHTBAR, PENDING_MSG); - } else - CPRINTS("LB %s() requests %d - ignored", f, num); -} - -/****************************************************************************/ -/* Get notifications from other parts of the system */ - -static uint8_t manual_suspend_control; - -static void lightbar_startup(void) -{ - manual_suspend_control = 0; - lightbar_sequence(LIGHTBAR_S5S3); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, lightbar_startup, HOOK_PRIO_DEFAULT); - -static void lightbar_resume(void) -{ - if (!manual_suspend_control) - lightbar_sequence(LIGHTBAR_S3S0); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, lightbar_resume, HOOK_PRIO_DEFAULT); - -static void lightbar_suspend(void) -{ - if (!manual_suspend_control) - lightbar_sequence(LIGHTBAR_S0S3); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, lightbar_suspend, HOOK_PRIO_DEFAULT); - -static void lightbar_shutdown(void) -{ - lightbar_sequence(LIGHTBAR_S3S5); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, lightbar_shutdown, HOOK_PRIO_DEFAULT); - -/****************************************************************************/ -/* Host commands via LPC bus */ -/****************************************************************************/ - -static enum ec_status lpc_cmd_lightbar(struct host_cmd_handler_args *args) -{ - const struct ec_params_lightbar *in = args->params; - struct ec_response_lightbar *out = args->response; - int rv; - - switch (in->cmd) { - case LIGHTBAR_CMD_DUMP: - lb_hc_cmd_dump(out); - args->response_size = sizeof(out->dump); - break; - case LIGHTBAR_CMD_OFF: - lb_off(); - break; - case LIGHTBAR_CMD_ON: - lb_on(); - break; - case LIGHTBAR_CMD_INIT: - lb_init(1); - break; - case LIGHTBAR_CMD_SET_BRIGHTNESS: - lb_set_brightness(in->set_brightness.num); - break; - case LIGHTBAR_CMD_GET_BRIGHTNESS: - out->get_brightness.num = lb_get_brightness(); - args->response_size = sizeof(out->get_brightness); - break; - case LIGHTBAR_CMD_SEQ: - lightbar_sequence(in->seq.num); - break; - case LIGHTBAR_CMD_REG: - lb_hc_cmd_reg(in); - break; - case LIGHTBAR_CMD_SET_RGB: - lb_set_rgb(in->set_rgb.led, - in->set_rgb.red, - in->set_rgb.green, - in->set_rgb.blue); - break; - case LIGHTBAR_CMD_GET_RGB: - rv = lb_get_rgb(in->get_rgb.led, - &out->get_rgb.red, - &out->get_rgb.green, - &out->get_rgb.blue); - if (rv == EC_RES_SUCCESS) - args->response_size = sizeof(out->get_rgb); - return rv; - case LIGHTBAR_CMD_GET_SEQ: - out->get_seq.num = st.cur_seq; - args->response_size = sizeof(out->get_seq); - break; - case LIGHTBAR_CMD_DEMO: - demo_mode = in->demo.num ? 1 : 0; - CPRINTS("LB_demo %d", demo_mode); - break; - case LIGHTBAR_CMD_GET_DEMO: - out->get_demo.num = demo_mode; - args->response_size = sizeof(out->get_demo); - break; - case LIGHTBAR_CMD_GET_PARAMS_V0: - CPRINTS("LB_get_params_v0 not supported"); - return EC_RES_INVALID_VERSION; - break; - case LIGHTBAR_CMD_SET_PARAMS_V0: - CPRINTS("LB_set_params_v0 not supported"); - return EC_RES_INVALID_VERSION; - break; - case LIGHTBAR_CMD_GET_PARAMS_V1: - CPRINTS("LB_get_params_v1"); - memcpy(&out->get_params_v1, &st.p, sizeof(st.p)); - args->response_size = sizeof(out->get_params_v1); - break; - case LIGHTBAR_CMD_SET_PARAMS_V1: - CPRINTS("LB_set_params_v1"); - memcpy(&st.p, &in->set_params_v1, sizeof(st.p)); - break; - case LIGHTBAR_CMD_SET_PROGRAM: - CPRINTS("LB_set_program"); - memcpy(&next_prog, - &in->set_program, - sizeof(struct lightbar_program)); - break; - case LIGHTBAR_CMD_VERSION: - CPRINTS("LB_version"); - out->version.num = LIGHTBAR_IMPLEMENTATION_VERSION; - out->version.flags = LIGHTBAR_IMPLEMENTATION_FLAGS; - args->response_size = sizeof(out->version); - break; - case LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL: - CPRINTS("LB_manual_suspend_ctrl"); - manual_suspend_control = in->manual_suspend_ctrl.enable; - break; - case LIGHTBAR_CMD_SUSPEND: - CPRINTS("LB_suspend"); - lightbar_sequence(LIGHTBAR_S0S3); - break; - case LIGHTBAR_CMD_RESUME: - CPRINTS("LB_resume"); - lightbar_sequence(LIGHTBAR_S3S0); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_TIMING: - CPRINTS("LB_get_params_v2_timing"); - memcpy(&out->get_params_v2_timing, - &st.p_v2.timing, - sizeof(st.p_v2.timing)); - args->response_size = sizeof(out->get_params_v2_timing); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_TIMING: - CPRINTS("LB_set_params_v2_timing"); - memcpy(&st.p_v2.timing, - &in->set_v2par_timing, - sizeof(struct lightbar_params_v2_timing)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_TAP: - CPRINTS("LB_get_params_v2_tap"); - memcpy(&out->get_params_v2_tap, - &st.p_v2.tap, - sizeof(struct lightbar_params_v2_tap)); - args->response_size = sizeof(out->get_params_v2_tap); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_TAP: - CPRINTS("LB_set_params_v2_tap"); - memcpy(&st.p_v2.tap, - &in->set_v2par_tap, - sizeof(struct lightbar_params_v2_tap)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_OSCILLATION: - CPRINTS("LB_get_params_v2_oscillation"); - memcpy(&out->get_params_v2_osc, &st.p_v2.osc, - sizeof(struct lightbar_params_v2_oscillation)); - args->response_size = sizeof(out->get_params_v2_osc); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_OSCILLATION: - CPRINTS("LB_set_params_v2_oscillation"); - memcpy(&st.p_v2.osc, - &in->set_v2par_osc, - sizeof(struct lightbar_params_v2_oscillation)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_BRIGHTNESS: - CPRINTS("LB_get_params_v2_brightness"); - memcpy(&out->get_params_v2_bright, - &st.p_v2.bright, - sizeof(struct lightbar_params_v2_brightness)); - args->response_size = sizeof(out->get_params_v2_bright); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_BRIGHTNESS: - CPRINTS("LB_set_params_v2_brightness"); - memcpy(&st.p_v2.bright, - &in->set_v2par_bright, - sizeof(struct lightbar_params_v2_brightness)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_THRESHOLDS: - CPRINTS("LB_get_params_v2_thlds"); - memcpy(&out->get_params_v2_thlds, - &st.p_v2.thlds, - sizeof(struct lightbar_params_v2_thresholds)); - args->response_size = sizeof(out->get_params_v2_thlds); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_THRESHOLDS: - CPRINTS("LB_set_params_v2_thlds"); - memcpy(&st.p_v2.thlds, - &in->set_v2par_thlds, - sizeof(struct lightbar_params_v2_thresholds)); - break; - case LIGHTBAR_CMD_GET_PARAMS_V2_COLORS: - CPRINTS("LB_get_params_v2_colors"); - memcpy(&out->get_params_v2_colors, - &st.p_v2.colors, - sizeof(struct lightbar_params_v2_colors)); - args->response_size = sizeof(out->get_params_v2_colors); - break; - case LIGHTBAR_CMD_SET_PARAMS_V2_COLORS: - CPRINTS("LB_set_params_v2_colors"); - memcpy(&st.p_v2.colors, - &in->set_v2par_colors, - sizeof(struct lightbar_params_v2_colors)); - break; - default: - CPRINTS("LB bad cmd 0x%x", in->cmd); - return EC_RES_INVALID_PARAM; - } - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_LIGHTBAR_CMD, - lpc_cmd_lightbar, - EC_VER_MASK(0)); - -/****************************************************************************/ -/* EC console commands */ -/****************************************************************************/ - -#ifdef CONFIG_CONSOLE_CMDHELP -static int help(const char *cmd) -{ - ccprintf("Usage:\n"); - ccprintf(" %s - dump all regs\n", cmd); - ccprintf(" %s off - enter standby\n", cmd); - ccprintf(" %s on - leave standby\n", cmd); - ccprintf(" %s init - load default vals\n", cmd); - ccprintf(" %s brightness [NUM] - set intensity (0-ff)\n", cmd); - ccprintf(" %s seq [NUM|SEQUENCE] - run given pattern" - " (no arg for list)\n", cmd); - ccprintf(" %s CTRL REG VAL - set LED controller regs\n", cmd); - ccprintf(" %s LED RED GREEN BLUE - set color manually" - " (LED=%d for all)\n", cmd, NUM_LEDS); - ccprintf(" %s LED - get current LED color\n", cmd); - ccprintf(" %s demo [0|1] - turn demo mode on & off\n", cmd); -#ifdef LIGHTBAR_SIMULATION - ccprintf(" %s program filename - load lightbyte program\n", cmd); -#endif - ccprintf(" %s version - show current version\n", cmd); - return EC_SUCCESS; -} -#endif - -static uint8_t find_msg_by_name(const char *str) -{ - uint8_t i; - for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) - if (!strcasecmp(str, lightbar_cmds[i].string)) - return i; - - return LIGHTBAR_NUM_SEQUENCES; -} - -static void show_msg_names(void) -{ - int i; - ccprintf("Sequences:"); - for (i = 0; i < LIGHTBAR_NUM_SEQUENCES; i++) - ccprintf(" %s", lightbar_cmds[i].string); - ccprintf("\nCurrent = 0x%x %s\n", st.cur_seq, - lightbar_cmds[st.cur_seq].string); -} - -static int command_lightbar(int argc, char **argv) -{ - int i; - uint8_t num, led, r = 0, g = 0, b = 0; - struct ec_response_lightbar out; - char *e; - - if (argc == 1) { /* no args = dump 'em all */ - lb_hc_cmd_dump(&out); - for (i = 0; i < ARRAY_SIZE(out.dump.vals); i++) - ccprintf(" %02x %02x %02x\n", - out.dump.vals[i].reg, - out.dump.vals[i].ic0, - out.dump.vals[i].ic1); - - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "init")) { - lb_init(1); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "off")) { - lb_off(); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "on")) { - lb_on(); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "version")) { - ccprintf("version %d flags 0x%x\n", - LIGHTBAR_IMPLEMENTATION_VERSION, - LIGHTBAR_IMPLEMENTATION_FLAGS); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "brightness")) { - if (argc > 2) { - num = 0xff & strtoi(argv[2], &e, 16); - lb_set_brightness(num); - } - ccprintf("brightness is %02x\n", lb_get_brightness()); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "demo")) { - if (argc > 2) { - if (!strcasecmp(argv[2], "on") || - argv[2][0] == '1') - demo_mode = 1; - else if (!strcasecmp(argv[2], "off") || - argv[2][0] == '0') - demo_mode = 0; - else - return EC_ERROR_PARAM1; - } - ccprintf("demo mode is %s\n", demo_mode ? "on" : "off"); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "seq")) { - if (argc == 2) { - show_msg_names(); - return 0; - } - num = 0xff & strtoi(argv[2], &e, 16); - if (*e) - num = find_msg_by_name(argv[2]); - if (num >= LIGHTBAR_NUM_SEQUENCES) - return EC_ERROR_PARAM2; - if (argc > 3) /* for testing TAP direction */ - force_dir = strtoi(argv[3], 0, 0); - lightbar_sequence(num); - return EC_SUCCESS; - } - -#ifdef LIGHTBAR_SIMULATION - /* Load a program. */ - if (argc >= 3 && !strcasecmp(argv[1], "program")) { - return lb_load_program(argv[2], &next_prog); - } -#endif - - if (argc == 4) { - struct ec_params_lightbar in; - in.reg.ctrl = strtoi(argv[1], &e, 16); - in.reg.reg = strtoi(argv[2], &e, 16); - in.reg.value = strtoi(argv[3], &e, 16); - lb_hc_cmd_reg(&in); - return EC_SUCCESS; - } - - if (argc == 5) { - led = strtoi(argv[1], &e, 16); - r = strtoi(argv[2], &e, 16); - g = strtoi(argv[3], &e, 16); - b = strtoi(argv[4], &e, 16); - lb_set_rgb(led, r, g, b); - return EC_SUCCESS; - } - - /* Only thing left is to try to read an LED value */ - num = strtoi(argv[1], &e, 16); - if (!(e && *e)) { - if (num >= NUM_LEDS) { - for (i = 0; i < NUM_LEDS; i++) { - lb_get_rgb(i, &r, &g, &b); - ccprintf("%x: %02x %02x %02x\n", i, r, g, b); - } - } else { - lb_get_rgb(num, &r, &g, &b); - ccprintf("%02x %02x %02x\n", r, g, b); - } - return EC_SUCCESS; - } - - -#ifdef CONFIG_CONSOLE_CMDHELP - help(argv[0]); -#endif - - return EC_ERROR_INVAL; -} -DECLARE_CONSOLE_COMMAND(lightbar, command_lightbar, - "[help | COMMAND [ARGS]]", - "Get/set lightbar state"); diff --git a/common/mkbp_fifo.c b/common/mkbp_fifo.c deleted file mode 100644 index 428d6412fc..0000000000 --- a/common/mkbp_fifo.c +++ /dev/null @@ -1,241 +0,0 @@ -/* 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. - * - * Matrix KeyBoard Protocol FIFO buffer implementation - */ - -#include "atomic.h" -#include "common.h" -#include "keyboard_config.h" -#include "mkbp_event.h" -#include "mkbp_fifo.h" -#include "system.h" -#include "task.h" -#include "util.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -/* - * Common FIFO depth. This needs to be big enough not to overflow if a - * series of keys is pressed in rapid succession and the kernel is too busy - * to read them out right away. - * - * RAM usage is (depth * #cols); A 16-entry FIFO will consume 16x13=208 bytes, - * which is non-trivial but not horrible. - */ - -static uint32_t fifo_start; /* first entry */ -static uint32_t fifo_end; /* last entry */ -static uint32_t fifo_entries; /* number of existing entries */ -static uint8_t fifo_max_depth = FIFO_DEPTH; -static struct ec_response_get_next_event fifo[FIFO_DEPTH]; - -/* - * Mutex for critical sections of mkbp_fifo_add(), which is called - * from various tasks. - */ -K_MUTEX_DEFINE(fifo_add_mutex); -/* - * Mutex for critical sections of fifo_remove(), which is called from the - * hostcmd task and from keyboard_clear_buffer(). - */ -K_MUTEX_DEFINE(fifo_remove_mutex); - -static int get_data_size(enum ec_mkbp_event e) -{ - switch (e) { - case EC_MKBP_EVENT_KEY_MATRIX: - return KEYBOARD_COLS_MAX; - -#ifdef CONFIG_HOST_EVENT64 - case EC_MKBP_EVENT_HOST_EVENT64: - return sizeof(uint64_t); -#endif - - case EC_MKBP_EVENT_HOST_EVENT: - case EC_MKBP_EVENT_BUTTON: - case EC_MKBP_EVENT_SWITCH: - case EC_MKBP_EVENT_SYSRQ: - return sizeof(uint32_t); - default: - /* For unknown types, say it's 0. */ - return 0; - } -} - -/** - * Pop MKBP event data from FIFO - * - * @return EC_SUCCESS if entry popped, EC_ERROR_UNKNOWN if FIFO is empty - */ -static int fifo_remove(uint8_t *buffp) -{ - int size; - - mutex_lock(&fifo_remove_mutex); - if (!fifo_entries) { - /* no entry remaining in FIFO : return last known state */ - int last = (fifo_start + FIFO_DEPTH - 1) % FIFO_DEPTH; - - size = get_data_size(fifo[last].event_type); - - memcpy(buffp, &fifo[last].data, size); - mutex_unlock(&fifo_remove_mutex); - - /* - * Bail out without changing any FIFO indices and let the - * caller know something strange happened. The buffer will - * will contain the last known state of the keyboard. - */ - return EC_ERROR_UNKNOWN; - } - - /* Return just the event data. */ - if (buffp) { - size = get_data_size(fifo[fifo_start].event_type); - /* skip over event_type. */ - memcpy(buffp, &fifo[fifo_start].data, size); - } - - fifo_start = (fifo_start + 1) % FIFO_DEPTH; - atomic_sub(&fifo_entries, 1); - mutex_unlock(&fifo_remove_mutex); - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Interface */ - -void mkbp_fifo_depth_update(uint8_t new_max_depth) -{ - fifo_max_depth = new_max_depth; -} - - -void mkbp_fifo_clear_keyboard(void) -{ - int i, new_fifo_entries = 0; - - CPRINTS("clear keyboard MKBP fifo"); - - /* - * Order of these locks is important to prevent deadlock since - * mkbp_fifo_add() may call fifo_remove(). - */ - mutex_lock(&fifo_add_mutex); - mutex_lock(&fifo_remove_mutex); - - /* Reset the end position */ - fifo_end = fifo_start; - - for (i = 0; i < fifo_entries; i++) { - int cur = (fifo_start + i) % FIFO_DEPTH; - - /* Drop keyboard events */ - if (fifo[cur].event_type == EC_MKBP_EVENT_KEY_MATRIX) - continue; - - /* And move other events to the front */ - memmove(&fifo[fifo_end], &fifo[cur], sizeof(fifo[cur])); - fifo_end = (fifo_end + 1) % FIFO_DEPTH; - ++new_fifo_entries; - } - fifo_entries = new_fifo_entries; - - mutex_unlock(&fifo_remove_mutex); - mutex_unlock(&fifo_add_mutex); -} - -void mkbp_clear_fifo(void) -{ - int i; - - CPRINTS("clear MKBP fifo"); - - /* - * Order of these locks is important to prevent deadlock since - * mkbp_fifo_add() may call fifo_remove(). - */ - mutex_lock(&fifo_add_mutex); - mutex_lock(&fifo_remove_mutex); - - fifo_start = 0; - fifo_end = 0; - /* This assignment is safe since both mutexes are held. */ - fifo_entries = 0; - for (i = 0; i < FIFO_DEPTH; i++) - memset(&fifo[i], 0, sizeof(struct ec_response_get_next_event)); - - mutex_unlock(&fifo_remove_mutex); - mutex_unlock(&fifo_add_mutex); -} - -test_mockable int mkbp_fifo_add(uint8_t event_type, const uint8_t *buffp) -{ - uint8_t size; - - mutex_lock(&fifo_add_mutex); - if (fifo_entries >= fifo_max_depth) { - mutex_unlock(&fifo_add_mutex); - CPRINTS("MKBP common FIFO depth %d reached", - fifo_max_depth); - - return EC_ERROR_OVERFLOW; - } - - size = get_data_size(event_type); - fifo[fifo_end].event_type = event_type; - memcpy(&fifo[fifo_end].data, buffp, size); - fifo_end = (fifo_end + 1) % FIFO_DEPTH; - atomic_add(&fifo_entries, 1); - - /* - * If our event didn't generate an interrupt then the host is still - * asleep. In this case, we don't want to queue our event, except if - * another event just woke the host (and wake is already in progress). - */ - if (!mkbp_send_event(event_type) && fifo_entries == 1) - fifo_remove(NULL); - - mutex_unlock(&fifo_add_mutex); - return EC_SUCCESS; -} - -int mkbp_fifo_get_next_event(uint8_t *out, enum ec_mkbp_event evt) -{ - uint8_t t = fifo[fifo_start].event_type; - uint8_t size; - - if (!fifo_entries) - return -1; - - /* - * We need to peek at the next event to check that we were called with - * the correct event. - */ - if (t != (uint8_t)evt) { - /* - * We were called with the wrong event. The next element in the - * FIFO's event type doesn't match with what we were called - * with. Return an error that we're busy. The caller will need - * to call us with the correct event first. - */ - return -EC_ERROR_BUSY; - } - - fifo_remove(out); - - /* Keep sending events if FIFO is not empty */ - if (fifo_entries) - mkbp_send_event(fifo[fifo_start].event_type); - - /* Return the correct size of the data. */ - size = get_data_size(t); - if (size) - return size; - else - return -EC_ERROR_UNKNOWN; -} diff --git a/common/mkbp_info.c b/common/mkbp_info.c deleted file mode 100644 index b3835367cf..0000000000 --- a/common/mkbp_info.c +++ /dev/null @@ -1,147 +0,0 @@ -/* 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. - */ - -/* MKBP info host command */ - -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "keyboard_config.h" -#include "keyboard_mkbp.h" -#include "keyboard_scan.h" -#include "mkbp_input_devices.h" -#include "util.h" - -static uint32_t get_supported_buttons(void) -{ - uint32_t val = 0; - -#ifdef CONFIG_VOLUME_BUTTONS - val |= BIT(EC_MKBP_VOL_UP) | BIT(EC_MKBP_VOL_DOWN); -#endif /* defined(CONFIG_VOLUME_BUTTONS) */ - -#ifdef CONFIG_DEDICATED_RECOVERY_BUTTON - val |= BIT(EC_MKBP_RECOVERY); -#endif /* defined(CONFIG_DEDICATED_RECOVERY_BUTTON) */ - -#ifdef CONFIG_POWER_BUTTON - val |= BIT(EC_MKBP_POWER_BUTTON); -#endif /* defined(CONFIG_POWER_BUTTON) */ - - return val; -} - -static uint32_t get_supported_switches(void) -{ - uint32_t val = 0; - -#ifdef CONFIG_LID_SWITCH - val |= BIT(EC_MKBP_LID_OPEN); -#endif -#ifdef CONFIG_TABLET_MODE_SWITCH - val |= BIT(EC_MKBP_TABLET_MODE); -#endif -#ifdef CONFIG_BASE_ATTACHED_SWITCH - val |= BIT(EC_MKBP_BASE_ATTACHED); -#endif -#ifdef CONFIG_FRONT_PROXIMITY_SWITCH - val |= BIT(EC_MKBP_FRONT_PROXIMITY); -#endif - return val; -} - -static enum ec_status mkbp_get_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_mkbp_info *p = args->params; - - if (args->params_size == 0 || p->info_type == EC_MKBP_INFO_KBD) { - struct ec_response_mkbp_info *r = args->response; - -#ifdef CONFIG_KEYBOARD_PROTOCOL_MKBP - /* Version 0 just returns info about the keyboard. */ - r->rows = KEYBOARD_ROWS; - r->cols = KEYBOARD_COLS_MAX; -#else - r->rows = 0; - r->cols = 0; -#endif /* CONFIG_KEYBOARD_PROTOCOL_MKBP */ - - /* This used to be "switches" which was previously 0. */ - r->reserved = 0; - - args->response_size = sizeof(struct ec_response_mkbp_info); - } else { - union ec_response_get_next_data *r = args->response; - - /* Version 1 (other than EC_MKBP_INFO_KBD) */ - switch (p->info_type) { - case EC_MKBP_INFO_SUPPORTED: - switch (p->event_type) { - case EC_MKBP_EVENT_BUTTON: - r->buttons = get_supported_buttons(); - args->response_size = sizeof(r->buttons); - break; - - case EC_MKBP_EVENT_SWITCH: - r->switches = get_supported_switches(); - args->response_size = sizeof(r->switches); - break; - - default: - /* Don't care for now for other types. */ - return EC_RES_INVALID_PARAM; - } - break; - - case EC_MKBP_INFO_CURRENT: - switch (p->event_type) { -#ifdef HAS_TASK_KEYSCAN - case EC_MKBP_EVENT_KEY_MATRIX: - memcpy(r->key_matrix, keyboard_scan_get_state(), - sizeof(r->key_matrix)); - args->response_size = sizeof(r->key_matrix); - break; -#endif - case EC_MKBP_EVENT_HOST_EVENT: - r->host_event = (uint32_t)host_get_events(); - args->response_size = sizeof(r->host_event); - break; - -#ifdef CONFIG_HOST_EVENT64 - case EC_MKBP_EVENT_HOST_EVENT64: - r->host_event64 = host_get_events(); - args->response_size = sizeof(r->host_event64); - break; -#endif - -#ifdef CONFIG_MKBP_INPUT_DEVICES - case EC_MKBP_EVENT_BUTTON: - r->buttons = mkbp_get_button_state(); - args->response_size = sizeof(r->buttons); - break; - - case EC_MKBP_EVENT_SWITCH: - r->switches = mkbp_get_switch_state(); - args->response_size = sizeof(r->switches); - break; -#endif /* CONFIG_MKBP_INPUT_DEVICES */ - - default: - /* Doesn't make sense for other event types. */ - return EC_RES_INVALID_PARAM; - } - break; - - default: - /* Unsupported query. */ - return EC_RES_ERROR; - } - } - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_MKBP_INFO, mkbp_get_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); diff --git a/common/mkbp_input_devices.c b/common/mkbp_input_devices.c deleted file mode 100644 index e058c9d320..0000000000 --- a/common/mkbp_input_devices.c +++ /dev/null @@ -1,245 +0,0 @@ -/* 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. - */ - -/* Input devices using Matrix Keyboard Protocol [MKBP] events for Chrome EC */ - -#include "base_state.h" -#include "button.h" -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_mkbp.h" -#include "keyboard_scan.h" -#include "lid_switch.h" -#include "mkbp_event.h" -#include "mkbp_fifo.h" -#include "mkbp_input_devices.h" -#include "power_button.h" -#include "tablet_mode.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -/* Buttons and switch state. */ -static uint32_t mkbp_button_state; -static uint32_t mkbp_switch_state; - -static bool mkbp_init_done; - -uint32_t mkbp_get_switch_state(void) -{ - return mkbp_switch_state; -}; - -uint32_t mkbp_get_button_state(void) -{ - return mkbp_button_state; -}; - -void mkbp_button_update(enum keyboard_button_type button, int is_pressed) -{ - switch (button) { - case KEYBOARD_BUTTON_POWER: - mkbp_button_state &= ~BIT(EC_MKBP_POWER_BUTTON); - mkbp_button_state |= (is_pressed << EC_MKBP_POWER_BUTTON); - break; - - case KEYBOARD_BUTTON_VOLUME_UP: - mkbp_button_state &= ~BIT(EC_MKBP_VOL_UP); - mkbp_button_state |= (is_pressed << EC_MKBP_VOL_UP); - break; - - case KEYBOARD_BUTTON_VOLUME_DOWN: - mkbp_button_state &= ~BIT(EC_MKBP_VOL_DOWN); - mkbp_button_state |= (is_pressed << EC_MKBP_VOL_DOWN); - break; - - case KEYBOARD_BUTTON_RECOVERY: - mkbp_button_state &= ~BIT(EC_MKBP_RECOVERY); - mkbp_button_state |= (is_pressed << EC_MKBP_RECOVERY); - break; - - default: - /* ignored. */ - return; - } - - CPRINTS("mkbp buttons: %x", mkbp_button_state); - - mkbp_fifo_add(EC_MKBP_EVENT_BUTTON, - (const uint8_t *)&mkbp_button_state); -}; - -void mkbp_update_switches(uint32_t sw, int state) -{ - mkbp_switch_state &= ~BIT(sw); - mkbp_switch_state |= (!!state << sw); - - CPRINTS("mkbp switches: %x", mkbp_switch_state); - - /* - * Only inform AP mkbp changes when all switches initialized, in case - * of the middle states causing the weird behaviour in the AP side, - * especially when sysjumped while AP up. - */ - if (mkbp_init_done) - mkbp_fifo_add(EC_MKBP_EVENT_SWITCH, - (const uint8_t *)&mkbp_switch_state); -} - - -/*****************************************************************************/ -/* Hooks */ - -#ifdef CONFIG_POWER_BUTTON -/** - * Handle power button changing state. - */ -static void keyboard_power_button(void) -{ - mkbp_button_update(KEYBOARD_BUTTON_POWER, - power_button_is_pressed()); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, keyboard_power_button, - HOOK_PRIO_DEFAULT); -#endif /* defined(CONFIG_POWER_BUTTON) */ - -#ifdef CONFIG_LID_SWITCH -/** - * Handle lid changing state. - */ -static void mkbp_lid_change(void) -{ - mkbp_update_switches(EC_MKBP_LID_OPEN, lid_is_open()); -} -DECLARE_HOOK(HOOK_LID_CHANGE, mkbp_lid_change, HOOK_PRIO_LAST); -DECLARE_HOOK(HOOK_INIT, mkbp_lid_change, HOOK_PRIO_INIT_LID+1); -#endif - -#ifdef CONFIG_TABLET_MODE_SWITCH -static void mkbp_tablet_mode_change(void) -{ - mkbp_update_switches(EC_MKBP_TABLET_MODE, tablet_get_mode()); -} -DECLARE_HOOK(HOOK_TABLET_MODE_CHANGE, mkbp_tablet_mode_change, HOOK_PRIO_LAST); -DECLARE_HOOK(HOOK_INIT, mkbp_tablet_mode_change, HOOK_PRIO_INIT_LID+1); -#endif - -#ifdef CONFIG_BASE_ATTACHED_SWITCH -static void mkbp_base_attached_change(void) -{ - mkbp_update_switches(EC_MKBP_BASE_ATTACHED, base_get_state()); -} -DECLARE_HOOK(HOOK_BASE_ATTACHED_CHANGE, mkbp_base_attached_change, - HOOK_PRIO_LAST); -DECLARE_HOOK(HOOK_INIT, mkbp_base_attached_change, HOOK_PRIO_INIT_LID+1); -#endif - -static void mkbp_report_switch_on_init(void) -{ - /* All switches initialized, report switch state to AP */ - mkbp_init_done = true; - mkbp_fifo_add(EC_MKBP_EVENT_SWITCH, - (const uint8_t *)&mkbp_switch_state); -} -DECLARE_HOOK(HOOK_INIT, mkbp_report_switch_on_init, HOOK_PRIO_LAST); - -#ifdef CONFIG_EMULATED_SYSRQ -void host_send_sysrq(uint8_t key) -{ - uint32_t value = key; - - mkbp_fifo_add(EC_MKBP_EVENT_SYSRQ, (const uint8_t *)&value); -} -#endif - -/*****************************************************************************/ -/* Events */ - -static int mkbp_button_get_next_event(uint8_t *out) -{ - return mkbp_fifo_get_next_event(out, EC_MKBP_EVENT_BUTTON); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_BUTTON, mkbp_button_get_next_event); - -static int switch_get_next_event(uint8_t *out) -{ - return mkbp_fifo_get_next_event(out, EC_MKBP_EVENT_SWITCH); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_SWITCH, switch_get_next_event); - -#ifdef CONFIG_EMULATED_SYSRQ -static int sysrq_get_next_event(uint8_t *out) -{ - return mkbp_fifo_get_next_event(out, EC_MKBP_EVENT_SYSRQ); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_SYSRQ, sysrq_get_next_event); -#endif - -/************************ Keyboard press simulation ************************/ -#ifndef HAS_TASK_KEYSCAN -/* Keys simulated-pressed */ -static uint8_t __bss_slow simulated_key[KEYBOARD_COLS_MAX]; -uint8_t keyboard_cols = KEYBOARD_COLS_MAX; - -/* For boards without a keyscan task, try and simulate keyboard presses. */ -static void simulate_key(int row, int col, int pressed) -{ - if ((simulated_key[col] & BIT(row)) == ((pressed ? 1 : 0) << row)) - return; /* No change */ - - simulated_key[col] &= ~BIT(row); - if (pressed) - simulated_key[col] |= BIT(row); - - mkbp_fifo_add((uint8_t)EC_MKBP_EVENT_KEY_MATRIX, simulated_key); -} - -static int command_mkbp_keyboard_press(int argc, char **argv) -{ - if (argc == 1) { - int i, j; - - ccputs("Simulated keys:\n"); - for (i = 0; i < keyboard_cols; ++i) { - if (simulated_key[i] == 0) - continue; - for (j = 0; j < KEYBOARD_ROWS; ++j) - if (simulated_key[i] & BIT(j)) - ccprintf("\t%d %d\n", i, j); - } - - } else if (argc == 3 || argc == 4) { - int r, c, p; - char *e; - - c = strtoi(argv[1], &e, 0); - if (*e || c < 0 || c >= keyboard_cols) - return EC_ERROR_PARAM1; - - r = strtoi(argv[2], &e, 0); - if (*e || r < 0 || r >= KEYBOARD_ROWS) - return EC_ERROR_PARAM2; - - if (argc == 3) { - /* Simulate a press and release */ - simulate_key(r, c, 1); - simulate_key(r, c, 0); - } else { - p = strtoi(argv[3], &e, 0); - if (*e || p < 0 || p > 1) - return EC_ERROR_PARAM3; - - simulate_key(r, c, p); - } - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(kbpress, command_mkbp_keyboard_press, - "[col row [0 | 1]]", - "Simulate keypress"); - -#endif /* !defined(HAS_TASK_KEYSCAN) */ diff --git a/common/mock/README.md b/common/mock/README.md deleted file mode 100644 index c7695531b6..0000000000 --- a/common/mock/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Common Mocks - -This directory holds mock implementations for use in fuzzers and tests. - -Each mock is given some friendly build name, like ROLLBACK or FP_SENSOR. This -name is defined in [common/mock/build.mk](build.mk) and referenced from unit -tests and fuzzers' `.mocklist` file. - -## Creating a new mock - -* Add the mock source to [common/mock](/common/mock) and the optional header - file to [include/mock](/include/mock). Header files are only necessary if - you want to expose additional [mock control](#mock-controls) - functions/variables. See the [Design Patterns](#design-patterns) section for - more detail on design patterns. -* Add a new entry in [common/mock/build.mk](build.mk) that is conditioned on - your mock's name. - -If a unit test or fuzzer requests this mock, the build system will set the -variable `HAS_MOCK_<BUILD_NAME>` to `y` at build time. This variable is used to -conditionally include the mock source in [common/mock/build.mk](build.mk). - -Example line from [common/mock/build.mk](build.mk): - -```make -# Mocks -mock-$(HAS_MOCK_ROLLBACK) += mock/rollback_mock.o -``` - -## Using a mock - -Unit tests and fuzzers can request a particular mock by adding an entry to their -`.mocklist` file. The mocklist file is similar to a `.tasklist` file, where it -is named according to the test/fuzz's name followed by `.mocklist`, like -`fpsensor.mocklist`. The mocklist file is optional, so you may need to create -one. - -Example `.mocklist`: - -```c -/* 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. - */ - - #define CONFIG_TEST_MOCK_LIST \ - MOCK(ROLLBACK) \ - MOCK(FP_SENSOR) -``` - -If you need additional [mock control](#mock-controls) functionality, you may -need to include the mock's header file, which is prepended with `mock/` in the -include line. - -For example, to control the return values of the rollback mock: - -```c -#include "mock/rollback_mock.h" - -void yourfunction() { - mock_ctrl_rollback.get_secret_fail = true; -} -``` - -## Mock Controls - -Mocks can change their behavior by exposing "mock controls". - -We do this, most commonly, by exposing an additional global struct per mock that -acts as the settings for the mock implementation. The mock user can then modify -fields of the struct to change the mock's behavior. For example, the -`fp_sensor_init_return` field may control what value the mocked `fp_sensor_init` -function returns. - -The declaration for these controls are specified in the mock's header file, -which resides in [include/mock](/include/mock). - -## Design Patterns - -* When creating mock controls, consider placing all your mock parameters in - one externally facing struct, like in - [fp_sensor_mock.h](/include/mock/fp_sensor_mock.h). The primary reason for - this is to allow the mock to be easily used by a fuzzer (write random bytes - into the struct with memcpy). -* When following the above pattern, please provide a macro for resetting - default values for this struct, like in - [fp_sensor_mock.h](/include/mock/fp_sensor_mock.h). This allows unit tests - to quickly reset the mock state/parameters before each unrelated unit test. diff --git a/common/mock/battery_mock.c b/common/mock/battery_mock.c deleted file mode 100644 index 63e94c660b..0000000000 --- a/common/mock/battery_mock.c +++ /dev/null @@ -1,211 +0,0 @@ -/* 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 "battery.h" -#include "string.h" - -/***************************************************************************** - * Battery functions needed to enable CONFIG_BATTERY - */ -static int battery_soc_value = 100; -int board_get_battery_soc(void) -{ - return battery_soc_value; -} -void set_battery_soc(int new_value) -{ - battery_soc_value = new_value; -} - -static int battery_status_value; -int battery_status(int *status) -{ - *status = battery_status_value; - return EC_SUCCESS; -} -void set_battery_status(int new_value) -{ - battery_status_value = new_value; -} - -static int battery_serial_number_value; -int battery_serial_number(int *serial) -{ - *serial = battery_serial_number_value; - return EC_SUCCESS; -} -void set_battery_serial_number(int new_value) -{ - battery_serial_number_value = new_value; -} - -static int battery_design_voltage_value = 5000; -int battery_design_voltage(int *voltage) -{ - *voltage = battery_design_voltage_value; - return EC_SUCCESS; -} -void set_battery_design_voltage(int new_value) -{ - battery_design_voltage_value = new_value; -} - -static int battery_mode_value; -int battery_get_mode(int *mode) -{ - *mode = battery_mode_value; - return EC_SUCCESS; -} -void set_battery_mode(int new_value) -{ - battery_mode_value = new_value; -} - -static int battery_soc_abs_value = 100; -int battery_state_of_charge_abs(int *percent) -{ - *percent = battery_soc_abs_value; - return EC_SUCCESS; -} -void set_battery_soc_abs(int new_value) -{ - battery_soc_abs_value = new_value; -} - -static int battery_remaining_capacity_value = 100; -int battery_remaining_capacity(int *capacity) -{ - *capacity = battery_remaining_capacity_value; - return EC_SUCCESS; -} -void set_battery_remaining_capacity(int new_value) -{ - battery_remaining_capacity_value = new_value; -} - -static int battery_full_charge_capacity_value = 100; -int battery_full_charge_capacity(int *capacity) -{ - *capacity = battery_full_charge_capacity_value; - return EC_SUCCESS; -} -void set_battery_full_charge_capacity(int new_value) -{ - battery_full_charge_capacity_value = new_value; -} - -static int battery_design_capacity_value = 100; -int battery_design_capacity(int *capacity) -{ - *capacity = battery_design_capacity_value; - return EC_SUCCESS; -} -void set_battery_design_capacity(int new_value) -{ - battery_design_capacity_value = new_value; -} - -static int battery_time_to_empty_value = 60; -int battery_time_to_empty(int *minutes) -{ - *minutes = battery_time_to_empty_value; - return EC_SUCCESS; -} -void set_battery_time_to_empty(int new_value) -{ - battery_time_to_empty_value = new_value; -} - -static int battery_run_time_to_empty_value = 60; -int battery_run_time_to_empty(int *minutes) -{ - *minutes = battery_run_time_to_empty_value; - return EC_SUCCESS; -} -void set_battery_run_time_to_empty(int new_value) -{ - battery_run_time_to_empty_value = new_value; -} - -static int battery_time_to_full_value; -int battery_time_to_full(int *minutes) -{ - *minutes = battery_time_to_full_value; - return EC_SUCCESS; -} -void set_battery_time_to_full(int new_value) -{ - battery_time_to_full_value = new_value; -} - -#define MAX_DEVICE_NAME_LENGTH 40 -static char battery_device_name_value[MAX_DEVICE_NAME_LENGTH+1] = "?"; -int battery_device_name(char *dest, int size) -{ - int i; - - for (i = 0; i < size && i < MAX_DEVICE_NAME_LENGTH; ++i) - dest[i] = battery_device_name_value[i]; - for (; i < size; ++i) - dest[i] = '\0'; - return EC_SUCCESS; -} -void set_battery_device_name(char *new_value) -{ - int i; - int size = strlen(new_value); - - for (i = 0; i < size && i < MAX_DEVICE_NAME_LENGTH; ++i) - battery_device_name_value[i] = new_value[i]; - for (; i < MAX_DEVICE_NAME_LENGTH+1; ++i) - battery_device_name_value[i] = '\0'; -} - -#define MAX_DEVICE_CHEMISTRY_LENGTH 40 -static char battery_device_chemistry_value[MAX_DEVICE_CHEMISTRY_LENGTH+1] = "?"; -int battery_device_chemistry(char *dest, int size) -{ - int i; - - for (i = 0; i < size && i < MAX_DEVICE_CHEMISTRY_LENGTH; ++i) - dest[i] = battery_device_chemistry_value[i]; - for (; i < size; ++i) - dest[i] = '\0'; - return EC_SUCCESS; -} -void set_battery_device_chemistry(char *new_value) -{ - int i; - int size = strlen(new_value); - - for (i = 0; i < size && i < MAX_DEVICE_CHEMISTRY_LENGTH; ++i) - battery_device_chemistry_value[i] = new_value[i]; - for (; i < MAX_DEVICE_CHEMISTRY_LENGTH+1; ++i) - battery_device_chemistry_value[i] = '\0'; -} - -static int battery_current_value = 3000; -static int battery_desired_current_value = 3000; -static int battery_desired_voltage_value = 5000; -static int battery_is_present_value = BP_YES; -static int battery_temperature_value = 20; -static int battery_voltage_value = 5000; -void battery_get_params(struct batt_params *batt) -{ - struct batt_params batt_new = {0}; - - batt_new.temperature = battery_temperature_value; - batt_new.state_of_charge = battery_soc_value; - batt_new.voltage = battery_voltage_value; - batt_new.current = battery_current_value; - batt_new.desired_voltage = battery_desired_voltage_value; - batt_new.desired_current = battery_desired_current_value; - batt_new.remaining_capacity = battery_remaining_capacity_value; - batt_new.full_capacity = battery_full_charge_capacity_value; - batt_new.status = battery_status_value; - batt_new.is_present = battery_is_present_value; - - memcpy(batt, &batt_new, sizeof(*batt)); -} diff --git a/common/mock/build.mk b/common/mock/build.mk deleted file mode 100644 index 91607b2b1e..0000000000 --- a/common/mock/build.mk +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -# See common/mock/README.md for more information. - -mock-$(HAS_MOCK_BATTERY) += battery_mock.o -mock-$(HAS_MOCK_CHARGE_MANAGER) += charge_manager_mock.o -mock-$(HAS_MOCK_FP_SENSOR) += fp_sensor_mock.o -mock-$(HAS_MOCK_FPSENSOR_DETECT) += fpsensor_detect_mock.o -mock-$(HAS_MOCK_FPSENSOR_STATE) += fpsensor_state_mock.o -mock-$(HAS_MOCK_MKBP_EVENTS) += mkbp_events_mock.o -mock-$(HAS_MOCK_ROLLBACK) += rollback_mock.o -mock-$(HAS_MOCK_TCPC) += tcpc_mock.o -mock-$(HAS_MOCK_TCPM) += tcpm_mock.o -mock-$(HAS_MOCK_TCPCI_I2C) += tcpci_i2c_mock.o -mock-$(HAS_MOCK_TIMER) += timer_mock.o -mock-$(HAS_MOCK_USB_MUX) += usb_mux_mock.o -mock-$(HAS_MOCK_USB_PE_SM) += usb_pe_sm_mock.o -mock-$(HAS_MOCK_USB_TC_SM) += usb_tc_sm_mock.o -mock-$(HAS_MOCK_USB_PD_DPM) += usb_pd_dpm_mock.o -mock-$(HAS_MOCK_DP_ALT_MODE) += dp_alt_mode_mock.o -mock-$(HAS_MOCK_USB_PRL) += usb_prl_mock.o diff --git a/common/mock/charge_manager_mock.c b/common/mock/charge_manager_mock.c deleted file mode 100644 index 11661d2b2e..0000000000 --- a/common/mock/charge_manager_mock.c +++ /dev/null @@ -1,50 +0,0 @@ -/* 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. - */ - -/** - * @file - * @brief Mock charge_manager - */ - -#include <stdlib.h> - -#include "charge_manager.h" -#include "common.h" -#include "mock/charge_manager_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap) -{ -} - -void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil) -{ -} - -int charge_manager_get_selected_charge_port(void) -{ - return 0; -} - -int charge_manager_get_active_charge_port(void) -{ - return 0; -} - -int charge_manager_get_vbus_voltage(int port) -{ - return mock_ctrl_charge_manager.vbus_voltage_mv; -} - -void mock_charge_manager_set_vbus_voltage(int voltage_mv) -{ - mock_ctrl_charge_manager.vbus_voltage_mv = voltage_mv; -} - -struct mock_ctrl_charge_manager mock_ctrl_charge_manager = -MOCK_CTRL_DEFAULT_CHARGE_MANAGER; diff --git a/common/mock/dp_alt_mode_mock.c b/common/mock/dp_alt_mode_mock.c deleted file mode 100644 index c489d39830..0000000000 --- a/common/mock/dp_alt_mode_mock.c +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Mock for DisplayPort alternate mode support - * Refer to VESA DisplayPort Alt Mode on USB Type-C Standard, version 2.0, - * section 5.2 - */ - -#include "usb_dp_alt_mode.h" -#include "mock/dp_alt_mode_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -void mock_dp_alt_mode_reset(void) -{ - /* Nothing to do right now, but in the future ... */ -} - -void dp_init(int port) -{ - CPRINTS("C%d: DP init", port); -} diff --git a/common/mock/fp_sensor_mock.c b/common/mock/fp_sensor_mock.c deleted file mode 100644 index 363f092ff1..0000000000 --- a/common/mock/fp_sensor_mock.c +++ /dev/null @@ -1,87 +0,0 @@ -/* 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. - */ - -/** - * @file - * @brief Mock fpsensor private driver - */ - -#include <stdlib.h> - -#include "common.h" -#include "fpsensor.h" -#include "mock/fp_sensor_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_ctrl_fp_sensor mock_ctrl_fp_sensor = MOCK_CTRL_DEFAULT_FP_SENSOR; - -int fp_sensor_init(void) -{ - return mock_ctrl_fp_sensor.fp_sensor_init_return; -} - -int fp_sensor_deinit(void) -{ - return mock_ctrl_fp_sensor.fp_sensor_deinit_return; -} - -int fp_sensor_get_info(struct ec_response_fp_info *resp) -{ - resp->version = 0; - return mock_ctrl_fp_sensor.fp_sensor_get_info_return; -} - -void fp_sensor_low_power(void) -{ -} - -void fp_sensor_configure_detect(void) -{ -} - -enum finger_state fp_sensor_finger_status(void) -{ - return mock_ctrl_fp_sensor.fp_sensor_finger_status_return; -} - -int fp_sensor_acquire_image(uint8_t *image_data) -{ - return mock_ctrl_fp_sensor.fp_sensor_acquire_image_return; -} - -int fp_sensor_acquire_image_with_mode(uint8_t *image_data, int mode) -{ - return mock_ctrl_fp_sensor.fp_sensor_acquire_image_with_mode_return; -} - -int fp_finger_match(void *templ, uint32_t templ_count, - uint8_t *image, int32_t *match_index, - uint32_t *update_bitmap) -{ - return mock_ctrl_fp_sensor.fp_finger_match_return; -} - -int fp_enrollment_begin(void) -{ - return mock_ctrl_fp_sensor.fp_enrollment_begin_return; -} - -int fp_enrollment_finish(void *templ) -{ - return mock_ctrl_fp_sensor.fp_enrollment_finish_return; -} - -int fp_finger_enroll(uint8_t *image, int *completion) -{ - return mock_ctrl_fp_sensor.fp_finger_enroll_return; -} - -int fp_maintenance(void) -{ - return mock_ctrl_fp_sensor.fp_maintenance_return; -} diff --git a/common/mock/fpsensor_detect_mock.c b/common/mock/fpsensor_detect_mock.c deleted file mode 100644 index 6e3ca839f1..0000000000 --- a/common/mock/fpsensor_detect_mock.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2020 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 "mock/fpsensor_detect_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_ctrl_fpsensor_detect mock_ctrl_fpsensor_detect = - MOCK_CTRL_DEFAULT_FPSENSOR_DETECT; - -enum fp_sensor_type get_fp_sensor_type(void) -{ - return mock_ctrl_fpsensor_detect.get_fp_sensor_type_return; -} - -enum fp_transport_type get_fp_transport_type(void) -{ - return mock_ctrl_fpsensor_detect.get_fp_transport_type_return; -} - -enum fp_sensor_spi_select get_fp_sensor_spi_select(void) -{ - return mock_ctrl_fpsensor_detect.get_fp_sensor_spi_select_return; -} diff --git a/common/mock/fpsensor_state_mock.c b/common/mock/fpsensor_state_mock.c deleted file mode 100644 index c3092fe860..0000000000 --- a/common/mock/fpsensor_state_mock.c +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright 2020 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 <stddef.h> -#include <string.h> - -#include "common.h" -#include "ec_commands.h" -#include "test_util.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -const uint8_t default_fake_tpm_seed[] = { - 0xd9, 0x71, 0xaf, 0xc4, 0xcd, 0x36, 0xe3, 0x60, 0xf8, 0x5a, 0xa0, - 0xa6, 0x2c, 0xb3, 0xf5, 0xe2, 0xeb, 0xb9, 0xd8, 0x2f, 0xb5, 0x78, - 0x5c, 0x79, 0x82, 0xce, 0x06, 0x3f, 0xcc, 0x23, 0xb9, 0xe7, -}; -BUILD_ASSERT(sizeof(default_fake_tpm_seed) == FP_CONTEXT_TPM_BYTES); - -int fpsensor_state_mock_set_tpm_seed( - const uint8_t tpm_seed[FP_CONTEXT_TPM_BYTES]) -{ - struct ec_params_fp_seed params; - - params.struct_version = FP_TEMPLATE_FORMAT_VERSION; - memcpy(params.seed, tpm_seed, FP_CONTEXT_TPM_BYTES); - - return test_send_host_command(EC_CMD_FP_SEED, 0, ¶ms, - sizeof(params), NULL, 0); -} diff --git a/common/mock/mkbp_events_mock.c b/common/mock/mkbp_events_mock.c deleted file mode 100644 index d42c06fdec..0000000000 --- a/common/mock/mkbp_events_mock.c +++ /dev/null @@ -1,26 +0,0 @@ -/* 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. - */ - -/** - * @file - * @brief Mock event handling for MKBP keyboard protocol - */ - -#include <stdint.h> - -#include "common.h" -#include "mock/mkbp_events_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_ctrl_mkbp_events mock_ctrl_mkbp_events = - MOCK_CTRL_DEFAULT_MKBP_EVENTS; - -int mkbp_send_event(uint8_t event_type) -{ - return mock_ctrl_mkbp_events.mkbp_send_event_return; -} diff --git a/common/mock/rollback_mock.c b/common/mock/rollback_mock.c deleted file mode 100644 index 2b26d9d8d7..0000000000 --- a/common/mock/rollback_mock.c +++ /dev/null @@ -1,41 +0,0 @@ -/* 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. - */ - -/** - * @file - * @brief Mock rollback block library - */ - -#include <stdint.h> -#include <string.h> - -#include "common.h" -#include "compile_time_macros.h" -#include "util.h" -#include "mock/rollback_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_ctrl_rollback mock_ctrl_rollback = MOCK_CTRL_DEFAULT_ROLLBACK; - -static const uint8_t fake_rollback_secret[] = { - 0xcf, 0xe3, 0x23, 0x76, 0x35, 0x04, 0xc2, 0x0f, - 0x0d, 0xb6, 0x02, 0xa9, 0x68, 0xba, 0x2a, 0x61, - 0x86, 0x2a, 0x85, 0xd1, 0xca, 0x09, 0x54, 0x8a, - 0x6b, 0xe2, 0xe3, 0x38, 0xde, 0x5d, 0x59, 0x14, -}; - -BUILD_ASSERT(sizeof(fake_rollback_secret) == CONFIG_ROLLBACK_SECRET_SIZE); - -/* Mock the rollback for unit or fuzz tests. */ -int rollback_get_secret(uint8_t *secret) -{ - if (mock_ctrl_rollback.get_secret_fail) - return EC_ERROR_UNKNOWN; - memcpy(secret, fake_rollback_secret, sizeof(fake_rollback_secret)); - return EC_SUCCESS; -} diff --git a/common/mock/tcpc_mock.c b/common/mock/tcpc_mock.c deleted file mode 100644 index 7097837268..0000000000 --- a/common/mock/tcpc_mock.c +++ /dev/null @@ -1,227 +0,0 @@ -/* 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. - */ -/* Mock for the TCPC interface */ - -#include "common.h" -#include "console.h" -#include "memory.h" -#include "mock/tcpc_mock.h" -#include "test_util.h" -#include "tests/enum_strings.h" -#include "timer.h" -#include "usb_pd_tcpm.h" - -#ifndef CONFIG_COMMON_RUNTIME -#define cprints(format, args...) -#endif - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -/* Public API for controlling/inspecting this mock */ -struct mock_tcpc_ctrl mock_tcpc; - -void mock_tcpc_reset(void) -{ - /* Reset all control values to 0. See also build assert below */ - memset(&mock_tcpc, 0, sizeof(mock_tcpc)); - - /* Reset all last viewed variables to -1 to make them invalid */ - memset(&mock_tcpc.last, 0xff, sizeof(mock_tcpc.last)); -} -BUILD_ASSERT(TYPEC_CC_VOLT_OPEN == 0, "Ensure Open is 0-value for memset"); - -static int mock_init(int port) -{ - return EC_SUCCESS; -} - -static int mock_release(int port) -{ - return EC_SUCCESS; -} - -static int mock_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - *cc1 = mock_tcpc.cc1; - *cc2 = mock_tcpc.cc2; - return EC_SUCCESS; -} - -static bool mock_check_vbus_level(int port, enum vbus_level level) -{ - if (level == VBUS_PRESENT) - return mock_tcpc.vbus_level; - else if (level == VBUS_SAFE0V || level == VBUS_REMOVED) - return !mock_tcpc.vbus_level; - - /* - * Unknown vbus_level was added, force a failure. - * Note that TCPC drivers and pd_check_vbus_level() implementations - * should be carefully checked on new level additions in case they - * need updated. - */ - ccprints("[TCPC] Unhandled Vbus check %d", level); - TEST_ASSERT(0); -} - -static int mock_select_rp_value(int port, int rp) -{ - mock_tcpc.last.rp = rp; - - if (!mock_tcpc.should_print_call) - return EC_SUCCESS; - - ccprints("[TCPC] Setting TCPM-side Rp to %s", from_tcpc_rp_value(rp)); - - return EC_SUCCESS; -} - -static int mock_set_cc(int port, int pull) -{ - mock_tcpc.last.cc = pull; - - if (mock_tcpc.callbacks.set_cc) - mock_tcpc.callbacks.set_cc(port, pull); - - if (!mock_tcpc.should_print_call) - return EC_SUCCESS; - - ccprints("[TCPC] Setting TCPM-side CC to %s", from_tcpc_cc_pull(pull)); - - return EC_SUCCESS; -} - -static int mock_set_polarity(int port, enum tcpc_cc_polarity polarity) -{ - mock_tcpc.last.polarity = polarity; - - if (!mock_tcpc.should_print_call) - return EC_SUCCESS; - - ccprints("[TCPC] Setting TCPM-side polarity to %s", - from_tcpc_cc_polarity(polarity)); - - return EC_SUCCESS; -} - -static int mock_set_vconn(int port, int enable) -{ - return EC_SUCCESS; -} - -static int mock_set_msg_header(int port, int power_role, int data_role) -{ - ++mock_tcpc.num_calls_to_set_header; - - mock_tcpc.last.power_role = power_role; - mock_tcpc.last.data_role = data_role; - - if (!mock_tcpc.should_print_call) - return EC_SUCCESS; - - ccprints("[TCPC] Setting TCPM-side header to %s %s", - from_pd_power_role(power_role), - from_pd_data_role(data_role)); - - return EC_SUCCESS; -} - -static int mock_set_rx_enable(int port, int enable) -{ - return EC_SUCCESS; -} - -static int mock_get_message_raw(int port, uint32_t *payload, int *head) -{ - return EC_SUCCESS; -} - -static int mock_transmit(int port, enum tcpci_msg_type type, - uint16_t header, const uint32_t *data) -{ - return EC_SUCCESS; -} - -void mock_tcpc_alert(int port) -{ -} - -void mock_tcpc_discharge_vbus(int port, int enable) -{ -} - -__maybe_unused static int mock_drp_toggle(int port) -{ - /* Only set the time the first time this is called. */ - if (mock_tcpc.first_call_to_enable_auto_toggle == 0) - mock_tcpc.first_call_to_enable_auto_toggle = get_time().val; - - if (!mock_tcpc.should_print_call) - return EC_SUCCESS; - - ccprints("[TCPC] Enabling Auto Toggle"); - - return EC_SUCCESS; -} - -static int mock_get_chip_info(int port, int live, - struct ec_response_pd_chip_info_v1 *info) -{ - return EC_SUCCESS; -} - -__maybe_unused static int mock_set_snk_ctrl(int port, int enable) -{ - return EC_SUCCESS; -} - -__maybe_unused static int mock_set_src_ctrl(int port, int enable) -{ - return EC_SUCCESS; -} - -__maybe_unused static int mock_enter_low_power_mode(int port) -{ - return EC_SUCCESS; -} - -int mock_set_frs_enable(int port, int enable) -{ - return EC_SUCCESS; -} - -const struct tcpm_drv mock_tcpc_driver = { - .init = &mock_init, - .release = &mock_release, - .get_cc = &mock_get_cc, - .check_vbus_level = &mock_check_vbus_level, - .select_rp_value = &mock_select_rp_value, - .set_cc = &mock_set_cc, - .set_polarity = &mock_set_polarity, - .set_vconn = &mock_set_vconn, - .set_msg_header = &mock_set_msg_header, - .set_rx_enable = &mock_set_rx_enable, - .get_message_raw = &mock_get_message_raw, - .transmit = &mock_transmit, - .tcpc_alert = &mock_tcpc_alert, - .tcpc_discharge_vbus = &mock_tcpc_discharge_vbus, -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - .drp_toggle = &mock_drp_toggle, -#endif - .get_chip_info = &mock_get_chip_info, -#ifdef CONFIG_USB_PD_PPC - .set_snk_ctrl = &mock_set_snk_ctrl, - .set_src_ctrl = &mock_set_src_ctrl, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - .enter_low_power_mode = &mock_enter_low_power_mode, -#endif -#ifdef CONFIG_USB_PD_FRS_TCPC - .set_frs_enable = &mock_set_frs_enable, -#endif -}; diff --git a/common/mock/tcpci_i2c_mock.c b/common/mock/tcpci_i2c_mock.c deleted file mode 100644 index 8ec7556fca..0000000000 --- a/common/mock/tcpci_i2c_mock.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* Copyright 2020 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 "mock/tcpci_i2c_mock.h" -#include "task.h" -#include "tcpm/tcpci.h" -#include "test_util.h" -#include "timer.h" -#include "usb_pd_tcpm.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -#define BUFFER_SIZE 100 -#define VERIFY_TIMEOUT (5 * SECOND) - -struct tcpci_reg { - uint8_t offset; - uint8_t size; - uint16_t value; - const char *name; -}; - -#define TCPCI_REG(reg_name, reg_size) \ - [reg_name] = { .offset = (reg_name), .size = (reg_size), \ - .value = 0, .name = #reg_name, } - -static struct tcpci_reg tcpci_regs[] = { - TCPCI_REG(TCPC_REG_VENDOR_ID, 2), - TCPCI_REG(TCPC_REG_PRODUCT_ID, 2), - TCPCI_REG(TCPC_REG_BCD_DEV, 2), - TCPCI_REG(TCPC_REG_TC_REV, 2), - TCPCI_REG(TCPC_REG_PD_REV, 2), - TCPCI_REG(TCPC_REG_PD_INT_REV, 2), - TCPCI_REG(TCPC_REG_ALERT, 2), - TCPCI_REG(TCPC_REG_ALERT_MASK, 2), - TCPCI_REG(TCPC_REG_POWER_STATUS_MASK, 1), - TCPCI_REG(TCPC_REG_FAULT_STATUS_MASK, 1), - TCPCI_REG(TCPC_REG_EXT_STATUS_MASK, 1), - TCPCI_REG(TCPC_REG_ALERT_EXTENDED_MASK, 1), - TCPCI_REG(TCPC_REG_CONFIG_STD_OUTPUT, 1), - TCPCI_REG(TCPC_REG_TCPC_CTRL, 1), - TCPCI_REG(TCPC_REG_ROLE_CTRL, 1), - TCPCI_REG(TCPC_REG_FAULT_CTRL, 1), - TCPCI_REG(TCPC_REG_POWER_CTRL, 1), - TCPCI_REG(TCPC_REG_CC_STATUS, 1), - TCPCI_REG(TCPC_REG_POWER_STATUS, 1), - TCPCI_REG(TCPC_REG_FAULT_STATUS, 1), - TCPCI_REG(TCPC_REG_EXT_STATUS, 1), - TCPCI_REG(TCPC_REG_ALERT_EXT, 1), - TCPCI_REG(TCPC_REG_DEV_CAP_1, 2), - TCPCI_REG(TCPC_REG_DEV_CAP_2, 2), - TCPCI_REG(TCPC_REG_STD_INPUT_CAP, 1), - TCPCI_REG(TCPC_REG_STD_OUTPUT_CAP, 1), - TCPCI_REG(TCPC_REG_CONFIG_EXT_1, 1), - TCPCI_REG(TCPC_REG_MSG_HDR_INFO, 1), - TCPCI_REG(TCPC_REG_RX_DETECT, 1), - TCPCI_REG(TCPC_REG_RX_BUFFER, BUFFER_SIZE), - TCPCI_REG(TCPC_REG_TRANSMIT, 1), - TCPCI_REG(TCPC_REG_TX_BUFFER, BUFFER_SIZE), - TCPCI_REG(TCPC_REG_VBUS_VOLTAGE, 2), - TCPCI_REG(TCPC_REG_VBUS_SINK_DISCONNECT_THRESH, 2), - TCPCI_REG(TCPC_REG_VBUS_STOP_DISCHARGE_THRESH, 2), - TCPCI_REG(TCPC_REG_VBUS_VOLTAGE_ALARM_HI_CFG, 2), - TCPCI_REG(TCPC_REG_VBUS_VOLTAGE_ALARM_LO_CFG, 2), - TCPCI_REG(TCPC_REG_COMMAND, 1), -}; - -static uint8_t tx_buffer[BUFFER_SIZE]; -static int tx_pos = -1; -static int tx_msg_cnt; -static int tx_retry_cnt = -1; -static uint8_t rx_buffer[BUFFER_SIZE]; -static int rx_pos = -1; - -static const char * const ctrl_msg_name[] = { - [0] = "C-RSVD_0", - [PD_CTRL_GOOD_CRC] = "C-GOODCRC", - [PD_CTRL_GOTO_MIN] = "C-GOTOMIN", - [PD_CTRL_ACCEPT] = "C-ACCEPT", - [PD_CTRL_REJECT] = "C-REJECT", - [PD_CTRL_PING] = "C-PING", - [PD_CTRL_PS_RDY] = "C-PSRDY", - [PD_CTRL_GET_SOURCE_CAP] = "C-GET_SRC_CAP", - [PD_CTRL_GET_SINK_CAP] = "C-GET_SNK_CAP", - [PD_CTRL_DR_SWAP] = "C-DR_SWAP", - [PD_CTRL_PR_SWAP] = "C-PR_SWAP", - [PD_CTRL_VCONN_SWAP] = "C-VCONN_SW", - [PD_CTRL_WAIT] = "C-WAIT", - [PD_CTRL_SOFT_RESET] = "C-SOFT-RESET", - [14] = "C-RSVD_14", - [15] = "C-RSVD_15", - [PD_CTRL_NOT_SUPPORTED] = "C-NOT_SUPPORTED", - [PD_CTRL_GET_SOURCE_CAP_EXT] = "C-GET_SRC_CAP-EXT", - [PD_CTRL_GET_STATUS] = "C-GET-STATUS", - [PD_CTRL_FR_SWAP] = "C-FR_SWAP", - [PD_CTRL_GET_PPS_STATUS] = "C-GET_PPS_STATUS", - [PD_CTRL_GET_COUNTRY_CODES] = "C-GET_COUNTRY_CODES", -}; - -static const char * const data_msg_name[] = { - [0] = "D-RSVD_0", - [PD_DATA_SOURCE_CAP] = "D-SRC_CAP", - [PD_DATA_REQUEST] = "D-REQUEST", - [PD_DATA_BIST] = "D-BIST", - [PD_DATA_SINK_CAP] = "D-SNK_CAP", - /* 5-14 Reserved for REV 2.0 */ - [PD_DATA_BATTERY_STATUS] = "D-BATTERY_STATUS", - [PD_DATA_ALERT] = "D-ALERT", - [PD_DATA_GET_COUNTRY_INFO] = "D-GET_COUNTRY_CODES", - /* 8-14 Reserved for REV 3.0 */ - [PD_DATA_ENTER_USB] = "D-ENTER_USB", - [PD_DATA_VENDOR_DEF] = "D-VDM", -}; - -static const char * const ext_msg_name[] = { - [0] = "X-RSVD_0", - [PD_EXT_SOURCE_CAP] = "X-SRC_CAP", - [PD_EXT_STATUS] = "X-STATUS", - [PD_EXT_GET_BATTERY_CAP] = "X-GET_BATTERY_CAP", - [PD_EXT_GET_BATTERY_STATUS] = "X-GET_BATTERY_STATUS", - [PD_EXT_BATTERY_CAP] = "X-BATTERY_CAP", - [PD_EXT_GET_MANUFACTURER_INFO] = "X-GET_MFR_INFO", - [PD_EXT_MANUFACTURER_INFO] = "X-MFR_INFO", - [PD_EXT_SECURITY_REQUEST] = "X-SECURITY_REQ", - [PD_EXT_SECURITY_RESPONSE] = "X-SECURITY_RESP", - [PD_EXT_FIRMWARE_UPDATE_REQUEST] = "X-FW_UP_REQ", - [PD_EXT_FIRMWARE_UPDATE_RESPONSE] = "X-FW_UP_RESP", - [PD_EXT_PPS_STATUS] = "X-PPS_STATUS", - [PD_EXT_COUNTRY_INFO] = "X-COUNTRY_INFO", - [PD_EXT_COUNTRY_CODES] = "X-COUNTRY_CODES", -}; - -static const char * const rev_name[] = { - [PD_REV10] = "1.0", - [PD_REV20] = "2.0", - [PD_REV30] = "3.0", - [3] = "RSVD", -}; - -static const char * const drole_name[] = { - [PD_ROLE_UFP] = "UFP", - [PD_ROLE_DFP] = "DFP", -}; - -static const char * const prole_name[] = { - [PD_ROLE_SINK] = "SNK", - [PD_ROLE_SOURCE] = "SRC", -}; - -static void print_header(const char *prefix, uint16_t header) -{ - int type = PD_HEADER_TYPE(header); - int drole = PD_HEADER_DROLE(header); - int rev = PD_HEADER_REV(header); - int prole = PD_HEADER_PROLE(header); - int id = PD_HEADER_ID(header); - int cnt = PD_HEADER_CNT(header); - int ext = PD_HEADER_EXT(header); - const char *name = ext ? ext_msg_name[type] - : cnt - ? data_msg_name[type] - : ctrl_msg_name[type]; - - ccprints("%s header=0x%x [%s %s %s %s id=%d cnt=%d ext=%d]", - prefix, header, - name, drole_name[drole], rev_name[rev], prole_name[prole], - id, cnt, ext); -} - -static bool dead_battery(void) -{ - return false; -} - -static bool debug_accessory_indicator_supported(void) -{ - return true; -} - -static int verify_transmit(enum tcpci_msg_type want_tx_type, - int want_tx_retry, - enum pd_ctrl_msg_type want_ctrl_msg, - enum pd_data_msg_type want_data_msg, - int timeout) -{ - uint64_t end_time = get_time().val + timeout; - - /* - * Check that nothing was already transmitted. This ensures that all - * transmits are checked, and the test stays in sync with the code - * being tested. - */ - TEST_EQ(tcpci_regs[TCPC_REG_TRANSMIT].value, 0, "%d"); - - /* Now wait for the expected message to be transmitted. */ - while (get_time().val < end_time) { - if (tcpci_regs[TCPC_REG_TRANSMIT].value != 0) { - int tx_type = TCPC_REG_TRANSMIT_TYPE( - tcpci_regs[TCPC_REG_TRANSMIT].value); - int tx_retry = TCPC_REG_TRANSMIT_RETRY( - tcpci_regs[TCPC_REG_TRANSMIT].value); - uint16_t header = UINT16_FROM_BYTE_ARRAY_LE( - tx_buffer, 1); - int pd_type = PD_HEADER_TYPE(header); - int pd_cnt = PD_HEADER_CNT(header); - - TEST_EQ(tx_type, want_tx_type, "%d"); - if (want_tx_retry >= 0) - TEST_EQ(tx_retry, want_tx_retry, "%d"); - - if (want_ctrl_msg != 0) { - TEST_EQ(pd_type, want_ctrl_msg, "0x%x"); - TEST_EQ(pd_cnt, 0, "%d"); - } - if (want_data_msg != 0) { - TEST_EQ(pd_type, want_data_msg, "0x%x"); - TEST_GE(pd_cnt, 1, "%d"); - } - - tcpci_regs[TCPC_REG_TRANSMIT].value = 0; - return EC_SUCCESS; - } - task_wait_event(5 * MSEC); - } - TEST_ASSERT(0); - return EC_ERROR_UNKNOWN; -} - -int verify_tcpci_transmit(enum tcpci_msg_type tx_type, - enum pd_ctrl_msg_type ctrl_msg, - enum pd_data_msg_type data_msg) -{ - return verify_transmit(tx_type, -1, - ctrl_msg, data_msg, - VERIFY_TIMEOUT); -} - -int verify_tcpci_tx_timeout(enum tcpci_msg_type tx_type, - enum pd_ctrl_msg_type ctrl_msg, - enum pd_data_msg_type data_msg, - int timeout) -{ - return verify_transmit(tx_type, -1, - ctrl_msg, data_msg, - timeout); -} - -int verify_tcpci_tx_retry_count(enum tcpci_msg_type tx_type, - enum pd_ctrl_msg_type ctrl_msg, - enum pd_data_msg_type data_msg, - int retry_count) -{ - return verify_transmit(tx_type, retry_count, - ctrl_msg, data_msg, - VERIFY_TIMEOUT); -} - -int verify_tcpci_tx_with_data(enum tcpci_msg_type tx_type, - enum pd_data_msg_type data_msg, - uint8_t *data, - int data_bytes, - int *msg_len, - int timeout) -{ - int rv; - - if (timeout <= 0) - timeout = VERIFY_TIMEOUT; - - rv = verify_transmit(tx_type, -1, - 0, data_msg, - timeout); - if (!rv) { - TEST_NE(data, NULL, "%p"); - TEST_GE(data_bytes, tx_msg_cnt, "%d"); - memcpy(data, tx_buffer, tx_msg_cnt); - if (msg_len) - *msg_len = tx_msg_cnt; - } - return rv; -} - -int verify_tcpci_possible_tx(struct possible_tx possible[], - int possible_cnt, - int *found_index, - uint8_t *data, - int data_bytes, - int *msg_len, - int timeout) -{ - bool assert_on_timeout = true; - uint64_t end_time; - - *found_index = -1; - - if (timeout <= 0) { - timeout = VERIFY_TIMEOUT; - assert_on_timeout = false; - } - end_time = get_time().val + timeout; - - /* - * Check that nothing was already transmitted. This ensures that all - * transmits are checked, and the test stays in sync with the code - * being tested. - */ - TEST_EQ(tcpci_regs[TCPC_REG_TRANSMIT].value, 0, "%d"); - - /* Now wait for the expected message to be transmitted. */ - while (get_time().val < end_time) { - if (tcpci_regs[TCPC_REG_TRANSMIT].value != 0) { - int i; - int tx_type = TCPC_REG_TRANSMIT_TYPE( - tcpci_regs[TCPC_REG_TRANSMIT].value); - uint16_t header = UINT16_FROM_BYTE_ARRAY_LE( - tx_buffer, 1); - int pd_type = PD_HEADER_TYPE(header); - int pd_cnt = PD_HEADER_CNT(header); - - for (i = 0; i < possible_cnt; ++i) { - int want_tx_type = possible[i].tx_type; - int want_ctrl_msg = possible[i].ctrl_msg; - int want_data_msg = possible[i].data_msg; - - if (tx_type != want_tx_type) - continue; - - if (want_ctrl_msg != 0) { - if (pd_type != want_ctrl_msg || - pd_cnt != 0) - continue; - } - if (want_data_msg != 0) { - if (pd_type != want_data_msg || - pd_cnt == 0) - continue; - - if (data != NULL) { - TEST_GE(data_bytes, - tx_msg_cnt, "%d"); - memcpy(data, tx_buffer, - tx_msg_cnt); - } - if (msg_len != NULL) - *msg_len = tx_msg_cnt; - } - *found_index = i; - tcpci_regs[TCPC_REG_TRANSMIT].value = 0; - return EC_SUCCESS; - } - return EC_ERROR_UNKNOWN; - } - task_wait_event(5 * MSEC); - } - if (assert_on_timeout) - TEST_ASSERT(0); - - return EC_ERROR_TIMEOUT; -} - -void mock_tcpci_receive(enum tcpci_msg_type sop, uint16_t header, - uint32_t *payload) -{ - int i; - - rx_buffer[0] = 3 + (PD_HEADER_CNT(header) * 4); - rx_buffer[1] = sop; - rx_buffer[2] = header & 0xFF; - rx_buffer[3] = (header >> 8) & 0xFF; - - if (rx_buffer[0] >= BUFFER_SIZE) { - ccprints("ERROR: rx too large"); - return; - } - - for (i = 4; i < rx_buffer[0]; i += 4) { - rx_buffer[i] = *payload & 0xFF; - rx_buffer[i+1] = (*payload >> 8) & 0xFF; - rx_buffer[i+2] = (*payload >> 16) & 0xFF; - rx_buffer[i+3] = (*payload >> 24) & 0xFF; - payload++; - } - - rx_pos = 0; -} - -/***************************************************************************** - * TCPCI register reset values - * - * These values are from USB Type-C Port Controller Interface Specification - * Revision 2.0, Version 1.2, - */ -static void tcpci_reset_register_masks(void) -{ - /* - * Using table 4-1 for default mask values - */ - tcpci_regs[TCPC_REG_ALERT_MASK].value = 0x7FFF; - tcpci_regs[TCPC_REG_POWER_STATUS_MASK].value = 0xFF; - tcpci_regs[TCPC_REG_FAULT_STATUS_MASK].value = 0xFF; - tcpci_regs[TCPC_REG_EXT_STATUS_MASK].value = 0x01; - tcpci_regs[TCPC_REG_ALERT_EXTENDED_MASK].value = 0x07; -} - -static void tcpci_reset_register_defaults(void) -{ - int i; - - /* Default all registers to 0 and then overwrite if they are not */ - for (i = 0; i < ARRAY_SIZE(tcpci_regs); i++) - tcpci_regs[i].value = 0; - - /* Type-C Release 1,3 */ - tcpci_regs[TCPC_REG_TC_REV].value = 0x0013; - /* PD Revision 3.0 Version 1.2 */ - tcpci_regs[TCPC_REG_PD_REV].value = 0x3012; - /* PD Interface Revision 2.0, Version 1.1 */ - tcpci_regs[TCPC_REG_PD_INT_REV].value = 0x2011; - - tcpci_reset_register_masks(); - - tcpci_regs[TCPC_REG_CONFIG_STD_OUTPUT].value = - TCPC_REG_CONFIG_STD_OUTPUT_AUDIO_CONN_N | - TCPC_REG_CONFIG_STD_OUTPUT_DBG_ACC_CONN_N; - - tcpci_regs[TCPC_REG_POWER_CTRL].value = - TCPC_REG_POWER_CTRL_VOLT_ALARM_DIS | - TCPC_REG_POWER_CTRL_VBUS_VOL_MONITOR_DIS; - - tcpci_regs[TCPC_REG_FAULT_STATUS].value = - TCPC_REG_FAULT_STATUS_ALL_REGS_RESET; - - tcpci_regs[TCPC_REG_DEV_CAP_1].value = - TCPC_REG_DEV_CAP_1_SOURCE_VBUS | - TCPC_REG_DEV_CAP_1_SINK_VBUS | - TCPC_REG_DEV_CAP_1_PWRROLE_SRC_SNK_DRP | - TCPC_REG_DEV_CAP_1_SRC_RESISTOR_RP_3P0_1P5_DEF; - - /* - * Using table 4-17 to get the default Role Control and - * Message Header Info register values. - */ - switch (mock_tcpci_get_reg(TCPC_REG_DEV_CAP_1) & - TCPC_REG_DEV_CAP_1_PWRROLE_MASK) { - case TCPC_REG_DEV_CAP_1_PWRROLE_SRC_OR_SNK: - case TCPC_REG_DEV_CAP_1_PWRROLE_SNK: - case TCPC_REG_DEV_CAP_1_PWRROLE_SNK_ACC: - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x0A; - tcpci_regs[TCPC_REG_MSG_HDR_INFO].value = 0x04; - break; - - case TCPC_REG_DEV_CAP_1_PWRROLE_DRP: - if (dead_battery()) - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x0A; - else if (debug_accessory_indicator_supported()) - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x4A; - else - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x0F; - tcpci_regs[TCPC_REG_MSG_HDR_INFO].value = 0x04; - break; - - case TCPC_REG_DEV_CAP_1_PWRROLE_SRC: - if (!dead_battery()) - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x05; - tcpci_regs[TCPC_REG_MSG_HDR_INFO].value = 0x0D; - break; - - case TCPC_REG_DEV_CAP_1_PWRROLE_SRC_SNK_DRP_ADPT_CBL: - case TCPC_REG_DEV_CAP_1_PWRROLE_SRC_SNK_DRP: - if (dead_battery()) - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x0A; - else if (debug_accessory_indicator_supported()) - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x4A; - else - tcpci_regs[TCPC_REG_ROLE_CTRL].value = 0x0F; - tcpci_regs[TCPC_REG_MSG_HDR_INFO].value = 0x04; - break; - } -} -/*****************************************************************************/ - -void mock_tcpci_reset(void) -{ - tcpci_reset_register_defaults(); -} - -void mock_tcpci_set_reg(int reg_offset, uint16_t value) -{ - struct tcpci_reg *reg = tcpci_regs + reg_offset; - - reg->value = value; - ccprints("TCPCI mock set %s = 0x%x", reg->name, reg->value); -} - -void mock_tcpci_set_reg_bits(int reg_offset, uint16_t mask) -{ - struct tcpci_reg *reg = tcpci_regs + reg_offset; - uint16_t old_value = reg->value; - - reg->value |= mask; - ccprints("TCPCI mock set bits %s (mask=0x%x) = 0x%x -> 0x%x", - reg->name, mask, old_value, reg->value); -} - -void mock_tcpci_clr_reg_bits(int reg_offset, uint16_t mask) -{ - struct tcpci_reg *reg = tcpci_regs + reg_offset; - uint16_t old_value = reg->value; - - reg->value &= ~mask; - ccprints("TCPCI mock clr bits %s (mask=0x%x) = 0x%x -> 0x%x", - reg->name, mask, old_value, reg->value); -} - -uint16_t mock_tcpci_get_reg(int reg_offset) -{ - return tcpci_regs[reg_offset].value; -} - -int tcpci_i2c_xfer(int port, uint16_t addr_flags, - const uint8_t *out, int out_size, - uint8_t *in, int in_size, int flags) -{ - struct tcpci_reg *reg; - - if (port != I2C_PORT_HOST_TCPC) { - ccprints("ERROR: wrong I2C port %d", port); - return EC_ERROR_UNKNOWN; - } - if (addr_flags != MOCK_TCPCI_I2C_ADDR_FLAGS) { - ccprints("ERROR: wrong I2C address 0x%x", addr_flags); - return EC_ERROR_UNKNOWN; - } - - if (rx_pos > 0) { - if (rx_pos + in_size > rx_buffer[0] + 1) { - ccprints("ERROR: rx in_size"); - return EC_ERROR_UNKNOWN; - } - memcpy(in, rx_buffer + rx_pos, in_size); - rx_pos += in_size; - if (rx_pos == rx_buffer[0] + 1) { - print_header("RX", UINT16_FROM_BYTE_ARRAY_LE( - rx_buffer, 2)); - rx_pos = -1; - } - return EC_SUCCESS; - } - - if (out_size == 0) { - ccprints("ERROR: out_size == 0"); - return EC_ERROR_UNKNOWN; - } - if (tx_pos != -1) { - if (tx_pos + out_size > BUFFER_SIZE) { - ccprints("ERROR: tx out_size"); - return EC_ERROR_UNKNOWN; - } - memcpy(tx_buffer + tx_pos, out, out_size); - tx_pos += out_size; - tx_msg_cnt = tx_pos; - if (tx_pos > 0 && tx_pos == tx_buffer[0] + 1) { - print_header("TX", UINT16_FROM_BYTE_ARRAY_LE( - tx_buffer, 1)); - tx_pos = -1; - tx_retry_cnt = -1; - } - return EC_SUCCESS; - } - reg = tcpci_regs + *out; - if (*out >= ARRAY_SIZE(tcpci_regs) || reg->size == 0) { - ccprints("ERROR: unknown reg 0x%x", *out); - return EC_ERROR_UNKNOWN; - } - if (reg->offset == TCPC_REG_TX_BUFFER) { - if (tx_pos != -1) { - ccprints("ERROR: TCPC_REG_TX_BUFFER not ready"); - return EC_ERROR_UNKNOWN; - } - tx_pos = 0; - tx_msg_cnt = 0; - if (out_size != 1) { - ccprints("ERROR: TCPC_REG_TX_BUFFER out_size != 1"); - return EC_ERROR_UNKNOWN; - } - } else if (reg->offset == TCPC_REG_RX_BUFFER) { - if (rx_pos != 0) { - ccprints("ERROR: TCPC_REG_RX_BUFFER not ready"); - return EC_ERROR_UNKNOWN; - } - if (in_size > BUFFER_SIZE || in_size > rx_buffer[0]) { - ccprints("ERROR: TCPC_REG_RX_BUFFER in_size"); - return EC_ERROR_UNKNOWN; - } - memcpy(in, rx_buffer, in_size); - rx_pos += in_size; - } else if (out_size == 1) { - if (in_size != reg->size) { - ccprints("ERROR: %s in_size %d != %d", reg->name, - in_size, reg->size); - return EC_ERROR_UNKNOWN; - } - if (reg->size == 1) - in[0] = reg->value; - else if (reg->size == 2) { - in[0] = reg->value; - in[1] = reg->value >> 8; - } - } else { - uint16_t value = 0; - - if (in_size != 0) { - ccprints("ERROR: in_size != 0"); - return EC_ERROR_UNKNOWN; - } - if (out_size != reg->size + 1) { - ccprints("ERROR: out_size != %d", reg->size + 1); - return EC_ERROR_UNKNOWN; - } - if (reg->size == 1) - value = out[1]; - else if (reg->size == 2) - value = out[1] + (out[2] << 8); - ccprints("%s TCPCI write %s = 0x%x", - task_get_name(task_get_current()), - reg->name, value); - if (reg->offset == TCPC_REG_ALERT) - reg->value &= ~value; - else - reg->value = value; - } - return EC_SUCCESS; -} -DECLARE_TEST_I2C_XFER(tcpci_i2c_xfer); - -void tcpci_register_dump(void) -{ - int reg; - int cc1, cc2; - - ccprints("********* TCPCI Register Dump ***********"); - reg = mock_tcpci_get_reg(TCPC_REG_ALERT); - ccprints("TCPC_REG_ALERT = 0x%08X", reg); - if (reg) { - if (reg & BIT(0)) - ccprints("\t0001: CC Status"); - if (reg & BIT(1)) - ccprints("\t0002: Power Status"); - if (reg & BIT(2)) - ccprints("\t0004: Received SOP* Message Status"); - if (reg & BIT(3)) - ccprints("\t0008: Received Hard Reset"); - if (reg & BIT(4)) - ccprints("\t0010: Transmit SOP* Message Failed"); - if (reg & BIT(5)) - ccprints("\t0020: Transmit SOP* Message Discarded"); - if (reg & BIT(6)) - ccprints("\t0040: Transmit SOP* Message Successful"); - if (reg & BIT(7)) - ccprints("\t0080: Vbus Voltage Alarm Hi"); - if (reg & BIT(8)) - ccprints("\t0100: Vbus Voltage Alarm Lo"); - if (reg & BIT(9)) - ccprints("\t0200: Fault"); - if (reg & BIT(10)) - ccprints("\t0400: Rx Buffer Overflow"); - if (reg & BIT(11)) - ccprints("\t0800: Vbus Sink Disconnect Detected"); - if (reg & BIT(12)) - ccprints("\t1000: Beginning SOP* Message Status"); - if (reg & BIT(13)) - ccprints("\t2000: Extended Status"); - if (reg & BIT(14)) - ccprints("\t4000: Alert Extended"); - if (reg & BIT(15)) - ccprints("\t8000: Vendor Defined Alert"); - } - - reg = mock_tcpci_get_reg(TCPC_REG_TCPC_CTRL); - ccprints("TCPC_REG_TCPC_CTRL = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Plug Orientation FLIP"); - if (reg & BIT(1)) - ccprints("\t02: BIST Test Mode"); - if (reg & (BIT(2) | BIT(3))) { - switch ((reg >> 2) & 3) { - case 2: - ccprints("\t08: Enable Clock Stretching"); - break; - case 3: - ccprints("\t0C: Enable Clock Stretching if !Alert"); - break; - } - } - if (reg & BIT(4)) - ccprints("\t10: Debug Accessory controlled by TCPM"); - if (reg & BIT(5)) - ccprints("\t20: Watchdog Timer enabled"); - if (reg & BIT(6)) - ccprints("\t40: Looking4Connection Alert enabled"); - if (reg & BIT(7)) - ccprints("\t80: SMBus PEC enabled"); - - reg = mock_tcpci_get_reg(TCPC_REG_ROLE_CTRL); - ccprints("TCPC_REG_ROLE_CTRL = 0x%04X", reg); - cc1 = (reg >> 0) & 3; - switch (cc1) { - case 0: - ccprints("\t00: CC1 == Ra"); - break; - case 1: - ccprints("\t01: CC1 == Rp"); - break; - case 2: - ccprints("\t02: CC1 == Rd"); - break; - case 3: - ccprints("\t03: CC1 == OPEN"); - break; - } - cc2 = (reg >> 2) & 3; - switch (cc2) { - case 0: - ccprints("\t00: CC2 == Ra"); - break; - case 1: - ccprints("\t04: CC2 == Rp"); - break; - case 2: - ccprints("\t08: CC2 == Rd"); - break; - case 3: - ccprints("\t0C: CC2 == OPEN"); - break; - } - switch ((reg >> 4) & 3) { - case 0: - ccprints("\t00: Rp Value == default"); - break; - case 1: - ccprints("\t10: Rp Value == 1.5A"); - break; - case 2: - ccprints("\t20: Rp Value == 3A"); - break; - } - if (reg & BIT(6)) - ccprints("\t40: DRP"); - - reg = mock_tcpci_get_reg(TCPC_REG_FAULT_CTRL); - ccprints("TCPC_REG_FAULT_CTRL = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Vconn Over Current Fault"); - if (reg & BIT(1)) - ccprints("\t02: Vbus OVP Fault"); - if (reg & BIT(2)) - ccprints("\t04: Vbus OCP Fault"); - if (reg & BIT(3)) - ccprints("\t08: Vbus Discharge Fault"); - if (reg & BIT(4)) - ccprints("\t10: Force OFF Vbus"); - - reg = mock_tcpci_get_reg(TCPC_REG_POWER_CTRL); - ccprints("TCPC_REG_POWER_CTRL = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Enable Vconn"); - if (reg & BIT(1)) - ccprints("\t02: Vconn Power Supported"); - if (reg & BIT(2)) - ccprints("\t04: Force Discharge"); - if (reg & BIT(3)) - ccprints("\t08: Enable Bleed Discharge"); - if (reg & BIT(4)) - ccprints("\t10: Auto Discharge Disconnect"); - if (reg & BIT(5)) - ccprints("\t20: Disable Voltage Alarms"); - if (reg & BIT(6)) - ccprints("\t40: VBUS_VOLTAGE monitor disabled"); - if (reg & BIT(7)) - ccprints("\t80: Fast Role Swap enabled"); - - reg = mock_tcpci_get_reg(TCPC_REG_CC_STATUS); - ccprints("TCPC_REG_CC_STATUS = 0x%04X", reg); - switch ((reg >> 0) & 3) { - case 0: - switch (cc1) { - case 1: - ccprints("\t00: CC1-Rp SRC.Open"); - break; - case 2: - ccprints("\t00: CC1-Rd SNK.Open"); - break; - } - break; - case 1: - switch (cc1) { - case 1: - ccprints("\t01: CC1-Rp SRC.Ra"); - break; - case 2: - ccprints("\t01: CC1-Rd SNK.Default"); - break; - } - break; - case 2: - switch (cc1) { - case 1: - ccprints("\t02: CC1-Rp SRC.Rd"); - break; - case 2: - ccprints("\t02: CC1-Rd SNK.Power1.5"); - break; - } - break; - case 3: - switch (cc1) { - case 2: - ccprints("\t03: CC1-Rd SNK.Power3.0"); - break; - } - break; - } - switch ((reg >> 2) & 3) { - case 0: - switch (cc2) { - case 1: - ccprints("\t00: CC2-Rp SRC.Open"); - break; - case 2: - ccprints("\t00: CC2-Rd SNK.Open"); - break; - } - break; - case 1: - switch (cc2) { - case 1: - ccprints("\t04: CC2-Rp SRC.Ra"); - break; - case 2: - ccprints("\t04: CC2-Rd SNK.Default"); - break; - } - break; - case 2: - switch (cc2) { - case 1: - ccprints("\t08: CC2-Rp SRC.Rd"); - break; - case 2: - ccprints("\t08: CC2-Rd SNK.Power1.5"); - break; - } - break; - case 3: - switch (cc2) { - case 2: - ccprints("\t0C: CC2-Rd SNK.Power3.0"); - break; - } - break; - } - if (reg & BIT(4)) - ccprints("\t10: Presenting Rd"); - else - ccprints("\t00: Presenting Rp"); - if (reg & BIT(5)) - ccprints("\t20: Looking4Connection"); - - reg = mock_tcpci_get_reg(TCPC_REG_POWER_STATUS); - ccprints("TCPC_REG_POWER_STATUS = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Sinking Vbus"); - if (reg & BIT(1)) - ccprints("\t02: Vconn Present"); - if (reg & BIT(2)) - ccprints("\t04: Vbus Present"); - if (reg & BIT(3)) - ccprints("\t08: Vbus Detect enabled"); - if (reg & BIT(4)) - ccprints("\t10: Sourcing Vbus"); - if (reg & BIT(5)) - ccprints("\t20: Sourcing non-default voltage"); - if (reg & BIT(6)) - ccprints("\t40: TCPC Initialization"); - if (reg & BIT(7)) - ccprints("\t80: Debug Accessory Connected"); - - reg = mock_tcpci_get_reg(TCPC_REG_FAULT_STATUS); - ccprints("TCPC_REG_FAULT_STATUS = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: I2C Interface Error"); - if (reg & BIT(1)) - ccprints("\t02: Vconn Over Current Fault"); - if (reg & BIT(2)) - ccprints("\t04: Vbus OVP Fault"); - if (reg & BIT(3)) - ccprints("\t08: Vbus OCP Fault"); - if (reg & BIT(4)) - ccprints("\t10: Forced Discharge Failed"); - if (reg & BIT(5)) - ccprints("\t20: Auto Discharge Failed"); - if (reg & BIT(6)) - ccprints("\t40: Force OFF Vbus"); - if (reg & BIT(7)) - ccprints("\t80: TCPCI Registers Reset2Default"); - - reg = mock_tcpci_get_reg(TCPC_REG_EXT_STATUS); - ccprints("TCPC_REG_EXT_STATUS = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Vbus is at vSafe0V"); - - reg = mock_tcpci_get_reg(TCPC_REG_ALERT_EXT); - ccprints("TCPC_REG_ALERT_EXT = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: SNK Fast Role Swap"); - if (reg & BIT(1)) - ccprints("\t02: SRC Fast Role Swap"); - if (reg & BIT(2)) - ccprints("\t04: Timer Expired"); - - reg = mock_tcpci_get_reg(TCPC_REG_COMMAND); - ccprints("TCPC_REG_COMMAND = 0x%04X", reg); - switch (reg) { - case 0x11: - ccprints("\t11: WakeI2C"); - break; - case 0x22: - ccprints("\t22: DisableVbusDetect"); - break; - case 0x33: - ccprints("\t33: EnableVbusDetect"); - break; - case 0x44: - ccprints("\t44: DisableSinkVbus"); - break; - case 0x55: - ccprints("\t55: SinkVbus"); - break; - case 0x66: - ccprints("\t66: DisableSourceVbus"); - break; - case 0x77: - ccprints("\t77: SourceVbusDefaultVoltage"); - break; - case 0x88: - ccprints("\t88: SourceVbusNondefaultVoltage"); - break; - case 0x99: - ccprints("\t99: Looking4Connection"); - break; - case 0xAA: - ccprints("\tAA: RxOneMore"); - break; - case 0xCC: - ccprints("\tCC: SendFRSwapSignal"); - break; - case 0xDD: - ccprints("\tDD: ResetTransmitBuffer"); - break; - case 0xEE: - ccprints("\tEE: ResetReceiveBuffer"); - break; - case 0xFF: - ccprints("\tFF: I2C Idle"); - break; - } - - reg = mock_tcpci_get_reg(TCPC_REG_MSG_HDR_INFO); - ccprints("TCPC_REG_MSG_HDR_INFO = 0x%04X", reg); - if (reg & BIT(0)) - ccprints("\t01: Power Role SRC"); - else - ccprints("\t00: Power Role SNK"); - switch ((reg >> 1) & 3) { - case 0: - ccprints("\t00: PD Revision 1.0"); - break; - case 1: - ccprints("\t02: PD Revision 2.0"); - break; - case 2: - ccprints("\t04: PD Revision 3.0"); - break; - } - if (reg & BIT(3)) - ccprints("\t08: Data Role DFP"); - else - ccprints("\t00: Data Role UFP"); - if (reg & BIT(4)) - ccprints("\t10: Message originating from Cable Plug"); - else - ccprints("\t00: Message originating from SRC/SNK/DRP"); - - reg = mock_tcpci_get_reg(TCPC_REG_RX_BUFFER); - ccprints("TCPC_REG_RX_BUFFER = 0x%04X", reg); - - reg = mock_tcpci_get_reg(TCPC_REG_TRANSMIT); - ccprints("TCPC_REG_TRANSMIT = 0x%04X", reg); - ccprints("*****************************************"); -} diff --git a/common/mock/tcpm_mock.c b/common/mock/tcpm_mock.c deleted file mode 100644 index 2c212cf8c9..0000000000 --- a/common/mock/tcpm_mock.c +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2020 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. - */ -/* Mock for the TCPM interface */ - -#include "common.h" -#include "console.h" -#include "memory.h" -#include "mock/tcpm_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_tcpm_t mock_tcpm[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/** - * Gets the next waiting RX message. - * - * @param port Type-C port number - * @param payload Pointer to location to copy payload of PD message - * @param header The header of PD message - * - * @return EC_SUCCESS or error - */ -int tcpm_dequeue_message(int port, uint32_t *payload, int *header) -{ - if (!tcpm_has_pending_message(port)) - return EC_ERROR_BUSY; - - *header = mock_tcpm[port].mock_header; - memcpy(payload, mock_tcpm[port].mock_rx_chk_buf, - sizeof(mock_tcpm[port].mock_rx_chk_buf)); - - return EC_SUCCESS; -} - -/** - * Returns true if the tcpm has RX messages waiting to be consumed. - */ -int tcpm_has_pending_message(int port) -{ - return mock_tcpm[port].mock_has_pending_message; -} - -/** - * Resets all mock TCPM ports - */ -void mock_tcpm_reset(void) -{ - int port; - - for (port = 0 ; port < CONFIG_USB_PD_PORT_MAX_COUNT ; ++port) - mock_tcpm[port].mock_has_pending_message = 0; -} - -/** - * Sets up a message to be received, with optional data payload. If cnt==0, - * then data can be NULL. - */ -void mock_tcpm_rx_msg(int port, uint16_t header, int cnt, const uint32_t *data) -{ - mock_tcpm[port].mock_header = header; - if (cnt > 0) { - int idx; - - for (idx = 0 ; (idx < cnt) && (idx < MOCK_CHK_BUF_SIZE) ; ++idx) - mock_tcpm[port].mock_rx_chk_buf[idx] = data[idx]; - } - mock_tcpm[port].mock_has_pending_message = 1; -} diff --git a/common/mock/timer_mock.c b/common/mock/timer_mock.c deleted file mode 100644 index dc83aa24d5..0000000000 --- a/common/mock/timer_mock.c +++ /dev/null @@ -1,22 +0,0 @@ -/* 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. - */ - -#include "mock/timer_mock.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -static timestamp_t now; - -void set_time(timestamp_t now_) -{ - now = now_; -} - -timestamp_t get_time(void) -{ - return now; -}; diff --git a/common/mock/usb_mux_mock.c b/common/mock/usb_mux_mock.c deleted file mode 100644 index f2db5cf8bd..0000000000 --- a/common/mock/usb_mux_mock.c +++ /dev/null @@ -1,63 +0,0 @@ -/* 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. - */ -/* Mock USB Type-C mux */ - -#include "common.h" -#include "console.h" -#include "usb_mux.h" -#include "mock/usb_mux_mock.h" -#include "memory.h" - -#ifndef CONFIG_COMMON_RUNTIME -#define cprints(format, args...) -#endif - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -/* Public API for controlling/inspecting this mock */ -struct mock_usb_mux_ctrl mock_usb_mux; - -void mock_usb_mux_reset(void) -{ - memset(&mock_usb_mux, 0, sizeof(mock_usb_mux)); -} - -static int mock_init(const struct usb_mux *me) -{ - return EC_SUCCESS; -} - -static int mock_set(const struct usb_mux *me, mux_state_t mux_state, - bool *ack_required) -{ - /* Mock does not use host command ACKs */ - *ack_required = false; - - mock_usb_mux.state = mux_state; - ++mock_usb_mux.num_set_calls; - ccprints("[MUX] Set to 0x%02x", mux_state); - - return EC_SUCCESS; -} - -int mock_get(const struct usb_mux *me, mux_state_t *mux_state) -{ - *mux_state = mock_usb_mux.state; - return EC_SUCCESS; -} - -static int mock_enter_low_power_mode(const struct usb_mux *me) -{ - return EC_SUCCESS; -} - -const struct usb_mux_driver mock_usb_mux_driver = { - .init = &mock_init, - .set = &mock_set, - .get = &mock_get, - .enter_low_power_mode = &mock_enter_low_power_mode, -}; diff --git a/common/mock/usb_pd_dpm_mock.c b/common/mock/usb_pd_dpm_mock.c deleted file mode 100644 index 8b6fbaa30e..0000000000 --- a/common/mock/usb_pd_dpm_mock.c +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Mock of Device Policy Manager implementation - * Refer to USB PD 3.0 spec, version 2.0, sections 8.2 and 8.3 - */ - -#include "usb_pd.h" -#include "mock/usb_pd_dpm_mock.h" -#include "memory.h" -#include "usb_pd_tcpm.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_dpm_port_t dpm[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void mock_dpm_reset(void) -{ - /* Reset all values to 0. */ - memset(dpm, 0, sizeof(dpm)); -} - -void dpm_init(int port) -{ - dpm[port].mode_entry_done = false; - dpm[port].mode_exit_request = false; -} - -void dpm_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ -} - -void dpm_vdm_naked(int port, enum tcpci_msg_type type, uint16_t svid, - uint8_t vdm_cmd) -{ -} - -void dpm_set_mode_exit_request(int port) -{ -} - -void dpm_run(int port) -{ -} - -void dpm_evaluate_sink_fixed_pdo(int port, uint32_t vsafe5v_pdo) -{ -} - -void dpm_add_non_pd_sink(int port) -{ -} - -void dpm_remove_sink(int port) -{ -} - -void dpm_remove_source(int port) -{ -} - -int dpm_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} diff --git a/common/mock/usb_pe_sm_mock.c b/common/mock/usb_pe_sm_mock.c deleted file mode 100644 index 8d1a25324b..0000000000 --- a/common/mock/usb_pe_sm_mock.c +++ /dev/null @@ -1,120 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Mock USB PE state machine */ - -#include "common.h" -#include "console.h" -#include "usb_pd.h" -#include "usb_pe_sm.h" -#include "mock/usb_pe_sm_mock.h" -#include "memory.h" -#include "usb_pd_tcpm.h" - -#ifndef CONFIG_COMMON_RUNTIME -#define cprints(format, args...) -#endif - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_pe_port_t mock_pe_port[CONFIG_USB_PD_PORT_MAX_COUNT]; - - -/** - * Resets all mock PE ports to initial values - */ -void mock_pe_port_reset(void) -{ - int port; - - for (port = 0 ; port < CONFIG_USB_PD_PORT_MAX_COUNT ; ++port) { - mock_pe_port[port].mock_pe_error = -1; - /* These mock variable only get set to 1 by various functions, - * so initialize them to 0. Tests can verify they are still 0 - * if that's part of the pass criteria. - */ - mock_pe_port[port].mock_pe_message_received = 0; - mock_pe_port[port].mock_pe_message_sent = 0; - mock_pe_port[port].mock_pe_message_discarded = 0; - mock_pe_port[port].mock_got_soft_reset = 0; - mock_pe_port[port].mock_pe_got_hard_reset = 0; - mock_pe_port[port].mock_pe_hard_reset_sent = 0; - } -} - -void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) -{ - mock_pe_port[port].mock_pe_error = e; - mock_pe_port[port].sop = type; -} - -void pe_report_discard(int port) -{ - mock_pe_port[port].mock_pe_message_discarded = 1; -} - -void pe_got_hard_reset(int port) -{ - mock_pe_port[port].mock_pe_got_hard_reset = 1; -} - -void pe_message_received(int port) -{ - mock_pe_port[port].mock_pe_message_received = 1; -} - -void pe_message_sent(int port) -{ - mock_pe_port[port].mock_pe_message_sent = 1; -} - -void pe_hard_reset_sent(int port) -{ - mock_pe_port[port].mock_pe_hard_reset_sent = 1; -} - -void pe_got_soft_reset(int port) -{ - mock_pe_port[port].mock_got_soft_reset = 1; -} - -bool pe_in_frs_mode(int port) -{ - return false; -} - -bool pe_in_local_ams(int port) -{ - /* We will probably want to change this in the future */ - return false; -} - -const uint32_t * const pd_get_src_caps(int port) -{ - return NULL; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - return 0; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ -} - -void pd_request_power_swap(int port) -{} - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ - return IS_ENABLED(CONFIG_USB_PD_REV30) ? PD_REV30 : PD_REV20; -} - -void pe_invalidate_explicit_contract(int port) -{ -} diff --git a/common/mock/usb_prl_mock.c b/common/mock/usb_prl_mock.c deleted file mode 100644 index d5f4781829..0000000000 --- a/common/mock/usb_prl_mock.c +++ /dev/null @@ -1,200 +0,0 @@ -/* 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. - * - * Mock Protocol Layer module. - */ -#include <string.h> -#include "common.h" -#include "usb_emsg.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "mock/usb_prl_mock.h" -#include "task.h" -#include "test_util.h" -#include "timer.h" -#include "usb_pd_tcpm.h" - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -/* Defaults should all be 0 values. */ -struct extended_msg rx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; -struct extended_msg tx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -struct mock_prl_port_t { - enum pd_ctrl_msg_type last_ctrl_msg; - enum pd_data_msg_type last_data_msg; - enum tcpci_msg_type last_tx_type; - bool message_sent; - bool message_received; - enum pe_error error; - enum tcpci_msg_type error_tx_type; -}; - -struct mock_prl_port_t mock_prl_port[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void mock_prl_reset(void) -{ - int port; - - /* Reset all values to 0. */ - memset(rx_emsg, 0, sizeof(rx_emsg)); - memset(tx_emsg, 0, sizeof(tx_emsg)); - - memset(mock_prl_port, 0, sizeof(mock_prl_port)); - - for (port = 0 ; port < CONFIG_USB_PD_PORT_MAX_COUNT ; ++port) { - mock_prl_port[port].last_tx_type = TCPCI_MSG_INVALID; - mock_prl_port[port].error_tx_type = TCPCI_MSG_INVALID; - } -} - -void prl_end_ams(int port) -{} - -void prl_execute_hard_reset(int port) -{ - mock_prl_port[port].last_ctrl_msg = 0; - mock_prl_port[port].last_data_msg = 0; - mock_prl_port[port].last_tx_type = TCPCI_MSG_TX_HARD_RESET; -} - -enum pd_rev_type prl_get_rev(int port, enum tcpci_msg_type partner) -{ - return PD_REV30; -} - -void prl_hard_reset_complete(int port) -{} - -int prl_is_running(int port) -{ - return 1; -} - -__overridable bool prl_is_busy(int port) -{ - return false; -} - -void prl_reset_soft(int port) -{} - -void prl_send_ctrl_msg(int port, enum tcpci_msg_type type, - enum pd_ctrl_msg_type msg) -{ - mock_prl_port[port].last_ctrl_msg = msg; - mock_prl_port[port].last_data_msg = 0; - mock_prl_port[port].last_tx_type = type; -} - -void prl_send_data_msg(int port, enum tcpci_msg_type type, - enum pd_data_msg_type msg) -{ - mock_prl_port[port].last_data_msg = msg; - mock_prl_port[port].last_ctrl_msg = 0; - mock_prl_port[port].last_tx_type = type; -} - -void prl_send_ext_data_msg(int port, enum tcpci_msg_type type, - enum pd_ext_msg_type msg) -{} - -void prl_set_rev(int port, enum tcpci_msg_type partner, - enum pd_rev_type rev) -{} - - -int mock_prl_wait_for_tx_msg(int port, - enum tcpci_msg_type tx_type, - enum pd_ctrl_msg_type ctrl_msg, - enum pd_data_msg_type data_msg, - int timeout) -{ - uint64_t end_time = get_time().val + timeout; - - while (get_time().val < end_time) { - if (mock_prl_port[port].last_tx_type != TCPCI_MSG_INVALID) { - TEST_EQ(mock_prl_port[port].last_tx_type, - tx_type, "%d"); - TEST_EQ(mock_prl_port[port].last_ctrl_msg, - ctrl_msg, "%d"); - TEST_EQ(mock_prl_port[port].last_data_msg, - data_msg, "%d"); - mock_prl_clear_last_sent_msg(port); - return EC_SUCCESS; - } - task_wait_event(5 * MSEC); - } - /* A message of the expected type should have been sent by end_time. */ - TEST_ASSERT(0); - return EC_ERROR_UNKNOWN; -} - -enum pd_ctrl_msg_type mock_prl_get_last_sent_ctrl_msg(int port) -{ - enum pd_ctrl_msg_type last = mock_prl_port[port].last_ctrl_msg; - - mock_prl_clear_last_sent_msg(port); - return last; -} - -enum pd_data_msg_type mock_prl_get_last_sent_data_msg(int port) -{ - enum pd_data_msg_type last = mock_prl_port[port].last_data_msg; - - mock_prl_clear_last_sent_msg(port); - return last; -} - -void mock_prl_clear_last_sent_msg(int port) -{ - mock_prl_port[port].last_data_msg = 0; - mock_prl_port[port].last_ctrl_msg = 0; - mock_prl_port[port].last_tx_type = TCPCI_MSG_INVALID; -} - -timestamp_t prl_get_tcpc_tx_success_ts(int port) -{ - return get_time(); -} -void mock_prl_message_sent(int port) -{ - mock_prl_port[port].message_sent = 1; -} - -void mock_prl_message_received(int port) -{ - mock_prl_port[port].message_received = 1; -} - -void mock_prl_report_error(int port, enum pe_error e, - enum tcpci_msg_type tx_type) -{ - mock_prl_port[port].error = e; - mock_prl_port[port].error_tx_type = tx_type; -} - -void prl_run(int port, int evt, int en) -{ - if (mock_prl_port[port].message_sent) { - ccprints("message_sent"); - pe_message_sent(port); - mock_prl_port[port].message_sent = 0; - } - if (mock_prl_port[port].message_received) { - ccprints("message_received"); - pe_message_received(port); - mock_prl_port[port].message_received = 0; - } - if (mock_prl_port[port].error_tx_type != TCPCI_MSG_INVALID) { - ccprints("pe_error %d", mock_prl_port[port].error); - pe_report_error(port, - mock_prl_port[port].error, - mock_prl_port[port].error_tx_type); - mock_prl_port[port].error = 0; - mock_prl_port[port].error_tx_type = TCPCI_MSG_INVALID; - } -} diff --git a/common/mock/usb_tc_sm_mock.c b/common/mock/usb_tc_sm_mock.c deleted file mode 100644 index d55def12e2..0000000000 --- a/common/mock/usb_tc_sm_mock.c +++ /dev/null @@ -1,214 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Mock USB TC state machine */ - -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "usb_tc_sm.h" -#include "mock/usb_tc_sm_mock.h" -#include "memory.h" - -#ifndef CONFIG_COMMON_RUNTIME -#define cprints(format, args...) -#endif - -#ifndef TEST_BUILD -#error "Mocks should only be in the test build." -#endif - -struct mock_tc_port_t mock_tc_port[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void mock_tc_port_reset(void) -{ - int port; - - for (port = 0 ; port < CONFIG_USB_PD_PORT_MAX_COUNT ; ++port) { - mock_tc_port[port].rev = PD_REV30; - mock_tc_port[port].pd_enable = 0; - mock_tc_port[port].msg_tx_id = 0; - mock_tc_port[port].msg_rx_id = 0; - mock_tc_port[port].sop = TCPCI_MSG_INVALID; - mock_tc_port[port].lcl_rp = TYPEC_RP_RESERVED; - mock_tc_port[port].attached_snk = 0; - mock_tc_port[port].attached_src = 0; - mock_tc_port[port].vconn_src = false; - mock_tc_port[port].data_role = PD_ROLE_UFP; - mock_tc_port[port].power_role = PD_ROLE_SINK; - } -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - return PD_PLUG_FROM_DFP_UFP; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return mock_tc_port[port].pd_enable; -} - -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ - mock_tc_port[port].lcl_rp = rp; -} - -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ -} - -int tc_is_attached_src(int port) -{ - return mock_tc_port[port].attached_src; -} - -int tc_is_attached_snk(int port) -{ - return mock_tc_port[port].attached_snk; -} - -void tc_prs_snk_src_assert_rp(int port) -{ - mock_tc_port[port].attached_snk = 0; - mock_tc_port[port].attached_src = 1; -} - -void tc_prs_src_snk_assert_rd(int port) -{ - mock_tc_port[port].attached_snk = 1; - mock_tc_port[port].attached_src = 0; -} - -int typec_update_cc(int port) -{ - return EC_SUCCESS; -} - -int tc_check_vconn_swap(int port) -{ - return 0; -} - -void tc_ctvpd_detected(int port) -{} - -int tc_is_vconn_src(int port) -{ - return mock_tc_port[port].vconn_src; -} - -void tc_hard_reset_request(int port) -{ - mock_tc_port_reset(); -} - -void tc_partner_dr_data(int port, int en) -{} - -void tc_partner_dr_power(int port, int en) -{} - -void tc_partner_unconstrainedpower(int port, int en) -{} - -void tc_partner_usb_comm(int port, int en) -{} - -void tc_pd_connection(int port, int en) -{} - -void tc_pr_swap_complete(int port, bool success) -{} - -void tc_src_power_off(int port) -{} - -void tc_start_error_recovery(int port) -{} - -void tc_snk_power_off(int port) -{} - -void tc_request_power_swap(int port) -{ -} - -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return PD_DRP_TOGGLE_ON; -} - -enum pd_data_role pd_get_data_role(int port) -{ - return mock_tc_port[port].data_role; -} - -enum pd_power_role pd_get_power_role(int port) -{ - return mock_tc_port[port].power_role; -} - -enum pd_cc_states pd_get_task_cc_state(int port) -{ - return PD_CC_NONE; -} - -int pd_is_connected(int port) -{ - return 1; -} - -bool pd_is_disconnected(int port) -{ - return false; -} - -bool pd_get_partner_usb_comm_capable(int port) -{ - return true; -} - -bool pd_get_partner_dual_role_power(int port) -{ - return true; -} - -bool pd_capable(int port) -{ - return true; -} - -bool pd_waiting_on_partner_src_caps(int port) -{ - return false; -} - -void pd_set_suspend(int port, int suspend) -{ -} - -void pd_set_error_recovery(int port) -{ -} - -enum tcpc_cc_polarity pd_get_polarity(int port) -{ - return POLARITY_CC1; -} - -void pd_request_data_swap(int port) -{} - -void pd_request_vconn_swap_off(int port) -{} - -void pd_request_vconn_swap_on(int port) -{} - -bool pd_alt_mode_capable(int port) -{ - return false; -} diff --git a/common/motion_orientation.c b/common/motion_orientation.c deleted file mode 100644 index 9a20ff8499..0000000000 --- a/common/motion_orientation.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Implement an orientation sensor. */ - -#include "motion_orientation.h" - -/* - * Orientation mode vectors, must match sequential ordering of - * known orientations from enum motionsensor_orientation - */ -static const intv3_t orientation_modes[] = { - [MOTIONSENSE_ORIENTATION_LANDSCAPE] = { 0, -1, 0 }, - [MOTIONSENSE_ORIENTATION_PORTRAIT] = { 1, 0, 0 }, - [MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT] = { -1, 0, 0 }, - [MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE] = { 0, 1, 0 }, -}; - -enum motionsensor_orientation motion_orientation_remap( - const struct motion_sensor_t *s, - enum motionsensor_orientation orientation) -{ - enum motionsensor_orientation rotated_orientation; - const intv3_t *orientation_v; - intv3_t rotated_orientation_v; - - if (orientation == MOTIONSENSE_ORIENTATION_UNKNOWN) - return MOTIONSENSE_ORIENTATION_UNKNOWN; - - orientation_v = &orientation_modes[orientation]; - rotate(*orientation_v, *s->rot_standard_ref, rotated_orientation_v); - rotated_orientation = ((2 * rotated_orientation_v[1] + - rotated_orientation_v[0] + 4) % 5); - return rotated_orientation; -} diff --git a/common/newton_fit.c b/common/newton_fit.c deleted file mode 100644 index ae81a45f07..0000000000 --- a/common/newton_fit.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright 2020 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 "common.h" -#include "console.h" -#include "newton_fit.h" -#include "math.h" -#include "math_util.h" -#include <string.h> - -#define CPRINTS(fmt, args...) cprints(CC_MOTION_SENSE, fmt, ##args) - -static fp_t distance_squared(fpv3_t a, fpv3_t b) -{ - fpv3_t delta; - - fpv3_init(delta, a[X] - b[X], a[Y] - b[Y], a[Z] - b[Z]); - return fpv3_dot(delta, delta); -} - -static fp_t compute_error(struct newton_fit *fit, fpv3_t center) -{ - fp_t error = FLOAT_TO_FP(0.0f); - struct queue_iterator it; - struct newton_fit_orientation *_it; - - for (queue_begin(fit->orientations, &it); it.ptr != NULL; - queue_next(fit->orientations, &it)) { - fp_t e; - - _it = (struct newton_fit_orientation *)it.ptr; - e = FLOAT_TO_FP(1.0f) - - distance_squared(_it->orientation, center); - error += fp_mul(e, e); - } - - return error; -} - -static bool is_ready_to_compute(struct newton_fit *fit, bool prune) -{ - bool has_min_samples = true; - struct queue_iterator it; - struct newton_fit_orientation *_it; - - /* Not full, not ready to compute. */ - if (!queue_is_full(fit->orientations)) - return false; - - /* Inspect all the orientations. */ - for (queue_begin(fit->orientations, &it); it.ptr != NULL; - queue_next(fit->orientations, &it)) { - _it = (struct newton_fit_orientation *)it.ptr; - /* If an orientation has too few samples, flag that. */ - CPRINTS(" orientation %u/%u", _it->nsamples, - fit->min_orientation_samples); - if (_it->nsamples < fit->min_orientation_samples) { - has_min_samples = false; - break; - } - } - - /* If all orientations have the minimum samples, we're done and can - * compute the bias. - */ - if (has_min_samples) - return true; - - /* If we got here and prune is true, then we need to remove the oldest - * entry to make room for new orientations. - */ - if (prune) - queue_advance_head(fit->orientations, 1); - - return false; -} - -void newton_fit_reset(struct newton_fit *fit) -{ - queue_init(fit->orientations); -} - -bool newton_fit_accumulate(struct newton_fit *fit, fp_t x, fp_t y, fp_t z) -{ - struct queue_iterator it; - struct newton_fit_orientation *_it; - fpv3_t v, delta; - - fpv3_init(v, x, y, z); - - /* Check if we can merge this new data point with an existing - * orientation. - */ - for (queue_begin(fit->orientations, &it); it.ptr != NULL; - queue_next(fit->orientations, &it)) { - _it = (struct newton_fit_orientation *)it.ptr; - - fpv3_sub(delta, v, _it->orientation); - /* Skip entries that are too far away. */ - if (fpv3_dot(delta, delta) >= fit->nearness_threshold) - continue; - - /* Merge new data point with this orientation. */ - fpv3_scalar_mul(_it->orientation, - FLOAT_TO_FP(1.0f) - fit->new_pt_weight); - fpv3_scalar_mul(v, fit->new_pt_weight); - fpv3_add(_it->orientation, _it->orientation, v); - if (_it->nsamples < 0xff) - _it->nsamples++; - return is_ready_to_compute(fit, false); - } - - /* If queue isn't full. */ - if (!queue_is_full(fit->orientations)) { - struct newton_fit_orientation entry; - - entry.nsamples = 1; - fpv3_init(entry.orientation, x, y, z); - queue_add_unit(fit->orientations, &entry); - - return is_ready_to_compute(fit, false); - } - - return is_ready_to_compute(fit, true); -} - -void newton_fit_compute(struct newton_fit *fit, fpv3_t bias, fp_t *radius) -{ - struct queue_iterator it; - struct newton_fit_orientation *_it; - fpv3_t new_bias, offset, delta; - fp_t error, new_error; - uint32_t iteration = 0; - fp_t inv_orient_count; - - if (queue_is_empty(fit->orientations)) - return; - - inv_orient_count = fp_div(FLOAT_TO_FP(1.0f), - queue_count(fit->orientations)); - - memcpy(new_bias, bias, sizeof(fpv3_t)); - new_error = compute_error(fit, new_bias); - - do { - memcpy(bias, new_bias, sizeof(fpv3_t)); - error = new_error; - fpv3_zero(offset); - - for (queue_begin(fit->orientations, &it); it.ptr != NULL; - queue_next(fit->orientations, &it)) { - fp_t mag; - - _it = (struct newton_fit_orientation *)it.ptr; - - fpv3_sub(delta, _it->orientation, bias); - mag = fpv3_norm(delta); - fpv3_scalar_mul(delta, - fp_div(mag - FLOAT_TO_FP(1.0f), mag)); - fpv3_add(offset, offset, delta); - } - - fpv3_scalar_mul(offset, inv_orient_count); - fpv3_add(new_bias, bias, offset); - new_error = compute_error(fit, new_bias); - if (new_error > error) - memcpy(new_bias, bias, sizeof(fpv3_t)); - ++iteration; - } while (iteration < fit->max_iterations && new_error < error && - new_error > fit->error_threshold); - - memcpy(bias, new_bias, sizeof(fpv3_t)); - - if (radius) { - *radius = FLOAT_TO_FP(0.0f); - for (queue_begin(fit->orientations, &it); it.ptr != NULL; - queue_next(fit->orientations, &it)) { - _it = (struct newton_fit_orientation *)it.ptr; - fpv3_sub(delta, _it->orientation, bias); - *radius += fpv3_norm(delta); - } - *radius *= inv_orient_count; - } -} diff --git a/common/ocpc.c b/common/ocpc.c deleted file mode 100644 index 3bc2a265d3..0000000000 --- a/common/ocpc.c +++ /dev/null @@ -1,767 +0,0 @@ -/* Copyright 2020 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. - */ - -/* OCPC - One Charger IC Per Type-C module */ - -#include "battery.h" -#include "battery_fuel_gauge.h" -#include "charge_manager.h" -#include "charge_state_v2.h" -#include "charger.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "math_util.h" -#include "ocpc.h" -#include "timer.h" -#include "usb_pd.h" -#include "util.h" - -/* - * These constants were chosen by tuning the PID loop to reduce oscillations and - * minimize overshoot. - */ -#define KP 1 -#define KP_DIV 4 -#define KI 1 -#define KI_DIV 15 -#define KD 1 -#define KD_DIV 10 - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHARGER, outstr) -#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) -#define CPRINT_VIZ(format, args...) \ -do { \ - if (viz_output) \ - cprintf(CC_CHARGER, format, ## args); \ -} while (0) -#define CPRINTS_DBG(format, args...) \ -do { \ - if (debug_output) \ - cprints(CC_CHARGER, format, ## args); \ -} while (0) -#define CPRINTF_DBG(format, args...) \ -do { \ - if (debug_output) \ - cprintf(CC_CHARGER, format, ## args); \ -} while (0) - - -/* Code refactor will be needed if more than 2 charger chips are present */ -BUILD_ASSERT(CHARGER_NUM == 2); - -static int k_p = KP; -static int k_i = KI; -static int k_d = KD; -static int k_p_div = KP_DIV; -static int k_i_div = KI_DIV; -static int k_d_div = KD_DIV; -static int debug_output; -static int viz_output; - -#define NUM_RESISTANCE_SAMPLES 8 -#define COMBINED_IDX 0 -#define RBATT_IDX 1 -#define RSYS_IDX 2 -static int resistance_tbl[NUM_RESISTANCE_SAMPLES][3] = { - /* Rsys+Rbatt Rbatt Rsys */ - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, - {CONFIG_OCPC_DEF_RBATT_MOHMS, CONFIG_OCPC_DEF_RBATT_MOHMS, 0}, -}; -static int resistance_tbl_idx; -static int mean_resistance[3]; -static int stddev_resistance[3]; -static int ub[3]; -static int lb[3]; - -enum phase { - PHASE_UNKNOWN = -1, - PHASE_PRECHARGE, - PHASE_CC, - PHASE_CV_TRIP, - PHASE_CV_COMPLETE, -}; - -__overridable void board_ocpc_init(struct ocpc_data *ocpc) -{ -} - -static enum ec_error_list ocpc_precharge_enable(bool enable); - -static void calc_resistance_stats(struct ocpc_data *ocpc) -{ - int i; - int j; - int sum; - int cols = 3; - int act_chg = ocpc->active_chg_chip; - - /* Only perform separate stats on Rsys and Rbatt if necessary. */ - if ((ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP)) - cols = 1; - - /* Calculate mean */ - for (i = 0; i < cols; i++) { - sum = 0; - for (j = 0; j < NUM_RESISTANCE_SAMPLES; j++) { - sum += resistance_tbl[j][i]; - CPRINTF_DBG("%d ", resistance_tbl[j][i]); - } - CPRINTF_DBG("\n"); - - mean_resistance[i] = sum / NUM_RESISTANCE_SAMPLES; - - /* Calculate standard deviation */ - sum = 0; - for (j = 0; j < NUM_RESISTANCE_SAMPLES; j++) - sum += POW2(resistance_tbl[j][i] - mean_resistance[i]); - - stddev_resistance[i] = fp_sqrtf(INT_TO_FP(sum / - NUM_RESISTANCE_SAMPLES)); - stddev_resistance[i] = FP_TO_INT(stddev_resistance[i]); - /* - * Don't let our stddev collapse to 0 to continually consider - * new values. - */ - stddev_resistance[i] = MAX(stddev_resistance[i], 1); - CPRINTS_DBG("%d: mean: %d stddev: %d", i, mean_resistance[i], - stddev_resistance[i]); - lb[i] = MAX(0, mean_resistance[i] - (3 * stddev_resistance[i])); - ub[i] = mean_resistance[i] + (3 * stddev_resistance[i]); - } -} - -static bool is_within_range(struct ocpc_data *ocpc, int combined, int rbatt, - int rsys) -{ - int act_chg = ocpc->active_chg_chip; - bool valid; - - /* Discard measurements not within a 6 std. dev. window. */ - if ((ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP)) { - /* We only know the combined Rsys+Rbatt */ - valid = (combined > 0) && - (combined <= ub[COMBINED_IDX]) && - (combined >= lb[COMBINED_IDX]); - } else { - valid = (rsys <= ub[RSYS_IDX]) && (rsys >= lb[RSYS_IDX]) && - (rbatt <= ub[RBATT_IDX]) && (rbatt >= lb[RBATT_IDX]) && - (rsys > 0) && (rbatt > 0); - } - - if (!valid) - CPRINTS_DBG("Discard Rc:%d Rb:%d Rs:%d", combined, rbatt, rsys); - - return valid; -} - -enum ec_error_list ocpc_calc_resistances(struct ocpc_data *ocpc, - struct batt_params *battery) -{ - int act_chg = ocpc->active_chg_chip; - static bool seeded; - static int initial_samples; - int combined; - int rsys = -1; - int rbatt = -1; - - /* - * In order to actually calculate the resistance, we need to make sure - * we're actually charging the battery at a significant rate. The LSB - * of a charger IC can be as high as 96mV. Assuming a resistance of 60 - * mOhms, we would need a current of 1666mA to have a voltage delta of - * 100mV. - */ - if ((battery->current <= 1666) || - (!(ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP) && - (ocpc->isys_ma <= 0)) || - (ocpc->vsys_aux_mv < ocpc->vsys_mv)) { - CPRINTS_DBG("Not charging... won't determine resistance"); - CPRINTS_DBG("vsys_aux_mv: %dmV vsys_mv: %dmV", - ocpc->vsys_aux_mv, ocpc->vsys_mv); - return EC_ERROR_INVALID_CONFIG; /* We must be charging */ - } - - /* - * The combined system and battery resistance is the delta between Vsys - * and Vbatt divided by Ibatt. - */ - if ((ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP)) { - /* - * There's no provision to measure Isys, so we cannot separate - * out Rsys from Rbatt. - */ - combined = ((ocpc->vsys_aux_mv - battery->voltage) * 1000) / - battery->current; - } else { - rsys = ((ocpc->vsys_aux_mv - ocpc->vsys_mv) * 1000) / - ocpc->isys_ma; - rbatt = ((ocpc->vsys_mv - battery->voltage) * 1000) / - battery->current; - combined = rsys + rbatt; - } - - /* Discard measurements not within a 6 std dev window. */ - if ((!seeded) || - (seeded && is_within_range(ocpc, combined, rbatt, rsys))) { - if (!(ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP)) { - resistance_tbl[resistance_tbl_idx][RSYS_IDX] = - MAX(rsys, 0); - resistance_tbl[resistance_tbl_idx][RBATT_IDX] = - MAX(rbatt, CONFIG_OCPC_DEF_RBATT_MOHMS); - } - resistance_tbl[resistance_tbl_idx][COMBINED_IDX] = - MAX(combined, CONFIG_OCPC_DEF_RBATT_MOHMS); - calc_resistance_stats(ocpc); - resistance_tbl_idx = (resistance_tbl_idx + 1) % - NUM_RESISTANCE_SAMPLES; - } - - if (seeded) { - ocpc->combined_rsys_rbatt_mo = - MAX(mean_resistance[COMBINED_IDX], - CONFIG_OCPC_DEF_RBATT_MOHMS); - - if (!(ocpc->chg_flags[act_chg] & OCPC_NO_ISYS_MEAS_CAP)) { - ocpc->rsys_mo = mean_resistance[RSYS_IDX]; - ocpc->rbatt_mo = MAX(mean_resistance[RBATT_IDX], - CONFIG_OCPC_DEF_RBATT_MOHMS); - CPRINTS_DBG("Rsys: %dmOhm Rbatt: %dmOhm", - ocpc->rsys_mo, ocpc->rbatt_mo); - } - - CPRINTS_DBG("Rsys+Rbatt: %dmOhm", ocpc->combined_rsys_rbatt_mo); - } else { - seeded = ++initial_samples >= (2 * NUM_RESISTANCE_SAMPLES) ? - true : false; - } - - return EC_SUCCESS; -} - -int ocpc_config_secondary_charger(int *desired_input_current, - struct ocpc_data *ocpc, - int voltage_mv, int current_ma) -{ - int rv = EC_SUCCESS; - struct batt_params batt; - const struct battery_info *batt_info; - struct charger_params charger; - int vsys_target = 0; - int drive = 0; - int i_ma = 0; - static int i_ma_CC_CV; - int min_vsys_target; - int error = 0; - int derivative = 0; - static enum phase ph; - static int prev_limited; - int chgnum; - enum ec_error_list result; - static int iterations; - int i_step; - static timestamp_t delay; - int i, step, loc; - bool icl_reached = false; - static timestamp_t precharge_exit; - - /* - * There's nothing to do if we're not using this charger. Should - * there be more than two charger ICs in the future, the following check - * should change to ensure that only the active charger IC is acted - * upon. - */ - chgnum = charge_get_active_chg_chip(); - if (chgnum != CHARGER_SECONDARY) - return EC_ERROR_INVAL; - - batt_info = battery_get_info(); - - if (current_ma == 0) { - vsys_target = voltage_mv; - goto set_vsys; - } - - /* - * Check to see if the charge FET is disabled. If it's disabled, the - * charging loop is broken and increasing VSYS will not actually help. - * Therefore, don't make any changes at this time. - */ - if (battery_is_charge_fet_disabled() && - (battery_get_disconnect_state() == BATTERY_NOT_DISCONNECTED)) { - CPRINTS("CFET disabled; not changing VSYS!"); - - /* - * Let's check back in 5 seconds to see if the CFET is enabled - * now. Note that if this continues to occur, we'll keep - * pushing this out. - */ - delay = get_time(); - delay.val += (5 * SECOND); - return EC_ERROR_INVALID_CONFIG; - } - - /* - * The CFET status changed recently, wait until it's no longer disabled - * for awhile before modifying VSYS. This could be the fuel gauge - * performing some impedence calculations. - */ - if (!timestamp_expired(delay, NULL)) - return EC_ERROR_BUSY; - - result = charger_set_vsys_compensation(chgnum, ocpc, current_ma, - voltage_mv); - switch (result) { - case EC_SUCCESS: - /* No further action required, so we're done here. */ - return EC_SUCCESS; - - case EC_ERROR_UNIMPLEMENTED: - /* Let's get to work */ - break; - - default: - /* Something went wrong configuring the auxiliary charger IC. */ - CPRINTS("Failed to set VSYS compensation! (%d) (result: %d)", - chgnum, result); - return result; - } - - if (ocpc->last_vsys == OCPC_UNINIT) { - ph = PHASE_UNKNOWN; - precharge_exit.val = 0; - iterations = 0; - } - - - /* - * We need to induce a current flow that matches the requested current - * by raising VSYS. Let's start by getting the latest data that we - * know of. - */ - batt_info = battery_get_info(); - battery_get_params(&batt); - ocpc_get_adcs(ocpc); - charger_get_params(&charger); - - - /* - * If the system is in S5/G3, we can calculate the board and battery - * resistances. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF | - CHIPSET_STATE_ANY_SUSPEND)) { - /* - * In the first few iterations of the loop, charging isn't - * stable/correct so making the calculation then leads to some - * strange values throwing off the loop even more. However, - * after those initial iterations it then begins to behave as - * expected. From there onwards, the resistance values aren't - * changing _too_ rapidly. This is why we calculate with every - * modulo 4 interval. - */ - iterations++; - if (!(iterations % 4)) - ocpc_calc_resistances(ocpc, &batt); - iterations %= 5; - } - - /* Set our current target accordingly. */ - if (batt.desired_voltage) { - if (((batt.voltage < batt_info->voltage_min) || - ((batt.voltage < batt_info->voltage_normal) && - (current_ma <= batt_info->precharge_current))) && - (ph != PHASE_PRECHARGE)) { - /* - * If the charger IC doesn't support the linear charge - * feature, proceed to the CC phase. - */ - result = ocpc_precharge_enable(true); - if (result == EC_ERROR_UNIMPLEMENTED) { - ph = PHASE_CC; - } else if (result == EC_SUCCESS) { - CPRINTS("OCPC: Enabling linear precharge"); - ph = PHASE_PRECHARGE; - i_ma = current_ma; - } - } else if (batt.voltage < batt.desired_voltage) { - if ((ph == PHASE_PRECHARGE) && - (current_ma > - batt_info->precharge_current)) { - /* - * Precharge phase is complete. Now set the - * target VSYS to the battery voltage to prevent - * a large current spike during the transition. - */ - /* - * If we'd like to exit precharge, let's wait a - * short delay. - */ - if (!precharge_exit.val) { - CPRINTS("OCPC: Preparing to exit " - "precharge"); - precharge_exit = get_time(); - precharge_exit.val += 3 * SECOND; - } - if (timestamp_expired(precharge_exit, NULL)) { - CPRINTS("OCPC: Precharge complete"); - charger_set_voltage(CHARGER_SECONDARY, - batt.voltage); - ocpc->last_vsys = batt.voltage; - ocpc_precharge_enable(false); - ph = PHASE_CC; - precharge_exit.val = 0; - } - } - - if ((ph != PHASE_PRECHARGE) && (ph < PHASE_CV_TRIP)) - ph = PHASE_CC; - i_ma = current_ma; - } else { - /* - * Once the battery voltage reaches the desired voltage, - * we should note that we've reached the CV step and set - * VSYS to the desired CV + offset. - */ - i_ma = batt.current; - ph = ph == PHASE_CC ? PHASE_CV_TRIP : PHASE_CV_COMPLETE; - if (ph == PHASE_CV_TRIP) - i_ma_CC_CV = batt.current; - - } - } - - /* Ensure our target is not negative. */ - i_ma = MAX(i_ma, 0); - - /* Convert desired mA to what the charger could actually regulate to. */ - i_step = (int)charger_get_info()->current_step; - i_ma = (i_ma / i_step) * i_step; - - /* - * We'll use our current target and our combined Rsys+Rbatt to seed our - * VSYS target. However, we'll use a PID loop to correct the error and - * help drive VSYS to what it _should_ be in order to reach our current - * target. The first time through this function, we won't make any - * corrections in order to determine our initial error. - */ - if (ocpc->last_vsys != OCPC_UNINIT) { - error = i_ma - batt.current; - /* Add some hysteresis. */ - if (ABS(error) < (i_step / 2)) - error = 0; - - /* Make a note if we're significantly over target. */ - if (error < -100) - CPRINTS("OCPC: over target %dmA", error * -1); - - derivative = error - ocpc->last_error; - ocpc->last_error = error; - ocpc->integral += error; - if (ocpc->integral > 500) - ocpc->integral = 500; - } - - CPRINTS_DBG("phase = %d", ph); - CPRINTS_DBG("error = %dmA", error); - CPRINTS_DBG("derivative = %d", derivative); - CPRINTS_DBG("integral = %d", ocpc->integral); - CPRINTS_DBG("batt.voltage = %dmV", batt.voltage); - CPRINTS_DBG("batt.desired_voltage = %dmV", batt.desired_voltage); - CPRINTS_DBG("batt.desired_current = %dmA", batt.desired_current); - CPRINTS_DBG("batt.current = %dmA", batt.current); - CPRINTS_DBG("i_ma = %dmA", i_ma); - - min_vsys_target = MIN(batt.voltage, batt.desired_voltage); - CPRINTS_DBG("min_vsys_target = %d", min_vsys_target); - - /* Obtain the drive from our PID controller. */ - if ((ocpc->last_vsys != OCPC_UNINIT) && - (ph > PHASE_PRECHARGE)) { - drive = (k_p * error / k_p_div) + - (k_i * ocpc->integral / k_i_div) + - (k_d * derivative / k_d_div); - /* - * Let's limit upward transitions to 10mV. It's okay to reduce - * VSYS rather quickly, but we'll be conservative on - * increasing VSYS. - */ - if (drive > 10) - drive = 10; - CPRINTS_DBG("drive = %d", drive); - } - - /* - * For the pre-charge phase, simply keep the VSYS target at the desired - * voltage. - */ - if (ph == PHASE_PRECHARGE) - vsys_target = batt.desired_voltage; - - /* - * Adjust our VSYS target by applying the calculated drive. Note that - * we won't apply our drive the first time through this function such - * that we can determine our initial error. - */ - if ((ocpc->last_vsys != OCPC_UNINIT) && (ph > PHASE_PRECHARGE)) - vsys_target = ocpc->last_vsys + drive; - - /* - * Once we're in the CV region, all we need to do is keep VSYS at the - * desired voltage. - */ - if (ph == PHASE_CV_TRIP) { - vsys_target = batt.desired_voltage + - ((i_ma_CC_CV * - ocpc->combined_rsys_rbatt_mo) / 1000); - CPRINTS_DBG("i_ma_CC_CV = %d", i_ma_CC_CV); - } - if (ph == PHASE_CV_COMPLETE) - vsys_target = batt.desired_voltage + - ((batt_info->precharge_current * - ocpc->combined_rsys_rbatt_mo) / 1000); - - /* - * Ensure VSYS is no higher than the specified maximum battery voltage - * plus the voltage drop across the system. - */ - vsys_target = CLAMP(vsys_target, min_vsys_target, - batt_info->voltage_max + - (i_ma * ocpc->combined_rsys_rbatt_mo / 1000)); - - /* If we're input current limited, we cannot increase VSYS any more. */ - CPRINTS_DBG("OCPC: Inst. Input Current: %dmA (Limit: %dmA)", - ocpc->secondary_ibus_ma, *desired_input_current); - - if (charger_is_icl_reached(chgnum, &icl_reached) != EC_SUCCESS) { - /* - * If the charger doesn't support telling us, assume that the - * input current limit is reached if we're consuming more than - * 95% of the limit. - */ - if (ocpc->secondary_ibus_ma >= - (*desired_input_current * 95 / 100)) - icl_reached = true; - } - - if (icl_reached && (vsys_target > ocpc->last_vsys) && - (ocpc->last_vsys != OCPC_UNINIT)) { - if (!prev_limited) - CPRINTS("Input limited! Not increasing VSYS"); - prev_limited = 1; - return rv; - } - prev_limited = 0; - -set_vsys: - /* VSYS should never be below the battery's min voltage. */ - vsys_target = MAX(vsys_target, batt_info->voltage_min); - /* To reduce spam, only print when we change VSYS significantly. */ - if ((ABS(vsys_target - ocpc->last_vsys) > 10) || debug_output) - CPRINTS("OCPC: Target VSYS: %dmV", vsys_target); - charger_set_voltage(CHARGER_SECONDARY, vsys_target); - ocpc->last_vsys = vsys_target; - - /* - * Print a visualization graph of the actual current vs. the target. - * Each position represents 5% of the target current. - */ - if (i_ma != 0) { - step = 5 * i_ma / 100; - loc = error / step; - loc = CLAMP(loc, -10, 10); - CPRINT_VIZ("["); - for (i = -10; i <= 10; i++) { - if (i == 0) - CPRINT_VIZ(loc == 0 ? "#" : "|"); - else - CPRINT_VIZ(i == loc ? "o" : "-"); - } - CPRINT_VIZ("] (actual)%dmA (desired)%dmA\n", batt.current, - i_ma); - } - - return rv; -} - -void ocpc_get_adcs(struct ocpc_data *ocpc) -{ - int val; - - val = 0; - if (!charger_get_vbus_voltage(CHARGER_PRIMARY, &val)) - ocpc->primary_vbus_mv = val; - - val = 0; - if (!charger_get_input_current(CHARGER_PRIMARY, &val)) - ocpc->primary_ibus_ma = val; - - val = 0; - if (!charger_get_actual_voltage(CHARGER_PRIMARY, &val)) - ocpc->vsys_mv = val; - - if (board_get_charger_chip_count() <= CHARGER_SECONDARY) { - ocpc->secondary_vbus_mv = 0; - ocpc->secondary_ibus_ma = 0; - ocpc->vsys_aux_mv = 0; - ocpc->isys_ma = 0; - return; - } - - val = 0; - if (!charger_get_vbus_voltage(CHARGER_SECONDARY, &val)) - ocpc->secondary_vbus_mv = val; - - val = 0; - if (!charger_get_input_current(CHARGER_SECONDARY, &val)) - ocpc->secondary_ibus_ma = val; - - val = 0; - if (!charger_get_actual_voltage(CHARGER_SECONDARY, &val)) - ocpc->vsys_aux_mv = val; - - val = 0; - if (!charger_get_actual_current(CHARGER_SECONDARY, &val)) - ocpc->isys_ma = val; -} - -__overridable void ocpc_get_pid_constants(int *kp, int *kp_div, - int *ki, int *ki_div, - int *kd, int *kd_div) -{ -} - -static enum ec_error_list ocpc_precharge_enable(bool enable) -{ - /* Enable linear charging on the primary charger IC. */ - int rv = charger_enable_linear_charge(CHARGER_PRIMARY, enable); - - if (rv) - CPRINTS("OCPC: Failed to %sble linear charge!", enable ? "ena" - : "dis"); - - return rv; -} - -void ocpc_reset(struct ocpc_data *ocpc) -{ - struct batt_params batt; - - battery_get_params(&batt); - ocpc->integral = 0; - ocpc->last_error = 0; - ocpc->last_vsys = OCPC_UNINIT; - - /* - * Initialize the VSYS target on the aux chargers to the current battery - * voltage to avoid a large spike. - */ - if (ocpc->active_chg_chip > CHARGER_PRIMARY && batt.voltage > 0) { - CPRINTS("OCPC: C%d Init VSYS to %dmV", ocpc->active_chg_chip, - batt.voltage); - charger_set_voltage(ocpc->active_chg_chip, batt.voltage); - } - - /* - * See(b:191347747) When linear precharge is enabled, it may affect - * the charging behavior from the primary charger IC. Therefore as - * a part of the reset process, we need to disable linear precharge. - */ - ocpc_precharge_enable(false); -} - -static void ocpc_set_pid_constants(void) -{ - ocpc_get_pid_constants(&k_p, &k_p_div, &k_i, &k_i_div, &k_d, &k_d_div); -} -DECLARE_HOOK(HOOK_INIT, ocpc_set_pid_constants, HOOK_PRIO_DEFAULT); - -void ocpc_init(struct ocpc_data *ocpc) -{ - /* - * We can start off assuming that the board resistance is 0 ohms - * and later on, we can update this value if we charge the - * system in suspend or off. - */ - ocpc->combined_rsys_rbatt_mo = CONFIG_OCPC_DEF_RBATT_MOHMS; - ocpc->rbatt_mo = CONFIG_OCPC_DEF_RBATT_MOHMS; - - board_ocpc_init(ocpc); -} - -static int command_ocpcdebug(int argc, char **argv) -{ - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strncmp(argv[1], "ena", 3)) { - debug_output = true; - viz_output = false; - } else if (!strncmp(argv[1], "dis", 3)) { - debug_output = false; - viz_output = false; - } else if (!strncmp(argv[1], "viz", 3)) { - debug_output = false; - viz_output = true; - } else if (!strncmp(argv[1], "all", 3)) { - debug_output = true; - viz_output = true; - } else { - return EC_ERROR_PARAM1; - } - - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(ocpcdebug, command_ocpcdebug, - "<enable/viz/all/disable", - "Enable/disable debug prints for OCPC data. " - "Enable turns on text debug, viz shows a graph." - "Each segment is 5% of current target. All shows" - " both. Disable shows no debug output."); - -static int command_ocpcpid(int argc, char **argv) -{ - int *num, *denom; - - if (argc == 4) { - switch (argv[1][0]) { - case 'p': - num = &k_p; - denom = &k_p_div; - break; - - case 'i': - num = &k_i; - denom = &k_i_div; - break; - - case 'd': - num = &k_d; - denom = &k_d_div; - break; - default: - return EC_ERROR_PARAM1; - } - - *num = atoi(argv[2]); - *denom = atoi(argv[3]); - } - - /* Print the current constants */ - ccprintf("Kp = %d / %d\n", k_p, k_p_div); - ccprintf("Ki = %d / %d\n", k_i, k_i_div); - ccprintf("Kd = %d / %d\n", k_d, k_d_div); - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(ocpcpid, command_ocpcpid, - "[<k/p/d> <numerator> <denominator>]", - "Show/Set PID constants for OCPC PID loop"); diff --git a/common/onewire.c b/common/onewire.c deleted file mode 100644 index cdb5837255..0000000000 --- a/common/onewire.c +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright 2012 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. - */ - -/* 1-wire interface module for Chrome EC */ - -#include "common.h" -#include "gpio.h" -#include "task.h" -#include "timer.h" - -/* - * Standard speed; all timings padded by 2 usec for safety. - * - * Note that these timing are actually _longer_ than legacy 1-wire standard - * speed because we're running the 1-wire bus at 3.3V instead of 5V. - */ -#define T_RSTL 602 /* Reset low pulse; 600-960 us */ -#define T_MSP 72 /* Presence detect sample time; 70-75 us */ -#define T_RSTH (68 + 260 + 5 + 2) /* Reset high; tPDHmax + tPDLmax + tRECmin */ -#define T_SLOT 70 /* Timeslot; >67 us */ -#define T_W0L 63 /* Write 0 low; 62-120 us */ -#define T_W1L 7 /* Write 1 low; 5-15 us */ -#define T_RL 7 /* Read low; 5-15 us */ -#define T_MSR 9 /* Read sample time; <15 us. Must be at least 200 ns after - * T_RL since that's how long the signal takes to be pulled - * up on our board. */ - -/** - * Output low on the bus for <usec> us, then switch back to open-drain input. - */ -static void output0(int usec) -{ - gpio_set_flags(GPIO_ONEWIRE, - GPIO_OPEN_DRAIN | GPIO_OUTPUT | GPIO_OUT_LOW); - udelay(usec); - gpio_set_flags(GPIO_ONEWIRE, GPIO_INPUT); -} - -/** - * Read a bit. - */ -static int readbit(void) -{ - int bit; - - /* - * The delay between sending the output pulse and reading the bit is - * extremely timing sensitive, so disable interrupts. - */ - interrupt_disable(); - - /* Output low */ - output0(T_RL); - - /* - * Delay to let peripheral release the line if it wants to send - * a 1-bit - */ - udelay(T_MSR - T_RL); - - /* Read bit */ - bit = gpio_get_level(GPIO_ONEWIRE); - - /* - * Enable interrupt as soon as we've read the bit. The delay to the - * end of the timeslot is a lower bound, so additional latency here is - * harmless. - */ - interrupt_enable(); - - /* Delay to end of timeslot */ - udelay(T_SLOT - T_MSR); - return bit; -} - -/** - * Write a bit. - */ -static void writebit(int bit) -{ - /* - * The delays in the output-low signal for sending 0 and 1 bits are - * extremely timing sensitive, so disable interrupts during that time. - * Interrupts can be enabled again as soon as the output is switched - * back to open-drain, since the delay for the rest of the timeslot is - * a lower bound. - */ - if (bit) { - interrupt_disable(); - output0(T_W1L); - interrupt_enable(); - udelay(T_SLOT - T_W1L); - } else { - interrupt_disable(); - output0(T_W0L); - interrupt_enable(); - udelay(T_SLOT - T_W0L); - } - -} - -int onewire_reset(void) -{ - /* Start transaction with controller reset pulse */ - output0(T_RSTL); - - /* Wait for presence detect sample time. - * - * (Alternately, we could poll waiting for a 1-bit indicating our pulse - * has let go, then poll up to max time waiting for a 0-bit indicating - * the peripheral has responded.) - */ - udelay(T_MSP); - - if (gpio_get_level(GPIO_ONEWIRE)) - return EC_ERROR_UNKNOWN; - - /* - * Wait for end of presence pulse. - * - * (Alternately, we could poll waiting for a 1-bit.) - */ - udelay(T_RSTH - T_MSP); - - return EC_SUCCESS; -} - -int onewire_read(void) -{ - int data = 0; - int i; - - for (i = 0; i < 8; i++) - data |= readbit() << i; /* LSB first */ - - return data; -} - -void onewire_write(int data) -{ - int i; - - for (i = 0; i < 8; i++) - writebit((data >> i) & 0x01); /* LSB first */ -} diff --git a/common/online_calibration.c b/common/online_calibration.c deleted file mode 100644 index 3bc56f85c7..0000000000 --- a/common/online_calibration.c +++ /dev/null @@ -1,394 +0,0 @@ -/* Copyright 2020 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 "accelgyro.h" -#include "atomic.h" -#include "hwtimer.h" -#include "online_calibration.h" -#include "common.h" -#include "mag_cal.h" -#include "util.h" -#include "vec3.h" -#include "task.h" -#include "ec_commands.h" -#include "accel_cal.h" -#include "mkbp_event.h" -#include "gyro_cal.h" - -#define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ##args) - -#ifndef CONFIG_MKBP_EVENT -#error "Must use CONFIG_MKBP_EVENT for online calibration" -#endif /* CONFIG_MKBP_EVENT */ - -/** Bitmap telling which online calibration values are valid. */ -static uint32_t sensor_calib_cache_valid_map; -/** Bitmap telling which online calibration values are dirty. */ -static uint32_t sensor_calib_cache_dirty_map; - -struct mutex g_calib_cache_mutex; - -static int get_temperature(struct motion_sensor_t *sensor, int *temp) -{ - struct online_calib_data *entry = sensor->online_calib_data; - uint32_t now; - - if (sensor->drv->read_temp == NULL) - return EC_ERROR_UNIMPLEMENTED; - - now = __hw_clock_source_read(); - if (entry->last_temperature < 0 || - time_until(entry->last_temperature_timestamp, now) > - CONFIG_TEMP_CACHE_STALE_THRES) { - int t; - int rc = sensor->drv->read_temp(sensor, &t); - - if (rc == EC_SUCCESS) { - entry->last_temperature = t; - entry->last_temperature_timestamp = now; - } else { - return rc; - } - } - - *temp = entry->last_temperature; - return EC_SUCCESS; -} - -static void data_int16_to_fp(const struct motion_sensor_t *s, - const int16_t *data, fpv3_t out) -{ - int i; - fp_t range = INT_TO_FP(s->current_range); - - for (i = 0; i < 3; ++i) { - fp_t v = INT_TO_FP((int32_t)data[i]); - - out[i] = fp_div(v, INT_TO_FP((data[i] >= 0) ? 0x7fff : 0x8000)); - out[i] = fp_mul(out[i], range); - /* Check for overflow */ - out[i] = CLAMP(out[i], -range, range); - } -} - -static void data_fp_to_int16(const struct motion_sensor_t *s, const fpv3_t data, - int16_t *out) -{ - int i; - fp_t range = INT_TO_FP(s->current_range); - - for (i = 0; i < 3; ++i) { - int32_t iv; - fp_t v = fp_div(data[i], range); - - v = fp_mul(v, INT_TO_FP(0x7fff)); - iv = FP_TO_INT(v); - /* Check for overflow */ - out[i] = ec_motion_sensor_clamp_i16(iv); - } -} - -/** - * Check a gyroscope for new bias. This function checks a given sensor (must be - * a gyroscope) for new bias values. If found, it will update the appropriate - * caches and notify the AP. - * - * @param sensor Pointer to the gyroscope sensor to check. - */ -static bool check_gyro_cal_new_bias(struct motion_sensor_t *sensor, - fpv3_t bias_out) -{ - struct online_calib_data *calib_data = - (struct online_calib_data *)sensor->online_calib_data; - struct gyro_cal_data *data = - (struct gyro_cal_data *)calib_data->type_specific_data; - int temp_out; - uint32_t timestamp_out; - - /* Check that we have a new bias. */ - if (data == NULL || calib_data == NULL || - !gyro_cal_new_bias_available(&data->gyro_cal)) - return false; - - /* Read the calibration values. */ - gyro_cal_get_bias(&data->gyro_cal, bias_out, &temp_out, ×tamp_out); - return true; -} - -static void set_gyro_cal_cache_values(struct motion_sensor_t *sensor, - fpv3_t bias) -{ - size_t sensor_num = sensor - motion_sensors; - struct online_calib_data *calib_data = - (struct online_calib_data *)sensor->online_calib_data; - - mutex_lock(&g_calib_cache_mutex); - /* Convert result to the right scale and save to cache. */ - data_fp_to_int16(sensor, bias, calib_data->cache); - /* Set valid and dirty. */ - sensor_calib_cache_valid_map |= BIT(sensor_num); - sensor_calib_cache_dirty_map |= BIT(sensor_num); - mutex_unlock(&g_calib_cache_mutex); - /* Notify the AP. */ - mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); -} - -/** - * Update the data stream (accel/mag) for a given sensor and data in all - * gyroscopes that are interested. - * - * @param sensor Pointer to the sensor that generated the data. - * @param data 3 floats/fixed point data points generated by the sensor. - * @param timestamp The timestamp at which the data was generated. - */ -static void update_gyro_cal(struct motion_sensor_t *sensor, fpv3_t data, - uint32_t timestamp) -{ - int i; - fpv3_t gyro_cal_data_out; - - /* - * Find gyroscopes, while we don't currently have instance where more - * than one are present in a board, this loop will work with any number - * of them. - */ - for (i = 0; i < SENSOR_COUNT; ++i) { - struct motion_sensor_t *s = motion_sensors + i; - struct gyro_cal_data *gyro_cal_data = - (struct gyro_cal_data *) - s->online_calib_data->type_specific_data; - bool has_new_gyro_cal_bias = false; - - /* - * If we're not looking at a gyroscope OR if the calibration - * data is NULL, skip this sensor. - */ - if (s->type != MOTIONSENSE_TYPE_GYRO || gyro_cal_data == NULL) - continue; - - /* - * Update the appropriate data stream (accel/mag) depending on - * which sensors the gyroscope is tracking. - */ - if (sensor->type == MOTIONSENSE_TYPE_ACCEL && - gyro_cal_data->accel_sensor_id == sensor - motion_sensors) { - gyro_cal_update_accel(&gyro_cal_data->gyro_cal, - timestamp, data[X], data[Y], - data[Z]); - has_new_gyro_cal_bias = - check_gyro_cal_new_bias(s, gyro_cal_data_out); - } else if (sensor->type == MOTIONSENSE_TYPE_MAG && - gyro_cal_data->mag_sensor_id == - sensor - motion_sensors) { - gyro_cal_update_mag(&gyro_cal_data->gyro_cal, timestamp, - data[X], data[Y], data[Z]); - has_new_gyro_cal_bias = - check_gyro_cal_new_bias(s, gyro_cal_data_out); - } - - if (has_new_gyro_cal_bias) - set_gyro_cal_cache_values(s, gyro_cal_data_out); - } -} - -void online_calibration_init(void) -{ - size_t i; - - for (i = 0; i < SENSOR_COUNT; i++) { - struct motion_sensor_t *s = motion_sensors + i; - void *type_specific_data = NULL; - - if (s->online_calib_data) { - s->online_calib_data->last_temperature = -1; - type_specific_data = - s->online_calib_data->type_specific_data; - } - - if (!type_specific_data) - continue; - - switch (s->type) { - case MOTIONSENSE_TYPE_ACCEL: { - accel_cal_reset((struct accel_cal *)type_specific_data); - break; - } - case MOTIONSENSE_TYPE_MAG: { - init_mag_cal((struct mag_cal_t *)type_specific_data); - break; - } - case MOTIONSENSE_TYPE_GYRO: { - init_gyro_cal( - &((struct gyro_cal_data *)type_specific_data) - ->gyro_cal); - break; - } - default: - break; - } - } -} - -bool online_calibration_has_new_values(void) -{ - bool has_dirty; - - mutex_lock(&g_calib_cache_mutex); - has_dirty = sensor_calib_cache_dirty_map != 0; - mutex_unlock(&g_calib_cache_mutex); - - return has_dirty; -} - -bool online_calibration_read(struct motion_sensor_t *sensor, - struct ec_response_online_calibration_data *out) -{ - int sensor_num = sensor - motion_sensors; - bool has_valid; - - mutex_lock(&g_calib_cache_mutex); - has_valid = (sensor_calib_cache_valid_map & BIT(sensor_num)) != 0; - if (has_valid) { - /* Update data in out */ - memcpy(out->data, sensor->online_calib_data->cache, - sizeof(out->data)); - /* Clear dirty bit */ - sensor_calib_cache_dirty_map &= ~(1 << sensor_num); - } - mutex_unlock(&g_calib_cache_mutex); - - return has_valid; -} - -int online_calibration_process_data(struct ec_response_motion_sensor_data *data, - struct motion_sensor_t *sensor, - uint32_t timestamp) -{ - int sensor_num = sensor - motion_sensors; - int rc; - int temperature; - struct online_calib_data *calib_data; - fpv3_t fdata; - bool is_spoofed = IS_ENABLED(CONFIG_ONLINE_CALIB_SPOOF_MODE) && - (sensor->flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE); - bool has_new_calibration_values = false; - - /* Convert data to fp. */ - data_int16_to_fp(sensor, data->data, fdata); - - calib_data = sensor->online_calib_data; - switch (sensor->type) { - case MOTIONSENSE_TYPE_ACCEL: { - struct accel_cal *cal = - (struct accel_cal *)(calib_data->type_specific_data); - - if (is_spoofed) { - /* Copy the data to the calibration result. */ - cal->bias[X] = fdata[X]; - cal->bias[Y] = fdata[Y]; - cal->bias[Z] = fdata[Z]; - has_new_calibration_values = true; - } else { - /* Possibly update the gyroscope calibration. */ - update_gyro_cal(sensor, fdata, timestamp); - - /* - * Temperature is required for accelerometer - * calibration. - */ - rc = get_temperature(sensor, &temperature); - if (rc != EC_SUCCESS) - return rc; - - has_new_calibration_values = accel_cal_accumulate( - cal, timestamp, fdata[X], fdata[Y], fdata[Z], - temperature); - } - - if (has_new_calibration_values) { - mutex_lock(&g_calib_cache_mutex); - /* Convert result to the right scale. */ - data_fp_to_int16(sensor, cal->bias, calib_data->cache); - /* Set valid and dirty. */ - sensor_calib_cache_valid_map |= BIT(sensor_num); - sensor_calib_cache_dirty_map |= BIT(sensor_num); - mutex_unlock(&g_calib_cache_mutex); - /* Notify the AP. */ - mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); - } - break; - } - case MOTIONSENSE_TYPE_MAG: { - struct mag_cal_t *cal = - (struct mag_cal_t *)(calib_data->type_specific_data); - int idata[] = { - (int)data->data[X], - (int)data->data[Y], - (int)data->data[Z], - }; - - if (is_spoofed) { - /* Copy the data to the calibration result. */ - cal->bias[X] = INT_TO_FP(idata[X]); - cal->bias[Y] = INT_TO_FP(idata[Y]); - cal->bias[Z] = INT_TO_FP(idata[Z]); - has_new_calibration_values = true; - } else { - /* Possibly update the gyroscope calibration. */ - update_gyro_cal(sensor, fdata, timestamp); - - has_new_calibration_values = mag_cal_update(cal, idata); - } - - if (has_new_calibration_values) { - mutex_lock(&g_calib_cache_mutex); - /* Copy the values */ - calib_data->cache[X] = cal->bias[X]; - calib_data->cache[Y] = cal->bias[Y]; - calib_data->cache[Z] = cal->bias[Z]; - /* Set valid and dirty. */ - sensor_calib_cache_valid_map |= BIT(sensor_num); - sensor_calib_cache_dirty_map |= BIT(sensor_num); - mutex_unlock(&g_calib_cache_mutex); - /* Notify the AP. */ - mkbp_send_event(EC_MKBP_EVENT_ONLINE_CALIBRATION); - } - break; - } - case MOTIONSENSE_TYPE_GYRO: { - if (is_spoofed) { - /* - * Gyroscope uses fdata to store the calibration - * result, so there's no need to copy anything. - */ - has_new_calibration_values = true; - } else { - struct gyro_cal_data *gyro_cal_data = - (struct gyro_cal_data *) - calib_data->type_specific_data; - struct gyro_cal *gyro_cal = &gyro_cal_data->gyro_cal; - - /* Temperature is required for gyro calibration. */ - rc = get_temperature(sensor, &temperature); - if (rc != EC_SUCCESS) - return rc; - - /* Update gyroscope calibration. */ - gyro_cal_update_gyro(gyro_cal, timestamp, fdata[X], - fdata[Y], fdata[Z], temperature); - has_new_calibration_values = - check_gyro_cal_new_bias(sensor, fdata); - } - - if (has_new_calibration_values) - set_gyro_cal_cache_values(sensor, fdata); - break; - } - default: - break; - } - - return EC_SUCCESS; -} diff --git a/common/pd_log.c b/common/pd_log.c deleted file mode 100644 index 3708aad72e..0000000000 --- a/common/pd_log.c +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2014 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 "charge_manager.h" -#include "console.h" -#include "event_log.h" -#include "host_command.h" -#include "timer.h" -#include "usb_pd.h" -#include "util.h" - -/* - * Ensure PD logging parameters are compatible with the generic logging - * framework that we're calling into. - */ -BUILD_ASSERT(sizeof(struct ec_response_pd_log) == - sizeof(struct event_log_entry)); -BUILD_ASSERT(PD_LOG_SIZE_MASK == EVENT_LOG_SIZE_MASK); -BUILD_ASSERT(PD_LOG_TIMESTAMP_SHIFT == EVENT_LOG_TIMESTAMP_SHIFT); -BUILD_ASSERT(PD_EVENT_NO_ENTRY == EVENT_LOG_NO_ENTRY); - -void pd_log_event(uint8_t type, uint8_t size_port, - uint16_t data, void *payload) -{ - uint32_t timestamp = get_time().val >> PD_LOG_TIMESTAMP_SHIFT; - - log_add_event(type, size_port, data, payload, timestamp); -} - -#ifdef HAS_TASK_HOSTCMD - -/* number of accessory entries we have queued since last check */ -static volatile int incoming_logs; - -void pd_log_recv_vdm(int port, int cnt, uint32_t *payload) -{ - struct ec_response_pd_log *r = (void *)&payload[1]; - /* update port number from MCU point of view */ - size_t size = PD_LOG_SIZE(r->size_port); - uint8_t size_port = PD_LOG_PORT_SIZE(port, size); - uint32_t timestamp; - - if ((cnt < 2 + DIV_ROUND_UP(size, sizeof(uint32_t))) || - !(payload[0] & VDO_SRC_RESPONDER)) - /* Not a proper log entry, bail out */ - return; - - if (r->type != PD_EVENT_NO_ENTRY) { - timestamp = (get_time().val >> PD_LOG_TIMESTAMP_SHIFT) - - r->timestamp; - log_add_event(r->type, size_port, r->data, r->payload, - timestamp); - /* record that we have enqueued new content */ - incoming_logs++; - } -} - -/* we are a PD MCU/EC, send back the events to the host */ -static enum ec_status hc_pd_get_log_entry(struct host_cmd_handler_args *args) -{ - struct ec_response_pd_log *r = args->response; - -dequeue_retry: - args->response_size = log_dequeue_event((struct event_log_entry *)r); - /* if the MCU log no longer has entries, try connected accessories */ - if (r->type == PD_EVENT_NO_ENTRY) { - int i, res; - incoming_logs = 0; - for (i = 0; i < board_get_usb_pd_port_count(); ++i) { - /* only accessories who knows Google logging format */ - if (pd_get_identity_vid(i) != USB_VID_GOOGLE) - continue; - res = pd_fetch_acc_log_entry(i); - if (res == EC_RES_BUSY) /* host should retry */ - return EC_RES_BUSY; - } - /* we have received new entries from an accessory */ - if (incoming_logs) - goto dequeue_retry; - /* else the current entry is already "PD_EVENT_NO_ENTRY" */ - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_GET_LOG_ENTRY, - hc_pd_get_log_entry, - EC_VER_MASK(0)); - -static enum ec_status hc_pd_write_log_entry(struct host_cmd_handler_args *args) -{ - const struct ec_params_pd_write_log_entry *p = args->params; - uint8_t type = p->type; - uint8_t port = p->port; - - if (type < PD_EVENT_MCU_BASE || type >= PD_EVENT_ACC_BASE) - return EC_RES_INVALID_PARAM; - if (port > 0 && port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - switch (type) { - /* Charge event: Log data for all ports */ - case PD_EVENT_MCU_CHARGE: -#ifdef CONFIG_CHARGE_MANAGER - charge_manager_save_log(port); -#endif - break; - - /* Other events: no extra data, just log event type + port */ - case PD_EVENT_MCU_CONNECT: - case PD_EVENT_MCU_BOARD_CUSTOM: - default: - pd_log_event(type, PD_LOG_PORT_SIZE(port, 0), 0, NULL); - break; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_WRITE_LOG_ENTRY, - hc_pd_write_log_entry, - EC_VER_MASK(0)); -#else /* !HAS_TASK_HOSTCMD */ -/* we are a PD accessory, send back the events as a VDM (VDO_CMD_GET_LOG) */ -int pd_vdm_get_log_entry(uint32_t *payload) -{ - struct ec_response_pd_log *r = (void *)&payload[1]; - int byte_size; - - byte_size = log_dequeue_event((struct event_log_entry *)r); - - return 1 + DIV_ROUND_UP(byte_size, sizeof(uint32_t)); -} -#endif /* !HAS_TASK_HOSTCMD */ diff --git a/common/peci.c b/common/peci.c deleted file mode 100644 index e0f03c95dd..0000000000 --- a/common/peci.c +++ /dev/null @@ -1,165 +0,0 @@ -/* 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. - */ - -/* PECI interface for Chrome EC */ - -#include "chipset.h" -#include "console.h" -#include "peci.h" -#include "util.h" - -static int peci_get_cpu_temp(int *cpu_temp) -{ - int rv; - uint8_t r_buf[PECI_GET_TEMP_READ_LENGTH] = {0}; - struct peci_data peci = { - .cmd_code = PECI_CMD_GET_TEMP, - .addr = PECI_TARGET_ADDRESS, - .w_len = PECI_GET_TEMP_WRITE_LENGTH, - .r_len = PECI_GET_TEMP_READ_LENGTH, - .w_buf = NULL, - .r_buf = r_buf, - .timeout_us = PECI_GET_TEMP_TIMEOUT_US, - }; - - rv = peci_transaction(&peci); - if (rv) - return rv; - - /* Get relative raw data of temperature. */ - *cpu_temp = (r_buf[1] << 8) | r_buf[0]; - - /* Convert relative raw data to degrees C. */ - *cpu_temp = ((*cpu_temp ^ 0xFFFF) + 1) >> 6; - - /* - * When the AP transitions into S0, it is possible, depending on the - * timing of the PECI sample, to read an invalid temperature. This is - * very rare, but when it does happen the temperature returned is - * greater than or equal to CONFIG_PECI_TJMAX. - */ - if (*cpu_temp >= CONFIG_PECI_TJMAX) - return EC_ERROR_UNKNOWN; - - /* temperature in K */ - *cpu_temp = CONFIG_PECI_TJMAX - *cpu_temp + 273; - - return EC_SUCCESS; -} - -int peci_temp_sensor_get_val(int idx, int *temp_ptr) -{ - int i, rv; - - if (!chipset_in_state(CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY)) - return EC_ERROR_NOT_POWERED; - - /* - * Retry reading PECI CPU temperature if the first sample is - * invalid or failed to obtain. - */ - for (i = 0; i < 2; i++) { - rv = peci_get_cpu_temp(temp_ptr); - if (!rv) - break; - } - - return rv; -} - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_PECI -static int peci_cmd(int argc, char **argv) -{ - uint8_t r_buf[PECI_READ_DATA_FIFO_SIZE] = {0}; - uint8_t w_buf[PECI_WRITE_DATA_FIFO_SIZE] = {0}; - struct peci_data peci = { - .w_buf = w_buf, - .r_buf = r_buf, - }; - - int param; - char *e; - - if ((argc < 6) || (argc > 8)) - return EC_ERROR_PARAM_COUNT; - - peci.addr = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - peci.w_len = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - peci.r_len = strtoi(argv[3], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - - peci.cmd_code = strtoi(argv[4], &e, 0); - if (*e) - return EC_ERROR_PARAM4; - - peci.timeout_us = strtoi(argv[5], &e, 0); - if (*e) - return EC_ERROR_PARAM5; - - if (argc > 6) { - param = strtoi(argv[6], &e, 0); - if (*e) - return EC_ERROR_PARAM6; - - /* MSB of parameter */ - w_buf[3] = (uint8_t)(param >> 24); - /* LSB of parameter */ - w_buf[2] = (uint8_t)(param >> 16); - /* Index */ - w_buf[1] = (uint8_t)(param >> 8); - /* Host ID[7:1] & Retry[0] */ - w_buf[0] = (uint8_t)(param >> 0); - - if (argc > 7) { - param = strtoi(argv[7], &e, 0); - if (*e) - return EC_ERROR_PARAM7; - - /* Data (1, 2 or 4 bytes) */ - w_buf[7] = (uint8_t)(param >> 24); - w_buf[6] = (uint8_t)(param >> 16); - w_buf[5] = (uint8_t)(param >> 8); - w_buf[4] = (uint8_t)(param >> 0); - } - } else { - peci.w_len = 0x00; - } - - if (peci_transaction(&peci)) { - ccprintf("PECI transaction error\n"); - return EC_ERROR_UNKNOWN; - } - ccprintf("PECI read data: %ph\n", HEX_BUF(r_buf, peci.r_len)); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(peci, peci_cmd, - "addr wlen rlen cmd timeout(us)", - "PECI command"); - -static int command_peci_temp(int argc, char **argv) -{ - int t; - - if (peci_get_cpu_temp(&t) != EC_SUCCESS) { - ccprintf("PECI get cpu temp error\n"); - return EC_ERROR_UNKNOWN; - } - - ccprintf("CPU temp: %d K, %d C\n", t, K_TO_C(t)); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pecitemp, command_peci_temp, - NULL, - "Print CPU temperature"); -#endif /* CONFIG_CMD_PECI */ diff --git a/common/peripheral_charger.c b/common/peripheral_charger.c deleted file mode 100644 index 0a597ad6bd..0000000000 --- a/common/peripheral_charger.c +++ /dev/null @@ -1,740 +0,0 @@ -/* Copyright 2020 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 "atomic.h" -#include "chipset.h" -#include "common.h" -#include "device_event.h" -#include "hooks.h" -#include "host_command.h" -#include "mkbp_event.h" -#include "peripheral_charger.h" -#include "queue.h" -#include "stdbool.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Peripheral Charge Manager */ - -#define CPRINTS(fmt, args...) cprints(CC_PCHG, "PCHG: " fmt, ##args) - -/* Currently only used for FW update. */ -static uint32_t pchg_host_events; - -static void pchg_queue_event(struct pchg *ctx, enum pchg_event event) -{ - mutex_lock(&ctx->mtx); - if (queue_add_unit(&ctx->events, &event) == 0) { - ctx->dropped_event_count++; - CPRINTS("ERR: Queue is full"); - } - mutex_unlock(&ctx->mtx); -} - -static void _send_host_event(const struct pchg *ctx, uint32_t event) -{ - int port = PCHG_CTX_TO_PORT(ctx); - - atomic_or(&pchg_host_events, event | port << EC_MKBP_PCHG_PORT_SHIFT); - mkbp_send_event(EC_MKBP_EVENT_PCHG); -} - -static const char *_text_state(enum pchg_state state) -{ - /* TODO: Use "S%d" for normal build. */ - static const char * const state_names[] = EC_PCHG_STATE_TEXT; - BUILD_ASSERT(ARRAY_SIZE(state_names) == PCHG_STATE_COUNT); - - if (state >= sizeof(state_names)) - return "UNDEF"; - - return state_names[state]; -} - -static const char *_text_event(enum pchg_event event) -{ - /* TODO: Use "S%d" for normal build. */ - static const char * const event_names[] = { - [PCHG_EVENT_NONE] = "NONE", - [PCHG_EVENT_IRQ] = "IRQ", - [PCHG_EVENT_RESET] = "RESET", - [PCHG_EVENT_INITIALIZED] = "INITIALIZED", - [PCHG_EVENT_ENABLED] = "ENABLED", - [PCHG_EVENT_DISABLED] = "DISABLED", - [PCHG_EVENT_DEVICE_DETECTED] = "DEVICE_DETECTED", - [PCHG_EVENT_DEVICE_CONNECTED] = "DEVICE_CONNECTED", - [PCHG_EVENT_DEVICE_LOST] = "DEVICE_LOST", - [PCHG_EVENT_CHARGE_STARTED] = "CHARGE_STARTED", - [PCHG_EVENT_CHARGE_UPDATE] = "CHARGE_UPDATE", - [PCHG_EVENT_CHARGE_ENDED] = "CHARGE_ENDED", - [PCHG_EVENT_CHARGE_STOPPED] = "CHARGE_STOPPED", - [PCHG_EVENT_UPDATE_OPENED] = "UPDATE_OPENED", - [PCHG_EVENT_UPDATE_CLOSED] = "UPDATE_CLOSED", - [PCHG_EVENT_UPDATE_WRITTEN] = "UPDATE_WRITTEN", - [PCHG_EVENT_IN_NORMAL] = "IN_NORMAL", - [PCHG_EVENT_CHARGE_ERROR] = "CHARGE_ERROR", - [PCHG_EVENT_UPDATE_ERROR] = "UPDATE_ERROR", - [PCHG_EVENT_OTHER_ERROR] = "OTHER_ERROR", - [PCHG_EVENT_ENABLE] = "ENABLE", - [PCHG_EVENT_DISABLE] = "DISABLE", - [PCHG_EVENT_UPDATE_OPEN] = "UPDATE_OPEN", - [PCHG_EVENT_UPDATE_WRITE] = "UPDATE_WRITE", - [PCHG_EVENT_UPDATE_CLOSE] = "UPDATE_CLOSE", - }; - BUILD_ASSERT(ARRAY_SIZE(event_names) == PCHG_EVENT_COUNT); - - if (event >= sizeof(event_names)) - return "UNDEF"; - - return event_names[event]; -} - -static void _clear_port(struct pchg *ctx) -{ - mutex_lock(&ctx->mtx); - queue_init(&ctx->events); - mutex_unlock(&ctx->mtx); - atomic_clear(&ctx->irq); - ctx->battery_percent = 0; - ctx->error = 0; - ctx->update.data_ready = 0; -} - -static enum pchg_state pchg_reset(struct pchg *ctx) -{ - enum pchg_state state = PCHG_STATE_RESET; - int rv; - - /* - * In case we get asynchronous reset, clear port though it's redundant - * for a synchronous reset. - */ - _clear_port(ctx); - - if (ctx->mode == PCHG_MODE_NORMAL) { - rv = ctx->cfg->drv->init(ctx); - if (rv == EC_SUCCESS) { - state = PCHG_STATE_INITIALIZED; - pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - } else if (rv != EC_SUCCESS_IN_PROGRESS) { - CPRINTS("ERR: Failed to reset to normal mode"); - } - } else { - state = PCHG_STATE_DOWNLOAD; - pchg_queue_event(ctx, PCHG_EVENT_UPDATE_OPEN); - } - - return state; -} - -static void pchg_state_reset(struct pchg *ctx) -{ - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_IN_NORMAL: - ctx->state = PCHG_STATE_INITIALIZED; - pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - break; - default: - break; - } -} - -static void pchg_state_initialized(struct pchg *ctx) -{ - int rv; - - if (ctx->event == PCHG_EVENT_ENABLE) - ctx->error &= ~PCHG_ERROR_HOST; - - /* Spin in INITIALIZED until error condition is cleared. */ - if (ctx->error) - return; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_ENABLE: - rv = ctx->cfg->drv->enable(ctx, true); - if (rv == EC_SUCCESS) - ctx->state = PCHG_STATE_ENABLED; - else if (rv != EC_SUCCESS_IN_PROGRESS) - CPRINTS("ERR: Failed to enable"); - break; - case PCHG_EVENT_ENABLED: - ctx->state = PCHG_STATE_ENABLED; - break; - default: - break; - } -} - -static void pchg_state_enabled(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_DISABLE: - ctx->error |= PCHG_ERROR_HOST; - rv = ctx->cfg->drv->enable(ctx, false); - if (rv == EC_SUCCESS) - ctx->state = PCHG_STATE_INITIALIZED; - else if (rv != EC_SUCCESS_IN_PROGRESS) - CPRINTS("ERR: Failed to disable"); - break; - case PCHG_EVENT_DISABLED: - ctx->state = PCHG_STATE_INITIALIZED; - break; - case PCHG_EVENT_DEVICE_DETECTED: - ctx->state = PCHG_STATE_DETECTED; - break; - case PCHG_EVENT_DEVICE_CONNECTED: - /* - * Proactively query SOC in case charging info won't be sent - * because device is already charged. - */ - ctx->cfg->drv->get_soc(ctx); - ctx->state = PCHG_STATE_CONNECTED; - break; - default: - break; - } -} - -static void pchg_state_detected(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_DISABLE: - ctx->error |= PCHG_ERROR_HOST; - rv = ctx->cfg->drv->enable(ctx, false); - if (rv == EC_SUCCESS) - ctx->state = PCHG_STATE_INITIALIZED; - else if (rv != EC_SUCCESS_IN_PROGRESS) - CPRINTS("ERR: Failed to disable"); - break; - case PCHG_EVENT_DISABLED: - ctx->state = PCHG_STATE_INITIALIZED; - break; - case PCHG_EVENT_DEVICE_CONNECTED: - /* - * Proactively query SOC in case charging info won't be sent - * because device is already charged. - */ - ctx->cfg->drv->get_soc(ctx); - ctx->state = PCHG_STATE_CONNECTED; - break; - case PCHG_EVENT_DEVICE_LOST: - ctx->battery_percent = 0; - ctx->state = PCHG_STATE_ENABLED; - break; - default: - break; - } -} - -static void pchg_state_connected(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_DISABLE: - ctx->error |= PCHG_ERROR_HOST; - rv = ctx->cfg->drv->enable(ctx, false); - if (rv == EC_SUCCESS) - ctx->state = PCHG_STATE_INITIALIZED; - else if (rv != EC_SUCCESS_IN_PROGRESS) - CPRINTS("ERR: Failed to disable"); - break; - case PCHG_EVENT_DISABLED: - ctx->state = PCHG_STATE_INITIALIZED; - break; - case PCHG_EVENT_CHARGE_STARTED: - ctx->state = PCHG_STATE_CHARGING; - break; - case PCHG_EVENT_DEVICE_LOST: - ctx->battery_percent = 0; - ctx->state = PCHG_STATE_ENABLED; - break; - case PCHG_EVENT_CHARGE_ERROR: - ctx->state = PCHG_STATE_INITIALIZED; - break; - default: - break; - } -} - -static void pchg_state_charging(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_DISABLE: - ctx->error |= PCHG_ERROR_HOST; - rv = ctx->cfg->drv->enable(ctx, false); - if (rv == EC_SUCCESS) - ctx->state = PCHG_STATE_INITIALIZED; - else if (rv != EC_SUCCESS_IN_PROGRESS) - CPRINTS("ERR: Failed to disable"); - break; - case PCHG_EVENT_DISABLED: - ctx->state = PCHG_STATE_INITIALIZED; - break; - case PCHG_EVENT_CHARGE_UPDATE: - break; - case PCHG_EVENT_DEVICE_LOST: - ctx->battery_percent = 0; - ctx->state = PCHG_STATE_ENABLED; - break; - case PCHG_EVENT_CHARGE_ERROR: - ctx->state = PCHG_STATE_INITIALIZED; - break; - case PCHG_EVENT_CHARGE_ENDED: - case PCHG_EVENT_CHARGE_STOPPED: - ctx->state = PCHG_STATE_CONNECTED; - break; - default: - break; - } -} - -static void pchg_state_download(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_UPDATE_OPEN: - rv = ctx->cfg->drv->update_open(ctx); - if (rv == EC_SUCCESS) { - ctx->state = PCHG_STATE_DOWNLOADING; - } else if (rv != EC_SUCCESS_IN_PROGRESS) { - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_ERROR); - CPRINTS("ERR: Failed to open"); - } - break; - case PCHG_EVENT_UPDATE_OPENED: - ctx->state = PCHG_STATE_DOWNLOADING; - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_OPENED); - break; - case PCHG_EVENT_UPDATE_ERROR: - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_ERROR); - break; - default: - break; - } -} - -static void pchg_state_downloading(struct pchg *ctx) -{ - int rv; - - switch (ctx->event) { - case PCHG_EVENT_RESET: - ctx->state = pchg_reset(ctx); - break; - case PCHG_EVENT_UPDATE_WRITE: - if (ctx->update.data_ready == 0) - break; - rv = ctx->cfg->drv->update_write(ctx); - if (rv != EC_SUCCESS && rv != EC_SUCCESS_IN_PROGRESS) { - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_ERROR); - CPRINTS("ERR: Failed to write"); - } - break; - case PCHG_EVENT_UPDATE_WRITTEN: - ctx->update.data_ready = 0; - _send_host_event(ctx, EC_MKBP_PCHG_WRITE_COMPLETE); - break; - case PCHG_EVENT_UPDATE_CLOSE: - rv = ctx->cfg->drv->update_close(ctx); - if (rv == EC_SUCCESS) { - ctx->state = PCHG_STATE_DOWNLOAD; - } else if (rv != EC_SUCCESS_IN_PROGRESS) { - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_ERROR); - CPRINTS("ERR: Failed to close"); - } - break; - case PCHG_EVENT_UPDATE_CLOSED: - ctx->state = PCHG_STATE_DOWNLOAD; - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_CLOSED); - break; - case PCHG_EVENT_UPDATE_ERROR: - CPRINTS("ERR: Failed to update"); - _send_host_event(ctx, EC_MKBP_PCHG_UPDATE_ERROR); - break; - default: - break; - } -} - -static int pchg_run(struct pchg *ctx) -{ - enum pchg_state previous_state = ctx->state; - uint8_t previous_battery = ctx->battery_percent; - int port = PCHG_CTX_TO_PORT(ctx); - int rv; - - mutex_lock(&ctx->mtx); - if (!queue_remove_unit(&ctx->events, &ctx->event)) { - mutex_unlock(&ctx->mtx); - CPRINTS("P%d No event in queue", port); - return 0; - } - mutex_unlock(&ctx->mtx); - - CPRINTS("P%d Run in STATE_%s for EVENT_%s", port, - _text_state(ctx->state), _text_event(ctx->event)); - - if (ctx->event == PCHG_EVENT_IRQ) { - rv = ctx->cfg->drv->get_event(ctx); - if (rv) { - CPRINTS("ERR: Failed to get event (%d)", rv); - return 0; - } - CPRINTS(" EVENT_%s", _text_event(ctx->event)); - } - - if (ctx->event == PCHG_EVENT_NONE) - return 0; - - switch (ctx->state) { - case PCHG_STATE_RESET: - pchg_state_reset(ctx); - break; - case PCHG_STATE_INITIALIZED: - pchg_state_initialized(ctx); - break; - case PCHG_STATE_ENABLED: - pchg_state_enabled(ctx); - break; - case PCHG_STATE_DETECTED: - pchg_state_detected(ctx); - break; - case PCHG_STATE_CONNECTED: - pchg_state_connected(ctx); - break; - case PCHG_STATE_CHARGING: - pchg_state_charging(ctx); - break; - case PCHG_STATE_DOWNLOAD: - pchg_state_download(ctx); - break; - case PCHG_STATE_DOWNLOADING: - pchg_state_downloading(ctx); - break; - default: - CPRINTS("ERR: Unknown state (%d)", ctx->state); - return 0; - } - - if (previous_state != ctx->state) - CPRINTS("->STATE_%s", _text_state(ctx->state)); - - if (ctx->battery_percent != previous_battery) - CPRINTS("Battery %u%%", ctx->battery_percent); - - /* - * Notify the host of - * - [S0] Charge update with SoC change and all other events. - * - [S3/S0IX] Device attach or detach (for wake-up) - * - [S5/G3] No events. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - return 0; - - if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - return (ctx->event == PCHG_EVENT_DEVICE_DETECTED) - || (ctx->event == PCHG_EVENT_DEVICE_LOST); - - if (ctx->event == PCHG_EVENT_CHARGE_UPDATE) - return ctx->battery_percent != previous_battery; - - return ctx->event != PCHG_EVENT_NONE; -} - -void pchg_irq(enum gpio_signal signal) -{ - struct pchg *ctx; - int i; - - for (i = 0; i < pchg_count; i++) { - ctx = &pchgs[i]; - if (signal == ctx->cfg->irq_pin) { - ctx->irq = 1; - task_wake(TASK_ID_PCHG); - return; - } - } -} - - -static void pchg_suspend_complete(void) -{ - CPRINTS("%s", __func__); - device_enable_event(EC_DEVICE_EVENT_WLC); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND_COMPLETE, pchg_suspend_complete, - HOOK_PRIO_DEFAULT); - -static void pchg_startup(void) -{ - struct pchg *ctx; - int p; - - CPRINTS("%s", __func__); - - for (p = 0; p < pchg_count; p++) { - ctx = &pchgs[p]; - _clear_port(ctx); - ctx->mode = PCHG_MODE_NORMAL; - ctx->cfg->drv->reset(ctx); - gpio_enable_interrupt(ctx->cfg->irq_pin); - } - - task_wake(TASK_ID_PCHG); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pchg_startup, HOOK_PRIO_DEFAULT); - -static void pchg_shutdown(void) -{ - struct pchg *ctx; - int p; - - CPRINTS("%s", __func__); - - for (p = 0; p < pchg_count; p++) { - ctx = &pchgs[0]; - gpio_disable_interrupt(ctx->cfg->irq_pin); - } -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pchg_shutdown, HOOK_PRIO_DEFAULT); - -void pchg_task(void *u) -{ - struct pchg *ctx; - int p; - - if (chipset_in_state(CHIPSET_STATE_ON)) - /* We are here after power-on (because of late sysjump). */ - pchg_startup(); - - while (true) { - /* Process pending events for all ports. */ - int rv = 0; - - for (p = 0; p < pchg_count; p++) { - ctx = &pchgs[p]; - do { - if (atomic_clear(&ctx->irq)) - pchg_queue_event(ctx, PCHG_EVENT_IRQ); - rv |= pchg_run(ctx); - } while (queue_count(&ctx->events)); - } - - /* Send one host event for all ports. */ - if (rv) - device_set_single_event(EC_DEVICE_EVENT_WLC); - - task_wait_event(-1); - } -} - -static enum ec_status hc_pchg_count(struct host_cmd_handler_args *args) -{ - struct ec_response_pchg_count *r = args->response; - - r->port_count = pchg_count; - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PCHG_COUNT, hc_pchg_count, EC_VER_MASK(0)); - -#define HCPRINTS(fmt, args...) cprints(CC_PCHG, "HC:PCHG: " fmt, ##args) - -static enum ec_status hc_pchg(struct host_cmd_handler_args *args) -{ - const struct ec_params_pchg *p = args->params; - struct ec_response_pchg *r = args->response; - int port = p->port; - struct pchg *ctx; - - if (port >= pchg_count) - return EC_RES_INVALID_PARAM; - - ctx = &pchgs[port]; - - if (ctx->state == PCHG_STATE_CONNECTED - && ctx->battery_percent >= ctx->cfg->full_percent) - r->state = PCHG_STATE_FULL; - else - r->state = ctx->state; - - r->battery_percentage = ctx->battery_percent; - r->error = ctx->error; - r->fw_version = ctx->fw_version; - r->dropped_event_count = ctx->dropped_event_count; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PCHG, hc_pchg, EC_VER_MASK(1)); - -int pchg_get_next_event(uint8_t *out) -{ - uint32_t events = atomic_clear(&pchg_host_events); - - memcpy(out, &events, sizeof(events)); - - return sizeof(events); -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_PCHG, pchg_get_next_event); - -static enum ec_status hc_pchg_update(struct host_cmd_handler_args *args) -{ - const struct ec_params_pchg_update *p = args->params; - struct ec_response_pchg_update *r = args->response; - int port = p->port; - struct pchg *ctx; - - if (port >= pchg_count) - return EC_RES_INVALID_PARAM; - - ctx = &pchgs[port]; - - switch (p->cmd) { - case EC_PCHG_UPDATE_CMD_RESET_TO_NORMAL: - HCPRINTS("Resetting to normal mode"); - - gpio_disable_interrupt(ctx->cfg->irq_pin); - _clear_port(ctx); - ctx->mode = PCHG_MODE_NORMAL; - ctx->cfg->drv->reset(ctx); - gpio_enable_interrupt(ctx->cfg->irq_pin); - break; - - case EC_PCHG_UPDATE_CMD_OPEN: - HCPRINTS("Resetting to download mode"); - - gpio_disable_interrupt(ctx->cfg->irq_pin); - _clear_port(ctx); - ctx->mode = PCHG_MODE_DOWNLOAD; - ctx->cfg->drv->reset(ctx); - gpio_enable_interrupt(ctx->cfg->irq_pin); - - ctx->update.version = p->version; - r->block_size = ctx->cfg->block_size; - args->response_size = sizeof(*r); - break; - - case EC_PCHG_UPDATE_CMD_WRITE: - if (ctx->state != PCHG_STATE_DOWNLOADING) - return EC_RES_ERROR; - if (p->size > sizeof(ctx->update.data)) - return EC_RES_OVERFLOW; - if (ctx->update.data_ready) - return EC_RES_BUSY; - - HCPRINTS("Writing %u bytes to 0x%x", p->size, p->addr); - ctx->update.addr = p->addr; - ctx->update.size = p->size; - memcpy(ctx->update.data, p->data, p->size); - pchg_queue_event(ctx, PCHG_EVENT_UPDATE_WRITE); - ctx->update.data_ready = 1; - break; - - case EC_PCHG_UPDATE_CMD_CLOSE: - if (ctx->state != PCHG_STATE_DOWNLOADING) - return EC_RES_ERROR; - if (ctx->update.data_ready) - return EC_RES_BUSY; - - HCPRINTS("Closing update session (crc=0x%x)", p->crc32); - ctx->update.crc32 = p->crc32; - pchg_queue_event(ctx, PCHG_EVENT_UPDATE_CLOSE); - break; - default: - return EC_RES_INVALID_PARAM; - } - - task_wake(TASK_ID_PCHG); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PCHG_UPDATE, hc_pchg_update, EC_VER_MASK(0)); - -static int cc_pchg(int argc, char **argv) -{ - int port; - char *end; - struct pchg *ctx; - - if (argc < 2 || 4 < argc) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &end, 0); - if (*end || port < 0 || port >= pchg_count) - return EC_ERROR_PARAM1; - ctx = &pchgs[port]; - - if (argc == 2) { - ccprintf("P%d STATE_%s EVENT_%s SOC=%d%%\n", - port, _text_state(ctx->state), _text_event(ctx->event), - ctx->battery_percent); - ccprintf("error=0x%x dropped=%u fw_version=0x%x\n", - ctx->error, ctx->dropped_event_count, ctx->fw_version); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[2], "reset")) { - if (argc == 3) - ctx->mode = PCHG_MODE_NORMAL; - else if (!strcasecmp(argv[3], "download")) - ctx->mode = PCHG_MODE_DOWNLOAD; - else - return EC_ERROR_PARAM3; - gpio_disable_interrupt(ctx->cfg->irq_pin); - _clear_port(ctx); - ctx->cfg->drv->reset(ctx); - gpio_enable_interrupt(ctx->cfg->irq_pin); - } else if (!strcasecmp(argv[2], "enable")) { - pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - } else if (!strcasecmp(argv[2], "disable")) { - pchg_queue_event(ctx, PCHG_EVENT_DISABLE); - } else { - return EC_ERROR_PARAM2; - } - - task_wake(TASK_ID_PCHG); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pchg, cc_pchg, - "\n\t<port>" - "\n\t<port> reset [download]" - "\n\t<port> enable" - "\n\t<port> disable", - "Control peripheral chargers"); diff --git a/common/port80.c b/common/port80.c deleted file mode 100644 index ab1f112112..0000000000 --- a/common/port80.c +++ /dev/null @@ -1,214 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Port 80 module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "display_7seg.h" -#include "hooks.h" -#include "host_command.h" -#include "port80.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_PORT80, format, ## args) - -#ifdef CONFIG_PORT80_4_BYTE -typedef uint32_t port80_code_t; -#else -typedef uint16_t port80_code_t; -#endif -static port80_code_t __bss_slow history[CONFIG_PORT80_HISTORY_LEN]; -static int __bss_slow writes; /* Number of port 80 writes so far */ -static uint16_t last_boot; /* Last code from previous boot */ -static int __bss_slow scroll; - -#ifdef CONFIG_BRINGUP -#undef CONFIG_PORT80_PRINT_IN_INT -#define CONFIG_PORT80_PRINT_IN_INT 1 -#endif - -static int print_in_int = CONFIG_PORT80_PRINT_IN_INT; - -static void port80_dump_buffer(void); -DECLARE_DEFERRED(port80_dump_buffer); - -void port_80_write(int data) -{ - /* - * By default print_in_int is disabled if: - * 1. CONFIG_BRINGUP is not defined - * 2. CONFIG_PRINT_IN_INT is set to disable by default - * - * This is done to prevent printing in interrupt context. Boards can - * enable this by either defining CONFIG_BRINGUP or enabling - * CONFIG_PRINT_IN_INT in board configs. - * - * If at runtime, print_in_int is disabled, then this function will - * schedule a deferred call 4 seconds after the last port80 write to - * dump the current port80 buffer to EC console. This is to allow - * developers to help debug BIOS progress by tracing port80 messages. - */ - if (print_in_int) - CPRINTF("%c[%pT Port 80: 0x%02x]", - scroll ? '\n' : '\r', PRINTF_TIMESTAMP_NOW, data); - - hook_call_deferred(&port80_dump_buffer_data, 4 * SECOND); - - /* Save current port80 code if system is resetting */ - if (data == PORT_80_EVENT_RESET && writes) { - port80_code_t prev = history[(writes-1) % ARRAY_SIZE(history)]; - - /* - * last_boot only reports 8-bit codes. - * Ignore special event codes and 4-byte codes. - */ - if (prev < 0x100) - last_boot = prev; - } - - history[writes % ARRAY_SIZE(history)] = data; - writes++; -} - -static void port80_dump_buffer(void) -{ - int printed = 0; - int i; - int head, tail; - int last_e = 0; - - /* - * Print the port 80 writes so far, clipped to the length of our - * history buffer. - * - * Technically, if a port 80 write comes in while we're printing this, - * we could print an incorrect history. Probably not worth the - * complexity to work around that. - */ - head = writes; - if (head > ARRAY_SIZE(history)) - tail = head - ARRAY_SIZE(history); - else - tail = 0; - - ccputs("Port 80 writes:"); - for (i = tail; i < head; i++) { - int e = history[i % ARRAY_SIZE(history)]; - switch (e) { - case PORT_80_EVENT_RESUME: - ccprintf("\n(S3->S0)"); - printed = 0; - break; - case PORT_80_EVENT_RESET: - ccprintf("\n(RESET)"); - printed = 0; - break; - default: - if (!(printed++ % 20)) { - ccputs("\n "); - cflush(); - } - ccprintf(" %02x", e); - last_e = e; - } - } - ccputs(" <--new\n"); - - /* Displaying last port80 msg on 7-segment if it is enabled */ - if (IS_ENABLED(CONFIG_SEVEN_SEG_DISPLAY) && last_e) - display_7seg_write(SEVEN_SEG_PORT80_DISPLAY, last_e); -} - -/*****************************************************************************/ -/* Console commands */ - -static int command_port80(int argc, char **argv) -{ - /* - * 'port80 scroll' toggles whether port 80 output begins with a newline - * (scrolling) or CR (non-scrolling). - */ - if (argc > 1) { - if (!strcasecmp(argv[1], "scroll")) { - scroll = !scroll; - ccprintf("scroll %sabled\n", scroll ? "en" : "dis"); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "intprint")) { - print_in_int = !print_in_int; - ccprintf("printing in interrupt %sabled\n", - print_in_int ? "en" : "dis"); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "flush")) { - writes = 0; - return EC_SUCCESS; - } else { - return EC_ERROR_PARAM1; - } - } - - port80_dump_buffer(); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(port80, command_port80, - "[scroll | intprint | flush]", - "Print port80 writes or toggle port80 scrolling"); - -enum ec_status port80_last_boot(struct host_cmd_handler_args *args) -{ - struct ec_response_port80_last_boot *r = args->response; - - args->response_size = sizeof(*r); - r->code = last_boot; - - return EC_RES_SUCCESS; -} - -enum ec_status port80_command_read(struct host_cmd_handler_args *args) -{ - const struct ec_params_port80_read *p = args->params; - uint32_t offset = p->read_buffer.offset; - uint32_t entries = p->read_buffer.num_entries; - int i; - struct ec_response_port80_read *rsp = args->response; - - if (args->version == 0) - return port80_last_boot(args); - - if (p->subcmd == EC_PORT80_GET_INFO) { - rsp->get_info.writes = writes; - rsp->get_info.history_size = ARRAY_SIZE(history); - args->response_size = sizeof(rsp->get_info); - return EC_RES_SUCCESS; - } else if (p->subcmd == EC_PORT80_READ_BUFFER) { - /* do not allow bad offset or size */ - if (offset >= ARRAY_SIZE(history) || entries == 0 || - entries > args->response_max) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < entries; i++) { - uint16_t e = history[(i + offset) % - ARRAY_SIZE(history)]; - rsp->data.codes[i] = e; - } - - args->response_size = entries*sizeof(uint16_t); - return EC_RES_SUCCESS; - } - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_PORT80_READ, - port80_command_read, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static void port80_log_resume(void) -{ - /* Store port 80 event so we know where resume happened */ - port_80_write(PORT_80_EVENT_RESUME); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, port80_log_resume, HOOK_PRIO_DEFAULT); diff --git a/common/power_button.c b/common/power_button.c deleted file mode 100644 index 1ac3893492..0000000000 --- a/common/power_button.c +++ /dev/null @@ -1,227 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Power button module for Chrome EC */ - -#include "button.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_scan.h" -#include "lid_switch.h" -#include "power_button.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_SWITCH, outstr) -#define CPRINTS(format, args...) cprints(CC_SWITCH, format, ## args) - -/* By default the power button is active low */ -#ifndef CONFIG_POWER_BUTTON_FLAGS -#define CONFIG_POWER_BUTTON_FLAGS 0 -#endif - -static int debounced_power_pressed; /* Debounced power button state */ -static int simulate_power_pressed; -static volatile int power_button_is_stable = 1; - -static const struct button_config power_button = { - .name = "power button", - .gpio = GPIO_POWER_BUTTON_L, - .debounce_us = BUTTON_DEBOUNCE_US, - .flags = CONFIG_POWER_BUTTON_FLAGS, -}; - -int power_button_signal_asserted(void) -{ - return !!(gpio_get_level(power_button.gpio) - == (power_button.flags & BUTTON_FLAG_ACTIVE_HIGH) ? 1 : 0); -} - -/** - * Get raw power button signal state. - * - * @return 1 if power button is pressed, 0 if not pressed. - */ -static int raw_power_button_pressed(void) -{ - if (simulate_power_pressed) - return 1; - -#ifndef CONFIG_POWER_BUTTON_IGNORE_LID - /* - * Always indicate power button released if the lid is closed. - * This prevents waking the system if the device is squashed enough to - * press the power button through the closed lid. - */ - if (!lid_is_open()) - return 0; -#endif - - return power_button_signal_asserted(); -} - -int power_button_is_pressed(void) -{ - return debounced_power_pressed; -} - -int power_button_wait_for_release(int timeout_us) -{ - timestamp_t deadline; - timestamp_t now = get_time(); - - deadline.val = now.val + timeout_us; - - while (!power_button_is_stable || power_button_is_pressed()) { - now = get_time(); - if (timeout_us >= 0 && timestamp_expired(deadline, &now)) { - CPRINTS("%s not released in time", power_button.name); - return EC_ERROR_TIMEOUT; - } - /* - * We use task_wait_event() instead of usleep() here. It will - * be woken up immediately if the power button is debouned and - * changed. However, it is not guaranteed, like the cases that - * the power button is debounced but not changed, or the power - * button has not been debounced. - */ - task_wait_event(MIN(power_button.debounce_us, - deadline.val - now.val)); - } - - CPRINTS("%s released in time", power_button.name); - return EC_SUCCESS; -} - -/** - * Handle power button initialization. - */ -static void power_button_init(void) -{ - if (raw_power_button_pressed()) - debounced_power_pressed = 1; - - /* Enable interrupts, now that we've initialized */ - gpio_enable_interrupt(power_button.gpio); -} -DECLARE_HOOK(HOOK_INIT, power_button_init, HOOK_PRIO_INIT_POWER_BUTTON); - -#ifdef CONFIG_POWER_BUTTON_INIT_IDLE -/* - * Set/clear AP_IDLE flag. It's set when the system gracefully shuts down and - * it's cleared when the system boots up. The result is the system tries to - * go back to the previous state upon AC plug-in. If the system uncleanly - * shuts down, it boots immediately. If the system shuts down gracefully, - * it'll stay at S5 and wait for power button press. - */ -static void pb_chipset_startup(void) -{ - chip_save_reset_flags(chip_read_reset_flags() & ~EC_RESET_FLAG_AP_IDLE); - system_clear_reset_flags(EC_RESET_FLAG_AP_IDLE); - CPRINTS("Cleared AP_IDLE flag"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pb_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pb_chipset_shutdown(void) -{ - chip_save_reset_flags(chip_read_reset_flags() | EC_RESET_FLAG_AP_IDLE); - system_set_reset_flags(EC_RESET_FLAG_AP_IDLE); - CPRINTS("Saved AP_IDLE flag"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pb_chipset_shutdown, - /* - * Slightly higher than handle_pending_reboot because - * it may clear AP_IDLE flag. - */ - HOOK_PRIO_DEFAULT - 1); -#endif - -/** - * Handle debounced power button changing state. - */ -static void power_button_change_deferred(void) -{ - const int new_pressed = raw_power_button_pressed(); - - /* Re-enable keyboard scanning if power button is no longer pressed */ - if (!new_pressed) - keyboard_scan_enable(1, KB_SCAN_DISABLE_POWER_BUTTON); - - /* If power button hasn't changed state, nothing to do */ - if (new_pressed == debounced_power_pressed) { - power_button_is_stable = 1; - return; - } - - debounced_power_pressed = new_pressed; - power_button_is_stable = 1; - - CPRINTS("%s %s", - power_button.name, new_pressed ? "pressed" : "released"); - - /* Call hooks */ - hook_notify(HOOK_POWER_BUTTON_CHANGE); - - /* Notify host if power button has been pressed */ - if (new_pressed) - host_set_single_event(EC_HOST_EVENT_POWER_BUTTON); -} -DECLARE_DEFERRED(power_button_change_deferred); - -void power_button_interrupt(enum gpio_signal signal) -{ - /* - * If power button is pressed, disable the matrix scan as soon as - * possible to reduce the risk of false-reboot triggered by those keys - * on the same column with refresh key. - */ - if (raw_power_button_pressed()) - keyboard_scan_enable(0, KB_SCAN_DISABLE_POWER_BUTTON); - - /* Reset power button debounce time */ - power_button_is_stable = 0; - hook_call_deferred(&power_button_change_deferred_data, - power_button.debounce_us); -} - -/*****************************************************************************/ -/* Console commands */ - -static int command_powerbtn(int argc, char **argv) -{ - int ms = 200; /* Press duration in ms */ - char *e; - - if (argc > 1) { - ms = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - } - - ccprintf("Simulating %d ms %s press.\n", ms, power_button.name); - simulate_power_pressed = 1; - power_button_is_stable = 0; - hook_call_deferred(&power_button_change_deferred_data, 0); - - if (ms > 0) - msleep(ms); - - ccprintf("Simulating %s release.\n", power_button.name); - simulate_power_pressed = 0; - power_button_is_stable = 0; - hook_call_deferred(&power_button_change_deferred_data, 0); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn, - "[msec]", - "Simulate power button press"); - diff --git a/common/power_button_x86.c b/common/power_button_x86.c deleted file mode 100644 index 661f9b2a3d..0000000000 --- a/common/power_button_x86.c +++ /dev/null @@ -1,575 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Power button state machine for x86 platforms */ - -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_scan.h" -#include "lid_switch.h" -#include "power_button.h" -#include "switch.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_SWITCH, outstr) -#define CPRINTS(format, args...) cprints(CC_SWITCH, format, ## args) - -/* - * x86 chipsets have a hardware timer on the power button input which causes - * them to reset when the button is pressed for more than 4 seconds. This is - * problematic for Chrome OS, which needs more time than that to transition - * through the lock and logout screens. So when the system is on, we need to - * stretch the power button signal so that the chipset will hard-reboot after 8 - * seconds instead of 4. - * - * When the button is pressed, we initially send a short pulse (t0); this - * allows the chipset to process its initial power button interrupt and do - * things like wake from suspend. We then deassert the power button signal to - * the chipset for (t1 = 4 sec - t0), which keeps the chipset from starting its - * hard reset timer. If the power button is still pressed after this period, - * we again assert the power button signal for the remainder of the press - * duration. Since (t0+t1) causes a 4-second offset, the hard reset timeout in - * the chipset triggers after 8 seconds as desired. - * - * PWRBTN# --- ---- - * to EC |______________________| - * - * - * PWRBTN# --- --------- ---- - * to PCH |__| |___________| - * t0 t1 held down - * - * scan code | | - * to host v v - * @S0 make code break code - */ -#define PWRBTN_DELAY_T0 (32 * MSEC) /* 32ms (PCH requires >16ms) */ -#define PWRBTN_DELAY_T1 (4 * SECOND - PWRBTN_DELAY_T0) /* 4 secs - t0 */ -/* - * Length of time to stretch initial power button press to give chipset a - * chance to wake up (~100ms) and react to the press (~16ms). Also used as - * pulse length for simulated power button presses when the system is off. - */ -#define PWRBTN_INITIAL_US (200 * MSEC) - -enum power_button_state { - /* Button up; state machine idle */ - PWRBTN_STATE_IDLE = 0, - /* Button pressed; debouncing done */ - PWRBTN_STATE_PRESSED, - /* Button down, chipset on; sending initial short pulse */ - PWRBTN_STATE_T0, - /* Button down, chipset on; delaying until we should reassert signal */ - PWRBTN_STATE_T1, - /* Button down, signal asserted to chipset */ - PWRBTN_STATE_HELD, - /* Force pulse due to lid-open event */ - PWRBTN_STATE_LID_OPEN, - /* Button released; debouncing done */ - PWRBTN_STATE_RELEASED, - /* Ignore next button release */ - PWRBTN_STATE_EAT_RELEASE, - /* - * Need to power on system after init, but waiting to find out if - * sufficient battery power. - */ - PWRBTN_STATE_INIT_ON, - /* Forced pulse at EC boot due to keyboard controlled reset */ - PWRBTN_STATE_BOOT_KB_RESET, - /* Power button pressed when chipset was off; stretching pulse */ - PWRBTN_STATE_WAS_OFF, -}; -static enum power_button_state pwrbtn_state = PWRBTN_STATE_IDLE; - -static const char * const state_names[] = { - "idle", - "pressed", - "t0", - "t1", - "held", - "lid-open", - "released", - "eat-release", - "init-on", - "recovery", - "was-off", -}; - -/* - * Time for next state transition of power button state machine, or 0 if the - * state doesn't have a timeout. - */ -static uint64_t tnext_state; - -/* - * Record the time when power button task starts. It can be used by any code - * path that needs to compare the current time with power button task start time - * to identify any timeouts e.g. PB state machine checks current time to - * identify if it should wait more for charger and battery to be initialized. In - * case of recovery using buttons (where the user could be holding the buttons - * for >30seconds), it is not right to compare current time with the time when - * EC was reset since the tasks would not have started. Hence, this variable is - * being added to record the time at which power button task starts. - */ -static uint64_t tpb_task_start; - -/* - * Determines whether to execute power button pulse (t0 stage) - */ -static int power_button_pulse_enabled = 1; - -static void set_pwrbtn_to_pch(int high, int init) -{ - /* - * If the battery is discharging and low enough we'd shut down the - * system, don't press the power button. Also, don't press the - * power button if the battery is charging but the battery level - * is too low. - */ -#ifdef CONFIG_CHARGER - if (chipset_in_state(CHIPSET_STATE_ANY_OFF) && !high && - (charge_want_shutdown() || charge_prevent_power_on(!init))) { - CPRINTS("PB PCH pwrbtn ignored due to battery level"); - high = 1; - } -#endif - CPRINTS("PB PCH pwrbtn=%s", high ? "HIGH" : "LOW"); - if (IS_ENABLED(CONFIG_POWER_BUTTON_TO_PCH_CUSTOM)) - board_pwrbtn_to_pch(high); - else - gpio_set_level(GPIO_PCH_PWRBTN_L, high); -} - -void power_button_pch_press(void) -{ - CPRINTS("PB PCH force press"); - - /* Assert power button signal to PCH */ - if (!power_button_is_pressed()) - set_pwrbtn_to_pch(0, 0); -} - -void power_button_pch_release(void) -{ - CPRINTS("PB PCH force release"); - - /* Deassert power button signal to PCH */ - set_pwrbtn_to_pch(1, 0); - - /* - * If power button is actually pressed, eat the next release so we - * don't send an extra release. - */ - if (power_button_is_pressed()) - pwrbtn_state = PWRBTN_STATE_EAT_RELEASE; - else - pwrbtn_state = PWRBTN_STATE_IDLE; -} - -void power_button_pch_pulse(void) -{ - CPRINTS("PB PCH pulse"); - - chipset_exit_hard_off(); - set_pwrbtn_to_pch(0, 0); - pwrbtn_state = PWRBTN_STATE_LID_OPEN; - tnext_state = get_time().val + PWRBTN_INITIAL_US; - task_wake(TASK_ID_POWERBTN); -} - -/** - * Handle debounced power button down. - */ -static void power_button_pressed(uint64_t tnow) -{ - CPRINTS("PB pressed"); - pwrbtn_state = PWRBTN_STATE_PRESSED; - tnext_state = tnow; -} - -/** - * Handle debounced power button up. - */ -static void power_button_released(uint64_t tnow) -{ - CPRINTS("PB released"); - pwrbtn_state = PWRBTN_STATE_RELEASED; - tnext_state = tnow; -} - -/** - * Set initial power button state. - */ -static void set_initial_pwrbtn_state(void) -{ - uint32_t reset_flags = system_get_reset_flags(); - - if (system_jumped_to_this_image() && - chipset_in_state(CHIPSET_STATE_ON)) { - /* - * Jumped to this image while the chipset was already on, so - * simply reflect the actual power button state unless power - * button pulse is disabled. If power button SMI pulse is - * enabled, then it should be honored, else setting power - * button to PCH could lead to x86 platform shutting down. If - * power button is still held by the time control reaches - * state_machine(), it would take the appropriate action there. - */ - if (power_button_is_pressed() && power_button_pulse_enabled) { - CPRINTS("PB init-jumped-held"); - set_pwrbtn_to_pch(0, 0); - } else { - CPRINTS("PB init-jumped"); - } - return; - } else if ((reset_flags & EC_RESET_FLAG_AP_OFF) || - (keyboard_scan_get_boot_keys() == BOOT_KEY_DOWN_ARROW)) { - /* Clear AP_OFF so that it won't be carried over to RW. */ - system_clear_reset_flags(EC_RESET_FLAG_AP_OFF); - /* - * Reset triggered by keyboard-controlled reset, and down-arrow - * was held down. Or reset flags request AP off. - * - * Leave the main processor off. This is a fail-safe - * combination for debugging failures booting the main - * processor. - * - * Don't let the PCH see that the power button was pressed. - * Otherwise, it might power on. - */ - CPRINTS("PB init-off"); - power_button_pch_release(); - return; - } else if (reset_flags & EC_RESET_FLAG_AP_IDLE) { - system_clear_reset_flags(EC_RESET_FLAG_AP_IDLE); - pwrbtn_state = PWRBTN_STATE_IDLE; - CPRINTS("PB idle"); - return; - } - -#ifdef CONFIG_BRINGUP - pwrbtn_state = PWRBTN_STATE_IDLE; -#else - pwrbtn_state = PWRBTN_STATE_INIT_ON; -#endif - CPRINTS("PB %s", - pwrbtn_state == PWRBTN_STATE_INIT_ON ? "init-on" : "idle"); -} - -/** - * Power button state machine. - * - * @param tnow Current time from usec counter - */ -static void state_machine(uint64_t tnow) -{ - /* Not the time to move onto next state */ - if (tnow < tnext_state) - return; - - /* States last forever unless otherwise specified */ - tnext_state = 0; - - switch (pwrbtn_state) { - case PWRBTN_STATE_PRESSED: - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - /* - * Chipset is off, so wake the chipset and send it a - * long enough pulse to wake up. After that we'll - * reflect the true power button state. If we don't - * stretch the pulse here, the user may release the - * power button before the chipset finishes waking from - * hard off state. - */ - chipset_exit_hard_off(); - tnext_state = tnow + PWRBTN_INITIAL_US; - pwrbtn_state = PWRBTN_STATE_WAS_OFF; - set_pwrbtn_to_pch(0, 0); - } else { - if (power_button_pulse_enabled) { - /* Chipset is on, so send the chipset a pulse */ - tnext_state = tnow + PWRBTN_DELAY_T0; - pwrbtn_state = PWRBTN_STATE_T0; - set_pwrbtn_to_pch(0, 0); - } else { - tnext_state = tnow + PWRBTN_DELAY_T1; - pwrbtn_state = PWRBTN_STATE_T1; - } - } - break; - case PWRBTN_STATE_T0: - tnext_state = tnow + PWRBTN_DELAY_T1; - pwrbtn_state = PWRBTN_STATE_T1; - set_pwrbtn_to_pch(1, 0); - break; - case PWRBTN_STATE_T1: - /* - * If the chipset is already off, don't tell it the power - * button is down; it'll just cause the chipset to turn on - * again. - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - CPRINTS("PB chipset already off"); - else - set_pwrbtn_to_pch(0, 0); - pwrbtn_state = PWRBTN_STATE_HELD; - break; - case PWRBTN_STATE_RELEASED: - case PWRBTN_STATE_LID_OPEN: - set_pwrbtn_to_pch(1, 0); - pwrbtn_state = PWRBTN_STATE_IDLE; - break; - case PWRBTN_STATE_INIT_ON: - - /* - * Before attempting to power the system on, we need to allow - * time for charger, battery and USB-C PD initialization to be - * ready to supply sufficient power. Check every 100 - * milliseconds, and give up CONFIG_POWER_BUTTON_INIT_TIMEOUT - * seconds after the PB task was started. Here, it is - * important to check the current time against PB task start - * time to prevent unnecessary timeouts happening in recovery - * case where the tasks could start as late as 30 seconds - * after EC reset. - */ - - if (!IS_ENABLED(CONFIG_CHARGER) || charge_prevent_power_on(0)) { - if (tnow > - (tpb_task_start + - CONFIG_POWER_BUTTON_INIT_TIMEOUT * SECOND)) { - pwrbtn_state = PWRBTN_STATE_IDLE; - break; - } - - if (IS_ENABLED(CONFIG_CHARGER)) { - tnext_state = tnow + 100 * MSEC; - break; - } - } - - /* - * Power the system on if possible. Gating due to insufficient - * battery is handled inside set_pwrbtn_to_pch(). - */ - chipset_exit_hard_off(); -#ifdef CONFIG_DELAY_DSW_PWROK_TO_PWRBTN - /* Check if power button is ready. If not, we'll come back. */ - if (get_time().val - get_time_dsw_pwrok() < - CONFIG_DSW_PWROK_TO_PWRBTN_US) { - tnext_state = get_time_dsw_pwrok() + - CONFIG_DSW_PWROK_TO_PWRBTN_US; - break; - } -#endif - - set_pwrbtn_to_pch(0, 1); - tnext_state = get_time().val + PWRBTN_INITIAL_US; - pwrbtn_state = PWRBTN_STATE_BOOT_KB_RESET; - break; - - case PWRBTN_STATE_BOOT_KB_RESET: - /* Initial forced pulse is done. Ignore the actual power - * button until it's released, so that holding down the - * recovery combination doesn't cause the chipset to shut back - * down. */ - set_pwrbtn_to_pch(1, 0); - if (power_button_is_pressed()) - pwrbtn_state = PWRBTN_STATE_EAT_RELEASE; - else - pwrbtn_state = PWRBTN_STATE_IDLE; - break; - case PWRBTN_STATE_WAS_OFF: - /* Done stretching initial power button signal, so show the - * true power button state to the PCH. */ - if (power_button_is_pressed()) { - /* User is still holding the power button */ - pwrbtn_state = PWRBTN_STATE_HELD; - } else { - /* Stop stretching the power button press */ - power_button_released(tnow); - } - break; - case PWRBTN_STATE_IDLE: - case PWRBTN_STATE_HELD: - case PWRBTN_STATE_EAT_RELEASE: - /* Do nothing */ - break; - } -} - -void power_button_task(void *u) -{ - uint64_t t; - uint64_t tsleep; - - /* - * Record the time when the task starts so that the state machine can - * use this to identify any timeouts. - */ - tpb_task_start = get_time().val; - - while (1) { - t = get_time().val; - - /* Update state machine */ - CPRINTS("PB task %d = %s", pwrbtn_state, - state_names[pwrbtn_state]); - - state_machine(t); - - /* Sleep until our next timeout */ - tsleep = -1; - if (tnext_state && tnext_state < tsleep) - tsleep = tnext_state; - t = get_time().val; - if (tsleep > t) { - unsigned d = tsleep == -1 ? -1 : (unsigned)(tsleep - t); - /* - * (Yes, the conversion from uint64_t to unsigned could - * theoretically overflow if we wanted to sleep for - * more than 2^32 us, but our timeouts are small enough - * that can't happen - and even if it did, we'd just go - * back to sleep after deciding that we woke up too - * early.) - */ - CPRINTS("PB task %d = %s, wait %d", pwrbtn_state, - state_names[pwrbtn_state], d); - task_wait_event(d); - } - } -} - -/*****************************************************************************/ -/* Hooks */ - -static void powerbtn_x86_init(void) -{ - set_initial_pwrbtn_state(); -} -DECLARE_HOOK(HOOK_INIT, powerbtn_x86_init, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_LID_SWITCH -/** - * Handle switch changes based on lid event. - */ -static void powerbtn_x86_lid_change(void) -{ - /* If chipset is off, pulse the power button on lid open to wake it. */ - if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF) - && pwrbtn_state != PWRBTN_STATE_INIT_ON) - power_button_pch_pulse(); -} -DECLARE_HOOK(HOOK_LID_CHANGE, powerbtn_x86_lid_change, HOOK_PRIO_DEFAULT); -#endif - -/** - * Handle debounced power button changing state. - */ -static void powerbtn_x86_changed(void) -{ - if (pwrbtn_state == PWRBTN_STATE_BOOT_KB_RESET || - pwrbtn_state == PWRBTN_STATE_INIT_ON || - pwrbtn_state == PWRBTN_STATE_LID_OPEN || - pwrbtn_state == PWRBTN_STATE_WAS_OFF) { - /* Ignore all power button changes during an initial pulse */ - CPRINTS("PB ignoring change"); - return; - } - - if (power_button_is_pressed()) { - /* Power button pressed */ - power_button_pressed(get_time().val); - } else { - /* Power button released */ - if (pwrbtn_state == PWRBTN_STATE_EAT_RELEASE) { - /* - * Ignore the first power button release if we already - * told the PCH the power button was released. - */ - CPRINTS("PB ignoring release"); - pwrbtn_state = PWRBTN_STATE_IDLE; - return; - } - - power_button_released(get_time().val); - } - - /* Wake the power button task */ - task_wake(TASK_ID_POWERBTN); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_x86_changed, HOOK_PRIO_DEFAULT); - -/** - * Handle configuring the power button behavior through a host command - */ -static enum ec_status hc_config_powerbtn_x86(struct host_cmd_handler_args *args) -{ - const struct ec_params_config_power_button *p = args->params; - - power_button_pulse_enabled = - !!(p->flags & EC_POWER_BUTTON_ENABLE_PULSE); - - return EC_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_CONFIG_POWER_BUTTON, hc_config_powerbtn_x86, - EC_VER_MASK(0)); - - -/* - * Currently, the only reason why we disable power button pulse is to allow - * detachable menu on AP to use power button for selection purpose without - * triggering SMI. Thus, re-enable the pulse any time there is a chipset - * state transition event. - */ -static void power_button_pulse_setting_reset(void) -{ - power_button_pulse_enabled = 1; -} - -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, power_button_pulse_setting_reset, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, power_button_pulse_setting_reset, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, power_button_pulse_setting_reset, - HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_CHIPSET_RESUME, power_button_pulse_setting_reset, - HOOK_PRIO_DEFAULT); - -#define POWER_BUTTON_SYSJUMP_TAG 0x5042 /* PB */ -#define POWER_BUTTON_HOOK_VERSION 1 - -static void power_button_pulse_setting_restore_state(void) -{ - const int *state; - int version, size; - - state = (const int *)system_get_jump_tag(POWER_BUTTON_SYSJUMP_TAG, - &version, &size); - - if (state && (version == POWER_BUTTON_HOOK_VERSION) && - (size == sizeof(power_button_pulse_enabled))) - power_button_pulse_enabled = *state; -} -DECLARE_HOOK(HOOK_INIT, power_button_pulse_setting_restore_state, - HOOK_PRIO_INIT_POWER_BUTTON + 1); - -static void power_button_pulse_setting_preserve_state(void) -{ - system_add_jump_tag(POWER_BUTTON_SYSJUMP_TAG, - POWER_BUTTON_HOOK_VERSION, - sizeof(power_button_pulse_enabled), - &power_button_pulse_enabled); -} -DECLARE_HOOK(HOOK_SYSJUMP, power_button_pulse_setting_preserve_state, - HOOK_PRIO_DEFAULT); diff --git a/common/pstore_commands.c b/common/pstore_commands.c deleted file mode 100644 index 52270cd1cf..0000000000 --- a/common/pstore_commands.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Persistent storage commands for Chrome EC */ - -#include "common.h" -#include "eeprom.h" -#include "host_command.h" -#include "util.h" - -enum ec_status pstore_command_get_info(struct host_cmd_handler_args *args) -{ - struct ec_response_pstore_info *r = args->response; - - ASSERT(EEPROM_BLOCK_START_PSTORE + EEPROM_BLOCK_COUNT_PSTORE <= - eeprom_get_block_count()); - - r->pstore_size = EEPROM_BLOCK_COUNT_PSTORE * eeprom_get_block_size(); - r->access_size = sizeof(uint32_t); - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PSTORE_INFO, - pstore_command_get_info, - EC_VER_MASK(0)); - -enum ec_status pstore_command_read(struct host_cmd_handler_args *args) -{ - const struct ec_params_pstore_read *p = args->params; - char *dest = args->response; - int block_size = eeprom_get_block_size(); - int block = p->offset / block_size + EEPROM_BLOCK_START_PSTORE; - int offset = p->offset % block_size; - int bytes_left = p->size; - - if (p->size > args->response_max) - return EC_RES_INVALID_PARAM; - - while (bytes_left) { - /* Read what we can from the current block */ - int bytes_this = MIN(bytes_left, block_size - offset); - - if (block >= - EEPROM_BLOCK_START_PSTORE + EEPROM_BLOCK_COUNT_PSTORE) - return EC_RES_ERROR; - - if (eeprom_read(block, offset, bytes_this, dest)) - return EC_RES_ERROR; - - /* Continue to the next block if necessary */ - offset = 0; - block++; - bytes_left -= bytes_this; - dest += bytes_this; - } - - args->response_size = p->size; - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PSTORE_READ, - pstore_command_read, - EC_VER_MASK(0)); - -enum ec_status pstore_command_write(struct host_cmd_handler_args *args) -{ - const struct ec_params_pstore_write *p = args->params; - - const char *src = p->data; - int block_size = eeprom_get_block_size(); - int block = p->offset / block_size + EEPROM_BLOCK_START_PSTORE; - int offset = p->offset % block_size; - int bytes_left = p->size; - - if (p->size > sizeof(p->data)) - return EC_RES_ERROR; - - while (bytes_left) { - /* Write what we can to the current block */ - int bytes_this = MIN(bytes_left, block_size - offset); - - if (block >= - EEPROM_BLOCK_START_PSTORE + EEPROM_BLOCK_COUNT_PSTORE) - return EC_RES_ERROR; - - if (eeprom_write(block, offset, bytes_this, src)) - return EC_RES_ERROR; - - /* Continue to the next block if necessary */ - offset = 0; - block++; - bytes_left -= bytes_this; - src += bytes_this; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PSTORE_WRITE, - pstore_command_write, - EC_VER_MASK(0)); diff --git a/common/pwm.c b/common/pwm.c deleted file mode 100644 index 41989a94e0..0000000000 --- a/common/pwm.c +++ /dev/null @@ -1,180 +0,0 @@ -/* Copyright 2013 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 "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "pwm.h" -#include "util.h" - -#ifdef CONFIG_ZEPHYR -#include "pwm/pwm.h" -#endif - -#ifdef CONFIG_PWM - -/* - * Get target channel based on type / index host command parameters. - * Returns 0 if a valid channel is selected, -1 on error. - */ -static int get_target_channel(enum pwm_channel *channel, int type, int index) -{ - switch (type) { - case EC_PWM_TYPE_GENERIC: - *channel = index; - break; -#ifdef CONFIG_PWM_KBLIGHT - case EC_PWM_TYPE_KB_LIGHT: - *channel = PWM_CH_KBLIGHT; - break; -#endif -#ifdef CONFIG_PWM_DISPLIGHT - case EC_PWM_TYPE_DISPLAY_LIGHT: - *channel = PWM_CH_DISPLIGHT; - break; -#endif - default: - return -1; - } - - return *channel >= PWM_CH_COUNT; -} - -__attribute__((weak)) void pwm_set_raw_duty(enum pwm_channel ch, uint16_t duty) -{ - int percent; - - /* Convert 16 bit duty to percent on [0, 100] */ - percent = DIV_ROUND_NEAREST((uint32_t)duty * 100, 65535); - pwm_set_duty(ch, percent); -} - -__attribute__((weak)) uint16_t pwm_get_raw_duty(enum pwm_channel ch) -{ - return (pwm_get_duty(ch) * 65535) / 100; -} - -static enum ec_status -host_command_pwm_set_duty(struct host_cmd_handler_args *args) -{ - const struct ec_params_pwm_set_duty *p = args->params; - enum pwm_channel channel; - - if (get_target_channel(&channel, p->pwm_type, p->index)) - return EC_RES_INVALID_PARAM; - - pwm_set_raw_duty(channel, p->duty); - pwm_enable(channel, p->duty > 0); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_DUTY, - host_command_pwm_set_duty, - EC_VER_MASK(0)); - -static enum ec_status -host_command_pwm_get_duty(struct host_cmd_handler_args *args) -{ - const struct ec_params_pwm_get_duty *p = args->params; - struct ec_response_pwm_get_duty *r = args->response; - - enum pwm_channel channel; - - if (get_target_channel(&channel, p->pwm_type, p->index)) - return EC_RES_INVALID_PARAM; - - r->duty = pwm_get_raw_duty(channel); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_DUTY, - host_command_pwm_get_duty, - EC_VER_MASK(0)); - -/** - * Print status of a PWM channel. - * - * @param ch Channel to print. - */ -static void print_channel(enum pwm_channel ch, int max_duty) -{ - if (pwm_get_enabled(ch)) - if (max_duty == 100) - ccprintf(" %d: %d%%\n", ch, pwm_get_duty(ch)); - else - ccprintf(" %d: %d\n", ch, pwm_get_raw_duty(ch)); - else - ccprintf(" %d: disabled\n", ch); -} - -static int cc_pwm_duty(int argc, char **argv) -{ - int value = 0; - int max_duty = 100; - int ch; - char *e; - char *raw; - - if (argc < 2) { - ccprintf("PWM channels:\n"); - for (ch = 0; ch < PWM_CH_COUNT; ch++) - print_channel(ch, max_duty); - return EC_SUCCESS; - } - - ch = strtoi(argv[1], &e, 0); - if (*e || ch < 0 || ch >= PWM_CH_COUNT) - return EC_ERROR_PARAM1; - - if (argc > 2) { - raw = argv[2]; - if (!strcasecmp(raw, "raw")) { - /* use raw duty */ - value = strtoi(argv[3], &e, 0); - max_duty = EC_PWM_MAX_DUTY; - } else { - /* use percent duty */ - value = strtoi(argv[2], &e, 0); - max_duty = 100; - } - - if (*e || value > max_duty) { - /* Bad param */ - return EC_ERROR_PARAM2; - } else if (value < 0) { - /* Negative = disable */ - pwm_enable(ch, 0); - } else { - ccprintf("Setting channel %d to %d\n", ch, value); - pwm_enable(ch, 1); - (max_duty == 100) ? pwm_set_duty(ch, value) : - pwm_set_raw_duty(ch, value); - } - } - - print_channel(ch, max_duty); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pwmduty, cc_pwm_duty, - "[channel [<percent> | -1=disable] | [raw <value>]]", - "Get/set PWM duty cycles "); -#endif /* CONFIG_PWM */ - -#ifndef CONFIG_ZEPHYR -/* - * Initialize all PWM pins as functional. This is not required under - * Zephyr as pin configuration is automatically performed by chip driver - */ -static void pwm_pin_init(void) -{ - gpio_config_module(MODULE_PWM, 1); -} -/* HOOK_PRIO_INIT_PWM may be used for chip PWM unit init, so use PRIO + 1 */ -DECLARE_HOOK(HOOK_INIT, pwm_pin_init, HOOK_PRIO_INIT_PWM + 1); -#endif /* CONFIG_ZEPHYR */ diff --git a/common/pwm_kblight.c b/common/pwm_kblight.c deleted file mode 100644 index 4967d36df5..0000000000 --- a/common/pwm_kblight.c +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2013 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. - */ - -/* PWM control module for keyboard backlight. */ - -#include "common.h" -#include "keyboard_backlight.h" -#include "pwm.h" -#include "system.h" -#include "util.h" - -const enum pwm_channel kblight_pwm_ch = PWM_CH_KBLIGHT; - -static int kblight_pwm_set(int percent) -{ - pwm_set_duty(kblight_pwm_ch, percent); - return EC_SUCCESS; -} - -static int kblight_pwm_get(void) -{ - return pwm_get_duty(kblight_pwm_ch); -} - -static int kblight_pwm_init(void) -{ - /* dnojiri: Why do we need save/restore setting over sysjump? */ - kblight_pwm_set(0); - pwm_enable(kblight_pwm_ch, 0); - return EC_SUCCESS; -} - -static int kblight_pwm_enable(int enable) -{ - pwm_enable(kblight_pwm_ch, enable); - return EC_SUCCESS; -} - -const struct kblight_drv kblight_pwm = { - .init = kblight_pwm_init, - .set = kblight_pwm_set, - .get = kblight_pwm_get, - .enable = kblight_pwm_enable, -}; diff --git a/common/regulator.c b/common/regulator.c deleted file mode 100644 index c3f5d0b99d..0000000000 --- a/common/regulator.c +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright 2020 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. - */ - -/* Regulator control module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "regulator.h" - -static enum ec_status -hc_regulator_get_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_regulator_get_info *p = args->params; - struct ec_response_regulator_get_info *r = args->response; - int rv; - - rv = board_regulator_get_info(p->index, r->name, &r->num_voltages, - r->voltages_mv); - - if (rv) - return EC_RES_ERROR; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REGULATOR_GET_INFO, hc_regulator_get_info, - EC_VER_MASK(0)); - -static enum ec_status -hc_regulator_enable(struct host_cmd_handler_args *args) -{ - const struct ec_params_regulator_enable *p = args->params; - int rv; - - rv = board_regulator_enable(p->index, p->enable); - - if (rv) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REGULATOR_ENABLE, hc_regulator_enable, - EC_VER_MASK(0)); - -static enum ec_status -hc_regulator_is_enabled(struct host_cmd_handler_args *args) -{ - const struct ec_params_regulator_is_enabled *p = args->params; - struct ec_response_regulator_is_enabled *r = args->response; - int rv; - - rv = board_regulator_is_enabled(p->index, &r->enabled); - - if (rv) - return EC_RES_ERROR; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REGULATOR_IS_ENABLED, hc_regulator_is_enabled, - EC_VER_MASK(0)); - -static enum ec_status -hc_regulator_get_voltage(struct host_cmd_handler_args *args) -{ - const struct ec_params_regulator_get_voltage *p = args->params; - struct ec_response_regulator_get_voltage *r = args->response; - int rv; - - rv = board_regulator_get_voltage(p->index, &r->voltage_mv); - - if (rv) - return EC_RES_ERROR; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REGULATOR_GET_VOLTAGE, hc_regulator_get_voltage, - EC_VER_MASK(0)); - -static enum ec_status -hc_regulator_set_voltage(struct host_cmd_handler_args *args) -{ - const struct ec_params_regulator_set_voltage *p = args->params; - int rv; - - rv = board_regulator_set_voltage(p->index, p->min_mv, p->max_mv); - - if (rv) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REGULATOR_SET_VOLTAGE, hc_regulator_set_voltage, - EC_VER_MASK(0)); diff --git a/common/rollback.c b/common/rollback.c deleted file mode 100644 index 984058c49a..0000000000 --- a/common/rollback.c +++ /dev/null @@ -1,520 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Rollback protection logic. */ - -#include "common.h" -#include "console.h" -#ifdef CONFIG_LIBCRYPTOC -#include "cryptoc/util.h" -#endif -#include "flash.h" -#include "hooks.h" -#include "host_command.h" -#ifdef CONFIG_MPU -#include "mpu.h" -#endif -#include "rollback.h" -#include "rollback_private.h" -#include "sha256.h" -#include "system.h" -#include "task.h" -#include "trng.h" -#include "util.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) - -/* Number of rollback regions */ -#define ROLLBACK_REGIONS 2 - -static int get_rollback_offset(int region) -{ -#ifdef CONFIG_FLASH_MULTIPLE_REGION - int rv; - int rollback_start_bank = crec_flash_bank_index(CONFIG_ROLLBACK_OFF); - - rv = crec_flash_bank_start_offset(rollback_start_bank + region); - ASSERT(rv >= 0); - return rv; -#else - return CONFIG_ROLLBACK_OFF + region * CONFIG_FLASH_ERASE_SIZE; -#endif -} - -#ifdef SECTION_IS_RO -static int get_rollback_erase_size_bytes(int region) -{ - int erase_size; - -#ifndef CONFIG_FLASH_MULTIPLE_REGION - erase_size = CONFIG_FLASH_ERASE_SIZE; -#else - int rollback_start_bank = crec_flash_bank_index(CONFIG_ROLLBACK_OFF); - - erase_size = crec_flash_bank_erase_size(rollback_start_bank + region); -#endif - ASSERT(erase_size > 0); - ASSERT(ROLLBACK_REGIONS * erase_size <= CONFIG_ROLLBACK_SIZE); - ASSERT(sizeof(struct rollback_data) <= erase_size); - return erase_size; -} -#endif - -/* - * When MPU is available, read rollback with interrupts disabled, to minimize - * time protection is left open. - */ -static void lock_rollback(void) -{ -#ifdef CONFIG_ROLLBACK_MPU_PROTECT - mpu_lock_rollback(1); - interrupt_enable(); -#endif -} - -static void unlock_rollback(void) -{ -#ifdef CONFIG_ROLLBACK_MPU_PROTECT - interrupt_disable(); - mpu_lock_rollback(0); -#endif -} - -static void clear_rollback(struct rollback_data *data) -{ -#ifdef CONFIG_ROLLBACK_SECRET_SIZE - always_memset(data->secret, 0, sizeof(data->secret)); -#endif -} - -int read_rollback(int region, struct rollback_data *data) -{ - int offset; - int ret = EC_SUCCESS; - - offset = get_rollback_offset(region); - - unlock_rollback(); - if (crec_flash_read(offset, sizeof(*data), (char *)data)) - ret = EC_ERROR_UNKNOWN; - lock_rollback(); - - return ret; -} - -/* - * Get the most recent rollback information. - * - * @data: Returns most recent rollback data block. The data is filled - * with zeros if no valid rollback block is present - * - * Return most recent region index on success (>= 0, or 0 if no rollback - * region is valid), negative value on error. - */ -static int get_latest_rollback(struct rollback_data *data) -{ - int ret = -1; - int region; - int min_region = -1; - int max_id = -1; - struct rollback_data tmp_data; - - for (region = 0; region < ROLLBACK_REGIONS; region++) { - if (read_rollback(region, &tmp_data)) - goto failed; - - /* Check if not initialized or invalid cookie. */ - if (tmp_data.cookie != CROS_EC_ROLLBACK_COOKIE) - continue; - - if (tmp_data.id > max_id) { - min_region = region; - max_id = tmp_data.id; - } - } - - if (min_region >= 0) { - if (read_rollback(min_region, data)) - goto failed; - } else { - min_region = 0; - clear_rollback(data); - } - ret = min_region; - -failed: - clear_rollback(&tmp_data); - return ret; -} - -int32_t rollback_get_minimum_version(void) -{ - struct rollback_data data; - int32_t ret = -1; - - if (get_latest_rollback(&data) < 0) - goto failed; - ret = data.rollback_min_version; - -failed: - clear_rollback(&data); - return ret; -} - -#ifdef CONFIG_ROLLBACK_SECRET_SIZE -test_mockable int rollback_get_secret(uint8_t *secret) -{ - int ret = EC_ERROR_UNKNOWN; - struct rollback_data data; - - if (get_latest_rollback(&data) < 0) - goto failed; - - /* Check that secret is not full of 0x00 or 0xff */ - if (bytes_are_trivial(data.secret, sizeof(data.secret))) - goto failed; - - memcpy(secret, data.secret, sizeof(data.secret)); - ret = EC_SUCCESS; -failed: - clear_rollback(&data); - return ret; -} -#endif - -#ifdef CONFIG_ROLLBACK_UPDATE - -#ifdef CONFIG_ROLLBACK_SECRET_SIZE -static int add_entropy(uint8_t *dst, const uint8_t *src, - const uint8_t *add, unsigned int add_len) -{ - int ret = 0; -#ifdef CONFIG_SHA256 -BUILD_ASSERT(SHA256_DIGEST_SIZE == CONFIG_ROLLBACK_SECRET_SIZE); - struct sha256_ctx ctx; - uint8_t *hash; -#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE - uint8_t extra; - int i; -#endif - - SHA256_init(&ctx); - SHA256_update(&ctx, src, CONFIG_ROLLBACK_SECRET_SIZE); - SHA256_update(&ctx, add, add_len); -#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE - /* Add some locally produced entropy */ - for (i = 0; i < CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE; i++) { - if (!board_get_entropy(&extra, 1)) - goto failed; - SHA256_update(&ctx, &extra, 1); - } -#endif - hash = SHA256_final(&ctx); - - memcpy(dst, hash, CONFIG_ROLLBACK_SECRET_SIZE); - ret = 1; - -#ifdef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE -failed: -#endif - always_memset(&ctx, 0, sizeof(ctx)); -#else -#error "Adding entropy to secret in rollback region requires SHA256." -#endif - return ret; -} -#endif /* CONFIG_ROLLBACK_SECRET_SIZE */ - -/** - * Update rollback block. - * - * @param next_min_version Minimum version to update in rollback block. Can - * be a negative value if entropy is provided (in - * that case the current minimum version is kept). - * @param entropy Entropy to be added to rollback block secret - * (can be NULL, in that case no entropy is added). - * @param len entropy length - * - * @return EC_SUCCESS on success, EC_ERROR_* on error. - */ -static int rollback_update(int32_t next_min_version, - const uint8_t *entropy, unsigned int length) -{ - /* - * When doing flash_write operation, the data needs to be in blocks - * of CONFIG_FLASH_WRITE_SIZE, pad rollback_data as required. - */ - uint8_t block[CONFIG_FLASH_WRITE_SIZE * - DIV_ROUND_UP(sizeof(struct rollback_data), - CONFIG_FLASH_WRITE_SIZE)]; - struct rollback_data *data = (struct rollback_data *)block; - BUILD_ASSERT(sizeof(block) >= sizeof(*data)); - int erase_size, offset, region, ret; - - if (crec_flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_NOW) { - ret = EC_ERROR_ACCESS_DENIED; - goto out; - } - - /* Initialize the rest of the block. */ - memset(&block[sizeof(*data)], 0xff, sizeof(block)-sizeof(*data)); - - region = get_latest_rollback(data); - - if (region < 0) { - ret = EC_ERROR_UNKNOWN; - goto out; - } - -#ifdef CONFIG_ROLLBACK_SECRET_SIZE - if (entropy) { - /* Do not accept to decrease the value. */ - if (next_min_version < data->rollback_min_version) - next_min_version = data->rollback_min_version; - } else -#endif - { - /* Do not accept to decrease the value. */ - if (next_min_version < data->rollback_min_version) { - ret = EC_ERROR_INVAL; - goto out; - } - - /* No need to update if version is already correct. */ - if (next_min_version == data->rollback_min_version) { - ret = EC_SUCCESS; - goto out; - } - } - - /* Use the other region. */ - region = (region + 1) % ROLLBACK_REGIONS; - - offset = get_rollback_offset(region); - - data->id = data->id + 1; - data->rollback_min_version = next_min_version; -#ifdef CONFIG_ROLLBACK_SECRET_SIZE - /* - * If we are provided with some entropy, add it to secret. Otherwise, - * data.secret is left untouched and written back to the other region. - */ - if (entropy) { - if (!add_entropy(data->secret, data->secret, entropy, length)) { - ret = EC_ERROR_UNCHANGED; - goto out; - } - } -#endif - data->cookie = CROS_EC_ROLLBACK_COOKIE; - - erase_size = get_rollback_erase_size_bytes(region); - - if (erase_size < 0) { - ret = EC_ERROR_UNKNOWN; - goto out; - } - - /* Offset should never be part of active image. */ - if (system_unsafe_to_overwrite(offset, erase_size)) { - ret = EC_ERROR_UNKNOWN; - goto out; - } - - unlock_rollback(); - if (crec_flash_erase(offset, erase_size)) { - ret = EC_ERROR_UNKNOWN; - lock_rollback(); - goto out; - } - - ret = crec_flash_write(offset, sizeof(block), block); - lock_rollback(); - -out: - clear_rollback(data); - return ret; -} - -int rollback_update_version(int32_t next_min_version) -{ - return rollback_update(next_min_version, NULL, 0); -} - -int rollback_add_entropy(const uint8_t *data, unsigned int len) -{ - return rollback_update(-1, data, len); -} - -static int command_rollback_update(int argc, char **argv) -{ - int32_t min_version; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - min_version = strtoi(argv[1], &e, 0); - - if (*e || min_version < 0) - return EC_ERROR_PARAM1; - - return rollback_update_version(min_version); -} -DECLARE_CONSOLE_COMMAND(rollbackupdate, command_rollback_update, - "min_version", - "Update rollback info"); - -#ifdef CONFIG_ROLLBACK_SECRET_SIZE -static int command_rollback_add_entropy(int argc, char **argv) -{ - int len; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - len = strlen(argv[1]); - - return rollback_add_entropy(argv[1], len); -} -DECLARE_CONSOLE_COMMAND(rollbackaddent, command_rollback_add_entropy, - "data", - "Add entropy to rollback block"); - -#ifdef CONFIG_RNG -static int add_entropy_action; -static int add_entropy_rv = EC_RES_UNAVAILABLE; - -static void add_entropy_deferred(void) -{ - uint8_t rand[CONFIG_ROLLBACK_SECRET_SIZE]; - int repeat = 1; - - /* - * If asked to reset the old secret, just add entropy multiple times, - * which will ping-pong between the blocks. - */ - if (add_entropy_action == ADD_ENTROPY_RESET_ASYNC) - repeat = ROLLBACK_REGIONS; - - init_trng(); - do { - rand_bytes(rand, sizeof(rand)); - if (rollback_add_entropy(rand, sizeof(rand)) != EC_SUCCESS) { - add_entropy_rv = EC_RES_ERROR; - goto out; - } - } while (--repeat); - - add_entropy_rv = EC_RES_SUCCESS; -out: - exit_trng(); -} -DECLARE_DEFERRED(add_entropy_deferred); - -static enum ec_status -hc_rollback_add_entropy(struct host_cmd_handler_args *args) -{ - const struct ec_params_rollback_add_entropy *p = args->params; - - switch (p->action) { - case ADD_ENTROPY_ASYNC: - case ADD_ENTROPY_RESET_ASYNC: - if (add_entropy_rv == EC_RES_BUSY) - return EC_RES_BUSY; - - add_entropy_action = p->action; - add_entropy_rv = EC_RES_BUSY; - hook_call_deferred(&add_entropy_deferred_data, 0); - - return EC_RES_SUCCESS; - - case ADD_ENTROPY_GET_RESULT: - return add_entropy_rv; - } - - return EC_RES_INVALID_PARAM; -} -DECLARE_HOST_COMMAND(EC_CMD_ADD_ENTROPY, - hc_rollback_add_entropy, - EC_VER_MASK(0)); -#endif /* CONFIG_RNG */ -#endif /* CONFIG_ROLLBACK_SECRET_SIZE */ -#endif /* CONFIG_ROLLBACK_UPDATE */ - -static int command_rollback_info(int argc, char **argv) -{ - int ret = EC_ERROR_UNKNOWN; - int region, min_region; - int32_t rw_rollback_version; - struct rollback_data data; - - min_region = get_latest_rollback(&data); - - if (min_region < 0) - goto failed; - - rw_rollback_version = system_get_rollback_version(EC_IMAGE_RW); - - ccprintf("rollback minimum version: %d\n", data.rollback_min_version); - ccprintf("RW rollback version: %d\n", rw_rollback_version); - - for (region = 0; region < ROLLBACK_REGIONS; region++) { - ret = read_rollback(region, &data); - if (ret) - goto failed; - - ccprintf("rollback %d: %08x %08x %08x", - region, data.id, data.rollback_min_version, - data.cookie); -#ifdef CONFIG_ROLLBACK_SECRET_SIZE - if (!system_is_locked()) { - /* If system is unlocked, show some of the secret. */ - ccprintf(" [%02x..%02x]", data.secret[0], - data.secret[CONFIG_ROLLBACK_SECRET_SIZE-1]); - } -#endif - if (min_region == region) - ccprintf(" *"); - ccprintf("\n"); - } - ret = EC_SUCCESS; - -failed: - clear_rollback(&data); - return ret; -} -DECLARE_SAFE_CONSOLE_COMMAND(rollbackinfo, command_rollback_info, - NULL, - "Print rollback info"); - -static enum ec_status -host_command_rollback_info(struct host_cmd_handler_args *args) -{ - int ret = EC_RES_UNAVAILABLE; - struct ec_response_rollback_info *r = args->response; - int min_region; - struct rollback_data data; - - min_region = get_latest_rollback(&data); - - if (min_region < 0) - goto failed; - - r->id = data.id; - r->rollback_min_version = data.rollback_min_version; - r->rw_rollback_version = system_get_rollback_version(EC_IMAGE_RW); - - args->response_size = sizeof(*r); - ret = EC_RES_SUCCESS; - -failed: - clear_rollback(&data); - return ret; -} -DECLARE_HOST_COMMAND(EC_CMD_ROLLBACK_INFO, - host_command_rollback_info, - EC_VER_MASK(0)); diff --git a/common/rollback_private.h b/common/rollback_private.h deleted file mode 100644 index c757882f4f..0000000000 --- a/common/rollback_private.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2020 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. - */ - -/** Internal header file for rollback. - * - * EC code should not normally include this. These are exposed so they can be - * used by unit test code. - */ - -#ifndef __CROS_EC_ROLLBACK_PRIVATE_H -#define __CROS_EC_ROLLBACK_PRIVATE_H - -#include "config.h" - -/* - * Note: Do not change this structure without also updating - * common/firmware_image.S .image.ROLLBACK section. - */ -struct rollback_data { - int32_t id; /* Incrementing number to indicate which region to use. */ - int32_t rollback_min_version; -#ifdef CONFIG_ROLLBACK_SECRET_SIZE - uint8_t secret[CONFIG_ROLLBACK_SECRET_SIZE]; -#endif - /* cookie must always be last, as it validates the rest of the data. */ - uint32_t cookie; -}; - -int read_rollback(int region, struct rollback_data *data); - -#endif /* __CROS_EC_ROLLBACK_PRIVATE_H */ diff --git a/common/rsa.c b/common/rsa.c deleted file mode 100644 index 10f0afa4b4..0000000000 --- a/common/rsa.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Copyright 2014 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. - */ - -/* - * Implementation of RSA signature verification which uses a pre-processed key - * for computation. - */ - -#include "rsa.h" -#include "sha256.h" -#include "util.h" - -/** - * a[] -= mod - */ -static void sub_mod(const struct rsa_public_key *key, uint32_t *a) -{ - int64_t A = 0; - uint32_t i; - for (i = 0; i < RSANUMWORDS; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/** - * Return a[] >= mod - */ -static int ge_mod(const struct rsa_public_key *key, const uint32_t *a) -{ - uint32_t i; - for (i = RSANUMWORDS; i;) { - --i; - if (a[i] < key->n[i]) - return 0; - if (a[i] > key->n[i]) - return 1; - } - return 1; /* equal */ -} - -/** - * Montgomery c[] += a * b[] / R % mod - */ -static void mont_mul_add(const struct rsa_public_key *key, - uint32_t *c, - const uint32_t a, - const uint32_t *b) -{ - uint64_t A = mula32(a, b[0], c[0]); - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = mula32(d0, key->n[0], A); - uint32_t i; - - for (i = 1; i < RSANUMWORDS; ++i) { - A = mulaa32(a, b[i], c[i], A >> 32); - B = mulaa32(d0, key->n[i], A, B >> 32); - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) - sub_mod(key, c); -} - -#ifdef CONFIG_RSA_EXPONENT_3 -/** - * Montgomery c[] += 0 * b[] / R % mod - */ -static void mont_mul_add_0(const struct rsa_public_key *key, - uint32_t *c, - const uint32_t *b) -{ - uint32_t d0 = c[0] * key->n0inv; - uint64_t B = mula32(d0, key->n[0], c[0]); - uint32_t i; - - for (i = 1; i < RSANUMWORDS; ++i) { - B = mulaa32(d0, key->n[i], c[i], B >> 32); - c[i - 1] = (uint32_t)B; - } - - c[i - 1] = B >> 32; -} - -/* Montgomery c[] = a[] * 1 / R % key. */ -static void mont_mul_1(const struct rsa_public_key *key, - uint32_t *c, - const uint32_t *a) -{ - int i; - - for (i = 0; i < RSANUMWORDS; ++i) - c[i] = 0; - - mont_mul_add(key, c, 1, a); - for (i = 1; i < RSANUMWORDS; ++i) - mont_mul_add_0(key, c, a); -} -#endif - -/** - * Montgomery c[] = a[] * b[] / R % mod - */ -static void mont_mul(const struct rsa_public_key *key, - uint32_t *c, - const uint32_t *a, - const uint32_t *b) -{ - uint32_t i; - for (i = 0; i < RSANUMWORDS; ++i) - c[i] = 0; - - for (i = 0; i < RSANUMWORDS; ++i) - mont_mul_add(key, c, a[i], b); -} - -/** - * In-place public exponentiation. - * Exponent depends on the configuration (65537 (default), or 3). - * - * @param key Key to use in signing - * @param inout Input and output big-endian byte array - * @param workbuf32 Work buffer; caller must verify this is - * 3 x RSANUMWORDS elements long. - */ -static void mod_pow(const struct rsa_public_key *key, uint8_t *inout, - uint32_t *workbuf32) -{ - uint32_t *a = workbuf32; - uint32_t *a_r = a + RSANUMWORDS; - uint32_t *aa_r = a_r + RSANUMWORDS; - uint32_t *aaa = aa_r; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < RSANUMWORDS; ++i) { - uint32_t tmp = - (inout[((RSANUMWORDS - 1 - i) * 4) + 0] << 24) | - (inout[((RSANUMWORDS - 1 - i) * 4) + 1] << 16) | - (inout[((RSANUMWORDS - 1 - i) * 4) + 2] << 8) | - (inout[((RSANUMWORDS - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } - - /* TODO(drinkcat): This operation could be precomputed to save time. */ - mont_mul(key, a_r, a, key->rr); /* a_r = a * RR / R mod M */ -#ifdef CONFIG_RSA_EXPONENT_3 - mont_mul(key, aa_r, a_r, a_r); - mont_mul(key, a, aa_r, a_r); - mont_mul_1(key, aaa, a); -#else - /* Exponent 65537 */ - for (i = 0; i < 16; i += 2) { - mont_mul(key, aa_r, a_r, a_r); /* aa_r = a_r * a_r / R mod M */ - mont_mul(key, a_r, aa_r, aa_r);/* a_r = aa_r * aa_r / R mod M */ - } - mont_mul(key, aaa, a_r, a); /* aaa = a_r * a / R mod M */ -#endif - - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (ge_mod(key, aaa)) - sub_mod(key, aaa); - - /* Convert to bigendian byte array */ - for (i = RSANUMWORDS - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = (uint8_t)(tmp >> 24); - *inout++ = (uint8_t)(tmp >> 16); - *inout++ = (uint8_t)(tmp >> 8); - *inout++ = (uint8_t)(tmp >> 0); - } -} - -/* - * PKCS#1 padding (from the RSA PKCS#1 v2.1 standard) - * - * The DER-encoded padding is defined as follows : - * 0x00 || 0x01 || PS || 0x00 || T - * - * T: DER Encoded DigestInfo value which depends on the hash function used, - * for SHA-256: - * (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H. - * - * Length(T) = 51 octets for SHA-256 - * - * PS: octet string consisting of {Length(RSA Key) - Length(T) - 3} 0xFF - */ -static const uint8_t sha256_tail[] = { - 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, - 0x05, 0x00, 0x04, 0x20 -}; - -#define PKCS_PAD_SIZE (RSANUMBYTES - SHA256_DIGEST_SIZE) - -/** - * Check PKCS#1 padding bytes - * - * @param sig Signature to verify - * @return 0 if the padding is correct. - */ -static int check_padding(const uint8_t *sig) -{ - uint8_t *ptr = (uint8_t *)sig; - int result = 0; - int i; - - /* First 2 bytes are always 0x00 0x01 */ - result |= *ptr++ ^ 0x00; - result |= *ptr++ ^ 0x01; - - /* Then 0xff bytes until the tail */ - for (i = 0; i < PKCS_PAD_SIZE - sizeof(sha256_tail) - 2; i++) - result |= *ptr++ ^ 0xff; - - /* Check the tail. */ - result |= memcmp(ptr, sha256_tail, sizeof(sha256_tail)); - - return !!result; -} - -/* - * Verify a SHA256WithRSA PKCS#1 v1.5 signature against an expected - * SHA256 hash. - * - * @param key RSA public key - * @param signature RSA signature - * @param sha SHA-256 digest of the content to verify - * @param workbuf32 Work buffer; caller must verify this is - * 3 x RSANUMWORDS elements long. - * @return 0 on failure, 1 on success. - */ -int rsa_verify(const struct rsa_public_key *key, const uint8_t *signature, - const uint8_t *sha, uint32_t *workbuf32) -{ - uint8_t buf[RSANUMBYTES]; - - /* Copy input to local workspace. */ - memcpy(buf, signature, RSANUMBYTES); - - mod_pow(key, buf, workbuf32); /* In-place exponentiation. */ - - /* Check the PKCS#1 padding */ - if (check_padding(buf) != 0) - return 0; - - /* Check the digest. */ - if (memcmp(buf + PKCS_PAD_SIZE, sha, SHA256_DIGEST_SIZE) != 0) - return 0; - - return 1; /* All checked out OK. */ -} diff --git a/common/rtc.c b/common/rtc.c deleted file mode 100644 index 670e86d707..0000000000 --- a/common/rtc.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright 2017 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. - */ - -/* RTC cross-platform code for Chrome EC */ -/* TODO(chromium:733844): Move this conversion to kernel rtc-cros-ec driver */ - -#include "rtc.h" - -static uint16_t days_since_year_start[12] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - -/* Conversion between calendar date and seconds eclapsed since 1970-01-01 */ -uint32_t date_to_sec(struct calendar_date time) -{ - int i; - uint32_t sec; - - sec = time.year * SECS_PER_YEAR; - for (i = 0; i < time.year; i++) { - if (IS_LEAP_YEAR(i)) - sec += SECS_PER_DAY; - } - - sec += (days_since_year_start[time.month - 1] + - (IS_LEAP_YEAR(time.year) && time.month > 2) + - (time.day - 1)) * SECS_PER_DAY; - - /* add the accumulated time in seconds from 1970 to 2000 */ - return sec + SECS_TILL_YEAR_2K; -} - -struct calendar_date sec_to_date(uint32_t sec) -{ - struct calendar_date time; - int day_tmp; /* for intermediate calculation */ - int i; - - /* RTC time must be after year 2000. */ - sec = (sec > SECS_TILL_YEAR_2K) ? (sec - SECS_TILL_YEAR_2K) : 0; - - day_tmp = sec / SECS_PER_DAY; - time.year = day_tmp / 365; - day_tmp %= 365; - for (i = 0; i < time.year; i++) { - if (IS_LEAP_YEAR(i)) - day_tmp -= 1; - } - day_tmp++; - if (day_tmp <= 0) { - time.year -= 1; - day_tmp += IS_LEAP_YEAR(time.year) ? 366 : 365; - } - for (i = 1; i < 12; i++) { - if (days_since_year_start[i] + - (IS_LEAP_YEAR(time.year) && (i >= 2)) >= day_tmp) - break; - } - time.month = i; - - day_tmp -= days_since_year_start[time.month - 1] + - (IS_LEAP_YEAR(time.year) && (time.month > 2)); - time.day = day_tmp; - - return time; -} diff --git a/common/rwsig.c b/common/rwsig.c deleted file mode 100644 index 418a69c1a1..0000000000 --- a/common/rwsig.c +++ /dev/null @@ -1,336 +0,0 @@ -/* Copyright 2014 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. - */ - -/* - * Implementation of the RW firmware signature verification and jump. - */ - -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "flash.h" -#include "host_command.h" -#include "rollback.h" -#include "rsa.h" -#include "rwsig.h" -#include "sha256.h" -#include "shared_mem.h" -#include "system.h" -#include "task.h" -#include "usb_pd.h" -#include "util.h" -#include "vb21_struct.h" -#include "vboot.h" - -/* Console output macros */ -#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) - -#if !defined(CONFIG_MAPPED_STORAGE) -#error rwsig implementation assumes mem-mapped storage. -#endif - -/* RW firmware reset vector */ -static uint32_t * const rw_rst = - (uint32_t *)(CONFIG_PROGRAM_MEMORY_BASE + CONFIG_RW_MEM_OFF + 4); - - -void rwsig_jump_now(void) -{ - /* Protect all flash before jumping to RW. */ - - /* This may do nothing if WP is not enabled, RO is not protected. */ - crec_flash_set_protect(EC_FLASH_PROTECT_ALL_NOW, -1); - - /* - * For chips that does not support EC_FLASH_PROTECT_ALL_NOW, use - * EC_FLASH_PROTECT_ALL_AT_BOOT. - */ - if (system_is_locked() && - !(crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW)) { - crec_flash_set_protect(EC_FLASH_PROTECT_ALL_AT_BOOT, -1); - - if (!(crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) && - crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_AT_BOOT) { - /* - * If flash protection is still not enabled (some chips - * may be able to enable it immediately), reboot. - */ - cflush(); - system_reset(SYSTEM_RESET_HARD | - SYSTEM_RESET_PRESERVE_FLAGS); - } - } - - /* When system is locked, only boot to RW if all flash is protected. */ - if (!system_is_locked() || - crec_flash_get_protect() & EC_FLASH_PROTECT_ALL_NOW) - system_run_image_copy(EC_IMAGE_RW); -} - -/* - * Check that memory between rwdata[start] and rwdata[len-1] is filled - * with ones. data, start and len must be aligned on 4-byte boundary. - */ -static int check_padding(const uint8_t *data, - unsigned int start, unsigned int len) -{ - unsigned int i; - const uint32_t *data32 = (const uint32_t *)data; - - if ((start % 4) != 0 || (len % 4) != 0) - return 0; - - for (i = start/4; i < len/4; i++) { - if (data32[i] != 0xffffffff) - return 0; - } - - return 1; -} - -int rwsig_check_signature(void) -{ - struct sha256_ctx ctx; - int res; - const struct rsa_public_key *key; - const uint8_t *sig; - uint8_t *hash; - uint32_t *rsa_workbuf = NULL; - const uint8_t *rwdata = (uint8_t *)CONFIG_PROGRAM_MEMORY_BASE - + CONFIG_RW_MEM_OFF; - int good = 0; - - unsigned int rwlen; -#ifdef CONFIG_RWSIG_TYPE_RWSIG - const struct vb21_packed_key *vb21_key; - const struct vb21_signature *vb21_sig; -#endif -#ifdef CONFIG_ROLLBACK - int32_t rw_rollback_version; - int32_t min_rollback_version; -#endif - - /* Check if we have a RW firmware flashed */ - if (*rw_rst == 0xffffffff) - goto out; - - CPRINTS("Verifying RW image..."); - -#ifdef CONFIG_ROLLBACK - rw_rollback_version = system_get_rollback_version(EC_IMAGE_RW); - min_rollback_version = rollback_get_minimum_version(); - - if (rw_rollback_version < 0 || min_rollback_version < 0 || - rw_rollback_version < min_rollback_version) { - CPRINTS("Rollback error (%d < %d)", - rw_rollback_version, min_rollback_version); - goto out; - } -#endif - - /* Large buffer for RSA computation : could be re-use afterwards... */ - res = SHARED_MEM_ACQUIRE_CHECK(3 * RSANUMBYTES, (char **)&rsa_workbuf); - if (res) { - CPRINTS("No memory for RW verification"); - goto out; - } - -#ifdef CONFIG_RWSIG_TYPE_USBPD1 - key = (const struct rsa_public_key *)CONFIG_RO_PUBKEY_ADDR; - sig = (const uint8_t *)CONFIG_RW_SIG_ADDR; - rwlen = CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE; -#elif defined(CONFIG_RWSIG_TYPE_RWSIG) - vb21_key = vb21_get_packed_key(); - vb21_sig = (const struct vb21_signature *)CONFIG_RW_SIG_ADDR; - - if (vb21_key->c.magic != VB21_MAGIC_PACKED_KEY || - vb21_key->key_size != sizeof(struct rsa_public_key)) { - CPRINTS("Invalid key."); - goto out; - } - - key = (const struct rsa_public_key *) - ((const uint8_t *)vb21_key + vb21_key->key_offset); - - /* - * TODO(crbug.com/690773): We could verify other parameters such - * as sig_alg/hash_alg actually matches what we build for. - */ - if (vb21_sig->c.magic != VB21_MAGIC_SIGNATURE || - vb21_sig->sig_size != RSANUMBYTES || - vb21_key->sig_alg != vb21_sig->sig_alg || - vb21_key->hash_alg != vb21_sig->hash_alg || - /* Validity check signature offset and data size. */ - vb21_sig->sig_offset < sizeof(vb21_sig) || - (vb21_sig->sig_offset + RSANUMBYTES) > CONFIG_RW_SIG_SIZE || - vb21_sig->data_size > (CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)) { - CPRINTS("Invalid signature."); - goto out; - } - - sig = (const uint8_t *)vb21_sig + vb21_sig->sig_offset; - rwlen = vb21_sig->data_size; -#endif - - /* - * Check that unverified RW region is actually filled with ones. - */ - good = check_padding(rwdata, rwlen, - CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE); - if (!good) { - CPRINTS("Invalid padding."); - goto out; - } - - /* SHA-256 Hash of the RW firmware */ - SHA256_init(&ctx); - SHA256_update(&ctx, rwdata, rwlen); - hash = SHA256_final(&ctx); - - good = rsa_verify(key, sig, hash, rsa_workbuf); - if (!good) - goto out; - -#ifdef CONFIG_ROLLBACK - /* - * Signature verified: we know that rw_rollback_version is valid, check - * if rollback information should be updated. - * - * If the RW region can be protected independently - * (CONFIG_FLASH_PROTECT_RW is defined), and system is locked, we only - * increment the rollback if RW is currently protected. - * - * Otherwise, we immediately increment the rollback version. - */ - if (rw_rollback_version != min_rollback_version -#ifdef CONFIG_FLASH_PROTECT_RW - && ((!system_is_locked() || - crec_flash_get_protect() & - EC_FLASH_PROTECT_RW_NOW)) -#endif - ) { - /* - * This will fail if the rollback block is protected (RW image - * will unprotect that block later on). - */ - int ret = rollback_update_version(rw_rollback_version); - - if (ret == 0) { - CPRINTS("Rollback updated to %d", - rw_rollback_version); - } else if (ret != EC_ERROR_ACCESS_DENIED) { - CPRINTS("Rollback update error %d", ret); - good = 0; - } - } -#endif -out: - CPRINTS("RW verify %s", good ? "OK" : "FAILED"); - - if (!good) { - pd_log_event(PD_EVENT_ACC_RW_FAIL, 0, 0, NULL); - /* RW firmware is invalid : do not jump there */ - if (system_is_locked()) - system_disable_jump(); - } - if (rsa_workbuf) - shared_mem_release(rsa_workbuf); - - return good; -} - -#ifdef HAS_TASK_RWSIG -#define TASK_EVENT_ABORT TASK_EVENT_CUSTOM_BIT(0) -#define TASK_EVENT_CONTINUE TASK_EVENT_CUSTOM_BIT(1) - -static enum rwsig_status rwsig_status; - -enum rwsig_status rwsig_get_status(void) -{ - return rwsig_status; -} - -void rwsig_abort(void) -{ - task_set_event(TASK_ID_RWSIG, TASK_EVENT_ABORT); -} - -void rwsig_continue(void) -{ - task_set_event(TASK_ID_RWSIG, TASK_EVENT_CONTINUE); -} - -void rwsig_task(void *u) -{ - uint32_t evt; - - if (system_get_image_copy() != EC_IMAGE_RO) - goto exit; - - /* Stay in RO if we were asked to when reset. */ - if (system_get_reset_flags() & EC_RESET_FLAG_STAY_IN_RO) { - rwsig_status = RWSIG_ABORTED; - goto exit; - } - - rwsig_status = RWSIG_IN_PROGRESS; - if (!rwsig_check_signature()) { - rwsig_status = RWSIG_INVALID; - goto exit; - } - rwsig_status = RWSIG_VALID; - - /* Jump to RW after a timeout */ - evt = task_wait_event(CONFIG_RWSIG_JUMP_TIMEOUT); - - /* Jump now if we timed out, or were told to continue. */ - if (evt == TASK_EVENT_TIMER || evt == TASK_EVENT_CONTINUE) - rwsig_jump_now(); - else - rwsig_status = RWSIG_ABORTED; - -exit: - /* We're done, yield forever. */ - while (1) - task_wait_event(-1); -} - -enum ec_status rwsig_cmd_action(struct host_cmd_handler_args *args) -{ - const struct ec_params_rwsig_action *p = args->params; - - switch (p->action) { - case RWSIG_ACTION_ABORT: - rwsig_abort(); - break; - case RWSIG_ACTION_CONTINUE: - rwsig_continue(); - break; - default: - return EC_RES_INVALID_PARAM; - } - args->response_size = 0; - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_RWSIG_ACTION, - rwsig_cmd_action, - EC_VER_MASK(0)); - -#else /* !HAS_TASK_RWSIG */ -enum ec_status rwsig_cmd_check_status(struct host_cmd_handler_args *args) -{ - struct ec_response_rwsig_check_status *r = args->response; - - memset(r, 0, sizeof(*r)); - r->status = rwsig_check_signature(); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_RWSIG_CHECK_STATUS, - rwsig_cmd_check_status, - EC_VER_MASK(0)); -#endif diff --git a/common/sha256.c b/common/sha256.c deleted file mode 120000 index 8c0778c3e6..0000000000 --- a/common/sha256.c +++ /dev/null @@ -1 +0,0 @@ -../third_party/sha2//sha256.c
\ No newline at end of file diff --git a/common/shmalloc.c b/common/shmalloc.c deleted file mode 100644 index b1705b52d1..0000000000 --- a/common/shmalloc.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2016 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. - */ - -/* Malloc/free memory module for Chrome EC */ -#include <stdint.h> - -#include "common.h" -#include "hooks.h" -#include "link_defs.h" -#include "shared_mem.h" -#include "system.h" -#include "task.h" -#include "util.h" - -static struct mutex shmem_lock; - -#ifndef TEST_SHMALLOC -#define set_map_bit(x) -#define TEST_GLOBAL static -#else -#define TEST_GLOBAL -#endif - -/* - * At the beginning there is a single free memory chunk which includes all - * memory available in the system. It then gets fragmented/defragmented based - * on actual allocations/releases. - */ -TEST_GLOBAL struct shm_buffer *free_buf_chain; - -/* At the beginning there is no allocated buffers */ -TEST_GLOBAL struct shm_buffer *allocced_buf_chain; - -/* The size of the biggest ever allocated buffer. */ -static int max_allocated_size; - -static void shared_mem_init(void) -{ - /* - * Use all the RAM we can. The shared memory buffer is the last thing - * allocated from the start of RAM, so we can use everything up to the - * jump data at the end of RAM. - */ - free_buf_chain = (struct shm_buffer *)__shared_mem_buf; - free_buf_chain->next_buffer = NULL; - free_buf_chain->prev_buffer = NULL; - free_buf_chain->buffer_size = system_usable_ram_end() - - (uintptr_t)__shared_mem_buf; -} -DECLARE_HOOK(HOOK_INIT, shared_mem_init, HOOK_PRIO_FIRST); - -/* Called with the mutex lock acquired. */ -static void do_release(struct shm_buffer *ptr) -{ - struct shm_buffer *pfb; - struct shm_buffer *top; - size_t released_size; - - /* Take the buffer out of the allocated buffers chain. */ - if (ptr == allocced_buf_chain) { - if (ptr->next_buffer) { - set_map_bit(BIT(20)); - ptr->next_buffer->prev_buffer = NULL; - } else { - set_map_bit(BIT(21)); - } - allocced_buf_chain = ptr->next_buffer; - } else { - /* - * Saninty check: verify that the buffer is in the allocated - * buffers chain. - */ - for (pfb = allocced_buf_chain->next_buffer; - pfb; - pfb = pfb->next_buffer) - if (pfb == ptr) - break; - if (!pfb) - return; - - ptr->prev_buffer->next_buffer = ptr->next_buffer; - if (ptr->next_buffer) { - set_map_bit(BIT(22)); - ptr->next_buffer->prev_buffer = ptr->prev_buffer; - } else { - set_map_bit(BIT(23)); - } - } - - /* - * Let's bring the released buffer back into the fold. Cache its size - * for quick reference. - */ - released_size = ptr->buffer_size; - if (!free_buf_chain) { - /* - * All memory had been allocated - this buffer is going to be - * the only available free space. - */ - set_map_bit(BIT(0)); - free_buf_chain = ptr; - free_buf_chain->buffer_size = released_size; - free_buf_chain->next_buffer = NULL; - free_buf_chain->prev_buffer = NULL; - return; - } - - if (ptr < free_buf_chain) { - /* - * Insert this buffer in the beginning of the chain, possibly - * merging it with the first buffer of the chain. - */ - pfb = (struct shm_buffer *)((uintptr_t)ptr + released_size); - if (pfb == free_buf_chain) { - set_map_bit(BIT(1)); - /* Merge the two buffers. */ - ptr->buffer_size = free_buf_chain->buffer_size + - released_size; - ptr->next_buffer = - free_buf_chain->next_buffer; - } else { - set_map_bit(BIT(2)); - ptr->buffer_size = released_size; - ptr->next_buffer = free_buf_chain; - free_buf_chain->prev_buffer = ptr; - } - if (ptr->next_buffer) { - set_map_bit(BIT(3)); - ptr->next_buffer->prev_buffer = ptr; - } else { - set_map_bit(BIT(4)); - } - ptr->prev_buffer = NULL; - free_buf_chain = ptr; - return; - } - - /* - * Need to merge the new free buffer into the existing chain. Find a - * spot for it, it should be above the highest address buffer which is - * still below the new one. - */ - pfb = free_buf_chain; - while (pfb->next_buffer && (pfb->next_buffer < ptr)) - pfb = pfb->next_buffer; - - top = (struct shm_buffer *)((uintptr_t)pfb + pfb->buffer_size); - if (top == ptr) { - /* - * The returned buffer is adjacent to an existing free buffer, - * below it, merge the two buffers. - */ - pfb->buffer_size += released_size; - - /* - * Is the returned buffer the exact gap between two free - * buffers? - */ - top = (struct shm_buffer *)((uintptr_t)ptr + released_size); - if (top == pfb->next_buffer) { - /* Yes, it is. */ - pfb->buffer_size += pfb->next_buffer->buffer_size; - pfb->next_buffer = - pfb->next_buffer->next_buffer; - if (pfb->next_buffer) { - set_map_bit(BIT(5)); - pfb->next_buffer->prev_buffer = pfb; - } else { - set_map_bit(BIT(6)); - } - } - return; - } - - top = (struct shm_buffer *)((uintptr_t)ptr + released_size); - if (top == pfb->next_buffer) { - /* The new buffer is adjacent with the one right above it. */ - set_map_bit(BIT(7)); - ptr->buffer_size = released_size + - pfb->next_buffer->buffer_size; - ptr->next_buffer = pfb->next_buffer->next_buffer; - } else { - /* Just include the new free buffer into the chain. */ - set_map_bit(BIT(8)); - ptr->next_buffer = pfb->next_buffer; - ptr->buffer_size = released_size; - } - ptr->prev_buffer = pfb; - pfb->next_buffer = ptr; - if (ptr->next_buffer) { - set_map_bit(BIT(9)); - ptr->next_buffer->prev_buffer = ptr; - } else { - set_map_bit(BIT(10)); - } -} - -/* Called with the mutex lock acquired. */ -static int do_acquire(int size, struct shm_buffer **dest_ptr) -{ - int headroom = 0x10000000; /* we'll never have this much. */ - struct shm_buffer *pfb; - struct shm_buffer *candidate = 0; - - /* To keep things simple let's align the size. */ - size = (size + sizeof(int) - 1) & ~(sizeof(int) - 1); - - /* And let's allocate room to fit the buffer header. */ - size += sizeof(struct shm_buffer); - - pfb = free_buf_chain; - while (pfb) { - if ((pfb->buffer_size >= size) && - ((pfb->buffer_size - size) < headroom)) { - /* this is a new candidate. */ - headroom = pfb->buffer_size - size; - candidate = pfb; - } - pfb = pfb->next_buffer; - } - - if (!candidate) { - set_map_bit(BIT(11)); - return EC_ERROR_BUSY; - } - - *dest_ptr = candidate; - - /* Now let's take the candidate out of the free buffer chain. */ - if (headroom <= sizeof(struct shm_buffer)) { - /* - * The entire buffer should be allocated, there is no need to - * re-define its tail as a new free buffer. - */ - if (candidate == free_buf_chain) { - /* - * The next buffer becomes the head of the free buffer - * chain. - */ - free_buf_chain = candidate->next_buffer; - if (free_buf_chain) { - set_map_bit(BIT(12)); - free_buf_chain->prev_buffer = 0; - } else { - set_map_bit(BIT(13)); - } - } else { - candidate->prev_buffer->next_buffer = - candidate->next_buffer; - if (candidate->next_buffer) { - set_map_bit(BIT(14)); - candidate->next_buffer->prev_buffer = - candidate->prev_buffer; - } else { - set_map_bit(BIT(15)); - } - } - return EC_SUCCESS; - } - - candidate->buffer_size = size; - - /* Candidate's tail becomes a new free buffer. */ - pfb = (struct shm_buffer *)((uintptr_t)candidate + size); - pfb->buffer_size = headroom; - pfb->next_buffer = candidate->next_buffer; - pfb->prev_buffer = candidate->prev_buffer; - - if (pfb->next_buffer) { - set_map_bit(BIT(16)); - pfb->next_buffer->prev_buffer = pfb; - } else { - set_map_bit(BIT(17)); - } - - if (candidate == free_buf_chain) { - set_map_bit(BIT(18)); - free_buf_chain = pfb; - } else { - set_map_bit(BIT(19)); - pfb->prev_buffer->next_buffer = pfb; - } - return EC_SUCCESS; -} - -int shared_mem_size(void) -{ - struct shm_buffer *pfb; - size_t max_available = 0; - - mutex_lock(&shmem_lock); - - /* Find the maximum available buffer size. */ - pfb = free_buf_chain; - while (pfb) { - if (pfb->buffer_size > max_available) - max_available = pfb->buffer_size; - pfb = pfb->next_buffer; - } - - mutex_unlock(&shmem_lock); - /* Leave room for shmem header */ - max_available -= sizeof(struct shm_buffer); - return max_available; -} - -int shared_mem_acquire(int size, char **dest_ptr) -{ - int rv; - struct shm_buffer *new_buf; - - *dest_ptr = NULL; - - if (in_interrupt_context()) - return EC_ERROR_INVAL; - - if (!free_buf_chain) - return EC_ERROR_BUSY; - - mutex_lock(&shmem_lock); - rv = do_acquire(size, &new_buf); - if (rv == EC_SUCCESS) { - new_buf->next_buffer = allocced_buf_chain; - new_buf->prev_buffer = NULL; - if (allocced_buf_chain) - allocced_buf_chain->prev_buffer = new_buf; - - allocced_buf_chain = new_buf; - - *dest_ptr = (void *)(new_buf + 1); - - if (size > max_allocated_size) - max_allocated_size = size; - } - mutex_unlock(&shmem_lock); - - return rv; -} - -void shared_mem_release(void *ptr) -{ - if (in_interrupt_context()) - return; - - mutex_lock(&shmem_lock); - do_release((struct shm_buffer *)ptr - 1); - mutex_unlock(&shmem_lock); -} - -#ifdef CONFIG_CMD_SHMEM - -static int command_shmem(int argc, char **argv) -{ - size_t allocated_size; - size_t free_size; - size_t max_free; - struct shm_buffer *buf; - - allocated_size = free_size = max_free = 0; - - mutex_lock(&shmem_lock); - - for (buf = free_buf_chain; buf; buf = buf->next_buffer) { - size_t buf_room; - - buf_room = buf->buffer_size; - - free_size += buf_room; - if (buf_room > max_free) - max_free = buf_room; - } - - for (buf = allocced_buf_chain; buf; - buf = buf->next_buffer) - allocated_size += buf->buffer_size; - - mutex_unlock(&shmem_lock); - - ccprintf("Total: %6zd\n", allocated_size + free_size); - ccprintf("Allocated: %6zd\n", allocated_size); - ccprintf("Free: %6zd\n", free_size); - ccprintf("Max free buf: %6zd\n", max_free); - ccprintf("Max allocated: %6d\n", max_allocated_size); - return EC_SUCCESS; -} -DECLARE_SAFE_CONSOLE_COMMAND(shmem, command_shmem, - NULL, - "Print shared memory stats"); - -#endif /* CONFIG_CMD_SHMEM ^^^^^^^ defined */ diff --git a/common/spi_commands.c b/common/spi_commands.c deleted file mode 100644 index 1a70a5be82..0000000000 --- a/common/spi_commands.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2015 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. - * - * SPI transfer command for debugging SPI devices. - */ - -#include "common.h" -#include "console.h" -#include "spi.h" -#include "timer.h" -#include "util.h" - -static int command_spixfer(int argc, char **argv) -{ - int dev_id; - uint8_t offset; - int v = 0; - uint8_t data[32]; - char *e; - int rv = 0; - - if (argc != 5) - return EC_ERROR_PARAM_COUNT; - - dev_id = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - offset = strtoi(argv[3], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - - v = strtoi(argv[4], &e, 0); - if (*e) - return EC_ERROR_PARAM4; - - if (strcasecmp(argv[1], "rlen") == 0) { - uint8_t cmd = 0x80 | offset; - - /* Arbitrary length read; param4 = len */ - if (v < 0 || v > sizeof(data)) - return EC_ERROR_PARAM4; - - rv = spi_transaction(&spi_devices[dev_id], &cmd, 1, data, v); - - if (!rv) - ccprintf("Data: %ph\n", HEX_BUF(data, v)); - - } else if (strcasecmp(argv[1], "w") == 0) { - /* 8-bit write */ - uint8_t cmd[2] = { offset, v }; - - rv = spi_transaction(&spi_devices[dev_id], cmd, 2, NULL, 0); - - /* - * Some SPI device needs a delay before accepting other - * commands, otherwise the write might be ignored. - */ - msleep(1); - } else { - return EC_ERROR_PARAM1; - } - - return rv; -} -DECLARE_CONSOLE_COMMAND(spixfer, command_spixfer, - "rlen/w id offset [value | len]", - "Read write spi. id is spi_devices array index"); - diff --git a/common/spi_flash.c b/common/spi_flash.c deleted file mode 100644 index e202e1e17d..0000000000 --- a/common/spi_flash.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright 2014 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. - * - * SPI flash driver for Chrome EC. - */ - -#include "common.h" -#include "console.h" -#include "host_command.h" -#include "shared_mem.h" -#include "spi.h" -#include "spi_flash.h" -#include "spi_flash_reg.h" -#include "timer.h" -#include "util.h" -#include "watchdog.h" -#include "ec_commands.h" -#include "flash.h" - -/* - * Time to sleep when chip is busy - */ -#define SPI_FLASH_SLEEP_USEC 100 - -/* - * This is the max time for 32kb flash erase - */ -#define SPI_FLASH_TIMEOUT_USEC (800*MSEC) - -/* Internal buffer used by SPI flash driver */ -static uint8_t buf[SPI_FLASH_MAX_MESSAGE_SIZE]; - -/** - * Waits for chip to finish current operation. Must be called after - * erase/write operations to ensure successive commands are executed. - * - * @return EC_SUCCESS or error on timeout - */ -int spi_flash_wait(void) -{ - timestamp_t timeout; - - timeout.val = get_time().val + SPI_FLASH_TIMEOUT_USEC; - /* Wait until chip is not busy */ - while (spi_flash_get_status1() & SPI_FLASH_SR1_BUSY) { - usleep(SPI_FLASH_SLEEP_USEC); - - if (get_time().val > timeout.val) - return EC_ERROR_TIMEOUT; - } - - return EC_SUCCESS; -} - -/** - * Set the write enable latch - */ -static int spi_flash_write_enable(void) -{ - uint8_t cmd = SPI_FLASH_WRITE_ENABLE; - return spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, NULL, 0); -} - -/** - * Returns the contents of SPI flash status register 1 - * @return register contents or 0xff on error - */ -uint8_t spi_flash_get_status1(void) -{ - uint8_t cmd = SPI_FLASH_READ_SR1; - uint8_t resp; - - if (spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, &resp, 1) != EC_SUCCESS) - return 0xff; - - return resp; -} - -/** - * Returns the contents of SPI flash status register 2 - * @return register contents or 0xff on error - */ -uint8_t spi_flash_get_status2(void) -{ - uint8_t cmd = SPI_FLASH_READ_SR2; - uint8_t resp; - - /* Second status register not present */ -#ifndef CONFIG_SPI_FLASH_HAS_SR2 - return 0; -#endif - - if (spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, &resp, 1) != EC_SUCCESS) - return 0xff; - - return resp; -} - -/** - * Sets the SPI flash status registers (non-volatile bits only) - * Pass reg2 == -1 to only set reg1. - * - * @param reg1 Status register 1 - * @param reg2 Status register 2 (optional) - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_set_status(int reg1, int reg2) -{ - uint8_t cmd[3] = {SPI_FLASH_WRITE_SR, reg1, reg2}; - int rv = EC_SUCCESS; - - /* fail if both HW pin is asserted and SRP(s) is 1 */ - if (spi_flash_check_wp() != SPI_WP_NONE && - (crec_flash_get_protect() & - EC_FLASH_PROTECT_GPIO_ASSERTED) != 0) - return EC_ERROR_ACCESS_DENIED; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Second status register not present */ -#ifndef CONFIG_SPI_FLASH_HAS_SR2 - reg2 = -1; -#endif - - if (reg2 == -1) - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 2, NULL, 0); - else - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 3, NULL, 0); - if (rv) - return rv; - - /* SRP update takes up to 10 ms, so wait for transaction to finish */ - spi_flash_wait(); - - return rv; -} - -/** - * Returns the content of SPI flash - * - * @param buf_usr Buffer to write flash contents - * @param offset Flash offset to start reading from - * @param bytes Number of bytes to read. - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_read(uint8_t *buf_usr, unsigned int offset, unsigned int bytes) -{ - int i, read_size, ret, spi_addr; - uint8_t cmd[4]; - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - cmd[0] = SPI_FLASH_READ; - for (i = 0; i < bytes; i += read_size) { - spi_addr = offset + i; - cmd[1] = (spi_addr >> 16) & 0xFF; - cmd[2] = (spi_addr >> 8) & 0xFF; - cmd[3] = spi_addr & 0xFF; - read_size = MIN((bytes - i), SPI_FLASH_MAX_READ_SIZE); - ret = spi_transaction(SPI_FLASH_DEVICE, - cmd, - 4, - buf_usr + i, - read_size); - if (ret != EC_SUCCESS) - break; - msleep(CONFIG_SPI_FLASH_READ_WAIT_MS); - } - return ret; -} - -/** - * Erase a block of SPI flash. - * - * @param offset Flash offset to start erasing - * @param block Block size in kb (4 or 32) - * - * @return EC_SUCCESS, or non-zero if any error. - */ -static int spi_flash_erase_block(unsigned int offset, unsigned int block) -{ - uint8_t cmd[4]; - int rv = EC_SUCCESS; - - /* Invalid block size */ - if (block != 4 && block != 32) - return EC_ERROR_INVAL; - - /* Not block aligned */ - if ((offset % (block * 1024)) != 0) - return EC_ERROR_INVAL; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Compose instruction */ - cmd[0] = (block == 4) ? SPI_FLASH_ERASE_4KB : SPI_FLASH_ERASE_32KB; - cmd[1] = (offset >> 16) & 0xFF; - cmd[2] = (offset >> 8) & 0xFF; - cmd[3] = offset & 0xFF; - - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 4, NULL, 0); - if (rv) - return rv; - - /* Wait for previous operation to complete */ - return spi_flash_wait(); -} - -/** - * Erase SPI flash. - * - * @param offset Flash offset to start erasing - * @param bytes Number of bytes to erase - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_erase(unsigned int offset, unsigned int bytes) -{ - int rv = EC_SUCCESS; - - /* Invalid input */ - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Not aligned to sector (4kb) */ - if (offset % 4096 || bytes % 4096) - return EC_ERROR_INVAL; - - /* Largest unit is block (32kb) */ - if (offset % (32 * 1024) == 0) { - while (bytes != (bytes % (32 * 1024))) { - rv = spi_flash_erase_block(offset, 32); - if (rv) - return rv; - - bytes -= 32 * 1024; - offset += 32 * 1024; - /* - * Refresh watchdog since we may be erasing a large - * number of blocks. - */ - watchdog_reload(); - } - } - - /* Largest unit is sector (4kb) */ - while (bytes != (bytes % (4 * 1024))) { - rv = spi_flash_erase_block(offset, 4); - if (rv) - return rv; - - bytes -= 4 * 1024; - offset += 4 * 1024; - } - - return rv; -} - -/** - * Write to SPI flash. Assumes already erased. - * Limited to SPI_FLASH_MAX_WRITE_SIZE by chip. - * - * @param offset Flash offset to write - * @param bytes Number of bytes to write - * @param data Data to write to flash - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_write(unsigned int offset, unsigned int bytes, - const uint8_t *data) -{ - int rv, write_size; - - /* Invalid input */ - if (!data || offset + bytes > CONFIG_FLASH_SIZE_BYTES || - bytes > SPI_FLASH_MAX_WRITE_SIZE) - return EC_ERROR_INVAL; - - while (bytes > 0) { - watchdog_reload(); - /* Write length can not go beyond the end of the flash page */ - write_size = MIN(bytes, SPI_FLASH_MAX_WRITE_SIZE - - (offset & (SPI_FLASH_MAX_WRITE_SIZE - 1))); - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Copy data to send buffer; buffers may overlap */ - memmove(buf + 4, data, write_size); - - /* Compose instruction */ - buf[0] = SPI_FLASH_PAGE_PRGRM; - buf[1] = (offset) >> 16; - buf[2] = (offset) >> 8; - buf[3] = offset; - - rv = spi_transaction(SPI_FLASH_DEVICE, - buf, 4 + write_size, NULL, 0); - if (rv) - return rv; - - data += write_size; - offset += write_size; - bytes -= write_size; - } - - /* Wait for previous operation to complete */ - return spi_flash_wait(); -} - -/** - * Gets the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity) - * - * @param dest Destination buffer; must be 3 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_jedec_id(uint8_t *dest) -{ - uint8_t cmd = SPI_FLASH_JEDEC_ID; - - return spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, dest, 3); -} - -/** - * Gets the SPI flash manufacturer and device ID - * - * @param dest Destination buffer; must be 2 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_mfr_dev_id(uint8_t *dest) -{ - uint8_t cmd[4] = {SPI_FLASH_MFR_DEV_ID, 0, 0, 0}; - - return spi_transaction(SPI_FLASH_DEVICE, cmd, sizeof(cmd), dest, 2); -} - -/** - * Gets the SPI flash unique ID (serial) - * - * @param dest Destination buffer; must be 8 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_unique_id(uint8_t *dest) -{ - uint8_t cmd[5] = {SPI_FLASH_UNIQUE_ID, 0, 0, 0, 0}; - - return spi_transaction(SPI_FLASH_DEVICE, cmd, sizeof(cmd), dest, 8); -} - -/** - * Check for SPI flash status register write protection - * Cannot sample WP pin, so caller should sample it if necessary, if - * SPI_WP_HARDWARE is returned. - * - * @return enum spi_flash_wp status based on protection - */ -enum spi_flash_wp spi_flash_check_wp(void) -{ - int sr1_prot = spi_flash_get_status1() & SPI_FLASH_SR1_SRP0; - int sr2_prot = spi_flash_get_status2() & SPI_FLASH_SR2_SRP1; - - if (sr2_prot) - return sr1_prot ? SPI_WP_PERMANENT : SPI_WP_POWER_CYCLE; - else if (sr1_prot) - return SPI_WP_HARDWARE; - - return SPI_WP_NONE; -} - -/** - * Set SPI flash status register write protection - * - * @param wp Status register write protection mode - * - * @return EC_SUCCESS for no protection, or non-zero if error. - */ -int spi_flash_set_wp(enum spi_flash_wp w) -{ - int sr1 = spi_flash_get_status1(); - int sr2 = spi_flash_get_status2(); - - switch (w) { - case SPI_WP_NONE: - sr1 &= ~SPI_FLASH_SR1_SRP0; - sr2 &= ~SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_HARDWARE: - sr1 |= SPI_FLASH_SR1_SRP0; - sr2 &= ~SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_POWER_CYCLE: - sr1 &= ~SPI_FLASH_SR1_SRP0; - sr2 |= SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_PERMANENT: - sr1 |= SPI_FLASH_SR1_SRP0; - sr2 |= SPI_FLASH_SR2_SRP1; - break; - default: - return EC_ERROR_INVAL; - } - - return spi_flash_set_status(sr1, sr2); -} - -/** - * Check for SPI flash block write protection - * - * @param offset Flash block offset to check - * @param bytes Flash block length to check - * - * @return EC_SUCCESS for no protection, or non-zero if error. - */ -int spi_flash_check_protect(unsigned int offset, unsigned int bytes) -{ - uint8_t sr1 = spi_flash_get_status1(); - uint8_t sr2 = spi_flash_get_status2(); - unsigned int start; - unsigned int len; - int rv = EC_SUCCESS; - - /* Invalid value */ - if (sr1 == 0xff || sr2 == 0xff || - offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Compute current protect range */ - rv = spi_flash_reg_to_protect(sr1, sr2, &start, &len); - if (rv) - return rv; - - /* Check if ranges overlap */ - if (MAX(start, offset) < MIN(start + len, offset + bytes)) - return EC_ERROR_ACCESS_DENIED; - - return EC_SUCCESS; -} - -/** - * Set SPI flash block write protection - * If offset == bytes == 0, remove protection. - * - * @param offset Flash block offset to protect - * @param bytes Flash block length to protect - * - * @return EC_SUCCESS, or non-zero if error. - */ -int spi_flash_set_protect(unsigned int offset, unsigned int bytes) -{ - int rv; - uint8_t sr1 = spi_flash_get_status1(); - uint8_t sr2 = spi_flash_get_status2(); - - /* Invalid values */ - if (sr1 == 0xff || sr2 == 0xff || - offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Compute desired protect range */ - rv = spi_flash_protect_to_reg(offset, bytes, &sr1, &sr2); - if (rv) - return rv; - - return spi_flash_set_status(sr1, sr2); -} - -static int command_spi_flashinfo(int argc, char **argv) -{ - uint8_t jedec[3]; - uint8_t unique[8]; - int rv; - - /* TODO(tomhughes): use board function to get devices. */ - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - spi_flash_get_jedec_id(jedec); - spi_flash_get_unique_id(unique); - - ccprintf("Manufacturer ID: %02x\nDevice ID: %02x %02x\n", - jedec[0], jedec[1], jedec[2]); - ccprintf("Unique ID: %02x %02x %02x %02x %02x %02x %02x %02x\n", - unique[0], unique[1], unique[2], unique[3], - unique[4], unique[5], unique[6], unique[7]); - ccprintf("Capacity: %4d kB\n", SPI_FLASH_SIZE(jedec[2]) / 1024); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spi_flashinfo, command_spi_flashinfo, - NULL, - "Print SPI flash info"); - -#ifdef CONFIG_HOSTCMD_FLASH_SPI_INFO -static enum ec_status flash_command_spi_info(struct host_cmd_handler_args *args) -{ - struct ec_response_flash_spi_info *r = args->response; - - spi_flash_get_jedec_id(r->jedec); - r->reserved0 = 0; - spi_flash_get_mfr_dev_id(r->mfr_dev_id); - r->sr1 = spi_flash_get_status1(); - r->sr2 = spi_flash_get_status2(); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_SPI_INFO, - flash_command_spi_info, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_FLASH_SPI_INFO */ - -#ifdef CONFIG_CMD_SPI_FLASH -static int command_spi_flasherase(int argc, char **argv) -{ - int offset = -1; - int bytes = 4096; - int rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Chip has protection */ - if (spi_flash_check_protect(offset, bytes)) - return EC_ERROR_ACCESS_DENIED; - - ccprintf("Erasing %d bytes at 0x%x...\n", bytes, offset); - return spi_flash_erase(offset, bytes); -} -DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase, - "offset [bytes]", - "Erase flash"); - -static int command_spi_flashwrite(int argc, char **argv) -{ - int offset = -1; - int bytes = SPI_FLASH_MAX_WRITE_SIZE; - int write_len; - int rv = EC_SUCCESS; - int i; - - rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Chip has protection */ - if (spi_flash_check_protect(offset, bytes)) - return EC_ERROR_ACCESS_DENIED; - - /* Fill the data buffer with a pattern */ - for (i = 0; i < SPI_FLASH_MAX_WRITE_SIZE; i++) - buf[i] = i; - - ccprintf("Writing %d bytes to 0x%x...\n", bytes, offset); - while (bytes > 0) { - /* First write multiples of 256, then (bytes % 256) last */ - write_len = ((bytes % SPI_FLASH_MAX_WRITE_SIZE) == bytes) ? - bytes : SPI_FLASH_MAX_WRITE_SIZE; - - /* Perform write */ - rv = spi_flash_write(offset, write_len, buf); - if (rv) - return rv; - - offset += write_len; - bytes -= write_len; - } - - ASSERT(bytes == 0); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spi_flashwrite, command_spi_flashwrite, - "offset [bytes]", - "Write pattern to flash"); - -static int command_spi_flashread(int argc, char **argv) -{ - int i; - int offset = -1; - int bytes = -1; - int read_len; - int rv; - - rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Can't read past size of memory */ - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - ccprintf("Reading %d bytes from 0x%x...\n", bytes, offset); - /* Read <= 256 bytes to avoid allocating another buffer */ - while (bytes > 0) { - watchdog_reload(); - - /* First read (bytes % 256), then in multiples of 256 */ - read_len = (bytes % SPI_FLASH_MAX_READ_SIZE) ? - (bytes % SPI_FLASH_MAX_READ_SIZE) : - SPI_FLASH_MAX_READ_SIZE; - - rv = spi_flash_read(buf, offset, read_len); - if (rv) - return rv; - - for (i = 0; i < read_len; i++) { - if (i % 16 == 0) - ccprintf("%02x:", offset + i); - - ccprintf(" %02x", buf[i]); - - if (i % 16 == 15 || i == read_len - 1) - ccputs("\n"); - } - - offset += read_len; - bytes -= read_len; - } - - ASSERT(bytes == 0); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(spi_flashread, command_spi_flashread, - "offset bytes", - "Read flash"); - -static int command_spi_flashread_sr(int argc, char **argv) -{ - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Status Register 1: 0x%02x\n", spi_flash_get_status1()); - ccprintf("Status Register 2: 0x%02x\n", spi_flash_get_status2()); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(spi_flash_rsr, command_spi_flashread_sr, - NULL, - "Read status registers"); - -static int command_spi_flashwrite_sr(int argc, char **argv) -{ - int val1 = 0; - int val2 = 0; - int rv = parse_offset_size(argc, argv, 1, &val1, &val2); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Writing 0x%02x to status register 1, ", val1); - ccprintf("0x%02x to status register 2...\n", val2); - return spi_flash_set_status(val1, val2); -} -DECLARE_CONSOLE_COMMAND(spi_flash_wsr, command_spi_flashwrite_sr, - "value1 value2", - "Write to status registers"); - -static int command_spi_flashprotect(int argc, char **argv) -{ - int val1 = 0; - int val2 = 0; - int rv = parse_offset_size(argc, argv, 1, &val1, &val2); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Setting protection for 0x%06x to 0x%06x\n", val1, val1+val2); - return spi_flash_set_protect(val1, val2); -} -DECLARE_CONSOLE_COMMAND(spi_flash_prot, command_spi_flashprotect, - "offset len", - "Set block protection"); -#endif diff --git a/common/spi_flash_reg.c b/common/spi_flash_reg.c deleted file mode 100644 index ee8d31fa06..0000000000 --- a/common/spi_flash_reg.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2015 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. - * - * SPI flash protection register translation functions for Chrome OS EC. - */ - -#include "common.h" -#include "spi_flash_reg.h" -#include "util.h" - -/* Bit state for protect range table */ -enum bit_state { - OFF = 0, - ON = 1, - IGN = -1, /* Don't care / Ignore */ -}; - -struct protect_range { - enum bit_state cmp; - enum bit_state sec; - enum bit_state tb; - enum bit_state bp[3]; /* Ordered {BP2, BP1, BP0} */ - uint32_t protect_start; - uint32_t protect_len; -}; - -/* Compare macro for (x =? b) for 'IGN' comparison */ -#define COMPARE_BIT(a, b) ((a) != IGN && (a) != !!(b)) -/* Assignment macro where 'IGN' = 0 */ -#define GET_BIT(a) ((a) == IGN ? 0 : (a)) - -/* - * Define flags and protect table for each SPI ROM part. It's not necessary - * to define all ranges in the datasheet since we'll usually protect only - * none or half of the ROM. The table is searched sequentially, so ordering - * according to likely configurations improves performance slightly. - */ -#if defined(CONFIG_SPI_FLASH_W25X40) || defined(CONFIG_SPI_FLASH_GD25Q41B) -static const struct protect_range spi_flash_protect_ranges[] = { - { IGN, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */ - { IGN, IGN, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */ - { IGN, IGN, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/4 */ -}; - -#elif defined(CONFIG_SPI_FLASH_W25Q40) || defined(CONFIG_SPI_FLASH_GD25LQ40) -/* Verified for W25Q40BV and W25Q40EW */ -/* For GD25LQ40, BP3 and BP4 have same meaning as TB and SEC */ -static const struct protect_range spi_flash_protect_ranges[] = { - /* CMP = 0 */ - { 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */ - { 0, 0, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/4 */ - { 0, 0, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */ - /* CMP = 1 */ - { 1, 0, 0, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */ - { 1, 0, IGN, { 1, IGN, IGN }, 0, 0 }, /* None (W25Q40EW only) */ -}; - -#elif defined(CONFIG_SPI_FLASH_W25Q64) -static const struct protect_range spi_flash_protect_ranges[] = { - { 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */ - { 0, 0, 1, { 1, 1, 0 }, 0, 0x400000 }, /* Lower 1/2 */ - { 0, 0, 1, { 1, 0, 1 }, 0, 0x200000 }, /* Lower 1/4 */ -}; - -#elif defined(CONFIG_SPI_FLASH_W25Q80) -static const struct protect_range spi_flash_protect_ranges[] = { - /* CMP = 0 */ - { 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */ - { 0, 0, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/8 */ - { 0, 0, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/4 */ - { 0, 0, 1, { 1, 0, 0 }, 0, 0x80000 }, /* Lower 1/2 */ -}; -#elif defined(CONFIG_SPI_FLASH_W25Q128) -static const struct protect_range spi_flash_protect_ranges[] = { - /* CMP = 0 */ - { 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */ - { 0, 0, 1, { 1, 0, 0 }, 0, 0x20000 }, /* Lower 1/8 */ - { 0, 0, 1, { 1, 0, 1 }, 0, 0x40000 }, /* Lower 1/4 */ - { 0, 0, 1, { 1, 1, 0 }, 0, 0x80000 }, /* Lower 1/2 */ -}; -#endif - -/** - * Computes block write protection range from registers - * Returns start == len == 0 for no protection - * - * @param sr1 Status register 1 - * @param sr2 Status register 2 - * @param start Output pointer for protection start offset - * @param len Output pointer for protection length - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start, - unsigned int *len) -{ - const struct protect_range *range; - int i; - uint8_t cmp; - uint8_t sec; - uint8_t tb; - uint8_t bp; - - /* Determine flags */ - cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0; - sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0; - tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0; - bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0)) - >> 2; - - /* Bad pointers or invalid data */ - if (!start || !len || sr1 == 0xff || sr2 == 0xff) - return EC_ERROR_INVAL; - - for (i = 0; i < ARRAY_SIZE(spi_flash_protect_ranges); ++i) { - range = &spi_flash_protect_ranges[i]; - if (COMPARE_BIT(range->cmp, cmp)) - continue; - if (COMPARE_BIT(range->sec, sec)) - continue; - if (COMPARE_BIT(range->tb, tb)) - continue; - if (COMPARE_BIT(range->bp[0], bp & 0x4)) - continue; - if (COMPARE_BIT(range->bp[1], bp & 0x2)) - continue; - if (COMPARE_BIT(range->bp[2], bp & 0x1)) - continue; - - *start = range->protect_start; - *len = range->protect_len; - return EC_SUCCESS; - } - - /* Invalid range, or valid range missing from our table */ - return EC_ERROR_INVAL; -} - -/** - * Computes block write protection registers from range - * - * @param start Desired protection start offset - * @param len Desired protection length - * @param sr1 Output pointer for status register 1 - * @param sr2 Output pointer for status register 2 - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_protect_to_reg(unsigned int start, unsigned int len, uint8_t *sr1, - uint8_t *sr2) -{ - const struct protect_range *range; - int i; - char cmp = 0; - char sec = 0; - char tb = 0; - char bp = 0; - - /* Bad pointers */ - if (!sr1 || !sr2) - return EC_ERROR_INVAL; - - /* Invalid data */ - if ((start && !len) || start + len > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - for (i = 0; i < ARRAY_SIZE(spi_flash_protect_ranges); ++i) { - range = &spi_flash_protect_ranges[i]; - if (range->protect_start == start && - range->protect_len == len) { - cmp = GET_BIT(range->cmp); - sec = GET_BIT(range->sec); - tb = GET_BIT(range->tb); - bp = GET_BIT(range->bp[0]) << 2 | - GET_BIT(range->bp[1]) << 1 | - GET_BIT(range->bp[2]); - - *sr1 = (sec ? SPI_FLASH_SR1_SEC : 0) | - (tb ? SPI_FLASH_SR1_TB : 0) | - (bp << 2); - *sr2 = (cmp ? SPI_FLASH_SR2_CMP : 0); - return EC_SUCCESS; - } - } - - /* Invalid range, or valid range missing from our table */ - return EC_ERROR_INVAL; -} diff --git a/common/spi_nor.c b/common/spi_nor.c deleted file mode 100644 index 0a719d63b3..0000000000 --- a/common/spi_nor.c +++ /dev/null @@ -1,1091 +0,0 @@ -/* Copyright 2015 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. - */ - -/* SFDP-based Serial NOR flash device module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "spi_nor.h" -#include "shared_mem.h" -#include "util.h" -#include "task.h" -#include "spi.h" -#include "sfdp.h" -#include "timer.h" -#include "watchdog.h" - -#ifdef CONFIG_SPI_NOR_DEBUG -#define CPRINTS(dev, string, args...) \ - cprints(CC_SPI, "SPI NOR %s: " string, (dev)->name, ## args) -#else -#define CPRINTS(dev, string, args...) -#endif - -/* Time to sleep while serial NOR flash write is in progress. */ -#define SPI_NOR_WIP_SLEEP_USEC 10 - -/* This driver only supports v1.* SFDP. */ -#define SPI_NOR_SUPPORTED_SFDP_MAJOR_VERSION 1 - -/* Ensure a Serial NOR Flash read command in 4B addressing mode fits. */ -BUILD_ASSERT(CONFIG_SPI_NOR_MAX_READ_SIZE + 5 <= - CONFIG_SPI_NOR_MAX_MESSAGE_SIZE); -/* The maximum write size must be a power of two so it can be used as an - * emulated maximum page size. */ -BUILD_ASSERT(POWER_OF_TWO(CONFIG_SPI_NOR_MAX_WRITE_SIZE)); -/* Ensure a Serial NOR Flash page program command in 4B addressing mode fits. */ -BUILD_ASSERT(CONFIG_SPI_NOR_MAX_WRITE_SIZE + 5 <= - CONFIG_SPI_NOR_MAX_MESSAGE_SIZE); - -/* A single mutex is used to protect the single buffer, SPI port, and all of the - * device mutable board defined device states, if the contention is too high it - * may be worthwhile to change the global mutex granularity to a finer-grained - * mutex granularity. */ -static struct mutex driver_mutex; - -/* Single internal buffer used to stage serial NOR flash commands for the - * public APIs (read, write, erase). */ -static uint8_t buf[CONFIG_SPI_NOR_MAX_MESSAGE_SIZE]; - -/******************************************************************************/ -/* Internal driver functions. */ - -/** - * Blocking read of the Serial Flash's first status register. - */ -static int spi_nor_read_status(const struct spi_nor_device_t *spi_nor_device, - uint8_t *status_register_value) -{ - uint8_t cmd = SPI_NOR_OPCODE_READ_STATUS; - - return spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &cmd, 1, status_register_value, 1); -} - -/** - * Set the write enable latch. Device and shared buffer mutexes must be held! - */ -static int spi_nor_write_enable(const struct spi_nor_device_t *spi_nor_device) -{ - uint8_t cmd = SPI_NOR_OPCODE_WRITE_ENABLE; - uint8_t status_register_value; - int rv = EC_SUCCESS; - - /* Set the write enable latch. */ - rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &cmd, 1, NULL, 0); - if (rv) - return rv; - - /* Verify the write enabled latch got set. */ - rv = spi_nor_read_status(spi_nor_device, &status_register_value); - if (rv) - return rv; - if ((status_register_value & SPI_NOR_STATUS_REGISTER_WEL) == 0) - return EC_ERROR_UNKNOWN; /* WEL not set but should be. */ - - return rv; -} - -/** - * Read from the extended address register. - * @param spi_nor_device The Serial NOR Flash device to use. - * @param value The value to read to. - * @return ec_error_list (non-zero on error and timeout). - */ -static int spi_nor_read_ear(const struct spi_nor_device_t *spi_nor_device, - uint8_t *value) -{ - uint8_t command = SPI_NOR_OPCODE_RDEAR; - - return spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &command, sizeof(command), value, 1); -} - -int spi_nor_write_ear(const struct spi_nor_device_t *spi_nor_device, - const uint8_t value) -{ - uint8_t buf[2]; - int rv; - uint8_t ear; - - mutex_lock(&driver_mutex); - - rv = spi_nor_write_enable(spi_nor_device); - if (rv) { - CPRINTS(spi_nor_device, "Failed to write enable"); - goto err_free; - } - - buf[0] = SPI_NOR_OPCODE_WREAR; - buf[1] = value; - - rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller], - buf, sizeof(buf), NULL, 0); - if (rv) { - CPRINTS(spi_nor_device, "Failed to write EAR, rv=%d", rv); - goto err_free; - } - - rv = spi_nor_read_ear(spi_nor_device, &ear); - if (rv) - goto err_free; - - if (ear != value) { - CPRINTS(spi_nor_device, - "Write EAR error: write=%d, read=%d", value, ear); - rv = EC_ERROR_UNKNOWN; /* WEL not set but should be. */ - goto err_free; - } - -err_free: - mutex_unlock(&driver_mutex); - return rv; -} - -/** - * Block until the Serial NOR Flash clears the BUSY/WIP bit in its status reg. - */ -static int spi_nor_wait(const struct spi_nor_device_t *spi_nor_device) -{ - int rv = EC_SUCCESS; - timestamp_t timeout; - uint8_t status_register_value; - - rv = spi_nor_read_status(spi_nor_device, &status_register_value); - if (rv) - return rv; - timeout.val = - get_time().val + spi_nor_device->timeout_usec; - while (status_register_value & SPI_NOR_STATUS_REGISTER_WIP) { - /* Reload the watchdog before sleeping. */ - watchdog_reload(); - usleep(SPI_NOR_WIP_SLEEP_USEC); - - /* Give up if the deadline has been exceeded. */ - if (get_time().val > timeout.val) - return EC_ERROR_TIMEOUT; - - /* Re-read the status register. */ - rv = spi_nor_read_status(spi_nor_device, - &status_register_value); - if (rv) - return rv; - } - - return rv; -} - -/** - * Read the Manufacturer bank and ID out of the JEDEC ID. - */ -static int spi_nor_read_jedec_mfn_id( - const struct spi_nor_device_t *spi_nor_device, - uint8_t *out_mfn_bank, - uint8_t *out_mfn_id) -{ - int rv = EC_SUCCESS; - uint8_t jedec_id[SPI_NOR_JEDEC_ID_BANKS]; - size_t i; - uint8_t cmd = SPI_NOR_OPCODE_JEDEC_ID; - - /* Read the standardized part of the JEDEC ID. */ - rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &cmd, 1, jedec_id, SPI_NOR_JEDEC_ID_BANKS); - if (rv) - return rv; - - *out_mfn_bank = 0; - /* Go through the JEDEC ID a byte a time to looking for a manufacturer - * ID instead of the next bank indicator (0x7F). */ - for (i = 0; i < SPI_NOR_JEDEC_ID_BANKS; i++) { - *out_mfn_id = jedec_id[i]; - if (*out_mfn_id != 0x7F) - return EC_SUCCESS; - *out_mfn_bank += 1; - } - /* JEDEC Manufacturer ID should be available, perhaps there is a bus - * problem or the JEP106 specification has grown the number of banks? */ - return EC_ERROR_UNKNOWN; -} - -/** - * Read a doubleword out of a SFDP table (DWs are 1-based like the SFDP spec). - */ -static int spi_nor_read_sfdp_dword( - const struct spi_nor_device_t *spi_nor_device, - uint32_t table_offset, - uint8_t table_double_word, - uint32_t *out_dw) { - uint8_t sfdp_cmd[5]; - /* Calculate the byte offset based on the double word. */ - uint32_t sfdp_offset = table_offset + ((table_double_word - 1) * 4); - - /* Read the DW out of the SFDP region. */ - sfdp_cmd[0] = SPI_NOR_OPCODE_SFDP; - sfdp_cmd[1] = (sfdp_offset & 0xFF0000) >> 16; - sfdp_cmd[2] = (sfdp_offset & 0xFF00) >> 8; - sfdp_cmd[3] = (sfdp_offset & 0xFF); - sfdp_cmd[4] = 0; /* Required extra cycle. */ - return spi_transaction(&spi_devices[spi_nor_device->spi_controller], - sfdp_cmd, 5, (uint8_t *)out_dw, 4); -} - -/** - * Returns a bool (1 or 0) based on whether the parameter header double words - * are for a SFDP v1.* Basic SPI Flash NOR Parameter Table. - */ -static int is_basic_flash_parameter_table(uint8_t sfdp_major_rev, - uint8_t sfdp_minor_rev, - uint32_t parameter_header_dw1, - uint32_t parameter_header_dw2) -{ - if (sfdp_major_rev == 1 && sfdp_minor_rev < 5) { - return (SFDP_GET_BITFIELD(SFDP_1_0_PARAMETER_HEADER_DW1_ID, - parameter_header_dw1) == - BASIC_FLASH_PARAMETER_TABLE_1_0_ID); - } else if (sfdp_major_rev == 1 && sfdp_minor_rev >= 5) { - return ((SFDP_GET_BITFIELD(SFDP_1_5_PARAMETER_HEADER_DW1_ID_LSB, - parameter_header_dw1) == - BASIC_FLASH_PARAMETER_TABLE_1_5_ID_LSB) && - (SFDP_GET_BITFIELD(SFDP_1_5_PARAMETER_HEADER_DW2_ID_MSB, - parameter_header_dw2) == - BASIC_FLASH_PARAMETER_TABLE_1_5_ID_MSB)); - } - - return 0; -} - -/** - * Helper function to locate the SFDP Basic SPI Flash NOR Parameter Table. - */ -static int locate_sfdp_basic_parameter_table( - const struct spi_nor_device_t *spi_nor_device, - uint8_t *out_sfdp_major_rev, - uint8_t *out_sfdp_minor_rev, - uint8_t *out_table_major_rev, - uint8_t *out_table_minor_rev, - uint32_t *out_table_offset, - size_t *out_table_size) -{ - int rv = EC_SUCCESS; - uint8_t number_parameter_headers; - uint32_t table_offset = 0; - int table_found = 0; - uint32_t dw1; - uint32_t dw2; - - /* Read the SFDP header. */ - rv = spi_nor_read_sfdp_dword(spi_nor_device, 0, 1, &dw1); - rv |= spi_nor_read_sfdp_dword(spi_nor_device, 0, 2, &dw2); - if (rv) - return rv; - - /* Ensure the SFDP table is valid. Note the versions are not checked - * through the SFDP table header, as there may be a backwards - * compatible, older basic parameter tables which are compatible with - * this driver in the parameter headers. */ - if (!SFDP_HEADER_DW1_SFDP_SIGNATURE_VALID(dw1)) { - CPRINTS(spi_nor_device, "SFDP signature invalid"); - return EC_ERROR_UNKNOWN; - } - - *out_sfdp_major_rev = - SFDP_GET_BITFIELD(SFDP_HEADER_DW2_SFDP_MAJOR, dw2); - *out_sfdp_minor_rev = - SFDP_GET_BITFIELD(SFDP_HEADER_DW2_SFDP_MINOR, dw2); - CPRINTS(spi_nor_device, "SFDP v%d.%d discovered", - *out_sfdp_major_rev, *out_sfdp_minor_rev); - - /* NPH is 0-based, so add 1. */ - number_parameter_headers = - SFDP_GET_BITFIELD(SFDP_HEADER_DW2_NPH, dw2) + 1; - CPRINTS(spi_nor_device, "There are %d SFDP parameter headers", - number_parameter_headers); - - /* Search for the newest, compatible basic flash parameter table. */ - *out_table_major_rev = 0; - *out_table_minor_rev = 0; - while (number_parameter_headers) { - uint8_t major_rev, minor_rev; - - table_offset += 8; - number_parameter_headers--; - - /* Read this parameter header's two dwords. */ - rv = spi_nor_read_sfdp_dword( - spi_nor_device, table_offset, 1, &dw1); - rv |= spi_nor_read_sfdp_dword( - spi_nor_device, table_offset, 2, &dw2); - if (rv) - return rv; - - /* Ensure it's the basic flash parameter table. */ - if (!is_basic_flash_parameter_table(*out_sfdp_major_rev, - *out_sfdp_minor_rev, - dw1, dw2)) - continue; - - /* The parameter header major and minor versioning is still the - * same as SFDP 1.0. */ - major_rev = SFDP_GET_BITFIELD( - SFDP_1_0_PARAMETER_HEADER_DW1_TABLE_MAJOR, dw1); - minor_rev = SFDP_GET_BITFIELD( - SFDP_1_0_PARAMETER_HEADER_DW1_TABLE_MINOR, dw1); - - /* Skip incompatible parameter tables. */ - if (major_rev != SPI_NOR_SUPPORTED_SFDP_MAJOR_VERSION) - continue; - - /* If this parameter table has a lower revision compared to a - * previously found compatible table, skip it. */ - if (minor_rev < *out_table_minor_rev) - continue; - - table_found = 1; - *out_table_major_rev = major_rev; - *out_table_minor_rev = minor_rev; - /* The parameter header ptp and ptl are still the same as - * SFDP 1.0. */ - *out_table_offset = SFDP_GET_BITFIELD( - SFDP_1_0_PARAMETER_HEADER_DW2_PTP, dw2); - /* Convert the size from DW to Bytes. */ - *out_table_size = SFDP_GET_BITFIELD( - SFDP_1_0_PARAMETER_HEADER_DW1_PTL, dw1) * 4; - } - - if (!table_found) { - CPRINTS(spi_nor_device, - "No compatible Basic Flash Parameter Table found"); - return EC_ERROR_UNKNOWN; - } - - CPRINTS(spi_nor_device, "Using Basic Flash Parameter Table v%d.%d", - *out_sfdp_major_rev, *out_sfdp_minor_rev); - - return EC_SUCCESS; -} - -/** - * Helper function to lookup the part's page size in the SFDP Basic SPI Flash - * NOR Parameter Table. - */ -static int spi_nor_device_discover_sfdp_page_size( - struct spi_nor_device_t *spi_nor_device, - uint8_t basic_parameter_table_major_version, - uint8_t basic_parameter_table_minor_version, - uint32_t basic_parameter_table_offset, - size_t *page_size) -{ - int rv = EC_SUCCESS; - uint32_t dw; - - if (basic_parameter_table_major_version == 1 && - basic_parameter_table_minor_version < 5) { - /* Use the Basic Flash Parameter v1.0 page size reporting. */ - rv = spi_nor_read_sfdp_dword( - spi_nor_device, basic_parameter_table_offset, 1, &dw); - if (rv) - return rv; - if (SFDP_GET_BITFIELD(BFPT_1_0_DW1_WRITE_GRANULARITY, dw)) - *page_size = 64; - else - *page_size = 1; - - } else if (basic_parameter_table_major_version == 1 && - basic_parameter_table_minor_version >= 5) { - /* Use the Basic Flash Parameter v1.5 page size reporting. */ - rv = spi_nor_read_sfdp_dword(spi_nor_device, - basic_parameter_table_offset, 11, &dw); - if (rv) - return rv; - *page_size = - 1 << SFDP_GET_BITFIELD(BFPT_1_5_DW11_PAGE_SIZE, dw); - } - - return EC_SUCCESS; -} - -/** - * Helper function to lookup the part's capacity in the SFDP Basic SPI Flash - * NOR Parameter Table. - */ -static int spi_nor_device_discover_sfdp_capacity( - struct spi_nor_device_t *spi_nor_device, - uint8_t basic_parameter_table_major_version, - uint8_t basic_parameter_table_minor_version, - uint32_t basic_parameter_table_offset, - uint32_t *capacity) -{ - int rv = EC_SUCCESS; - uint32_t dw; - - /* First attempt to discover the device's capacity. */ - if (basic_parameter_table_major_version == 1) { - /* Use the Basic Flash Parameter v1.0 capacity reporting. */ - rv = spi_nor_read_sfdp_dword(spi_nor_device, - basic_parameter_table_offset, 2, &dw); - if (rv) - return rv; - - if (SFDP_GET_BITFIELD(BFPT_1_0_DW2_GT_2_GIBIBITS, dw)) { - /* Ensure the capacity is less than 4GiB. */ - uint64_t tmp_capacity = 1 << - (SFDP_GET_BITFIELD(BFPT_1_0_DW2_N, dw) - 3); - if (tmp_capacity > UINT32_MAX) - return EC_ERROR_OVERFLOW; - *capacity = tmp_capacity; - } else { - *capacity = - 1 + - (SFDP_GET_BITFIELD(BFPT_1_0_DW2_N, dw) >> 3); - } - } - - return EC_SUCCESS; -} - -static int spi_nor_read_internal(const struct spi_nor_device_t *spi_nor_device, - uint32_t offset, size_t size, uint8_t *data) -{ - int rv; - - /* Split up the read operation into multiple transactions if the size - * is larger than the maximum read size. - */ - while (size > 0) { - size_t read_size = - MIN(size, CONFIG_SPI_NOR_MAX_READ_SIZE); - size_t read_command_size; - - /* Set up the read command in the TX buffer. */ - buf[0] = SPI_NOR_OPCODE_SLOW_READ; - if (spi_nor_device->in_4b_addressing_mode) { - buf[1] = (offset & 0xFF000000) >> 24; - buf[2] = (offset & 0xFF0000) >> 16; - buf[3] = (offset & 0xFF00) >> 8; - buf[4] = (offset & 0xFF); - read_command_size = 5; - } else { /* in 3 byte addressing mode */ - buf[1] = (offset & 0xFF0000) >> 16; - buf[2] = (offset & 0xFF00) >> 8; - buf[3] = (offset & 0xFF); - read_command_size = 4; - } - - rv = spi_transaction( - &spi_devices[spi_nor_device->spi_controller], - buf, read_command_size, data, read_size); - if (rv) - return rv; - - data += read_size; - offset += read_size; - size -= read_size; - } - return EC_SUCCESS; -} - -/******************************************************************************/ -/* External Serial NOR Flash API available to other modules. */ - -/** - * Initialize the module, assumes the Serial NOR Flash devices are currently - * all available for initialization. As part of the initialization the driver - * will check if the part has a compatible SFDP Basic Flash Parameter table - * and update the part's page_size, capacity, and forces the addressing mode. - * Parts with more than 16MiB of capacity are initialized into 4B addressing - * and parts with less are initialized into 3B addressing mode. - * - * WARNING: This must successfully return before invoking any other Serial NOR - * Flash APIs. - */ -int spi_nor_init(void) -{ - int rv = EC_SUCCESS; - size_t i; - - /* Initialize the state for each serial NOR flash device. */ - for (i = 0; i < SPI_NOR_DEVICE_COUNT; i++) { - uint8_t sfdp_major_rev, sfdp_minor_rev; - uint8_t table_major_rev, table_minor_rev; - uint32_t table_offset; - size_t table_size; - struct spi_nor_device_t *spi_nor_device = - &spi_nor_devices[i]; - - rv |= locate_sfdp_basic_parameter_table(spi_nor_device, - &sfdp_major_rev, - &sfdp_minor_rev, - &table_major_rev, - &table_minor_rev, - &table_offset, - &table_size); - - /* If we failed to find a compatible SFDP Basic Flash Parameter - * table, use the default capacity, page size, and addressing - * mode values. */ - if (rv == EC_SUCCESS) { - size_t page_size = 0; - uint32_t capacity = 0; - - rv |= spi_nor_device_discover_sfdp_page_size( - spi_nor_device, - table_major_rev, table_minor_rev, table_offset, - &page_size); - rv |= spi_nor_device_discover_sfdp_capacity( - spi_nor_device, - table_major_rev, table_minor_rev, table_offset, - &capacity); - if (rv == EC_SUCCESS) { - mutex_lock(&driver_mutex); - spi_nor_device->capacity = capacity; - spi_nor_device->page_size = page_size; - CPRINTS(spi_nor_device, - "Updated to SFDP params: %dKiB w/ %dB pages", - spi_nor_device->capacity >> 10, - spi_nor_device->page_size); - mutex_unlock(&driver_mutex); - } - } - - /* Ensure the device is in a determined addressing state by - * forcing a 4B addressing mode entry or exit depending on the - * device capacity. If the device is larger than 16MiB, enter - * 4B addressing mode. */ - rv |= spi_nor_set_4b_mode(spi_nor_device, - spi_nor_device->capacity > 0x1000000); - } - - return rv; -} - -/** - * Forces the Serial NOR Flash device to enter (or exit) 4 Byte addressing mode. - * - * WARNING: - * 1) In 3 Byte addressing mode only 16MiB of Serial NOR Flash is accessible. - * 2) If there's a second SPI controller communicating with this Serial - * NOR Flash part on the board, the user is responsible for ensuring - * addressing mode compatibility and cooperation. - * 3) The user must ensure that multiple users do not trample on each other - * by having multiple parties changing the device's addressing mode. - * - * @param spi_nor_device The Serial NOR Flash device to use. - * @param enter_4b_addressing_mode Whether to enter (1) or exit (0) 4B mode. - * @return ec_error_list (non-zero on error and timeout). - */ -int spi_nor_set_4b_mode(struct spi_nor_device_t *spi_nor_device, - int enter_4b_addressing_mode) -{ - uint8_t cmd; - int rv; - - rv = spi_nor_write_enable(spi_nor_device); - if (rv) - return rv; - - if (enter_4b_addressing_mode) - cmd = SPI_NOR_DRIVER_SPECIFIED_OPCODE_ENTER_4B; - else - cmd = SPI_NOR_DRIVER_SPECIFIED_OPCODE_EXIT_4B; - - /* Claim the driver mutex to modify the device state. */ - mutex_lock(&driver_mutex); - - rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &cmd, 1, NULL, 0); - if (rv == EC_SUCCESS) { - spi_nor_device->in_4b_addressing_mode = - enter_4b_addressing_mode; - } - - CPRINTS(spi_nor_device, "Entered %s Addressing Mode", - enter_4b_addressing_mode ? "4-Byte" : "3-Byte"); - - /* Release the driver mutex. */ - mutex_unlock(&driver_mutex); - return rv; -} - -/** - * Read JEDEC Identifier. - * - * @param spi_nor_device The Serial NOR Flash device to use. - * @param size Number of Bytes to read. - * @param data Destination buffer for data. - * @return ec_error_list (non-zero on error and timeout). - */ -int spi_nor_read_jedec_id(const struct spi_nor_device_t *spi_nor_device, - size_t size, uint8_t *data) { - int rv; - uint8_t cmd = SPI_NOR_OPCODE_JEDEC_ID; - - if (size > CONFIG_SPI_NOR_MAX_READ_SIZE) - return EC_ERROR_INVAL; - /* Claim the driver mutex. */ - mutex_lock(&driver_mutex); - /* Read the JEDEC ID. */ - rv = spi_transaction(&spi_devices[spi_nor_device->spi_controller], - &cmd, 1, data, size); - /* Release the driver mutex. */ - mutex_unlock(&driver_mutex); - - return rv; -} - -/** - * Read from the Serial NOR Flash device. - * - * @param spi_nor_device The Serial NOR Flash device to use. - * @param offset Flash offset to read. - * @param size Number of Bytes to read. - * @param data Destination buffer for data. - * @return ec_error_list (non-zero on error and timeout). - */ -int spi_nor_read(const struct spi_nor_device_t *spi_nor_device, - uint32_t offset, size_t size, uint8_t *data) -{ - int rv; - - /* Claim the driver mutex. */ - mutex_lock(&driver_mutex); - rv = spi_nor_read_internal(spi_nor_device, offset, size, data); - /* Release the driver mutex. */ - mutex_unlock(&driver_mutex); - - return rv; -} - -/** - * Erase flash on the Serial Flash Device. - * - * @param spi_nor_device The Serial NOR Flash device to use. - * @param offset Flash offset to erase, must be aligned to the minimum physical - * erase size. - * @param size Number of Bytes to erase, must be a multiple of the the minimum - * physical erase size. - * @return ec_error_list (non-zero on error and timeout). - */ -int spi_nor_erase(const struct spi_nor_device_t *spi_nor_device, - uint32_t offset, size_t size) -{ - int rv = EC_SUCCESS; - size_t erase_command_size, erase_size; - uint8_t erase_opcode; -#ifdef CONFIG_SPI_NOR_SMART_ERASE - BUILD_ASSERT((CONFIG_SPI_NOR_MAX_READ_SIZE % 4) == 0); - uint8_t buffer[CONFIG_SPI_NOR_MAX_READ_SIZE] __aligned(4); - size_t verify_offset, read_offset, read_size, read_left; -#endif - - /* Invalid input */ - if ((offset % 4096 != 0) || (size % 4096 != 0) || (size < 4096)) - return EC_ERROR_INVAL; - - /* Claim the driver mutex. */ - mutex_lock(&driver_mutex); - - while (size > 0) { - erase_opcode = SPI_NOR_DRIVER_SPECIFIED_OPCODE_4KIB_ERASE; - erase_size = 4096; - - /* Wait for the previous operation to finish. */ - rv = spi_nor_wait(spi_nor_device); - if (rv) - goto err_free; - -#ifdef CONFIG_SPI_NOR_BLOCK_ERASE - if (!(offset % 65536) && size >= 65536) { - erase_opcode = - SPI_NOR_DRIVER_SPECIFIED_OPCODE_64KIB_ERASE; - erase_size = 65536; - } -#endif -#ifdef CONFIG_SPI_NOR_SMART_ERASE - read_offset = offset; - read_left = erase_size; - while (read_left) { - read_size = MIN(read_left, - CONFIG_SPI_NOR_MAX_READ_SIZE); - /* Since CONFIG_SPI_NOR_MAX_READ_SIZE & erase_size are - * both guaranteed to be multiples of 4. - */ - assert(read_size >= 4 && (read_size % 4) == 0); - rv = spi_nor_read_internal(spi_nor_device, read_offset, - read_size, buffer); - - /* Note: the return value here is lost below - * at the write enable, this is not a problem, - * as this code is only an optimisation, if it - * fails, the full erase functionality still - * gets done, and the error from that returned - */ - if (rv != EC_SUCCESS) - break; - /* Aligned word verify reduced the overall (read + - * verify) time by ~20% (vs bytewise verify) on - * an m3@24MHz & SPI@24MHz. - */ - verify_offset = 0; - while (verify_offset <= read_size - 4) { - if (*(uint32_t *)(buffer + verify_offset) - != 0xffffffff) { - break; - } - verify_offset += 4; - } - if (verify_offset != read_size) - break; - read_offset += read_size; - read_left -= read_size; - watchdog_reload(); - } - if (!read_left) { - /* Sector/block already erased. */ - CPRINTS(spi_nor_device, - "Skipping erase [%x:%x] " - "(already erased)", - offset, erase_size); - offset += erase_size; - size -= erase_size; - continue; - } -#endif - /* Enable writing to serial NOR flash. */ - rv = spi_nor_write_enable(spi_nor_device); - if (rv) - goto err_free; - - /* Set up the erase instruction. */ - buf[0] = erase_opcode; - if (spi_nor_device->in_4b_addressing_mode) { - buf[1] = (offset & 0xFF000000) >> 24; - buf[2] = (offset & 0xFF0000) >> 16; - buf[3] = (offset & 0xFF00) >> 8; - buf[4] = (offset & 0xFF); - erase_command_size = 5; - } else { /* in 3 byte addressing mode */ - buf[1] = (offset & 0xFF0000) >> 16; - buf[2] = (offset & 0xFF00) >> 8; - buf[3] = (offset & 0xFF); - erase_command_size = 4; - } - - rv = spi_transaction( - &spi_devices[spi_nor_device->spi_controller], - buf, erase_command_size, NULL, 0); - if (rv) - goto err_free; - - offset += erase_size; - size -= erase_size; - } - - /* Wait for the previous operation to finish. */ - rv = spi_nor_wait(spi_nor_device); - -err_free: - /* Release the driver mutex. */ - mutex_unlock(&driver_mutex); - - return rv; -} - -/** - * Write to the Serial NOR Flash device. Assumes already erased. - * - * @param spi_nor_device The Serial NOR Flash device to use. - * @param offset Flash offset to write. - * @param size Number of Bytes to write. - * @param data Data to write to flash. - * @return ec_error_list (non-zero on error and timeout). - */ -int spi_nor_write(const struct spi_nor_device_t *spi_nor_device, - uint32_t offset, size_t size, const uint8_t *data) -{ - int rv = EC_SUCCESS; - size_t effective_page_size; - - /* Claim the driver mutex. */ - mutex_lock(&driver_mutex); - - /* Ensure the device's page size fits in the driver's buffer, if not - * emulate a smaller page size based on the buffer size. */ - effective_page_size = MIN(spi_nor_device->page_size, - CONFIG_SPI_NOR_MAX_WRITE_SIZE); - - /* Split the write into multiple writes if the size is too large. */ - while (size > 0) { - size_t prefix_size; - /* Figure out the size of the next write within 1 page. */ - uint32_t page_offset = offset & (effective_page_size - 1); - size_t write_size = - MIN(size, effective_page_size - page_offset); - - /* Wait for the previous operation to finish. */ - rv = spi_nor_wait(spi_nor_device); - if (rv) - goto err_free; - - /* Enable writing to serial NOR flash. */ - rv = spi_nor_write_enable(spi_nor_device); - if (rv) - goto err_free; - - /* Set up the page program command. */ - buf[0] = SPI_NOR_OPCODE_PAGE_PROGRAM; - if (spi_nor_device->in_4b_addressing_mode) { - buf[1] = (offset & 0xFF000000) >> 24; - buf[2] = (offset & 0xFF0000) >> 16; - buf[3] = (offset & 0xFF00) >> 8; - buf[4] = (offset & 0xFF); - prefix_size = 5; - } else { /* in 3 byte addressing mode */ - buf[1] = (offset & 0xFF0000) >> 16; - buf[2] = (offset & 0xFF00) >> 8; - buf[3] = (offset & 0xFF); - prefix_size = 4; - } - /* Copy data to write into the buffer after the prefix. */ - memmove(buf + prefix_size, data, write_size); - - rv = spi_transaction( - &spi_devices[spi_nor_device->spi_controller], - buf, prefix_size + write_size, NULL, 0); - if (rv) - goto err_free; - - data += write_size; - offset += write_size; - size -= write_size; - } - - /* Wait for the previous operation to finish. */ - rv = spi_nor_wait(spi_nor_device); - -err_free: - /* Release the driver mutex. */ - mutex_unlock(&driver_mutex); - - return rv; -} - -/******************************************************************************/ -/* Serial NOR Flash console commands. */ - -#ifdef CONFIG_CMD_SPI_NOR -static int command_spi_nor_info(int argc, char **argv) -{ - int rv = EC_SUCCESS; - - uint8_t sfdp_major_rev, sfdp_minor_rev; - uint8_t table_major_rev, table_minor_rev; - uint32_t table_offset; - uint8_t mfn_bank = 0, mfn_id = 0; - size_t table_size; - const struct spi_nor_device_t *spi_nor_device = 0; - int spi_nor_device_index = 0; - int spi_nor_device_index_limit = spi_nor_devices_used - 1; - - /* Set the device index limits if a device was specified. */ - if (argc == 2) { - spi_nor_device_index = strtoi(argv[1], NULL, 0); - if (spi_nor_device_index >= spi_nor_devices_used) - return EC_ERROR_PARAM1; - spi_nor_device_index_limit = spi_nor_device_index; - } else if (argc != 1) { - return EC_ERROR_PARAM_COUNT; - } - - for (; spi_nor_device_index <= spi_nor_device_index_limit; - spi_nor_device_index++) { - spi_nor_device = &spi_nor_devices[spi_nor_device_index]; - - ccprintf("Serial NOR Flash Device %d:\n", spi_nor_device_index); - ccprintf("\tName: %s\n", spi_nor_device->name); - ccprintf("\tSPI controller index: %d\n", - spi_nor_device->spi_controller); - ccprintf("\tTimeout: %d uSec\n", - spi_nor_device->timeout_usec); - ccprintf("\tCapacity: %d KiB\n", - spi_nor_device->capacity >> 10), - ccprintf("\tAddressing: %s addressing mode\n", - spi_nor_device->in_4b_addressing_mode ? "4B" : "3B"); - ccprintf("\tPage Size: %d Bytes\n", - spi_nor_device->page_size); - - /* Get JEDEC ID info. */ - rv = spi_nor_read_jedec_mfn_id(spi_nor_device, &mfn_bank, - &mfn_id); - if (rv != EC_SUCCESS) - return rv; - ccprintf("\tJEDEC ID bank %d manufacturing code 0x%x\n", - mfn_bank, mfn_id); - - /* Get SFDP info. */ - if (locate_sfdp_basic_parameter_table( - spi_nor_device, &sfdp_major_rev, &sfdp_minor_rev, - &table_major_rev, &table_minor_rev, &table_offset, - &table_size) != EC_SUCCESS) { - ccputs("\tNo JEDEC SFDP support detected\n"); - continue; /* Go on to the next device. */ - } - ccprintf("\tSFDP v%d.%d\n", sfdp_major_rev, sfdp_minor_rev); - ccprintf("\tFlash Parameter Table v%d.%d (%dB @ 0x%x)\n", - table_major_rev, table_minor_rev, - table_size, table_offset); - } - - return rv; -} -DECLARE_CONSOLE_COMMAND(spinorinfo, command_spi_nor_info, - "[device]", - "Report Serial NOR Flash device information"); -#endif /* CONFIG_CMD_SPI_NOR */ - -#ifdef CONFIG_CMD_SPI_NOR -static int command_spi_nor_erase(int argc, char **argv) -{ - const struct spi_nor_device_t *spi_nor_device; - int spi_nor_device_index; - int offset = 0; - int size = 4096; - int rv; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - spi_nor_device_index = strtoi(argv[1], NULL, 0); - if (spi_nor_device_index >= spi_nor_devices_used) - return EC_ERROR_PARAM1; - spi_nor_device = &spi_nor_devices[spi_nor_device_index]; - - rv = parse_offset_size(argc, argv, 2, &offset, &size); - if (rv) - return rv; - - ccprintf("Erasing %d bytes at 0x%x on %s...\n", - size, offset, spi_nor_device->name); - return spi_nor_erase(spi_nor_device, offset, size); -} -DECLARE_CONSOLE_COMMAND(spinorerase, command_spi_nor_erase, - "device [offset] [size]", - "Erase flash"); -#endif /* CONFIG_CMD_SPI_NOR */ - -#ifdef CONFIG_CMD_SPI_NOR -static int command_spi_nor_write(int argc, char **argv) -{ - const struct spi_nor_device_t *spi_nor_device; - int spi_nor_device_index; - int offset = 0; - int size = CONFIG_SPI_NOR_MAX_WRITE_SIZE; - int rv; - char *data; - int i; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - spi_nor_device_index = strtoi(argv[1], NULL, 0); - if (spi_nor_device_index >= spi_nor_devices_used) - return EC_ERROR_PARAM1; - spi_nor_device = &spi_nor_devices[spi_nor_device_index]; - - rv = parse_offset_size(argc, argv, 2, &offset, &size); - if (rv) - return rv; - - if (size > shared_mem_size()) - size = shared_mem_size(); - - /* Acquire the shared memory buffer */ - rv = shared_mem_acquire(size, &data); - if (rv) { - ccputs("Can't get shared mem\n"); - return rv; - } - - /* Fill the data buffer with a pattern */ - for (i = 0; i < size; i++) - data[i] = i; - - ccprintf("Writing %d bytes to 0x%x on %s...\n", - size, offset, spi_nor_device->name); - rv = spi_nor_write(spi_nor_device, offset, size, data); - - /* Free the buffer */ - shared_mem_release(data); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spinorwrite, command_spi_nor_write, - "device [offset] [size]", - "Write pattern to flash"); -#endif /* CONFIG_CMD_SPI_NOR */ - -#ifdef CONFIG_CMD_SPI_NOR -static int command_spi_nor_read(int argc, char **argv) -{ - const struct spi_nor_device_t *spi_nor_device; - int spi_nor_device_index; - int offset = 0; - int size = CONFIG_SPI_NOR_MAX_READ_SIZE; - int rv; - char *data; - int i; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - spi_nor_device_index = strtoi(argv[1], NULL, 0); - if (spi_nor_device_index >= spi_nor_devices_used) - return EC_ERROR_PARAM1; - spi_nor_device = &spi_nor_devices[spi_nor_device_index]; - - rv = parse_offset_size(argc, argv, 2, &offset, &size); - if (rv) - return rv; - - if (size > shared_mem_size()) - size = shared_mem_size(); - - /* Acquire the shared memory buffer */ - rv = shared_mem_acquire(size, &data); - if (rv) { - ccputs("Can't get shared mem\n"); - return rv; - } - - /* Read the data */ - ccprintf("Reading %d bytes from %s...", - size, spi_nor_device->name); - if (spi_nor_read(spi_nor_device, offset, size, data)) { - rv = EC_ERROR_INVAL; - goto err_free; - } - - /* Dump it */ - for (i = 0; i < size; i++) { - if ((offset + i) % 16) { - ccprintf(" %02x", data[i]); - } else { - ccprintf("\n%08x: %02x", offset + i, data[i]); - cflush(); - } - } - ccprintf("\n"); - -err_free: - /* Free the buffer */ - shared_mem_release(data); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spinorread, command_spi_nor_read, - "device [offset] [size]", - "Read flash"); -#endif /* CONFIG_CMD_SPI_NOR */ diff --git a/common/stillness_detector.c b/common/stillness_detector.c deleted file mode 100644 index c33472aa22..0000000000 --- a/common/stillness_detector.c +++ /dev/null @@ -1,110 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "stillness_detector.h" -#include "timer.h" -#include <string.h> - -static void still_det_reset(struct still_det *still_det) -{ - still_det->num_samples = 0; - still_det->acc_x = FLOAT_TO_FP(0.0f); - still_det->acc_y = FLOAT_TO_FP(0.0f); - still_det->acc_z = FLOAT_TO_FP(0.0f); - still_det->acc_xx = FLOAT_TO_FP(0.0f); - still_det->acc_yy = FLOAT_TO_FP(0.0f); - still_det->acc_zz = FLOAT_TO_FP(0.0f); -} - -static bool stillness_batch_complete(struct still_det *still_det, - uint32_t sample_time) -{ - bool complete = false; - uint32_t batch_window = time_until(still_det->window_start_time, - sample_time); - - /* Checking if enough data is accumulated */ - if (batch_window >= still_det->min_batch_window && - still_det->num_samples > still_det->min_batch_size) { - if (batch_window <= still_det->max_batch_window) { - complete = true; - } else { - /* Checking for too long batch window, reset and start - * over - */ - still_det_reset(still_det); - } - } else if (batch_window > still_det->min_batch_window && - still_det->num_samples < still_det->min_batch_size) { - /* Not enough samples collected, reset and start over */ - still_det_reset(still_det); - } - return complete; -} - -static inline fp_t compute_variance(fp_t acc_squared, fp_t acc, fp_t inv) -{ - /* (acc^2 - (acc * acc * inv)) * inv */ - return fp_mul((acc_squared - fp_mul(fp_sq(acc), inv)), inv); -} - -bool still_det_update(struct still_det *still_det, uint32_t sample_time, - fp_t x, fp_t y, fp_t z) -{ - fp_t inv = FLOAT_TO_FP(0.0f), var_x, var_y, var_z; - bool complete = false; - - /* Accumulate for mean and VAR */ - still_det->acc_x += x; - still_det->acc_y += y; - still_det->acc_z += z; - still_det->acc_xx += fp_mul(x, x); - still_det->acc_yy += fp_mul(y, y); - still_det->acc_zz += fp_mul(z, z); - - switch (++still_det->num_samples) { - case 0: - /* If we rolled over, go back. */ - still_det->num_samples--; - break; - case 1: - /* Set a new start time if new batch. */ - still_det->window_start_time = sample_time; - break; - } - - if (stillness_batch_complete(still_det, sample_time)) { - /* - * Compute 1/num_samples and check for num_samples == 0 (should - * never happen, but just in case) - */ - if (still_det->num_samples) { - inv = fp_div(1.0f, INT_TO_FP(still_det->num_samples)); - } else { - still_det_reset(still_det); - return complete; - } - /* Calculating the VAR = sum(x^2)/n - sum(x)^2/n^2 */ - var_x = compute_variance( - still_det->acc_xx, still_det->acc_x, inv); - var_y = compute_variance( - still_det->acc_yy, still_det->acc_y, inv); - var_z = compute_variance( - still_det->acc_zz, still_det->acc_z, inv); - /* Checking if sensor is still */ - if (var_x < still_det->var_threshold && - var_y < still_det->var_threshold && - var_z < still_det->var_threshold) { - still_det->mean_x = fp_mul(still_det->acc_x, inv); - still_det->mean_y = fp_mul(still_det->acc_y, inv); - still_det->mean_z = fp_mul(still_det->acc_z, inv); - complete = true; - } - /* Reset and start over */ - still_det_reset(still_det); - } - return complete; -} diff --git a/common/switch.c b/common/switch.c deleted file mode 100644 index 2c1ea804a8..0000000000 --- a/common/switch.c +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Switch module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "flash.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "lid_switch.h" -#include "power_button.h" -#include "switch.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_SWITCH, outstr) -#define CPRINTS(format, args...) cprints(CC_SWITCH, format, ## args) - -static uint8_t *memmap_switches; - -/** - * Update status of non-debounced switches. - * - * Note that deferred functions are called in the same context as lid and - * power button changes, so we don't need a mutex. - */ -static void switch_update(void) -{ - static uint8_t prev; - - /* Make sure this is safe to call before power_button_init() */ - if (!memmap_switches) - return; - - prev = *memmap_switches; - - if (power_button_is_pressed()) - *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED; - else - *memmap_switches &= ~EC_SWITCH_POWER_BUTTON_PRESSED; - - if (!IS_ENABLED(CONFIG_LID_SWITCH) || lid_is_open()) - *memmap_switches |= EC_SWITCH_LID_OPEN; - else - *memmap_switches &= ~EC_SWITCH_LID_OPEN; - - if ((crec_flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED) == 0) - *memmap_switches |= EC_SWITCH_WRITE_PROTECT_DISABLED; - else - *memmap_switches &= ~EC_SWITCH_WRITE_PROTECT_DISABLED; - -#ifdef CONFIG_SWITCH_DEDICATED_RECOVERY - if (gpio_get_level(GPIO_RECOVERY_L) == 0) - *memmap_switches |= EC_SWITCH_DEDICATED_RECOVERY; - else - *memmap_switches &= ~EC_SWITCH_DEDICATED_RECOVERY; -#endif - - if (prev != *memmap_switches) - CPRINTS("SW 0x%02x", *memmap_switches); -} -DECLARE_DEFERRED(switch_update); -DECLARE_HOOK(HOOK_LID_CHANGE, switch_update, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, switch_update, HOOK_PRIO_DEFAULT); - -static void switch_init(void) -{ - /* Set up memory-mapped switch positions */ - memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES); - *memmap_switches = 0; - - switch_update(); - - /* Switch data is now present */ - *host_get_memmap(EC_MEMMAP_SWITCHES_VERSION) = 1; - -#ifdef CONFIG_SWITCH_DEDICATED_RECOVERY - /* Enable interrupts, now that we've initialized */ - gpio_enable_interrupt(GPIO_RECOVERY_L); -#endif - - /* - * TODO(crosbug.com/p/23793): It's weird that flash_common.c owns - * reading the write protect signal, but we enable the interrupt for it - * here. Take ownership of WP back, or refactor it to its own module. - */ -#ifdef CONFIG_WP_ACTIVE_HIGH - gpio_enable_interrupt(GPIO_WP); -#else - gpio_enable_interrupt(GPIO_WP_L); -#endif -} -DECLARE_HOOK(HOOK_INIT, switch_init, HOOK_PRIO_INIT_SWITCH); - -void switch_interrupt(enum gpio_signal signal) -{ - hook_call_deferred(&switch_update_data, 0); -} - -#ifdef CONFIG_CMD_MMAPINFO -static int command_mmapinfo(int argc, char **argv) -{ - uint8_t *memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES); - uint8_t val = *memmap_switches; - int i; - const char *explanation[] = { - "lid_open", - "powerbtn", - "wp_off", - "kbd_rec", - "gpio_rec", - "fake_dev", - }; - ccprintf("memmap switches = 0x%x\n", val); - for (i = 0; i < ARRAY_SIZE(explanation); i++) - if (val & BIT(i)) - ccprintf(" %s\n", explanation[i]); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(mmapinfo, command_mmapinfo, - NULL, - "Print memmap switch state"); -#endif diff --git a/common/temp_sensor.c b/common/temp_sensor.c deleted file mode 100644 index 69d440a6d5..0000000000 --- a/common/temp_sensor.c +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Temperature sensor module for Chrome EC */ - -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "task.h" -#include "temp_sensor.h" -#include "thermal.h" -#include "timer.h" -#include "util.h" - -#ifdef CONFIG_ZEPHYR -#include "temp_sensor/temp_sensor.h" -#endif - -int temp_sensor_read(enum temp_sensor_id id, int *temp_ptr) -{ - const struct temp_sensor_t *sensor; - - if (id < 0 || id >= TEMP_SENSOR_COUNT) - return EC_ERROR_INVAL; - sensor = temp_sensors + id; - -#ifdef CONFIG_ZEPHYR - return sensor->read(sensor, temp_ptr); -#else - return sensor->read(sensor->idx, temp_ptr); -#endif -} - -static void update_mapped_memory(void) -{ - int i, t; - uint8_t *mptr = host_get_memmap(EC_MEMMAP_TEMP_SENSOR); - - for (i = 0; i < TEMP_SENSOR_COUNT; i++, mptr++) { - /* - * Switch to second range if first one is full, or stop if - * second range is also full. - */ - if (i == EC_TEMP_SENSOR_ENTRIES) - mptr = host_get_memmap(EC_MEMMAP_TEMP_SENSOR_B); - else if (i >= EC_TEMP_SENSOR_ENTRIES + - EC_TEMP_SENSOR_B_ENTRIES) - break; - - switch (temp_sensor_read(i, &t)) { - case EC_ERROR_NOT_POWERED: - *mptr = EC_TEMP_SENSOR_NOT_POWERED; - break; - case EC_ERROR_NOT_CALIBRATED: - *mptr = EC_TEMP_SENSOR_NOT_CALIBRATED; - break; - case EC_SUCCESS: - *mptr = t - EC_TEMP_SENSOR_OFFSET; - break; - default: - *mptr = EC_TEMP_SENSOR_ERROR; - } - } -} -/* Run after other TEMP tasks, so sensors will have updated first. */ -DECLARE_HOOK(HOOK_SECOND, update_mapped_memory, HOOK_PRIO_TEMP_SENSOR_DONE); - -static void temp_sensor_init(void) -{ - int i; - uint8_t *base, *base_b; - - /* - * Initialize memory-mapped data so that if a temperature value is read - * before we actually poll the sensors, we don't return an impossible - * or out-of-range value. - */ - base = host_get_memmap(EC_MEMMAP_TEMP_SENSOR); - base_b = host_get_memmap(EC_MEMMAP_TEMP_SENSOR_B); - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - if (i < EC_TEMP_SENSOR_ENTRIES) - base[i] = EC_TEMP_SENSOR_DEFAULT; - else - base_b[i - EC_TEMP_SENSOR_ENTRIES] = - EC_TEMP_SENSOR_DEFAULT; - } - - /* Set the rest of memory region to SENSOR_NOT_PRESENT */ - for (; i < EC_TEMP_SENSOR_ENTRIES + EC_TEMP_SENSOR_B_ENTRIES; ++i) { - if (i < EC_TEMP_SENSOR_ENTRIES) - base[i] = EC_TEMP_SENSOR_NOT_PRESENT; - else - base_b[i - EC_TEMP_SENSOR_ENTRIES] = - EC_TEMP_SENSOR_NOT_PRESENT; - } - - /* Temp sensor data is present, with B range supported. */ - *host_get_memmap(EC_MEMMAP_THERMAL_VERSION) = 2; -} -DECLARE_HOOK(HOOK_INIT, temp_sensor_init, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Console commands */ - -#ifdef CONFIG_CMD_TEMP_SENSOR -int console_command_temps(int argc, char **argv) -{ - int t, i; - int rv, rv1 = EC_SUCCESS; - - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - ccprintf(" %-20s: ", temp_sensors[i].name); - rv = temp_sensor_read(i, &t); - if (rv) - rv1 = rv; - - switch (rv) { - case EC_SUCCESS: - ccprintf("%d K = %d C", t, K_TO_C(t)); -#ifdef CONFIG_THROTTLE_AP - if (thermal_params[i].temp_fan_off && - thermal_params[i].temp_fan_max) - ccprintf(" %d%%", - thermal_fan_percent( - thermal_params[i].temp_fan_off, - thermal_params[i].temp_fan_max, - t)); -#endif - ccprintf("\n"); - break; - case EC_ERROR_NOT_POWERED: - ccprintf("Not powered\n"); - break; - case EC_ERROR_NOT_CALIBRATED: - ccprintf("Not calibrated\n"); - break; - default: - ccprintf("Error %d\n", rv); - } - } - - return rv1; -} -DECLARE_CONSOLE_COMMAND(temps, console_command_temps, - NULL, - "Print temp sensors"); -#endif - -/*****************************************************************************/ -/* Host commands */ - -enum ec_status temp_sensor_command_get_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_temp_sensor_get_info *p = args->params; - struct ec_response_temp_sensor_get_info *r = args->response; - int id = p->id; - - if (id >= TEMP_SENSOR_COUNT) - return EC_RES_ERROR; - - strzcpy(r->sensor_name, temp_sensors[id].name, sizeof(r->sensor_name)); - r->sensor_type = temp_sensors[id].type; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TEMP_SENSOR_GET_INFO, - temp_sensor_command_get_info, - EC_VER_MASK(0)); diff --git a/common/test_util.c b/common/test_util.c deleted file mode 100644 index b23f85509b..0000000000 --- a/common/test_util.c +++ /dev/null @@ -1,226 +0,0 @@ -/* Copyright 2013 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. - * - * Test utilities. - */ - -#if defined(TEST_COVERAGE) || defined(TEST_HOSTTEST) -/* We need signal() and exit() only when building to run on the host. */ -#include <signal.h> -#include <stdlib.h> -#endif - -#include "console.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "task.h" -#include "test_util.h" -#include "util.h" - -struct test_util_tag { - uint8_t error_count; -}; - -#define TEST_UTIL_SYSJUMP_TAG 0x5455 /* "TU" */ -#define TEST_UTIL_SYSJUMP_VERSION 1 - -int __test_error_count; - -/* Weak reference function as an entry point for unit test */ -test_mockable void run_test(int argc, char **argv) { } - -/* Default mock test init */ -test_mockable void test_init(void) { } - -/* Default mock before test */ -test_mockable void before_test(void) { } - -/* Default mock after test */ -test_mockable void after_test(void) { } - -#ifdef TEST_COVERAGE -extern void __gcov_flush(void); - -void emulator_flush(void) -{ - __gcov_flush(); -} -#else -void emulator_flush(void) -{ -} -#endif - -#if defined(TEST_HOSTTEST) || defined(TEST_COVERAGE) -/* Host-based unit tests need to exit(0) when they receive a SIGTERM. */ -void test_end_hook(int sig) -{ - emulator_flush(); - exit(0); -} - -void register_test_end_hook(void) -{ - signal(SIGTERM, test_end_hook); -} -#else -void register_test_end_hook(void) -{ -} -#endif - -void test_reset(void) -{ - if (!system_jumped_to_this_image()) - __test_error_count = 0; -} - -void test_pass(void) -{ - ccprintf("Pass!\n"); -} - -void test_fail(void) -{ - ccprintf("Fail!\n"); -} - -void test_print_result(void) -{ - if (__test_error_count) - ccprintf("Fail! (%d tests)\n", __test_error_count); - else - ccprintf("Pass!\n"); -} - -int test_get_error_count(void) -{ - return __test_error_count; -} - -uint32_t test_get_state(void) -{ - uint32_t state; - - system_get_scratchpad(&state); - return state; -} - -test_mockable void test_clean_up(void) -{ -} - -void test_reboot_to_next_step(enum test_state_t step) -{ - ccprintf("Rebooting to next test step...\n"); - cflush(); - system_set_scratchpad(TEST_STATE_MASK(step)); - system_reset(SYSTEM_RESET_HARD); -} - -test_mockable void test_run_step(uint32_t state) -{ -} - -void test_run_multistep(void) -{ - uint32_t state = test_get_state(); - - if (state & TEST_STATE_MASK(TEST_STATE_PASSED)) { - test_clean_up(); - system_set_scratchpad(0); - test_pass(); - } else if (state & TEST_STATE_MASK(TEST_STATE_FAILED)) { - test_clean_up(); - system_set_scratchpad(0); - test_fail(); - } - - if (state & TEST_STATE_STEP_1 || state == 0) { - task_wait_event(-1); /* Wait for run_test() */ - test_run_step(TEST_STATE_MASK(TEST_STATE_STEP_1)); - } else { - test_run_step(state); - } -} - -#ifdef HAS_TASK_HOSTCMD -int test_send_host_command(int command, int version, const void *params, - int params_size, void *resp, int resp_size) -{ - struct host_cmd_handler_args args; - - args.version = version; - args.command = command; - args.params = params; - args.params_size = params_size; - args.response = resp; - args.response_max = resp_size; - args.response_size = 0; - - return host_command_process(&args); -} -#endif /* TASK_HAS_HOSTCMD */ - -/* Linear congruential pseudo random number generator */ -uint32_t prng(uint32_t seed) -{ - return 22695477 * seed + 1; -} - -uint32_t prng_no_seed(void) -{ - static uint32_t seed = 0x1234abcd; - return seed = prng(seed); -} - -static void restore_state(void) -{ - const struct test_util_tag *tag; - int version, size; - - tag = (const struct test_util_tag *)system_get_jump_tag( - TEST_UTIL_SYSJUMP_TAG, &version, &size); - if (tag && version == TEST_UTIL_SYSJUMP_VERSION && - size == sizeof(*tag)) - __test_error_count = tag->error_count; - else - __test_error_count = 0; -} -DECLARE_HOOK(HOOK_INIT, restore_state, HOOK_PRIO_DEFAULT); - -static void preserve_state(void) -{ - struct test_util_tag tag; - tag.error_count = __test_error_count; - system_add_jump_tag(TEST_UTIL_SYSJUMP_TAG, TEST_UTIL_SYSJUMP_VERSION, - sizeof(tag), &tag); -} -DECLARE_HOOK(HOOK_SYSJUMP, preserve_state, HOOK_PRIO_DEFAULT); - -static int command_run_test(int argc, char **argv) -{ - run_test(argc, argv); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(runtest, command_run_test, - NULL, NULL); - -#ifndef CONFIG_ZEPHYR -void z_ztest_run_test_suite(const char *name, struct unit_test *suite) -{ - test_reset(); - - while (suite->test) { - suite->setup(); - RUN_TEST(suite->test); - suite->teardown(); - suite++; - } - - ccprintf("%s: ", name); - test_print_result(); -} -#endif /* CONFIG_ZEPHYR */ diff --git a/common/thermal.c b/common/thermal.c deleted file mode 100644 index e9750931be..0000000000 --- a/common/thermal.c +++ /dev/null @@ -1,345 +0,0 @@ -/* Copyright 2012 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. - */ - -/* NEW thermal engine module for Chrome EC. This is a completely different - * implementation from the original version that shipped on Link. - */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "fan.h" -#include "hooks.h" -#include "host_command.h" -#include "temp_sensor.h" -#include "thermal.h" -#include "throttle_ap.h" -#include "timer.h" -#include "util.h" - -#ifdef CONFIG_ZEPHYR -#include "temp_sensor/temp_sensor.h" -#endif - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_THERMAL, outstr) -#define CPRINTS(format, args...) cprints(CC_THERMAL, format, ## args) - -/*****************************************************************************/ -/* EC-specific thermal controls */ - -test_mockable_static void smi_sensor_failure_warning(void) -{ - CPRINTS("can't read any temp sensors!"); - host_set_single_event(EC_HOST_EVENT_THERMAL); -} - -int thermal_fan_percent(int low, int high, int cur) -{ - if (cur < low) - return 0; - if (cur > high) - return 100; - return 100 * (cur - low) / (high - low); -} - -/* The logic below is hard-coded for only three thresholds: WARN, HIGH, HALT. - * This is just a validity check to be sure we catch any changes in thermal.h - */ -BUILD_ASSERT(EC_TEMP_THRESH_COUNT == 3); - -/* Keep track of which thresholds have triggered */ -static cond_t cond_hot[EC_TEMP_THRESH_COUNT]; - -/* thermal sensor read delay */ -#if defined(CONFIG_TEMP_SENSOR_POWER_GPIO) && \ - defined(CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS) -static int first_read_delay = CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS; -#endif - -static void thermal_control(void) -{ - int i, j, t, rv, f; - int count_over[EC_TEMP_THRESH_COUNT]; - int count_under[EC_TEMP_THRESH_COUNT]; - int num_valid_limits[EC_TEMP_THRESH_COUNT]; - int num_sensors_read; - int fmax; - int temp_fan_configured; - -#ifdef CONFIG_CUSTOM_FAN_CONTROL - int temp[TEMP_SENSOR_COUNT]; -#endif - - /* add delay to ensure thermal sensor is ready when EC boot */ -#if defined(CONFIG_TEMP_SENSOR_POWER_GPIO) && \ - defined(CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS) - if (first_read_delay != 0) { - msleep(first_read_delay); - first_read_delay = 0; - } -#endif - - /* Get ready to count things */ - memset(count_over, 0, sizeof(count_over)); - memset(count_under, 0, sizeof(count_under)); - memset(num_valid_limits, 0, sizeof(num_valid_limits)); - num_sensors_read = 0; - fmax = 0; - temp_fan_configured = 0; - - /* go through all the sensors */ - for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { - - /* read one */ - rv = temp_sensor_read(i, &t); - -#ifdef CONFIG_CUSTOM_FAN_CONTROL - /* Store all sensors value */ - temp[i] = K_TO_C(t); -#endif - - if (rv != EC_SUCCESS) - continue; - else - num_sensors_read++; - - /* check all the limits */ - for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { - int limit = thermal_params[i].temp_host[j]; - int release = thermal_params[i].temp_host_release[j]; - if (limit) { - num_valid_limits[j]++; - if (t > limit) { - count_over[j]++; - } else if (release) { - if (t < release) - count_under[j]++; - } else if (t < limit) { - count_under[j]++; - } - } - } - - /* figure out the max fan needed, too */ - if (thermal_params[i].temp_fan_off && - thermal_params[i].temp_fan_max) { - f = thermal_fan_percent(thermal_params[i].temp_fan_off, - thermal_params[i].temp_fan_max, - t); - if (f > fmax) - fmax = f; - - temp_fan_configured = 1; - } - } - - if (!num_sensors_read) { - /* - * Trigger a SMI event if we can't read any sensors. - * - * In theory we could do something more elaborate like forcing - * the system to shut down if no sensors are available after - * several retries. This is a very unlikely scenario - - * particularly on LM4-based boards, since the LM4 has its own - * internal temp sensor. It's most likely to occur during - * bringup of a new board, where we haven't debugged the I2C - * bus to the sensors; forcing a shutdown in that case would - * merely hamper board bringup. - * - * If in G3, then there is no need trigger an SMI event since - * the AP is off and this can be an expected state if - * temperature sensors are powered by a power rail that's only - * on if the AP is out of G3. Note this could be 'ANY_OFF' as - * well, but that causes the thermal unit test to fail. - */ - if (!chipset_in_state(CHIPSET_STATE_HARD_OFF)) - smi_sensor_failure_warning(); - return; - } - - /* See what the aggregated limits are. Any temp over the limit - * means it's hot, but all temps have to be under the limit to - * be cool again. - */ - for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { - if (count_over[j]) - cond_set_true(&cond_hot[j]); - else if (count_under[j] == num_valid_limits[j]) - cond_set_false(&cond_hot[j]); - } - - /* What do we do about it? (note hard-coded logic). */ - - if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) { - CPRINTS("thermal SHUTDOWN"); - - /* Print temperature sensor values before shutting down AP */ - if (IS_ENABLED(CONFIG_CMD_TEMP_SENSOR)) { - console_command_temps(1, NULL); - cflush(); - } - - chipset_force_shutdown(CHIPSET_SHUTDOWN_THERMAL); - } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HALT])) { - /* We don't reboot automatically - the user has to push - * the power button. It's likely that we can't even - * detect this sensor transition until then, but we - * do have to check in order to clear the cond_t. - */ - CPRINTS("thermal no longer shutdown"); - } - - if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) { - CPRINTS("thermal HIGH"); - throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_THERMAL); - } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) { - CPRINTS("thermal no longer high"); - throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_THERMAL); - } - - if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) { - CPRINTS("thermal WARN"); - throttle_ap(THROTTLE_ON, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); - } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) { - CPRINTS("thermal no longer warn"); - throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); - } - - if (temp_fan_configured) { -#ifdef CONFIG_FANS -#ifdef CONFIG_CUSTOM_FAN_CONTROL - for (i = 0; i < fan_get_count(); i++) { - if (!is_thermal_control_enabled(i)) - continue; - - board_override_fan_control(i, temp); - } -#else - /* TODO(crosbug.com/p/23797): For now, we just treat all - * fans the same. It would be better if we could assign - * different thermal profiles to each fan - in case one - * fan cools the CPU while another cools the radios or - * battery. - */ - for (i = 0; i < fan_get_count(); i++) - fan_set_percent_needed(i, fmax); -#endif -#endif - } -} - -/* Wait until after the sensors have been read */ -DECLARE_HOOK(HOOK_SECOND, thermal_control, HOOK_PRIO_TEMP_SENSOR_DONE); - -/*****************************************************************************/ -/* Console commands */ - -static int command_thermalget(int argc, char **argv) -{ - int i; - - ccprintf("sensor warn high halt fan_off fan_max name\n"); - for (i = 0; i < TEMP_SENSOR_COUNT; i++) { - ccprintf(" %2d %3d %3d %3d %3d %3d %s\n", - i, - thermal_params[i].temp_host[EC_TEMP_THRESH_WARN], - thermal_params[i].temp_host[EC_TEMP_THRESH_HIGH], - thermal_params[i].temp_host[EC_TEMP_THRESH_HALT], - thermal_params[i].temp_fan_off, - thermal_params[i].temp_fan_max, - temp_sensors[i].name); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(thermalget, command_thermalget, - NULL, - "Print thermal parameters (degrees Kelvin)"); - - -static int command_thermalset(int argc, char **argv) -{ - unsigned int n; - int i, val; - char *e; - - if (argc < 3 || argc > 7) - return EC_ERROR_PARAM_COUNT; - - n = (unsigned int)strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - for (i = 2; i < argc; i++) { - val = strtoi(argv[i], &e, 0); - if (*e) - return EC_ERROR_PARAM1 + i - 1; - if (val < 0) - continue; - switch (i) { - case 2: - thermal_params[n].temp_host[EC_TEMP_THRESH_WARN] = val; - break; - case 3: - thermal_params[n].temp_host[EC_TEMP_THRESH_HIGH] = val; - break; - case 4: - thermal_params[n].temp_host[EC_TEMP_THRESH_HALT] = val; - break; - case 5: - thermal_params[n].temp_fan_off = val; - break; - case 6: - thermal_params[n].temp_fan_max = val; - break; - } - } - - command_thermalget(0, 0); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(thermalset, command_thermalset, - "sensor warn [high [shutdown [fan_off [fan_max]]]]", - "Set thermal parameters (degrees Kelvin)." - " Use -1 to skip."); - -/*****************************************************************************/ -/* Host commands. We'll reuse the host command number, but this is version 1, - * not version 0. Different structs, different meanings. - */ - -static enum ec_status -thermal_command_set_threshold(struct host_cmd_handler_args *args) -{ - const struct ec_params_thermal_set_threshold_v1 *p = args->params; - - if (p->sensor_num >= TEMP_SENSOR_COUNT) - return EC_RES_INVALID_PARAM; - - thermal_params[p->sensor_num] = p->cfg; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_THERMAL_SET_THRESHOLD, - thermal_command_set_threshold, - EC_VER_MASK(1)); - -static enum ec_status -thermal_command_get_threshold(struct host_cmd_handler_args *args) -{ - const struct ec_params_thermal_get_threshold_v1 *p = args->params; - struct ec_thermal_config *r = args->response; - - if (p->sensor_num >= TEMP_SENSOR_COUNT) - return EC_RES_INVALID_PARAM; - - *r = thermal_params[p->sensor_num]; - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_THERMAL_GET_THRESHOLD, - thermal_command_get_threshold, - EC_VER_MASK(1)); diff --git a/common/throttle_ap.c b/common/throttle_ap.c deleted file mode 100644 index cfa97d93a5..0000000000 --- a/common/throttle_ap.c +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Common chipset throttling code for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "dptf.h" -#include "hooks.h" -#include "host_command.h" -#include "task.h" -#include "throttle_ap.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_THERMAL, outstr) -#define CPRINTS(format, args...) cprints(CC_THERMAL, format, ## args) - -#define PROCHOT_IN_DEBOUNCE_US (100 * MSEC) - -/*****************************************************************************/ -/* This enforces the virtual OR of all throttling sources. */ -K_MUTEX_DEFINE(throttle_mutex); -static uint32_t throttle_request[NUM_THROTTLE_TYPES]; -static int debounced_prochot_in; -static enum gpio_signal gpio_prochot_in = GPIO_COUNT; - -void throttle_ap(enum throttle_level level, - enum throttle_type type, - enum throttle_sources source) -{ - uint32_t tmpval, bitmask; - - mutex_lock(&throttle_mutex); - - bitmask = BIT(source); - - switch (level) { - case THROTTLE_ON: - throttle_request[type] |= bitmask; - break; - case THROTTLE_OFF: - throttle_request[type] &= ~bitmask; - break; - } - - tmpval = throttle_request[type]; /* save for printing */ - - switch (type) { - case THROTTLE_SOFT: -#ifdef HAS_TASK_HOSTCMD - host_throttle_cpu(tmpval); -#endif - break; - case THROTTLE_HARD: -#ifdef CONFIG_CHIPSET_CAN_THROTTLE - chipset_throttle_cpu(tmpval); -#endif - break; - - case NUM_THROTTLE_TYPES: - /* Make the compiler shut up. Don't use 'default', because - * we still want to catch any new types. - */ - break; - } - - mutex_unlock(&throttle_mutex); - - /* print outside the mutex */ - CPRINTS("set AP throttling type %d to %s (0x%08x)", - type, tmpval ? "on" : "off", tmpval); - -} - -static void prochot_input_deferred(void) -{ - int prochot_in; - - /* - * Shouldn't be possible, but better to protect against buffer - * overflow - */ - ASSERT(signal_is_gpio(gpio_prochot_in)); - - prochot_in = gpio_get_level(gpio_prochot_in); - - if (IS_ENABLED(CONFIG_CPU_PROCHOT_ACTIVE_LOW)) - prochot_in = !prochot_in; - - if (prochot_in == debounced_prochot_in) - return; - - /* - * b/173180788 Confirmed from Intel internal that SLP_S3# asserts low - * about 10us before PROCHOT# asserts low, which means that - * the CPU is already in reset and therefore the PROCHOT# - * asserting low is normal behavior and not a concern - * for PROCHOT# event. Ignore all PROCHOT changes while the AP is off - */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - return; - - debounced_prochot_in = prochot_in; - - if (debounced_prochot_in) { - CPRINTS("External PROCHOT assertion detected"); -#ifdef CONFIG_FANS - dptf_set_fan_duty_target(100); -#endif - } else { - CPRINTS("External PROCHOT condition cleared"); -#ifdef CONFIG_FANS - /* Revert to automatic control of the fan */ - dptf_set_fan_duty_target(-1); -#endif - } -} -DECLARE_DEFERRED(prochot_input_deferred); - -void throttle_ap_prochot_input_interrupt(enum gpio_signal signal) -{ - /* - * Save the PROCHOT signal that generated the interrupt so we don't - * rely on a specific pin name. - */ - if (gpio_prochot_in == GPIO_COUNT) - gpio_prochot_in = signal; - - /* - * Trigger deferred notification of PROCHOT change so we can ignore - * any pulses that are too short. - */ - hook_call_deferred(&prochot_input_deferred_data, - PROCHOT_IN_DEBOUNCE_US); -} - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_APTHROTTLE -static int command_apthrottle(int argc, char **argv) -{ - int i; - uint32_t tmpval; - - for (i = 0; i < NUM_THROTTLE_TYPES; i++) { - mutex_lock(&throttle_mutex); - tmpval = throttle_request[i]; - mutex_unlock(&throttle_mutex); - - ccprintf("AP throttling type %d is %s (0x%08x)\n", i, - tmpval ? "on" : "off", tmpval); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(apthrottle, command_apthrottle, - NULL, - "Display the AP throttling state"); -#endif diff --git a/common/update_fw.c b/common/update_fw.c deleted file mode 100644 index 068758e7b0..0000000000 --- a/common/update_fw.c +++ /dev/null @@ -1,328 +0,0 @@ -/* Copyright 2016 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 "byteorder.h" -#include "console.h" -#include "flash.h" -#include "hooks.h" -#include "include/compile_time_macros.h" -#include "rollback.h" -#include "rwsig.h" -#include "sha256.h" -#include "system.h" -#include "uart.h" -#include "update_fw.h" -#include "util.h" -#include "vb21_struct.h" -#include "vboot.h" - -#if defined(CONFIG_TOUCHPAD_VIRTUAL_OFF) && defined(CONFIG_TOUCHPAD_HASH_FW) -#define CONFIG_TOUCHPAD_FW_CHUNKS \ - (CONFIG_TOUCHPAD_VIRTUAL_SIZE / CONFIG_UPDATE_PDU_SIZE) - -#include "touchpad_fw_hash.h" - -BUILD_ASSERT(sizeof(touchpad_fw_hashes) == - (CONFIG_TOUCHPAD_FW_CHUNKS * SHA256_DIGEST_SIZE)); -BUILD_ASSERT(sizeof(touchpad_fw_hashes[0]) == SHA256_DIGEST_SIZE); - -BUILD_ASSERT(sizeof(touchpad_fw_full_hash) == SHA256_DIGEST_SIZE); -#endif - -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) - -/* Section to be updated (i.e. not the current section). */ -struct { - uint32_t base_offset; - uint32_t top_offset; -} update_section; - -#ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF -/* - * Check if a block is within touchpad FW virtual address region, and - * is therefore meant to be flashed to the touchpad. - */ -static int is_touchpad_block(uint32_t block_offset, size_t body_size) -{ - return (block_offset >= CONFIG_TOUCHPAD_VIRTUAL_OFF) && - (block_offset + body_size) <= - (CONFIG_TOUCHPAD_VIRTUAL_OFF + - CONFIG_TOUCHPAD_VIRTUAL_SIZE); -} -#endif - -/* - * Verify that the passed in block fits into the valid area. If it does, and - * is destined to the base address of the area - erase the area contents. - * - * Return success, or indication of an erase failure or chunk not fitting into - * valid area. - * - * TODO(b/36375666): Each board/chip should be able to re-define this. - */ -static uint8_t check_update_chunk(uint32_t block_offset, size_t body_size) -{ - uint32_t base; - uint32_t size; - - /* Is this an RW chunk? */ - if (update_section.base_offset != update_section.top_offset && - (block_offset >= update_section.base_offset) && - ((block_offset + body_size) <= update_section.top_offset)) { - - base = update_section.base_offset; - size = update_section.top_offset - - update_section.base_offset; - /* - * If this is the first chunk for this section, it needs to - * be erased. - */ - if (block_offset == base) { - if (crec_flash_physical_erase(base, size) != - EC_SUCCESS) { - CPRINTF("%s:%d erase failure of 0x%x..+0x%x\n", - __func__, __LINE__, base, size); - return UPDATE_ERASE_FAILURE; - } - } - - return UPDATE_SUCCESS; - } - -#ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF - if (is_touchpad_block(block_offset, body_size)) - return UPDATE_SUCCESS; -#endif - - CPRINTF("%s:%d %x, %d section base %x top %x\n", - __func__, __LINE__, - block_offset, body_size, - update_section.base_offset, - update_section.top_offset); - - return UPDATE_BAD_ADDR; - -} - -int update_pdu_valid(struct update_command *cmd_body, size_t cmd_size) -{ - return 1; -} - -static int chunk_came_too_soon(uint32_t block_offset) -{ - return 0; -} - -static void new_chunk_written(uint32_t block_offset) -{ -} - -static int contents_allowed(uint32_t block_offset, - size_t body_size, void *update_data) -{ -#if defined(CONFIG_TOUCHPAD_VIRTUAL_OFF) && defined(CONFIG_TOUCHPAD_HASH_FW) - if (is_touchpad_block(block_offset, body_size)) { - struct sha256_ctx ctx; - uint8_t *tmp; - uint32_t fw_offset = block_offset - CONFIG_TOUCHPAD_VIRTUAL_OFF; - unsigned int chunk = fw_offset / CONFIG_UPDATE_PDU_SIZE; - int good = 0; - - if (chunk >= CONFIG_TOUCHPAD_FW_CHUNKS || - (fw_offset % CONFIG_UPDATE_PDU_SIZE) != 0) { - CPRINTF("%s: TP invalid offset %08x\n", - __func__, fw_offset); - return 0; - } - - SHA256_init(&ctx); - SHA256_update(&ctx, update_data, body_size); - tmp = SHA256_final(&ctx); - - good = !memcmp(tmp, touchpad_fw_hashes[chunk], - SHA256_DIGEST_SIZE); - - CPRINTF("%s: TP %08x %02x..%02x (%s)\n", __func__, - fw_offset, tmp[0], tmp[31], good ? "GOOD" : "BAD"); - - return good; - } -#endif - return 1; -} - -/* - * Setup internal state (e.g. valid sections, and fill first response). - * - * Assumes rpdu is already prefilled with 0, and that version has already - * been set. May set a return_value != 0 on error. - */ -void fw_update_start(struct first_response_pdu *rpdu) -{ - const char *version; -#ifdef CONFIG_RWSIG_TYPE_RWSIG - const struct vb21_packed_key *vb21_key; -#endif - - rpdu->header_type = htobe16(UPDATE_HEADER_TYPE_COMMON); - - /* Determine the valid update section. */ - switch (system_get_image_copy()) { - case EC_IMAGE_RO: - /* RO running, so update RW */ - update_section.base_offset = CONFIG_RW_MEM_OFF; - update_section.top_offset = CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE; - version = system_get_version(EC_IMAGE_RW); - break; - case EC_IMAGE_RW: - /* RW running, so update RO */ - update_section.base_offset = CONFIG_RO_MEM_OFF; - update_section.top_offset = CONFIG_RO_MEM_OFF + CONFIG_RO_SIZE; - version = system_get_version(EC_IMAGE_RO); - break; - default: - CPRINTF("%s:%d\n", __func__, __LINE__); - rpdu->return_value = htobe32(UPDATE_GEN_ERROR); - return; - } - - rpdu->common.maximum_pdu_size = htobe32(CONFIG_UPDATE_PDU_SIZE); - rpdu->common.flash_protection = htobe32(crec_flash_get_protect()); - rpdu->common.offset = htobe32(update_section.base_offset); - if (version) - memcpy(rpdu->common.version, version, - sizeof(rpdu->common.version)); - -#ifdef CONFIG_ROLLBACK - rpdu->common.min_rollback = htobe32(rollback_get_minimum_version()); -#else - rpdu->common.min_rollback = htobe32(-1); -#endif - -#ifdef CONFIG_RWSIG_TYPE_RWSIG - vb21_key = vb21_get_packed_key(); - rpdu->common.key_version = htobe32(vb21_key->key_version); -#endif - -#ifdef HAS_TASK_RWSIG - /* Do not allow the update to start if RWSIG is still running. */ - if (rwsig_get_status() == RWSIG_IN_PROGRESS) { - CPRINTF("RWSIG in progress\n"); - rpdu->return_value = htobe32(UPDATE_RWSIG_BUSY); - } -#endif -} - -void fw_update_command_handler(void *body, - size_t cmd_size, - size_t *response_size) -{ - struct update_command *cmd_body = body; - void *update_data; - uint8_t *error_code = body; /* Cache the address for code clarity. */ - size_t body_size; - uint32_t block_offset; - - *response_size = 1; /* One byte response unless this is a start PDU. */ - - if (cmd_size < sizeof(struct update_command)) { - CPRINTF("%s:%d\n", __func__, __LINE__); - *error_code = UPDATE_GEN_ERROR; - return; - } - body_size = cmd_size - sizeof(struct update_command); - - if (!cmd_body->block_base && !body_size) { - struct first_response_pdu *rpdu = body; - - /* - * This is the connection establishment request, the response - * allows the server to decide what sections of the image to - * send to program into the flash. - */ - - /* First, prepare the response structure. */ - memset(rpdu, 0, sizeof(*rpdu)); - /* - * TODO(b/36375666): The response size can be shorter depending - * on which board-specific type of response we provide. This - * may send trailing 0 bytes, which should be harmless. - */ - *response_size = sizeof(*rpdu); - rpdu->protocol_version = htobe16(UPDATE_PROTOCOL_VERSION); - - /* Setup internal state (e.g. valid sections, and fill rpdu) */ - fw_update_start(rpdu); - return; - } - - block_offset = be32toh(cmd_body->block_base); - - if (!update_pdu_valid(cmd_body, cmd_size)) { - *error_code = UPDATE_DATA_ERROR; - return; - } - - update_data = cmd_body + 1; - if (!contents_allowed(block_offset, body_size, update_data)) { - *error_code = UPDATE_ROLLBACK_ERROR; - return; - } - - /* Check if the block will fit into the valid area. */ - *error_code = check_update_chunk(block_offset, body_size); - if (*error_code) - return; - - if (chunk_came_too_soon(block_offset)) { - *error_code = UPDATE_RATE_LIMIT_ERROR; - return; - } - -#ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF - if (is_touchpad_block(block_offset, body_size)) { - if (touchpad_update_write( - block_offset - CONFIG_TOUCHPAD_VIRTUAL_OFF, - body_size, update_data) != EC_SUCCESS) { - *error_code = UPDATE_WRITE_FAILURE; - CPRINTF("%s:%d update write error\n", - __func__, __LINE__); - return; - } - - new_chunk_written(block_offset); - - *error_code = UPDATE_SUCCESS; - return; - } -#endif - - CPRINTF("update: 0x%x\n", block_offset + CONFIG_PROGRAM_MEMORY_BASE); - if (crec_flash_physical_write(block_offset, body_size, update_data) - != EC_SUCCESS) { - *error_code = UPDATE_WRITE_FAILURE; - CPRINTF("%s:%d update write error\n", __func__, __LINE__); - return; - } - - new_chunk_written(block_offset); - - /* Verify that data was written properly. */ - if (memcmp(update_data, (void *) - (block_offset + CONFIG_PROGRAM_MEMORY_BASE), - body_size)) { - *error_code = UPDATE_VERIFY_ERROR; - CPRINTF("%s:%d update verification error\n", - __func__, __LINE__); - return; - } - - *error_code = UPDATE_SUCCESS; -} - -void fw_update_complete(void) -{ -} diff --git a/common/usb_charger.c b/common/usb_charger.c deleted file mode 100644 index b8e8038811..0000000000 --- a/common/usb_charger.c +++ /dev/null @@ -1,136 +0,0 @@ -/* Copyright 2015 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. - */ - -/* - * USB charger interface routines. This code assumes that CONFIG_CHARGE_MANAGER - * is defined and implemented. - * usb_charger_set_switches() must be implemented by a companion - * usb_switch driver. - * In addition, USB switch-specific usb_charger task or interrupt routine - * is necessary to update charge_manager with detected charger attributes. - */ - -#include "charge_manager.h" -#include "charger.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "stddef.h" -#include "task.h" -#include "usb_charge.h" -#include "usb_pd.h" -#include "usb_pd_flags.h" -#include "usbc_ppc.h" -#include "util.h" - -static void update_vbus_supplier(int port, int vbus_level) -{ - struct charge_port_info charge = {0}; - - if (vbus_level && !usb_charger_port_is_sourcing_vbus(port)) { - charge.voltage = USB_CHARGER_VOLTAGE_MV; - charge.current = USB_CHARGER_MIN_CURR_MA; - } - - charge_manager_update_charge(CHARGE_SUPPLIER_VBUS, port, &charge); -} - -#ifdef CONFIG_USB_PD_5V_EN_CUSTOM -#define USB_5V_EN(port) board_is_sourcing_vbus(port) -#elif defined(CONFIG_USBC_PPC) -#define USB_5V_EN(port) ppc_is_sourcing_vbus(port) -#elif defined(CONFIG_USB_PD_PPC) -#define USB_5V_EN(port) tcpci_tcpm_get_src_ctrl(port) -#elif defined(CONFIG_USB_PD_5V_CHARGER_CTRL) -#define USB_5V_EN(port) charger_is_sourcing_otg_power(port) -#elif defined(CONFIG_USB_PD_5V_EN_ACTIVE_LOW) -#define USB_5V_EN(port) !gpio_get_level(GPIO_USB_C##port##_5V_EN_L) -#else -#define USB_5V_EN(port) gpio_get_level(GPIO_USB_C##port##_5V_EN) -#endif - -int usb_charger_port_is_sourcing_vbus(int port) -{ - if (port == 0) - return USB_5V_EN(0); -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 - else if (port == 1) - return USB_5V_EN(1); -#endif - /* Not a valid port */ - return 0; -} - -void usb_charger_vbus_change(int port, int vbus_level) -{ - /* If VBUS has transitioned low, notify PD module directly */ - if (!vbus_level) - pd_vbus_low(port); - - /* Update VBUS supplier and signal VBUS change to USB_CHG task */ - update_vbus_supplier(port, vbus_level); - -#ifdef HAS_TASK_USB_CHG_P0 - /* USB Charger task(s) */ - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), USB_CHG_EVENT_VBUS); - - /* If we swapped to sourcing, drop any related charge suppliers */ - if (usb_charger_port_is_sourcing_vbus(port)) - usb_charger_reset_charge(port); -#endif - - if ((get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_CHARGER) || - (get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_PPC)) { - /* USB PD task */ - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void usb_charger_reset_charge(int port) -{ - charge_manager_update_charge(CHARGE_SUPPLIER_PROPRIETARY, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_CDP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_DCP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_BC12_SDP, - port, NULL); - charge_manager_update_charge(CHARGE_SUPPLIER_OTHER, - port, NULL); -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - 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) -{ - int i; - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - usb_charger_reset_charge(i); - /* Initialize VBUS supplier based on whether VBUS is present. */ - update_vbus_supplier(i, pd_is_vbus_present(i)); - } -} -DECLARE_HOOK(HOOK_INIT, usb_charger_init, HOOK_PRIO_CHARGE_MANAGER_INIT + 1); - -void usb_charger_task(void *u) -{ - int port = TASK_ID_TO_USB_CHG_PORT(task_get_current()); - - ASSERT(bc12_ports[port].drv->usb_charger_task); - bc12_ports[port].drv->usb_charger_task(port); -} diff --git a/common/usb_common.c b/common/usb_common.c deleted file mode 100644 index 786bd118cf..0000000000 --- a/common/usb_common.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* 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. - */ - -/* - * Contains common USB functions shared between the old (i.e. usb_pd_protocol) - * and the new (i.e. usb_sm_*) USB-C PD stacks. - */ - -#include "atomic.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "stdbool.h" -#include "host_command.h" -#include "system.h" -#include "task.h" -#include "usb_api.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_flags.h" -#include "usb_pd_tcpm.h" -#include "usbc_ocp.h" -#include "usbc_ppc.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -/* - * If we are trying to upgrade PD firmwares (TCPC chips, retimer, etc), we - * need to ensure the battery has enough charge for this process. 100mAh - * is about 5% of most batteries, and it should be enough charge to get us - * through the EC jump to RW and PD upgrade. - */ -#define MIN_BATTERY_FOR_PD_UPGRADE_MAH 100 /* mAH */ - -#if defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) -int hex8tou32(char *str, uint32_t *val) -{ - char *ptr = str; - uint32_t tmp = 0; - - while (*ptr) { - char c = *ptr++; - - if (c >= '0' && c <= '9') - tmp = (tmp << 4) + (c - '0'); - else if (c >= 'A' && c <= 'F') - tmp = (tmp << 4) + (c - 'A' + 10); - else if (c >= 'a' && c <= 'f') - tmp = (tmp << 4) + (c - 'a' + 10); - else - return EC_ERROR_INVAL; - } - if (ptr != str + 8) - return EC_ERROR_INVAL; - *val = tmp; - return EC_SUCCESS; -} - -int remote_flashing(int argc, char **argv) -{ - int port, cnt, cmd; - uint32_t data[VDO_MAX_SIZE-1]; - char *e; - static int flash_offset[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (argc < 4 || argc > (VDO_MAX_SIZE + 4 - 1)) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &e, 10); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - cnt = 0; - if (!strcasecmp(argv[3], "erase")) { - cmd = VDO_CMD_FLASH_ERASE; - flash_offset[port] = 0; - ccprintf("ERASE ..."); - } else if (!strcasecmp(argv[3], "reboot")) { - cmd = VDO_CMD_REBOOT; - ccprintf("REBOOT ..."); - } else if (!strcasecmp(argv[3], "signature")) { - cmd = VDO_CMD_ERASE_SIG; - ccprintf("ERASE SIG ..."); - } else if (!strcasecmp(argv[3], "info")) { - cmd = VDO_CMD_READ_INFO; - ccprintf("INFO..."); - } else if (!strcasecmp(argv[3], "version")) { - cmd = VDO_CMD_VERSION; - ccprintf("VERSION..."); - } else { - int i; - - argc -= 3; - for (i = 0; i < argc; i++) - if (hex8tou32(argv[i+3], data + i)) - return EC_ERROR_INVAL; - cmd = VDO_CMD_FLASH_WRITE; - cnt = argc; - ccprintf("WRITE %d @%04x ...", argc * 4, - flash_offset[port]); - flash_offset[port] += argc * 4; - } - - pd_send_vdm(port, USB_VID_GOOGLE, cmd, data, cnt); - - /* Wait until VDM is done */ - while (pd[port].vdm_state > 0) - task_wait_event(100*MSEC); - - ccprintf("DONE %d\n", pd[port].vdm_state); - return EC_SUCCESS; -} -#endif /* defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) */ - -bool pd_firmware_upgrade_check_power_readiness(int port) -{ - if (IS_ENABLED(HAS_TASK_CHARGER)) { - struct batt_params batt = { 0 }; - /* - * Cannot rely on the EC's active charger data as the - * EC may just rebooted into RW and has not necessarily - * picked the active charger yet. Charger task may not - * initialized, so check battery directly. - * Prevent the upgrade if the battery doesn't have enough - * charge to finish the upgrade. - */ - battery_get_params(&batt); - if (batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY || - batt.remaining_capacity < - MIN_BATTERY_FOR_PD_UPGRADE_MAH) { - CPRINTS("C%d: Cannot suspend for upgrade, not " - "enough battery (%dmAh)!", - port, batt.remaining_capacity); - return false; - } - } else { - /* VBUS is present on the port (it is either a - * source or sink) to provide power, so don't allow - * PD firmware upgrade on the port. - */ - if (pd_is_vbus_present(port)) - return false; - } - - return true; -} - -int usb_get_battery_soc(void) -{ -#if defined(CONFIG_CHARGER) - return charge_get_percent(); -#elif defined(CONFIG_BATTERY) - return board_get_battery_soc(); -#else - return 0; -#endif -} - -#if defined(CONFIG_USB_PD_PREFER_MV) && defined(PD_PREFER_LOW_VOLTAGE) + \ - defined(PD_PREFER_HIGH_VOLTAGE) > 1 -#error "PD preferred voltage strategy should be mutually exclusive." -#endif - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -typec_current_t usb_get_typec_current_limit(enum tcpc_cc_polarity polarity, - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - typec_current_t charge = 0; - enum tcpc_cc_voltage_status cc; - enum tcpc_cc_voltage_status cc_alt; - - cc = polarity_rm_dts(polarity) ? cc2 : cc1; - cc_alt = polarity_rm_dts(polarity) ? cc1 : cc2; - - switch (cc) { - case TYPEC_CC_VOLT_RP_3_0: - if (!cc_is_rp(cc_alt) || cc_alt == TYPEC_CC_VOLT_RP_DEF) - charge = 3000; - else if (cc_alt == TYPEC_CC_VOLT_RP_1_5) - charge = 500; - break; - case TYPEC_CC_VOLT_RP_1_5: - charge = 1500; - break; - case TYPEC_CC_VOLT_RP_DEF: - charge = 500; - break; - default: - break; - } - - if (IS_ENABLED(CONFIG_USBC_DISABLE_CHARGE_FROM_RP_DEF) && charge == 500) - charge = 0; - - if (cc_is_rp(cc_alt)) - charge |= TYPEC_CURRENT_DTS_MASK; - - return charge; -} - -enum tcpc_cc_polarity get_snk_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - /* The following assumes: - * - * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5 - * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF - * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN - */ - if (cc_is_src_dbg_acc(cc1, cc2)) - return (cc1 > cc2) ? POLARITY_CC1_DTS : POLARITY_CC2_DTS; - - return (cc1 > cc2) ? POLARITY_CC1 : POLARITY_CC2; -} - -enum tcpc_cc_polarity get_src_polarity(enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2) -{ - return (cc1 == TYPEC_CC_VOLT_RD) ? POLARITY_CC1 : POLARITY_CC2; -} - -enum pd_cc_states pd_get_cc_state( - enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2) -{ - /* Port partner is a SNK */ - if (cc_is_snk_dbg_acc(cc1, cc2)) - return PD_CC_UFP_DEBUG_ACC; - if (cc_is_at_least_one_rd(cc1, cc2)) - return PD_CC_UFP_ATTACHED; - if (cc_is_audio_acc(cc1, cc2)) - return PD_CC_UFP_AUDIO_ACC; - - /* Port partner is a SRC */ - if (cc_is_rp(cc1) && cc_is_rp(cc2)) - return PD_CC_DFP_DEBUG_ACC; - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - return PD_CC_DFP_ATTACHED; - - /* - * 1) Both lines are Vopen or - * 2) Only an e-marked cabled without a partner on the other side - */ - return PD_CC_NONE; -} - -/** - * This function checks the current CC status of the port partner - * and returns true if the attached partner is debug accessory. - */ -bool pd_is_debug_acc(int port) -{ - enum pd_cc_states cc_state = pd_get_task_cc_state(port); - - return cc_state == PD_CC_UFP_DEBUG_ACC || - cc_state == PD_CC_DFP_DEBUG_ACC; -} - -void pd_set_polarity(int port, enum tcpc_cc_polarity polarity) -{ - tcpm_set_polarity(port, polarity); - - if (IS_ENABLED(CONFIG_USBC_PPC_POLARITY)) - ppc_set_polarity(port, polarity); -} - -__overridable int pd_board_check_request(uint32_t rdo, int pdo_cnt) -{ - return EC_SUCCESS; -} - -int pd_check_requested_voltage(uint32_t rdo, const int port) -{ - int max_ma = rdo & 0x3FF; - int op_ma = (rdo >> 10) & 0x3FF; - int idx = RDO_POS(rdo); - uint32_t pdo; - uint32_t pdo_ma; -#if defined(CONFIG_USB_PD_TCPMV2) && defined(CONFIG_USB_PE_SM) - const uint32_t *src_pdo; - const int pdo_cnt = dpm_get_source_pdo(&src_pdo, port); -#elif defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int pdo_cnt = pd_src_pdo_cnt; -#endif - - /* Check for invalid index */ - if (!idx || idx > pdo_cnt) - return EC_ERROR_INVAL; - - /* Board specific check for this request */ - if (pd_board_check_request(rdo, pdo_cnt)) - return EC_ERROR_INVAL; - - /* check current ... */ - pdo = src_pdo[idx - 1]; - pdo_ma = (pdo & 0x3ff); - - if (op_ma > pdo_ma) - return EC_ERROR_INVAL; /* too much op current */ - - if (max_ma > pdo_ma && !(rdo & RDO_CAP_MISMATCH)) - return EC_ERROR_INVAL; /* too much max current */ - - CPRINTF("Requested %d mV %d mA (for %d/%d mA)\n", - ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, - op_ma * 10, max_ma * 10); - - /* Accept the requested voltage */ - return EC_SUCCESS; -} - -__overridable uint8_t board_get_usb_pd_port_count(void) -{ - return CONFIG_USB_PD_PORT_MAX_COUNT; -} - -__overridable bool board_is_usb_pd_port_present(int port) -{ - /* - * Use board_get_usb_pd_port_count() instead of checking - * CONFIG_USB_PD_PORT_MAX_COUNT directly here for legacy boards - * that implement board_get_usb_pd_port_count() but do not - * implement board_is_usb_pd_port_present(). - */ - - return (port >= 0) && (port < board_get_usb_pd_port_count()); -} - -__overridable bool board_is_dts_port(int port) -{ - return true; -} - -int pd_get_retry_count(int port, enum tcpci_msg_type type) -{ - /* PD 3.0 6.7.7: nRetryCount = 2; PD 2.0 6.6.9: nRetryCount = 3 */ - return pd_get_rev(port, type) == PD_REV30 ? 2 : 3; -} - -enum pd_drp_next_states drp_auto_toggle_next_state( - uint64_t *drp_sink_time, - enum pd_power_role power_role, - enum pd_dual_role_states drp_state, - enum tcpc_cc_voltage_status cc1, - enum tcpc_cc_voltage_status cc2, - bool auto_toggle_supported) -{ - const bool hardware_debounced_unattached = - ((drp_state == PD_DRP_TOGGLE_ON) && - auto_toggle_supported); - - /* Set to appropriate port state */ - if (cc_is_open(cc1, cc2)) { - /* - * If nothing is attached then use drp_state to determine next - * state. If DRP auto toggle is still on, then remain in the - * DRP_AUTO_TOGGLE state. Otherwise, stop dual role toggling - * and go to a disconnected state. - */ - switch (drp_state) { - case PD_DRP_TOGGLE_OFF: - return DRP_TC_DEFAULT; - case PD_DRP_FREEZE: - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_FORCE_SINK: - return DRP_TC_UNATTACHED_SNK; - case PD_DRP_FORCE_SOURCE: - return DRP_TC_UNATTACHED_SRC; - case PD_DRP_TOGGLE_ON: - default: - if (!auto_toggle_supported) { - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - } - - return DRP_TC_DRP_AUTO_TOGGLE; - } - } else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && - drp_state != PD_DRP_FORCE_SOURCE) { - /* SNK allowed unless ForceSRC */ - if (hardware_debounced_unattached) - return DRP_TC_ATTACHED_WAIT_SNK; - return DRP_TC_UNATTACHED_SNK; - } else if (cc_is_at_least_one_rd(cc1, cc2) || - cc_is_audio_acc(cc1, cc2)) { - /* - * SRC allowed unless ForceSNK or Toggle Off - * - * Ideally we wouldn't use auto-toggle when drp_state is - * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't - * be prevented in low power mode. Try being a sink in case the - * connected device is dual-role (this ensures reliable charging - * from a hub, b/72007056). 100 ms is enough time for a - * dual-role partner to switch from sink to source. If the - * connected device is sink-only, then we will attempt - * TC_UNATTACHED_SNK twice (due to debounce time), then return - * to low power mode (and stay there). After 200 ms, reset - * ready for a new connection. - */ - if (drp_state == PD_DRP_TOGGLE_OFF || - drp_state == PD_DRP_FORCE_SINK) { - if (get_time().val > *drp_sink_time + 200*MSEC) - *drp_sink_time = get_time().val; - if (get_time().val < *drp_sink_time + 100*MSEC) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_DRP_AUTO_TOGGLE; - } else { - if (hardware_debounced_unattached) - return DRP_TC_ATTACHED_WAIT_SRC; - return DRP_TC_UNATTACHED_SRC; - } - } else { - /* Anything else, keep toggling */ - if (!auto_toggle_supported) { - if (power_role == PD_ROLE_SINK) - return DRP_TC_UNATTACHED_SNK; - else - return DRP_TC_UNATTACHED_SRC; - } - - return DRP_TC_DRP_AUTO_TOGGLE; - } -} - -__overridable bool usb_ufp_check_usb3_enable(int port) -{ - return false; -} - -mux_state_t get_mux_mode_to_set(int port) -{ - /* - * If the SoC is down, then we disconnect the MUX to save power since - * no one cares about the data lines. - */ - if (IS_ENABLED(CONFIG_POWER_COMMON) && - chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - return USB_PD_MUX_NONE; - - /* - * When PD stack is disconnected, then mux should be disconnected, which - * is also what happens in the set_state disconnection code. Once the - * PD state machine progresses out of disconnect, the MUX state will - * be set correctly again. - */ - if (pd_is_disconnected(port)) - return USB_PD_MUX_NONE; - - /* - * For type-c only connections, there may be a need to enable USB3.1 - * mode when the port is in a UFP data role, independent of any other - * conditions which are checked below. The default function returns - * false, so only boards that override this check will be affected. - */ - if (usb_ufp_check_usb3_enable(port) && pd_get_data_role(port) - == PD_ROLE_UFP) - return USB_PD_MUX_USB_ENABLED; - - /* If new data role isn't DFP & we only support DFP, also disconnect. */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_SS_MUX_DFP_ONLY) && - pd_get_data_role(port) != PD_ROLE_DFP) - return USB_PD_MUX_NONE; - - /* If new data role isn't UFP & we only support UFP then disconnect. */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_SS_MUX_UFP_ONLY) && - pd_get_data_role(port) != PD_ROLE_UFP) - return USB_PD_MUX_NONE; - - /* - * If the power role is sink and the PD partner device is not capable - * of USB communication then disconnect. - * - * On an entry into Unattached.SNK, the partner may be PD capable but - * hasn't yet sent source capabilities. In this case, hold off enabling - * USB3 termination until the PD capability is resolved. - * - * TODO(b/188588458): TCPMv2: Delay enabling USB3 termination when USB4 - * is supported. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - pd_get_power_role(port) == PD_ROLE_SINK && - (pd_capable(port) || pd_waiting_on_partner_src_caps(port)) && - !pd_get_partner_usb_comm_capable(port)) - return USB_PD_MUX_NONE; - - /* Otherwise connect mux since we are in S3+ */ - return USB_PD_MUX_USB_ENABLED; -} - -void set_usb_mux_with_current_data_role(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) { - mux_state_t mux_mode = get_mux_mode_to_set(port); - enum usb_switch usb_switch_mode = - (mux_mode == USB_PD_MUX_NONE) ? - USB_SWITCH_DISCONNECT : USB_SWITCH_CONNECT; - - usb_mux_set(port, mux_mode, usb_switch_mode, - polarity_rm_dts(pd_get_polarity(port))); - } -} - -void usb_mux_set_safe_mode(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) { - usb_mux_set(port, IS_ENABLED(CONFIG_USB_MUX_VIRTUAL) ? - USB_PD_MUX_SAFE_MODE : USB_PD_MUX_NONE, - USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - } - - /* Isolate the SBU lines. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 0); -} - -void usb_mux_set_safe_mode_exit(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - /* Isolate the SBU lines. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 0); -} - -static void pd_send_hard_reset(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET); -} - -#ifdef CONFIG_USBC_OCP - -static uint32_t port_oc_reset_req; - -static void re_enable_ports(void) -{ - uint32_t ports = atomic_clear(&port_oc_reset_req); - - while (ports) { - int port = __fls(ports); - - ports &= ~BIT(port); - - /* - * Let the board know that the overcurrent is - * over since we're going to attempt re-enabling - * the port. - */ - board_overcurrent_event(port, 0); - - pd_send_hard_reset(port); - /* - * TODO(b/117854867): PD3.0 to send an alert message - * indicating OCP after explicit contract. - */ - } -} -DECLARE_DEFERRED(re_enable_ports); - -void pd_handle_overcurrent(int port) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return; - } - - CPRINTS("C%d: overcurrent!", port); - - if (IS_ENABLED(CONFIG_USB_PD_LOGGING)) - pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), - PS_FAULT_OCP, NULL); - - /* No action to take if disconnected, just log. */ - if (pd_is_disconnected(port)) - return; - - /* Keep track of the overcurrent events. */ - usbc_ocp_add_event(port); - - /* Let the board specific code know about the OC event. */ - board_overcurrent_event(port, 1); - - /* Wait 1s before trying to re-enable the port. */ - atomic_or(&port_oc_reset_req, BIT(port)); - hook_call_deferred(&re_enable_ports_data, SECOND); -} - -#endif /* CONFIG_USBC_OCP */ - -__maybe_unused void pd_handle_cc_overvoltage(int port) -{ - pd_send_hard_reset(port); -} - -__overridable int pd_board_checks(void) -{ - return EC_SUCCESS; -} - -__overridable int pd_check_data_swap(int port, - enum pd_data_role data_role) -{ - /* Allow data swap if we are a UFP, otherwise don't allow. */ - return (data_role == PD_ROLE_UFP) ? 1 : 0; -} - -__overridable int pd_check_power_swap(int port) -{ - /* - * Allow power swap if we are acting as a dual role device. If we are - * not acting as dual role (ex. suspended), then only allow power swap - * if we are sourcing when we could be sinking. - */ - if (pd_get_dual_role(port) == PD_DRP_TOGGLE_ON) - return 1; - else if (pd_get_power_role(port) == PD_ROLE_SOURCE) - return 1; - - return 0; -} - -__overridable void pd_execute_data_swap(int port, - enum pd_data_role data_role) -{ -} - -__overridable enum pd_dual_role_states pd_get_drp_state_in_suspend(void) -{ - /* Disable dual role when going to suspend */ - return PD_DRP_TOGGLE_OFF; -} - -__overridable void pd_try_execute_vconn_swap(int port, int flags) -{ - /* - * If partner is dual-role power and vconn swap is enabled, consider - * if vconn swapping is necessary. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_VCONN_SWAP)) - pd_try_vconn_src(port); -} - -__overridable int pd_is_valid_input_voltage(int mv) -{ - return 1; -} - -__overridable void pd_transition_voltage(int idx) -{ - /* Most devices are fixed 5V output. */ -} - -__overridable void typec_set_source_current_limit(int p, enum tcpc_rp_value rp) -{ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_set_vbus_source_current_limit(p, rp); -} - -/* ---------------- Power Data Objects (PDOs) ----------------- */ -#ifndef CONFIG_USB_PD_CUSTOM_PDO -#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\ - PDO_FIXED_COMM_CAP) - -const uint32_t pd_src_pdo[] = { - PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); -const uint32_t pd_src_pdo_max[] = { - PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_max_cnt = ARRAY_SIZE(pd_src_pdo_max); - -const uint32_t pd_snk_pdo[] = { - PDO_FIXED(5000, - GENERIC_MIN((PD_OPERATING_POWER_MW / 5), PD_MAX_CURRENT_MA), - PDO_FIXED_FLAGS), - PDO_BATT(4750, PD_MAX_VOLTAGE_MV, PD_OPERATING_POWER_MW), - PDO_VAR(4750, PD_MAX_VOLTAGE_MV, PD_MAX_CURRENT_MA), -}; -const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); -#endif /* CONFIG_USB_PD_CUSTOM_PDO */ - -/* ----------------- Vendor Defined Messages ------------------ */ -#if defined(CONFIG_USB_PE_SM) && !defined(CONFIG_USB_VPD) && \ - !defined(CONFIG_USB_CTVPD) -__overridable int pd_custom_vdm(int port, int cnt, uint32_t *payload, - uint32_t **rpayload) -{ - int cmd = PD_VDO_CMD(payload[0]); - uint16_t dev_id = 0; - int is_rw, is_latest; - - /* make sure we have some payload */ - if (cnt == 0) - return 0; - - /* Only handle custom requests for SVID Google */ - if (PD_VDO_VID(*payload) != USB_VID_GOOGLE) - return 0; - - switch (cmd) { - case VDO_CMD_VERSION: - /* guarantee last byte of payload is null character */ - *(payload + cnt - 1) = 0; - CPRINTF("version: %s\n", (char *)(payload+1)); - break; - case VDO_CMD_READ_INFO: - case VDO_CMD_SEND_INFO: - /* copy hash */ - if (cnt == 7) { - dev_id = VDO_INFO_HW_DEV_ID(payload[6]); - is_rw = VDO_INFO_IS_RW(payload[6]); - - is_latest = pd_dev_store_rw_hash( - port, dev_id, payload + 1, - is_rw ? EC_IMAGE_RW : EC_IMAGE_RO); - - /* - * Send update host event unless our RW hash is - * already known to be the latest update RW. - */ - if (!is_rw || !is_latest) - pd_send_host_event(PD_EVENT_UPDATE_DEVICE); - - CPRINTF("DevId:%d.%d SW:%d RW:%d\n", - HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id), - VDO_INFO_SW_DBG_VER(payload[6]), - is_rw); - } else if (cnt == 6) { - /* really old devices don't have last byte */ - pd_dev_store_rw_hash(port, dev_id, payload + 1, - EC_IMAGE_UNKNOWN); - } - break; - case VDO_CMD_CURRENT: - CPRINTF("Current: %dmA\n", payload[1]); - break; - case VDO_CMD_FLIP: - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_flip(port); - break; -#ifdef CONFIG_USB_PD_LOGGING - case VDO_CMD_GET_LOG: - pd_log_recv_vdm(port, cnt, payload); - break; -#endif /* CONFIG_USB_PD_LOGGING */ - } - - return 0; -} -#endif /* CONFIG_USB_PE_SM && !CONFIG_USB_VPD && !CONFIG_USB_CTVPD */ - -__overridable bool vboot_allow_usb_pd(void) -{ - return false; -} - -/* VDM utility functions */ -static void pd_usb_billboard_deferred(void) -{ - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE) && - !IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP) && - !IS_ENABLED(CONFIG_USB_PD_SIMPLE_DFP) && - IS_ENABLED(CONFIG_USB_BOS)) { - /* - * TODO(tbroch) - * 1. Will we have multiple type-C port UFPs - * 2. Will there be other modes applicable to DFPs besides DP - */ - if (!pd_alt_mode(0, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT)) - usb_connect(); - } -} -DECLARE_DEFERRED(pd_usb_billboard_deferred); - -#ifdef CONFIG_USB_PD_DISCHARGE -static void gpio_discharge_vbus(int port, int enable) -{ -#ifdef CONFIG_USB_PD_DISCHARGE_GPIO - enum gpio_signal dischg_gpio[] = { - GPIO_USB_C0_DISCHARGE, -#if CONFIG_USB_PD_PORT_MAX_COUNT > 1 - GPIO_USB_C1_DISCHARGE, -#endif -#if CONFIG_USB_PD_PORT_MAX_COUNT > 2 - GPIO_USB_C2_DISCHARGE, -#endif - }; - BUILD_ASSERT(ARRAY_SIZE(dischg_gpio) == CONFIG_USB_PD_PORT_MAX_COUNT); - - gpio_set_level(dischg_gpio[port], enable); -#endif /* CONFIG_USB_PD_DISCHARGE_GPIO */ -} - -void pd_set_vbus_discharge(int port, int enable) -{ - static mutex_t discharge_lock[CONFIG_USB_PD_PORT_MAX_COUNT]; -#ifdef CONFIG_ZEPHYR - static bool inited[CONFIG_USB_PD_PORT_MAX_COUNT]; - - if (!inited[port]) { - (void)k_mutex_init(&discharge_lock[port]); - inited[port] = true; - } -#endif - if (port >= board_get_usb_pd_port_count()) - return; - - mutex_lock(&discharge_lock[port]); - enable &= !board_vbus_source_enabled(port); - - if (get_usb_pd_discharge() == USB_PD_DISCHARGE_GPIO) { - gpio_discharge_vbus(port, enable); - } else if (get_usb_pd_discharge() == USB_PD_DISCHARGE_TCPC) { -#ifdef CONFIG_USB_PD_DISCHARGE_PPC - tcpc_discharge_vbus(port, enable); -#endif - } else if (get_usb_pd_discharge() == USB_PD_DISCHARGE_PPC) { -#ifdef CONFIG_USB_PD_DISCHARGE_PPC - ppc_discharge_vbus(port, enable); -#endif - } - - mutex_unlock(&discharge_lock[port]); -} -#endif /* CONFIG_USB_PD_DISCHARGE */ - -#ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; -static void resume_pd_port(void) -{ - uint32_t port; - uint32_t suspended_ports = atomic_clear(&pd_ports_to_resume); - - while (suspended_ports) { - port = __builtin_ctz(suspended_ports); - suspended_ports &= ~BIT(port); - pd_set_suspend(port, 0); - } -} -DECLARE_DEFERRED(resume_pd_port); - -void pd_deferred_resume(int port) -{ - atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, 5 * SECOND); -} -#endif /* CONFIG_USB_PD_TCPM_TCPCI */ - -__overridable int pd_snk_is_vbus_provided(int port) -{ - return EC_SUCCESS; -} - -/* - * Check the specified Vbus level - * - * Note that boards may override this function if they have a method outside the - * TCPCI driver to verify vSafe0V. - */ -__overridable bool pd_check_vbus_level(int port, enum vbus_level level) -{ - if (IS_ENABLED(CONFIG_USB_PD_VBUS_DETECT_TCPC) && - (get_usb_pd_vbus_detect() == USB_PD_VBUS_DETECT_TCPC)) { - return tcpm_check_vbus_level(port, level); - } - else if (level == VBUS_PRESENT) - return pd_snk_is_vbus_provided(port); - else - return !pd_snk_is_vbus_provided(port); -} - -int pd_is_vbus_present(int port) -{ - return pd_check_vbus_level(port, VBUS_PRESENT); -} - -#ifdef CONFIG_USB_PD_FRS -__overridable int board_pd_set_frs_enable(int port, int enable) -{ - return EC_SUCCESS; -} - -int pd_set_frs_enable(int port, int enable) -{ - int rv = EC_SUCCESS; - - if (IS_ENABLED(CONFIG_USB_PD_FRS_PPC)) - rv = ppc_set_frs_enable(port, enable); - if (rv == EC_SUCCESS && IS_ENABLED(CONFIG_USB_PD_FRS_TCPC)) - rv = tcpm_set_frs_enable(port, enable); - if (rv == EC_SUCCESS) - rv = board_pd_set_frs_enable(port, enable); - return rv; -} -#endif /* defined(CONFIG_USB_PD_FRS) */ - -#ifdef CONFIG_CMD_TCPC_DUMP -/* - * Dump TCPC registers. - */ -void tcpc_dump_registers(int port, const struct tcpc_reg_dump_map *reg, - int count) -{ - int i, val; - - for (i = 0; i < count; i++, reg++) { - switch (reg->size) { - case 1: - tcpc_read(port, reg->addr, &val); - ccprintf(" %-30s(0x%02x) = 0x%02x\n", - reg->name, reg->addr, (uint8_t)val); - break; - case 2: - tcpc_read16(port, reg->addr, &val); - ccprintf(" %-30s(0x%02x) = 0x%04x\n", - reg->name, reg->addr, (uint16_t)val); - break; - } - cflush(); - } - -} - -static int command_tcpc_dump(int argc, char **argv) -{ - int port; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - port = atoi(argv[1]); - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - /* Dump TCPC registers. */ - tcpm_dump_registers(port); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(tcpci_dump, command_tcpc_dump, "<Type-C port>", - "dump the TCPC regs"); -#endif /* defined(CONFIG_CMD_TCPC_DUMP) */ - -void pd_srccaps_dump(int port) -{ - int i; - const uint32_t *const srccaps = pd_get_src_caps(port); - - for (i = 0; i < pd_get_src_cap_cnt(port); ++i) { - uint32_t max_ma, max_mv, min_mv; - - pd_extract_pdo_power(srccaps[i], &max_ma, &max_mv, &min_mv); - - if ((srccaps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - ccprintf("%d: %dmV-%dmV/%dmA\n", i, min_mv, - max_mv, max_ma); - } else { - ccprintf("%d: %dmV/%dmA\n", i, max_mv, max_ma); - } - } -} - -int pd_build_alert_msg(uint32_t *msg, uint32_t *len, enum pd_power_role pr) -{ - if (msg == NULL || len == NULL) - return EC_ERROR_INVAL; - - /* - * SOURCE: currently only supports OCP - * SINK: currently only supports OVP - */ - if (pr == PD_ROLE_SOURCE) - *msg = ADO_OCP_EVENT; - else - *msg = ADO_OVP_EVENT; - - /* Alert data is 4 bytes */ - *len = 4; - - return EC_SUCCESS; -} diff --git a/common/usb_console_stream.c b/common/usb_console_stream.c deleted file mode 100644 index 13dd7f8264..0000000000 --- a/common/usb_console_stream.c +++ /dev/null @@ -1,238 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "config.h" -#include "console.h" -#include "crc.h" -#include "link_defs.h" -#include "printf.h" -#include "queue.h" -#include "task.h" -#include "timer.h" -#include "usb-stream.h" - -#ifdef CONFIG_USB_CONSOLE -/* - * CONFIG_USB_CONSOLE and CONFIG_USB_CONSOLE_STREAM should be defined - * exclusively each other. - */ -#error "Do not enable CONFIG_USB_CONSOLE." -#endif - -/* Console output macro */ -#define USB_CONSOLE_TIMEOUT_US (30 * MSEC) - -#define QUEUE_SIZE_USB_TX CONFIG_USB_CONSOLE_TX_BUF_SIZE -#define QUEUE_SIZE_USB_RX USB_MAX_PACKET_SIZE - -static void usb_console_wr(struct queue_policy const *policy, size_t count); -static void uart_console_rd(struct queue_policy const *policy, size_t count); - - -static int last_tx_ok = 1; - -/* - * Start enabled, so we can queue early debug output before the board gets - * around to calling usb_console_enable(). - */ -static int is_enabled = 1; - -/* - * But start read-only, so we don't accept console input until we explicitly - * decide that we're ready for it. - */ -static int is_readonly = 1; - -/* - * This is a usb_console producer policy, which wakes up CONSOLE task whenever - * rx_q gets new data added. This shall be called by rx_stream_handler() in - * usb-stream.c. - */ -static struct queue_policy const usb_console_policy = { - .add = usb_console_wr, - .remove = uart_console_rd, -}; - -static struct queue const tx_q = QUEUE_NULL(QUEUE_SIZE_USB_TX, uint8_t); -static struct queue const rx_q = QUEUE(QUEUE_SIZE_USB_RX, uint8_t, - usb_console_policy); - -struct usb_stream_config const usb_console; - -USB_STREAM_CONFIG(usb_console, - USB_IFACE_CONSOLE, - USB_STR_CONSOLE_NAME, - USB_EP_CONSOLE, - USB_MAX_PACKET_SIZE, - USB_MAX_PACKET_SIZE, - rx_q, - tx_q) - -static void usb_console_wr(struct queue_policy const *policy, size_t count) -{ - console_has_input(); -} - -static void uart_console_rd(struct queue_policy const *policy, size_t count) -{ - /* do nothing */ -} - -static void handle_output(void) -{ - /* Wake up the Tx FIFO handler */ - usb_console.consumer.ops->written(&usb_console.consumer, 1); -} - -static int usb_wait_console(void) -{ - timestamp_t deadline = get_time(); - int wait_time_us = 1; - - if (!is_enabled || !tx_fifo_is_ready(&usb_console)) - return EC_SUCCESS; - - deadline.val += USB_CONSOLE_TIMEOUT_US; - - /* - * If the USB console is not used, Tx buffer would never free up. - * In this case, let's drop characters immediately instead of sitting - * for some time just to time out. On the other hand, if the last - * Tx is good, it's likely the host is there to receive data, and - * we should wait so that we don't clobber the buffer. - */ - if (last_tx_ok) { - while (queue_space(&tx_q) < USB_MAX_PACKET_SIZE || - !*usb_console.is_reset) { - if (timestamp_expired(deadline, NULL) || - in_interrupt_context()) { - last_tx_ok = 0; - return EC_ERROR_TIMEOUT; - } - if (wait_time_us < MSEC) - udelay(wait_time_us); - else - usleep(wait_time_us); - wait_time_us *= 2; - } - } else { - last_tx_ok = queue_space(&tx_q); - } - - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_CONSOLE_CRC -static uint32_t usb_tx_crc_ctx; - -void usb_console_crc_init(void) -{ - crc32_ctx_init(&usb_tx_crc_ctx); -} - -uint32_t usb_console_crc(void) -{ - return crc32_ctx_result(&usb_tx_crc_ctx); -} -#endif - -static int __tx_char(void *context, int c) -{ - int ret; - - if (c == '\n') { - ret = __tx_char(NULL, '\r'); - if (ret) - return ret; - } - -#ifdef CONFIG_USB_CONSOLE_CRC - crc32_ctx_hash8(&usb_tx_crc_ctx, c); - - while (queue_add_unit(&tx_q, &c) != 1) - usleep(500); - - return EC_SUCCESS; -#else - /* Return 0 on success */ - return queue_add_unit(&tx_q, &c) ? EC_SUCCESS : EC_ERROR_OVERFLOW; -#endif -} - -/* - * Public USB console implementation below. - */ -int usb_getc(void) -{ - int c; - - if (is_readonly || !is_enabled) - return -1; - - if (!queue_remove_unit(&rx_q, &c)) - return -1; - - return c; -} - -int usb_puts(const char *outstr) -{ - int ret; - - if (!is_enabled) - return EC_SUCCESS; - - ret = usb_wait_console(); - if (ret) - return ret; - - while (*outstr) { - ret = __tx_char(NULL, *outstr++); - if (ret) - break; - } - handle_output(); - - return ret; -} - -int usb_putc(int c) -{ - static char string[2] = { 0, '\0' }; - - string[0] = c; - - return usb_puts(string); -} - -int usb_vprintf(const char *format, va_list args) -{ - int ret; - - if (!is_enabled) - return EC_SUCCESS; - - ret = usb_wait_console(); - if (ret) - return ret; - - ret = vfnprintf(__tx_char, NULL, format, args); - - handle_output(); - - return ret; -} - -void usb_console_enable(int enabled, int readonly) -{ - is_enabled = enabled; - is_readonly = readonly; -} - -int usb_console_tx_blocked(void) -{ - return is_enabled && (queue_space(&tx_q) < USB_MAX_PACKET_SIZE); -} diff --git a/common/usb_i2c.c b/common/usb_i2c.c deleted file mode 100644 index ace2e7139c..0000000000 --- a/common/usb_i2c.c +++ /dev/null @@ -1,196 +0,0 @@ -/* Copyright 2016 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 "common.h" -#include "link_defs.h" -#include "registers.h" -#include "i2c.h" -#include "usb_descriptor.h" -#include "util.h" - -#include "common.h" -#include "console.h" -#include "consumer.h" -#include "producer.h" -#include "queue.h" -#include "queue_policies.h" -#include "task.h" -#include "usb-stream.h" -#include "usb_i2c.h" - - -#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) - - -USB_I2C_CONFIG(i2c, - USB_IFACE_I2C, - USB_STR_I2C_NAME, - USB_EP_I2C) - -static int (*cros_cmd_handler)(void *data_in, - size_t in_size, - void *data_out, - size_t out_size); - -static int16_t usb_i2c_map_error(int error) -{ - switch (error) { - case EC_SUCCESS: return USB_I2C_SUCCESS; - case EC_ERROR_TIMEOUT: return USB_I2C_TIMEOUT; - case EC_ERROR_BUSY: return USB_I2C_BUSY; - default: return USB_I2C_UNKNOWN_ERROR | (error & 0x7fff); - } -} - -/* - * Return value should be large enough to accommodate the entire read queue - * buffer size. Let's use 4 bytes in case future designs have a lot of RAM and - * allow for large buffers. - */ -static uint32_t usb_i2c_read_packet(struct usb_i2c_config const *config) -{ - return QUEUE_REMOVE_UNITS(config->consumer.queue, config->buffer, - queue_count(config->consumer.queue)); -} - -static void usb_i2c_write_packet(struct usb_i2c_config const *config, - size_t count) -{ - QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count); -} - -static uint8_t usb_i2c_executable(struct usb_i2c_config const *config) -{ - static size_t expected_size; - - if (!expected_size) { - uint8_t peek[4]; - - /* - * In order to support larger write payload, we need to peek - * the queue to see if we need to wait for more data. - */ - if (queue_peek_units(config->consumer.queue, - peek, 0, sizeof(peek)) - != sizeof(peek)) { - /* Not enough data to calculate expected_size. */ - return 0; - } - /* - * The first four bytes of the packet will describe its - * expected size. - */ - /* Header bytes and extra rc bytes, if present. */ - if (peek[3] & 0x80) - expected_size = 6; - else - expected_size = 4; - - /* write count */ - expected_size += (((size_t)peek[0] & 0xf0) << 4) | peek[2]; - } - - - if (queue_count(config->consumer.queue) >= expected_size) { - expected_size = 0; - return 1; - } - - return 0; -} - -static void usb_i2c_execute(struct usb_i2c_config const *config) -{ - /* Payload is ready to execute. */ - uint32_t count = usb_i2c_read_packet(config); - int portindex = (config->buffer[0] >> 0) & 0xf; - uint16_t addr_flags = (config->buffer[0] >> 8) & 0x7f; - int write_count = ((config->buffer[0] << 4) & 0xf00) | - ((config->buffer[1] >> 0) & 0xff); - int read_count = (config->buffer[1] >> 8) & 0xff; - int offset = 0; /* Offset for extended reading header. */ - - config->buffer[0] = 0; - config->buffer[1] = 0; - - if (read_count & 0x80) { - read_count = ((config->buffer[2] & 0xff) << 7) | - (read_count & 0x7f); - offset = 2; - } - - if (!count || (!read_count && !write_count)) - return; - - if (!usb_i2c_board_is_enabled()) { - config->buffer[0] = USB_I2C_DISABLED; - } else if (write_count > CONFIG_USB_I2C_MAX_WRITE_COUNT || - write_count != (count - 4 - offset)) { - config->buffer[0] = USB_I2C_WRITE_COUNT_INVALID; - } else if (read_count > CONFIG_USB_I2C_MAX_READ_COUNT) { - config->buffer[0] = USB_I2C_READ_COUNT_INVALID; - } else if (portindex >= i2c_ports_used) { - config->buffer[0] = USB_I2C_PORT_INVALID; - } else if (addr_flags == USB_I2C_CMD_ADDR_FLAGS) { - /* - * This is a non-i2c command, invoke the handler if it has - * been registered, if not - report the appropriate error. - */ - if (!cros_cmd_handler) - config->buffer[0] = USB_I2C_MISSING_HANDLER; - else - config->buffer[0] = cros_cmd_handler(config->buffer + 2, - write_count, - config->buffer + 2, - read_count); - } else { - int ret; - - /* - * TODO (crbug.com/750397): Add security. This currently - * blindly passes through ALL I2C commands on any bus the EC - * knows about. It should behave closer to - * EC_CMD_I2C_PASSTHRU, which can protect ports and ranges. - */ - ret = i2c_xfer(i2c_ports[portindex].port, addr_flags, - (uint8_t *)(config->buffer + 2) + offset, - write_count, - (uint8_t *)(config->buffer + 2), - read_count); - config->buffer[0] = usb_i2c_map_error(ret); - } - usb_i2c_write_packet(config, read_count + 4); -} - -void usb_i2c_deferred(struct usb_i2c_config const *config) -{ - /* Check if we can proceed the queue. */ - if (usb_i2c_executable(config)) - usb_i2c_execute(config); -} - -static void usb_i2c_written(struct consumer const *consumer, size_t count) -{ - struct usb_i2c_config const *config = - DOWNCAST(consumer, struct usb_i2c_config, consumer); - - hook_call_deferred(config->deferred, 0); -} - -struct consumer_ops const usb_i2c_consumer_ops = { - .written = usb_i2c_written, -}; - -int usb_i2c_register_cros_cmd_handler(int (*cmd_handler) - (void *data_in, - size_t in_size, - void *data_out, - size_t out_size)) -{ - if (cros_cmd_handler) - return -1; - cros_cmd_handler = cmd_handler; - return 0; -} diff --git a/common/usb_pd_alt_mode_dfp.c b/common/usb_pd_alt_mode_dfp.c deleted file mode 100644 index d7048f4c8e..0000000000 --- a/common/usb_pd_alt_mode_dfp.c +++ /dev/null @@ -1,1543 +0,0 @@ -/* Copyright 2020 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. - * - * Alternate Mode Downstream Facing Port (DFP) USB-PD module. - */ - -#include "chipset.h" -#include "console.h" -#include "task.h" -#include "task_id.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_charge.h" -#include "usb_dp_alt_mode.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_tbt_alt_mode.h" -#include "usbc_ppc.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -#ifndef PORT_TO_HPD -#define PORT_TO_HPD(port) ((port) ? GPIO_USB_C1_DP_HPD : GPIO_USB_C0_DP_HPD) -#endif /* PORT_TO_HPD */ - -/* Tracker for which task is waiting on sysjump prep to finish */ -static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; - -/* - * timestamp of the next possible toggle to ensure the 2-ms spacing - * between IRQ_HPD. Since this is used in overridable functions, this - * has to be global. - */ -uint64_t svdm_hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; - -int dp_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -uint32_t dp_status[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Console command multi-function preference set for a PD port. */ - -__maybe_unused bool dp_port_mf_allow[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... CONFIG_USB_PD_PORT_MAX_COUNT - 1] = true}; - - -__overridable const struct svdm_response svdm_rsp = { - .identity = NULL, - .svids = NULL, - .modes = NULL, -}; - -static int pd_get_mode_idx(int port, enum tcpci_msg_type type, - uint16_t svid) -{ - int amode_idx; - struct partner_active_modes *active = - pd_get_partner_active_modes(port, type); - - for (amode_idx = 0; amode_idx < PD_AMODE_COUNT; amode_idx++) { - if (active->amodes[amode_idx].fx && - (active->amodes[amode_idx].fx->svid == svid)) - return amode_idx; - } - return -1; -} - -static int pd_allocate_mode(int port, enum tcpci_msg_type type, - uint16_t svid) -{ - int i, j; - struct svdm_amode_data *modep; - int mode_idx = pd_get_mode_idx(port, type, svid); - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - struct partner_active_modes *active = - pd_get_partner_active_modes(port, type); - assert(active); - - if (mode_idx != -1) - return mode_idx; - - /* There's no space to enter another mode */ - if (active->amode_idx == PD_AMODE_COUNT) { - CPRINTF("ERR:NO AMODE SPACE\n"); - return -1; - } - - /* Allocate ... if SVID == 0 enter default supported policy */ - for (i = 0; i < supported_modes_cnt; i++) { - for (j = 0; j < disc->svid_cnt; j++) { - const struct svid_mode_data *svidp = &disc->svids[j]; - - /* - * Looking for a match between supported_modes and - * discovered SVIDs; must also match the passed-in SVID - * if that was non-zero. Otherwise, go to the next - * discovered SVID. - * TODO(b/155890173): Support AP-directed mode entry - * where the mode is unknown to the TCPM. - */ - if ((svidp->svid != supported_modes[i].svid) || - (svid && (svidp->svid != svid))) - continue; - - modep = &active->amodes[active->amode_idx]; - modep->fx = &supported_modes[i]; - modep->data = &disc->svids[j]; - active->amode_idx++; - return active->amode_idx - 1; - } - } - return -1; -} - -static int validate_mode_request(struct svdm_amode_data *modep, - uint16_t svid, int opos) -{ - if (!modep->fx) - return 0; - - if (svid != modep->fx->svid) { - CPRINTF("ERR:svid r:0x%04x != c:0x%04x\n", - svid, modep->fx->svid); - return 0; - } - - if (opos != modep->opos) { - CPRINTF("ERR:opos r:%d != c:%d\n", - opos, modep->opos); - return 0; - } - - return 1; -} - -void pd_prepare_sysjump(void) -{ - int i; - - /* Exit modes before sysjump so we can cleanly enter again later */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - /* - * If the port is not capable of Alternate mode no need to - * send the event. - */ - if (!pd_alt_mode_capable(i)) - continue; - - sysjump_task_waiting = task_get_current(); - task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_SYSJUMP); - task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1); - sysjump_task_waiting = TASK_ID_INVALID; - } -} - -/* - * This algorithm defaults to choosing higher pin config over lower ones in - * order to prefer multi-function if desired. - * - * NAME | SIGNALING | OUTPUT TYPE | MULTI-FUNCTION | PIN CONFIG - * ------------------------------------------------------------- - * A | USB G2 | ? | no | 00_0001 - * B | USB G2 | ? | yes | 00_0010 - * C | DP | CONVERTED | no | 00_0100 - * D | PD | CONVERTED | yes | 00_1000 - * E | DP | DP | no | 01_0000 - * F | PD | DP | yes | 10_0000 - * - * if UFP has NOT asserted multi-function preferred code masks away B/D/F - * leaving only A/C/E. For single-output dongles that should leave only one - * possible pin config depending on whether its a converter DP->(VGA|HDMI) or DP - * output. If UFP is a USB-C receptacle it may assert C/D/E/F. The DFP USB-C - * receptacle must always choose C/D in those cases. - */ -int pd_dfp_dp_get_pin_mode(int port, uint32_t status) -{ - struct svdm_amode_data *modep = - pd_get_amode_data(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - uint32_t mode_caps; - uint32_t pin_caps; - int mf_pref; - - /* - * Default dp_port_mf_allow is true, we allow mf operation - * if UFP_D supports it. - */ - - if (IS_ENABLED(CONFIG_CMD_MFALLOW)) - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]) && - dp_port_mf_allow[port]; - else - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]); - - if (!modep) - return 0; - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - - /* TODO(crosbug.com/p/39656) revisit with DFP that can be a sink */ - pin_caps = PD_DP_PIN_CAPS(mode_caps); - - /* if don't want multi-function then ignore those pin configs */ - if (!mf_pref) - pin_caps &= ~MODE_DP_PIN_MF_MASK; - - /* TODO(crosbug.com/p/39656) revisit if DFP drives USB Gen 2 signals */ - pin_caps &= ~MODE_DP_PIN_BR2_MASK; - - /* if C/D present they have precedence over E/F for USB-C->USB-C */ - if (pin_caps & (MODE_DP_PIN_C | MODE_DP_PIN_D)) - pin_caps &= ~(MODE_DP_PIN_E | MODE_DP_PIN_F); - - /* get_next_bit returns undefined for zero */ - if (!pin_caps) - return 0; - - return 1 << get_next_bit(&pin_caps); -} - -struct svdm_amode_data *pd_get_amode_data(int port, - enum tcpci_msg_type type, uint16_t svid) -{ - int idx = pd_get_mode_idx(port, type, svid); - struct partner_active_modes *active = - pd_get_partner_active_modes(port, type); - assert(active); - - return (idx == -1) ? NULL : &active->amodes[idx]; -} - -/* - * Enter default mode ( payload[0] == 0 ) or attempt to enter mode via svid & - * opos - */ -uint32_t pd_dfp_enter_mode(int port, enum tcpci_msg_type type, - uint16_t svid, int opos) -{ - int mode_idx = pd_allocate_mode(port, type, svid); - struct svdm_amode_data *modep; - uint32_t mode_caps; - - if (mode_idx == -1) - return 0; - modep = &pd_get_partner_active_modes(port, type)->amodes[mode_idx]; - - if (!opos) { - /* choose the lowest as default */ - modep->opos = 1; - } else if (opos <= modep->data->mode_cnt) { - modep->opos = opos; - } else { - CPRINTS("C%d: Invalid opos %d for SVID %x", port, opos, svid); - return 0; - } - - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - if (modep->fx->enter(port, mode_caps) == -1) - return 0; - - /* - * Strictly speaking, this should only happen when the request - * has been ACKed. - * For TCPMV1, still set modal flag pre-emptively. For TCPMv2, the modal - * flag is set when the ENTER command is ACK'd for each alt mode that is - * supported. - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPMV1)) - pd_set_dfp_enter_mode_flag(port, true); - - /* SVDM to send to UFP for mode entry */ - return VDO(modep->fx->svid, 1, CMD_ENTER_MODE | VDO_OPOS(modep->opos)); -} - -/* TODO(b/170372521) : Incorporate exit mode specific changes to DPM SM */ -int pd_dfp_exit_mode(int port, enum tcpci_msg_type type, uint16_t svid, - int opos) -{ - struct svdm_amode_data *modep; - struct partner_active_modes *active = - pd_get_partner_active_modes(port, type); - int idx; - - /* - * Empty svid signals we should reset DFP VDM state by exiting all - * entered modes then clearing state. This occurs when we've - * disconnected or for hard reset. - */ - if (!svid) { - for (idx = 0; idx < PD_AMODE_COUNT; idx++) - if (active->amodes[idx].fx) - active->amodes[idx].fx->exit(port); - - pd_dfp_mode_init(port); - return 0; - } - - /* - * TODO(crosbug.com/p/33946) : below needs revisited to allow multiple - * mode exit. Additionally it should honor OPOS == 7 as DFP's request - * to exit all modes. We currently don't have any UFPs that support - * multiple modes on one SVID. - */ - modep = pd_get_amode_data(port, type, svid); - if (!modep || !validate_mode_request(modep, svid, opos)) - return 0; - - /* call DFPs exit function */ - modep->fx->exit(port); - - pd_set_dfp_enter_mode_flag(port, false); - - /* exit the mode */ - modep->opos = 0; - return 1; -} - -/* - * Check if the SVID has been recorded previously. Some peripherals provide - * duplicated SVID. - */ -static bool is_svid_duplicated(const struct pd_discovery *disc, uint16_t svid) -{ - int i; - - for (i = 0; i < disc->svid_cnt; ++i) - if (disc->svids[i].svid == svid) { - CPRINTF("ERR:SVIDDUP\n"); - return true; - } - - return false; -} - -void dfp_consume_attention(int port, uint32_t *payload) -{ - uint16_t svid = PD_VDO_VID(payload[0]); - int opos = PD_VDO_OPOS(payload[0]); - struct svdm_amode_data *modep = - pd_get_amode_data(port, TCPCI_MSG_SOP, svid); - - if (!modep || !validate_mode_request(modep, svid, opos)) - return; - - if (modep->fx->attention) - modep->fx->attention(port, payload); -} - -void dfp_consume_identity(int port, enum tcpci_msg_type type, int cnt, - uint32_t *payload) -{ - int ptype; - struct pd_discovery *disc; - size_t identity_size; - - if (type == TCPCI_MSG_SOP_PRIME && - !IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) { - CPRINTF("ERR:Unexpected cable response\n"); - return; - } - - ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]); - disc = pd_get_am_discovery_and_notify_access(port, type); - identity_size = MIN(sizeof(union disc_ident_ack), - (cnt - 1) * sizeof(uint32_t)); - - /* Note: only store VDOs, not the VDM header */ - memcpy(disc->identity.raw_value, payload + 1, identity_size); - disc->identity_cnt = identity_size / sizeof(uint32_t); - - switch (ptype) { - case IDH_PTYPE_AMA: - /* Leave vbus ON if the following macro is false */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE) && - IS_ENABLED(CONFIG_USBC_VCONN_SWAP)) { - /* Adapter is requesting vconn, try to supply it */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)])) - pd_try_vconn_src(port); - - /* Only disable vbus if vconn was requested */ - if (PD_VDO_AMA_VCONN_REQ(payload[VDO_I(AMA)]) && - !PD_VDO_AMA_VBUS_REQ(payload[VDO_I(AMA)])) - pd_power_supply_reset(port); - } - break; - default: - break; - } - pd_set_identity_discovery(port, type, PD_DISC_COMPLETE); -} - -void dfp_consume_svids(int port, enum tcpci_msg_type type, int cnt, - uint32_t *payload) -{ - int i; - uint32_t *ptr = payload + 1; - int vdo = 1; - uint16_t svid0, svid1; - struct pd_discovery *disc = - pd_get_am_discovery_and_notify_access(port, type); - - for (i = disc->svid_cnt; i < disc->svid_cnt + 12; i += 2) { - if (i >= SVID_DISCOVERY_MAX) { - CPRINTF("ERR:SVIDCNT\n"); - break; - } - /* - * Verify we're still within the valid packet (count will be one - * for the VDM header + xVDOs) - */ - if (vdo >= cnt) - break; - - svid0 = PD_VDO_SVID_SVID0(*ptr); - if (!svid0) - break; - - if (!is_svid_duplicated(disc, svid0)) - disc->svids[disc->svid_cnt++].svid = svid0; - - svid1 = PD_VDO_SVID_SVID1(*ptr); - if (!svid1) - break; - - if (!is_svid_duplicated(disc, svid1)) - disc->svids[disc->svid_cnt++].svid = svid1; - - ptr++; - vdo++; - } - /* TODO(tbroch) need to re-issue discover svids if > 12 */ - if (i && ((i % 12) == 0)) - CPRINTF("ERR:SVID+12\n"); - - pd_set_svids_discovery(port, type, PD_DISC_COMPLETE); -} - -void dfp_consume_modes(int port, enum tcpci_msg_type type, int cnt, - uint32_t *payload) -{ - int svid_idx; - struct svid_mode_data *mode_discovery = NULL; - struct pd_discovery *disc = - pd_get_am_discovery_and_notify_access(port, type); - uint16_t response_svid = (uint16_t) PD_VDO_VID(payload[0]); - - for (svid_idx = 0; svid_idx < disc->svid_cnt; ++svid_idx) { - uint16_t svid = disc->svids[svid_idx].svid; - - if (svid == response_svid) { - mode_discovery = &disc->svids[svid_idx]; - break; - } - } - if (!mode_discovery) { - const struct svid_mode_data *requested_mode_data = - pd_get_next_mode(port, type); - CPRINTF("C%d: Mode response for undiscovered SVID %x, but TCPM " - "requested SVID %x\n", - port, response_svid, requested_mode_data->svid); - /* - * Although SVIDs discovery seemed like it succeeded before, the - * partner is now responding with undiscovered SVIDs. Discovery - * cannot reasonably continue under these circumstances. - */ - pd_set_modes_discovery(port, type, requested_mode_data->svid, - PD_DISC_FAIL); - return; - } - - mode_discovery->mode_cnt = cnt - 1; - if (mode_discovery->mode_cnt < 1) { - CPRINTF("ERR:NOMODE\n"); - pd_set_modes_discovery(port, type, mode_discovery->svid, - PD_DISC_FAIL); - return; - } - - memcpy(mode_discovery->mode_vdo, &payload[1], - sizeof(uint32_t) * mode_discovery->mode_cnt); - disc->svid_idx++; - pd_set_modes_discovery(port, type, mode_discovery->svid, - PD_DISC_COMPLETE); -} - -int pd_alt_mode(int port, enum tcpci_msg_type type, uint16_t svid) -{ - struct svdm_amode_data *modep = pd_get_amode_data(port, type, svid); - - return (modep) ? modep->opos : -1; -} - -void pd_set_identity_discovery(int port, enum tcpci_msg_type type, - enum pd_discovery_state disc) -{ - struct pd_discovery *pd = - pd_get_am_discovery_and_notify_access(port, type); - - pd->identity_discovery = disc; -} - -enum pd_discovery_state pd_get_identity_discovery(int port, - enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - - return disc->identity_discovery; -} - -const union disc_ident_ack *pd_get_identity_response(int port, - enum tcpci_msg_type type) -{ - if (type >= DISCOVERY_TYPE_COUNT) - return NULL; - - return &pd_get_am_discovery(port, type)->identity; -} - -uint16_t pd_get_identity_vid(int port) -{ - const union disc_ident_ack *resp = pd_get_identity_response(port, - TCPCI_MSG_SOP); - - return resp->idh.usb_vendor_id; -} - -uint16_t pd_get_identity_pid(int port) -{ - const union disc_ident_ack *resp = pd_get_identity_response(port, - TCPCI_MSG_SOP); - - return resp->product.product_id; -} - -uint8_t pd_get_product_type(int port) -{ - const union disc_ident_ack *resp = pd_get_identity_response(port, - TCPCI_MSG_SOP); - - return resp->idh.product_type; -} - -void pd_set_svids_discovery(int port, enum tcpci_msg_type type, - enum pd_discovery_state disc) -{ - struct pd_discovery *pd = - pd_get_am_discovery_and_notify_access(port, type); - - pd->svids_discovery = disc; -} - -enum pd_discovery_state pd_get_svids_discovery(int port, - enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - - return disc->svids_discovery; -} - -int pd_get_svid_count(int port, enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - - return disc->svid_cnt; -} - -uint16_t pd_get_svid(int port, uint16_t svid_idx, enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - - return disc->svids[svid_idx].svid; -} - -void pd_set_modes_discovery(int port, enum tcpci_msg_type type, - uint16_t svid, enum pd_discovery_state disc) -{ - struct pd_discovery *pd = - pd_get_am_discovery_and_notify_access(port, type); - int svid_idx; - - for (svid_idx = 0; svid_idx < pd->svid_cnt; ++svid_idx) { - struct svid_mode_data *mode_data = &pd->svids[svid_idx]; - - if (mode_data->svid != svid) - continue; - - mode_data->discovery = disc; - return; - } -} - -enum pd_discovery_state pd_get_modes_discovery(int port, - enum tcpci_msg_type type) -{ - const struct svid_mode_data *mode_data = pd_get_next_mode(port, type); - - /* - * If there are no SVIDs for which to discover modes, mode discovery is - * trivially complete. - */ - if (!mode_data) - return PD_DISC_COMPLETE; - - return mode_data->discovery; -} - -int pd_get_mode_vdo_for_svid(int port, enum tcpci_msg_type type, - uint16_t svid, uint32_t *vdo_out) -{ - int idx; - const struct pd_discovery *disc; - - if (type >= DISCOVERY_TYPE_COUNT) - return 0; - - disc = pd_get_am_discovery(port, type); - - for (idx = 0; idx < disc->svid_cnt; ++idx) { - if (pd_get_svid(port, idx, type) == svid) { - memcpy(vdo_out, disc->svids[idx].mode_vdo, - sizeof(uint32_t) * disc->svids[idx].mode_cnt); - return disc->svids[idx].mode_cnt; - } - } - return 0; -} - -const struct svid_mode_data *pd_get_next_mode(int port, - enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - const struct svid_mode_data *failed_mode_data = NULL; - bool svid_good_discovery = false; - int svid_idx; - - /* Walk through all of the discovery mode entries */ - for (svid_idx = 0; svid_idx < disc->svid_cnt; ++svid_idx) { - const struct svid_mode_data *mode_data = &disc->svids[svid_idx]; - - /* Discovery is needed, so send this one back now */ - if (mode_data->discovery == PD_DISC_NEEDED) - return mode_data; - - /* Discovery already succeeded, save that it was seen */ - if (mode_data->discovery == PD_DISC_COMPLETE) - svid_good_discovery = true; - /* Discovery already failed, save first failure */ - else if (!failed_mode_data) - failed_mode_data = mode_data; - } - - /* If no good entries were located, then return last failed */ - if (!svid_good_discovery) - return failed_mode_data; - - /* - * Mode discovery has been attempted for every discovered SVID (if - * any exist) - */ - return NULL; -} - -const uint32_t *pd_get_mode_vdo(int port, uint16_t svid_idx, - enum tcpci_msg_type type) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - - return disc->svids[svid_idx].mode_vdo; -} - -bool pd_is_mode_discovered_for_svid(int port, enum tcpci_msg_type type, - uint16_t svid) -{ - const struct pd_discovery *disc = pd_get_am_discovery(port, type); - const struct svid_mode_data *mode_data; - - for (mode_data = disc->svids; mode_data < disc->svids + disc->svid_cnt; - ++mode_data) { - if (mode_data->svid == svid && - mode_data->discovery == PD_DISC_COMPLETE) - return true; - } - - return false; -} - -void notify_sysjump_ready(void) -{ - /* - * If event was set from pd_prepare_sysjump, wake the - * task waiting on us to complete. - */ - if (sysjump_task_waiting != TASK_ID_INVALID) - task_set_event(sysjump_task_waiting, TASK_EVENT_SYSJUMP_READY); -} - -static inline bool is_pd_rev3(int port, enum tcpci_msg_type type) -{ - return pd_get_rev(port, type) == PD_REV30; -} - -/* - * ############################################################################ - * - * (Charge Through) Vconn Powered Device functions - * - * ############################################################################ - */ -bool is_vpd_ct_supported(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.ct_support; -} - -uint8_t get_vpd_ct_gnd_impedance(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.gnd_impedance; -} - -uint8_t get_vpd_ct_vbus_impedance(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.vbus_impedance; -} - -uint8_t get_vpd_ct_current_support(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.ct_current_support; -} - -uint8_t get_vpd_ct_max_vbus_voltage(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.max_vbus_voltage; -} - -uint8_t get_vpd_ct_vdo_version(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.vdo_version; -} - -uint8_t get_vpd_ct_firmware_verion(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.firmware_version; -} - -uint8_t get_vpd_ct_hw_version(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union vpd_vdo vpd = disc->identity.product_t1.vpd; - - return vpd.hw_version; -} - -/* - * ############################################################################ - * - * Cable communication functions - * - * ############################################################################ - */ -enum idh_ptype get_usb_pd_cable_type(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - return disc->identity.idh.product_type; -} - -bool is_usb2_cable_support(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - return disc->identity.idh.product_type == IDH_PTYPE_PCABLE || - pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc->identity.product_t2.a2_rev30.usb_20_support == - USB2_SUPPORTED; -} - -bool is_cable_speed_gen2_capable(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - switch (pd_get_rev(port, TCPCI_MSG_SOP_PRIME)) { - case PD_REV20: - return disc->identity.product_t1.p_rev20.ss == - USB_R20_SS_U31_GEN1_GEN2; - - case PD_REV30: - return disc->identity.product_t1.p_rev30.ss == - USB_R30_SS_U32_U40_GEN2 || - disc->identity.product_t1.p_rev30.ss == - USB_R30_SS_U40_GEN3; - default: - return false; - } -} - -bool is_active_cable_element_retimer(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - /* Ref: USB PD Spec 2.0 Table 6-29 Active Cable VDO - * Revision 2 Active cables do not have Active element support. - */ - return is_pd_rev3(port, TCPCI_MSG_SOP_PRIME) && - disc->identity.idh.product_type == IDH_PTYPE_ACABLE && - disc->identity.product_t2.a2_rev30.active_elem == - ACTIVE_RETIMER; -} - -/* - * ############################################################################ - * - * Thunderbolt-Compatible functions - * - * ############################################################################ - */ - -uint32_t pd_get_tbt_mode_vdo(int port, enum tcpci_msg_type type) -{ - uint32_t tbt_mode_vdo[PDO_MODES]; - - return pd_get_mode_vdo_for_svid(port, type, USB_VID_INTEL, - tbt_mode_vdo) ? tbt_mode_vdo[0] : 0; -} - -/* TODO (b/148528713): Need to enable Thunderbolt-compatible mode on TCPMv2 */ -void set_tbt_compat_mode_ready(int port) -{ - if (IS_ENABLED(CONFIG_USBC_SS_MUX) && - IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { - /* Connect the SBU and USB lines to the connector. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 1); - - /* Set usb mux to Thunderbolt-compatible mode */ - usb_mux_set(port, USB_PD_MUX_TBT_COMPAT_ENABLED, - USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - } -} - -/* - * Ref: USB Type-C Cable and Connector Specification - * Figure F-1 TBT3 Discovery Flow - */ -static bool is_tbt_cable_superspeed(int port) -{ - const struct pd_discovery *disc; - - if (!IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) || - !IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - return false; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - /* Product type is Active cable, hence don't check for speed */ - if (disc->identity.idh.product_type == IDH_PTYPE_ACABLE) - return true; - - if (disc->identity.idh.product_type != IDH_PTYPE_PCABLE) - return false; - - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - is_pd_rev3(port, TCPCI_MSG_SOP_PRIME)) - return disc->identity.product_t1.p_rev30.ss == - USB_R30_SS_U32_U40_GEN1 || - disc->identity.product_t1.p_rev30.ss == - USB_R30_SS_U32_U40_GEN2 || - disc->identity.product_t1.p_rev30.ss == - USB_R30_SS_U40_GEN3; - - return disc->identity.product_t1.p_rev20.ss == - USB_R20_SS_U31_GEN1 || - disc->identity.product_t1.p_rev20.ss == - USB_R20_SS_U31_GEN1_GEN2; -} - -static enum tbt_compat_cable_speed usb_rev30_to_tbt_speed(enum usb_rev30_ss ss) -{ - switch (ss) { - case USB_R30_SS_U32_U40_GEN1: - return TBT_SS_U31_GEN1; - case USB_R30_SS_U32_U40_GEN2: - return TBT_SS_U32_GEN1_GEN2; - case USB_R30_SS_U40_GEN3: - return TBT_SS_TBT_GEN3; - default: - return TBT_SS_U32_GEN1_GEN2; - } -} - -enum tbt_compat_cable_speed get_tbt_cable_speed(int port) -{ - union tbt_mode_resp_cable cable_mode_resp; - enum tbt_compat_cable_speed max_tbt_speed; - enum tbt_compat_cable_speed cable_tbt_speed; - - if (!is_tbt_cable_superspeed(port)) - return TBT_SS_RES_0; - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - max_tbt_speed = board_get_max_tbt_speed(port); - - /* - * Ref: TBT4 PD Discovery Flow Application Notes Revision 0.9, Figure 2 - * For passive cable, if cable doesn't support USB_VID_INTEL, enter - * Thunderbolt alternate mode with speed from USB Highest Speed field of - * the Passive Cable VDO - * For active cable, if the cable doesn't support USB_VID_INTEL, do not - * enter Thunderbolt alternate mode. - */ - if (!cable_mode_resp.raw_value) { - const struct pd_discovery *disc; - - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - return TBT_SS_RES_0; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - cable_tbt_speed = - usb_rev30_to_tbt_speed(disc->identity.product_t1.p_rev30.ss); - } else { - cable_tbt_speed = cable_mode_resp.tbt_cable_speed; - } - - return max_tbt_speed < cable_tbt_speed ? - max_tbt_speed : cable_tbt_speed; -} - -int enter_tbt_compat_mode(int port, enum tcpci_msg_type sop, - uint32_t *payload) -{ - union tbt_dev_mode_enter_cmd enter_dev_mode = { .raw_value = 0 }; - union tbt_mode_resp_device dev_mode_resp; - union tbt_mode_resp_cable cable_mode_resp; - enum tcpci_msg_type enter_mode_sop = - sop == TCPCI_MSG_SOP_PRIME_PRIME ? - TCPCI_MSG_SOP_PRIME : sop; - - /* Table F-12 TBT3 Cable Enter Mode Command */ - /* - * The port doesn't query Discover SOP'' to the cable so, the port - * doesn't have opos for SOP''. Hence, send Enter Mode SOP'' with same - * opos and revision as SOP'. - */ - payload[0] = pd_dfp_enter_mode(port, enter_mode_sop, USB_VID_INTEL, 0) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, enter_mode_sop)); - - /* - * Enter safe mode before sending Enter mode SOP/SOP'/SOP'' - * Ref: Tiger Lake Platform PD Controller Interface Requirements for - * Integrated USB C, section A.1.2 TBT as DFP. - */ - usb_mux_set_safe_mode(port); - - /* For TBT3 Cable Enter Mode Command, number of Objects is 1 */ - if ((sop == TCPCI_MSG_SOP_PRIME) || - (sop == TCPCI_MSG_SOP_PRIME_PRIME)) - return 1; - - dev_mode_resp.raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP); - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* Table F-13 TBT3 Device Enter Mode Command */ - enter_dev_mode.vendor_spec_b1 = dev_mode_resp.vendor_spec_b1; - enter_dev_mode.vendor_spec_b0 = dev_mode_resp.vendor_spec_b0; - enter_dev_mode.intel_spec_b0 = dev_mode_resp.intel_spec_b0; - - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE || - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) - enter_dev_mode.cable = TBT_ENTER_ACTIVE_CABLE; - - enter_dev_mode.lsrx_comm = cable_mode_resp.lsrx_comm; - enter_dev_mode.retimer_type = cable_mode_resp.retimer_type; - enter_dev_mode.tbt_cable = cable_mode_resp.tbt_cable; - enter_dev_mode.tbt_rounded = cable_mode_resp.tbt_rounded; - enter_dev_mode.tbt_cable_speed = get_tbt_cable_speed(port); - enter_dev_mode.tbt_alt_mode = TBT_ALTERNATE_MODE; - - payload[1] = enter_dev_mode.raw_value; - - /* For TBT3 Device Enter Mode Command, number of Objects are 2 */ - return 2; -} - -enum tbt_compat_rounded_support get_tbt_rounded_support(int port) -{ - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME) }; - - /* tbt_rounded_support is zero when uninitialized */ - return cable_mode_resp.tbt_rounded; -} - -__overridable enum tbt_compat_cable_speed board_get_max_tbt_speed(int port) -{ - return TBT_SS_TBT_GEN3; -} -/* - * ############################################################################ - * - * USB4 functions - * - * ############################################################################ - */ - -/* - * For Cable rev 3.0: USB4 cable speed is set according to speed supported by - * the port and the response received from the cable, whichever is least. - * - * For Cable rev 2.0: If get_tbt_cable_speed() is less than - * TBT_SS_U31_GEN1, return USB_R30_SS_U2_ONLY speed since the board - * doesn't support superspeed else the USB4 cable speed is set according to - * the cable response. - */ -enum usb_rev30_ss get_usb4_cable_speed(int port) -{ - enum tbt_compat_cable_speed tbt_speed = get_tbt_cable_speed(port); - enum usb_rev30_ss max_usb4_speed; - - - if (tbt_speed < TBT_SS_U31_GEN1) - return USB_R30_SS_U2_ONLY; - - /* - * Converting Thunderbolt-Compatible board speed to equivalent USB4 - * speed. - */ - max_usb4_speed = tbt_speed == TBT_SS_TBT_GEN3 ? - USB_R30_SS_U40_GEN3 : USB_R30_SS_U32_U40_GEN2; - - if ((get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) && - is_pd_rev3(port, TCPCI_MSG_SOP_PRIME)) { - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - union active_cable_vdo1_rev30 a_rev30 = - disc->identity.product_t1.a_rev30; - - if (a_rev30.vdo_ver >= VDO_VERSION_1_3) { - return max_usb4_speed < a_rev30.ss ? - max_usb4_speed : a_rev30.ss; - } - } - - return max_usb4_speed; -} - -uint32_t get_enter_usb_msg_payload(int port) -{ - /* - * Ref: USB Power Delivery Specification Revision 3.0, Version 2.0 - * Table 6-47 Enter_USB Data Object - */ - union enter_usb_data_obj eudo; - const struct pd_discovery *disc; - union tbt_mode_resp_cable cable_mode_resp; - - if (!IS_ENABLED(CONFIG_USB_PD_USB4)) - return 0; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - eudo.mode = USB_PD_40; - eudo.usb4_drd_cap = IS_ENABLED(CONFIG_USB_PD_USB4_DRD); - eudo.usb3_drd_cap = IS_ENABLED(CONFIG_USB_PD_USB32_DRD); - eudo.cable_speed = get_usb4_cable_speed(port); - - if (disc->identity.idh.product_type == IDH_PTYPE_ACABLE) { - if (is_pd_rev3(port, TCPCI_MSG_SOP_PRIME)) { - enum retimer_active_element active_element = - disc->identity.product_t2.a2_rev30.active_elem; - eudo.cable_type = active_element == ACTIVE_RETIMER ? - CABLE_TYPE_ACTIVE_RETIMER : - CABLE_TYPE_ACTIVE_REDRIVER; - } else { - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - eudo.cable_type = - cable_mode_resp.retimer_type == USB_RETIMER ? - CABLE_TYPE_ACTIVE_RETIMER : - CABLE_TYPE_ACTIVE_REDRIVER; - } - } else { - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - eudo.cable_type = - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE ? - CABLE_TYPE_ACTIVE_REDRIVER : CABLE_TYPE_PASSIVE; - } - - switch (disc->identity.product_t1.p_rev20.vbus_cur) { - case USB_VBUS_CUR_3A: - eudo.cable_current = USB4_CABLE_CURRENT_3A; - break; - case USB_VBUS_CUR_5A: - eudo.cable_current = USB4_CABLE_CURRENT_5A; - break; - default: - eudo.cable_current = USB4_CABLE_CURRENT_INVALID; - break; - } - eudo.pcie_supported = IS_ENABLED(CONFIG_USB_PD_PCIE_TUNNELING); - eudo.dp_supported = IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP); - eudo.tbt_supported = IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE); - eudo.host_present = 1; - - return eudo.raw_value; -} - -__overridable bool board_is_tbt_usb4_port(int port) -{ - return true; -} - -__overridable void svdm_safe_dp_mode(int port) -{ - /* make DP interface safe until configure */ - dp_flags[port] = 0; - dp_status[port] = 0; - - usb_mux_set_safe_mode(port); -} - -__overridable int svdm_enter_dp_mode(int port, uint32_t mode_caps) -{ - /* - * Don't enter the mode if the SoC is off. - * - * There's no need to enter the mode while the SoC is off; we'll - * actually enter the mode on the chipset resume hook. Entering DP Alt - * Mode twice will confuse some monitors and require and unplug/replug - * to get them to work again. The DP Alt Mode on USB-C spec says that - * if we don't need to maintain HPD connectivity info in a low power - * mode, then we shall exit DP Alt Mode. (This is why we don't enter - * when the SoC is off as opposed to suspend where adding a display - * could cause a wake up.) When in S5->S3 transition state, we - * should treat it as a SoC off state. - */ -#ifdef HAS_TASK_CHIPSET - if (!chipset_in_state(CHIPSET_STATE_ANY_SUSPEND | CHIPSET_STATE_ON)) - return -1; -#endif - - /* - * TCPMv2: Enable logging of CCD line state CCD_MODE_ODL. - * DisplayPort Alternate mode requires that the SBU lines are used for - * AUX communication. - * However, in Chromebooks SBU signals are repurposed as USB2 signals - * for CCD. This functionality is accomplished by override fets whose - * state is controlled by CCD_MODE_ODL. - * - * This condition helps in debugging unexpected AUX timeout issues by - * indicating the state of the CCD override fets. - */ -#ifdef GPIO_CCD_MODE_ODL - if (!gpio_get_level(GPIO_CCD_MODE_ODL)) - CPRINTS("WARNING: Tried to EnterMode DP with [CCD on AUX/SBU]"); -#endif - - /* Only enter mode if device is DFP_D capable */ - if (mode_caps & MODE_DP_SNK) { - svdm_safe_dp_mode(port); - - if (IS_ENABLED(CONFIG_MKBP_EVENT) && - chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - /* - * Wake the system up since we're entering DP AltMode. - */ - pd_notify_dp_alt_mode_entry(port); - - return 0; - } - - return -1; -} - -__overridable int svdm_dp_status(int port, uint32_t *payload) -{ - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - - payload[0] = VDO(USB_SID_DISPLAYPORT, 1, - CMD_DP_STATUS | VDO_OPOS(opos)); - payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */ - 0, /* HPD level ... not applicable */ - 0, /* exit DP? ... no */ - 0, /* usb mode? ... no */ - 0, /* multi-function ... no */ - (!!(dp_flags[port] & DP_FLAGS_DP_ON)), - 0, /* power low? ... no */ - (!!DP_FLAGS_DP_ON)); - return 2; -}; - -__overridable uint8_t get_dp_pin_mode(int port) -{ - return pd_dfp_dp_get_pin_mode(port, dp_status[port]); -} - -static mux_state_t svdm_dp_get_mux_mode(int port) -{ - int pin_mode = get_dp_pin_mode(port); - /* Default dp_port_mf_allow is true */ - int mf_pref; - - if (IS_ENABLED(CONFIG_CMD_MFALLOW)) - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]) && - dp_port_mf_allow[port]; - else - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]); - - /* - * Multi-function operation is only allowed if that pin config is - * supported. - */ - if ((pin_mode & MODE_DP_PIN_MF_MASK) && mf_pref) - return USB_PD_MUX_DOCK; - else - return USB_PD_MUX_DP_ENABLED; -} - -__overridable int svdm_dp_config(int port, uint32_t *payload) -{ - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - uint8_t pin_mode = get_dp_pin_mode(port); - mux_state_t mux_mode = svdm_dp_get_mux_mode(port); - /* Default dp_port_mf_allow is true */ - int mf_pref; - - if (IS_ENABLED(CONFIG_CMD_MFALLOW)) - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]) && - dp_port_mf_allow[port]; - else - mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]); - - if (!pin_mode) - return 0; - - CPRINTS("pin_mode: %x, mf: %d, mux: %d", pin_mode, mf_pref, mux_mode); - - /* - * Place the USB Type-C pins that are to be re-configured to DisplayPort - * Configuration into the Safe state. For USB_PD_MUX_DOCK, the - * superspeed signals can remain connected. For USB_PD_MUX_DP_ENABLED, - * disconnect the superspeed signals here, before the pins are - * re-configured to DisplayPort (in svdm_dp_post_config, when we receive - * the config ack). - */ - if (mux_mode == USB_PD_MUX_DP_ENABLED) - usb_mux_set_safe_mode(port); - - payload[0] = VDO(USB_SID_DISPLAYPORT, 1, - CMD_DP_CONFIG | VDO_OPOS(opos)); - payload[1] = VDO_DP_CFG(pin_mode, /* pin mode */ - 1, /* DPv1.3 signaling */ - 2); /* UFP connected */ - return 2; -}; - -#if defined(CONFIG_USB_PD_DP_HPD_GPIO) && \ - !defined(CONFIG_USB_PD_DP_HPD_GPIO_CUSTOM) -void svdm_set_hpd_gpio(int port, int en) -{ - gpio_set_level(PORT_TO_HPD(port), en); -} - -int svdm_get_hpd_gpio(int port) -{ - return gpio_get_level(PORT_TO_HPD(port)); -} -#endif - -__overridable void svdm_dp_post_config(int port) -{ - mux_state_t mux_mode = svdm_dp_get_mux_mode(port); - /* Connect the SBU and USB lines to the connector. */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 1); - usb_mux_set(port, mux_mode, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - dp_flags[port] |= DP_FLAGS_DP_ON; - if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING)) - return; - -#ifdef CONFIG_USB_PD_DP_HPD_GPIO - svdm_set_hpd_gpio(port, 1); - - /* set the minimum time delay (2ms) for the next HPD IRQ */ - svdm_hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; -#endif /* CONFIG_USB_PD_DP_HPD_GPIO */ - - usb_mux_hpd_update(port, USB_PD_MUX_HPD_LVL | - USB_PD_MUX_HPD_IRQ_DEASSERTED); - -#ifdef USB_PD_PORT_TCPC_MST - if (port == USB_PD_PORT_TCPC_MST) - baseboard_mst_enable_control(port, 1); -#endif -} - -__overridable int svdm_dp_attention(int port, uint32_t *payload) -{ - int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]); - int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]); -#ifdef CONFIG_USB_PD_DP_HPD_GPIO - int cur_lvl = svdm_get_hpd_gpio(port); -#endif /* CONFIG_USB_PD_DP_HPD_GPIO */ - mux_state_t mux_state; - - dp_status[port] = payload[1]; - - if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND) && - (irq || lvl)) - /* - * Wake up the AP. IRQ or level high indicates a DP sink is now - * present. - */ - if (IS_ENABLED(CONFIG_MKBP_EVENT)) - pd_notify_dp_alt_mode_entry(port); - - /* Its initial DP status message prior to config */ - if (!(dp_flags[port] & DP_FLAGS_DP_ON)) { - if (lvl) - dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING; - return 1; - } - -#ifdef CONFIG_USB_PD_DP_HPD_GPIO - if (irq && !lvl) { - /* - * IRQ can only be generated when the level is high, because - * the IRQ is signaled by a short low pulse from the high level. - */ - CPRINTF("ERR:HPD:IRQ&LOW\n"); - return 0; /* nak */ - } - - if (irq && cur_lvl) { - uint64_t now = get_time().val; - /* wait for the minimum spacing between IRQ_HPD if needed */ - if (now < svdm_hpd_deadline[port]) - usleep(svdm_hpd_deadline[port] - now); - - /* generate IRQ_HPD pulse */ - svdm_set_hpd_gpio(port, 0); - usleep(HPD_DSTREAM_DEBOUNCE_IRQ); - svdm_set_hpd_gpio(port, 1); - } else { - svdm_set_hpd_gpio(port, lvl); - } - - /* set the minimum time delay (2ms) for the next HPD IRQ */ - svdm_hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; -#endif /* CONFIG_USB_PD_DP_HPD_GPIO */ - - mux_state = (lvl ? USB_PD_MUX_HPD_LVL : USB_PD_MUX_HPD_LVL_DEASSERTED) | - (irq ? USB_PD_MUX_HPD_IRQ : USB_PD_MUX_HPD_IRQ_DEASSERTED); - usb_mux_hpd_update(port, mux_state); - -#ifdef USB_PD_PORT_TCPC_MST - if (port == USB_PD_PORT_TCPC_MST) - baseboard_mst_enable_control(port, lvl); -#endif - - /* ack */ - return 1; -} - -__overridable void svdm_exit_dp_mode(int port) -{ - dp_flags[port] = 0; - dp_status[port] = 0; -#ifdef CONFIG_USB_PD_DP_HPD_GPIO - svdm_set_hpd_gpio(port, 0); -#endif /* CONFIG_USB_PD_DP_HPD_GPIO */ - usb_mux_hpd_update(port, USB_PD_MUX_HPD_LVL_DEASSERTED | - USB_PD_MUX_HPD_IRQ_DEASSERTED); -#ifdef USB_PD_PORT_TCPC_MST - if (port == USB_PD_PORT_TCPC_MST) - baseboard_mst_enable_control(port, 0); -#endif -} - -__overridable int svdm_enter_gfu_mode(int port, uint32_t mode_caps) -{ - /* Always enter GFU mode */ - return 0; -} - -__overridable void svdm_exit_gfu_mode(int port) -{ -} - -__overridable int svdm_gfu_status(int port, uint32_t *payload) -{ - /* - * This is called after enter mode is successful, send unstructured - * VDM to read info. - */ - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_READ_INFO, NULL, 0); - return 0; -} - -__overridable int svdm_gfu_config(int port, uint32_t *payload) -{ - return 0; -} - -__overridable int svdm_gfu_attention(int port, uint32_t *payload) -{ - return 0; -} - -#ifdef CONFIG_USB_PD_TBT_COMPAT_MODE -__overridable int svdm_tbt_compat_enter_mode(int port, uint32_t mode_caps) -{ - return 0; -} - -__overridable void svdm_tbt_compat_exit_mode(int port) -{ -} - -__overridable int svdm_tbt_compat_status(int port, uint32_t *payload) -{ - return 0; -} - -__overridable int svdm_tbt_compat_config(int port, uint32_t *payload) -{ - return 0; -} - -__overridable int svdm_tbt_compat_attention(int port, uint32_t *payload) -{ - return 0; -} -#endif /* CONFIG_USB_PD_TBT_COMPAT_MODE */ - -/* - * TODO: b:169262276: For TCPMv2, move alternate mode specific entry, exit and - * configuration to Device Policy Manager. - */ -const struct svdm_amode_fx supported_modes[] = { - { - .svid = USB_SID_DISPLAYPORT, - .enter = &svdm_enter_dp_mode, - .status = &svdm_dp_status, - .config = &svdm_dp_config, - .post_config = &svdm_dp_post_config, - .attention = &svdm_dp_attention, - .exit = &svdm_exit_dp_mode, - }, - - { - .svid = USB_VID_GOOGLE, - .enter = &svdm_enter_gfu_mode, - .status = &svdm_gfu_status, - .config = &svdm_gfu_config, - .attention = &svdm_gfu_attention, - .exit = &svdm_exit_gfu_mode, - }, -#ifdef CONFIG_USB_PD_TBT_COMPAT_MODE - { - .svid = USB_VID_INTEL, - .enter = &svdm_tbt_compat_enter_mode, - .status = &svdm_tbt_compat_status, - .config = &svdm_tbt_compat_config, - .attention = &svdm_tbt_compat_attention, - .exit = &svdm_tbt_compat_exit_mode, - }, -#endif /* CONFIG_USB_PD_TBT_COMPAT_MODE */ -}; -const int supported_modes_cnt = ARRAY_SIZE(supported_modes); - -#ifdef CONFIG_CMD_MFALLOW -static int command_mfallow(int argc, char **argv) -{ - char *e; - int port; - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &e, 10); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - if (!strcasecmp(argv[2], "true")) - dp_port_mf_allow[port] = true; - else if (!strcasecmp(argv[2], "false")) - dp_port_mf_allow[port] = false; - else - return EC_ERROR_PARAM1; - - ccprintf("Port: %d multi function allowed is %s ", port, argv[2]); - return EC_SUCCESS; -} - -DECLARE_CONSOLE_COMMAND(mfallow, command_mfallow, "port [true | false]", - "Controls Multifunction choice during DP Altmode."); -#endif diff --git a/common/usb_pd_alt_mode_ufp.c b/common/usb_pd_alt_mode_ufp.c deleted file mode 100644 index 3db60166d2..0000000000 --- a/common/usb_pd_alt_mode_ufp.c +++ /dev/null @@ -1,22 +0,0 @@ -/* 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. - * - * Alternate Mode Upstream Facing Port (UFP) USB-PD module. - */ -#include "usb_pd.h" -#include "usb_tbt_alt_mode.h" - -static uint32_t ufp_enter_mode[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Save port partner's enter mode message */ -void pd_ufp_set_enter_mode(int port, uint32_t *payload) -{ - ufp_enter_mode[port] = payload[1]; -} - -/* Return port partner's enter mode message */ -uint32_t pd_ufp_get_enter_mode(int port) -{ - return ufp_enter_mode[port]; -} diff --git a/common/usb_pd_console_cmd.c b/common/usb_pd_console_cmd.c deleted file mode 100644 index 3ad1944494..0000000000 --- a/common/usb_pd_console_cmd.c +++ /dev/null @@ -1,224 +0,0 @@ -/* Copyright 2020 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. - * - * Console commands for USB-PD module. - */ - -#include "console.h" -#include "usb_pd.h" -#include "util.h" -#include "usb_pd_tcpm.h" - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -#ifdef CONFIG_CMD_USB_PD_PE -static void dump_pe(int port) -{ - int i, j, idh_ptype; - struct svdm_amode_data *modep; - uint32_t mode_caps; - const union disc_ident_ack *resp; - enum tcpci_msg_type type; - /* TODO(b/152417597): Output SOP' discovery results */ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - static const char * const idh_ptype_names[] = { - "UNDEF", "Hub", "Periph", "PCable", "ACable", "AMA", - "RSV6", "RSV7"}; - static const char * const tx_names[] = {"SOP", "SOP'", "SOP''"}; - - for (type = TCPCI_MSG_SOP; type < DISCOVERY_TYPE_COUNT; type++) { - resp = pd_get_identity_response(port, type); - if (pd_get_identity_discovery(port, type) != PD_DISC_COMPLETE) { - ccprintf("No %s identity discovered yet.\n", - tx_names[type]); - continue; - } - - idh_ptype = resp->idh.product_type; - ccprintf("IDENT %s:\n", tx_names[type]); - ccprintf("\t[ID Header] %08x :: %s, VID:%04x\n", - resp->raw_value[0], - idh_ptype_names[idh_ptype], - resp->idh.usb_vendor_id); - - ccprintf("\t[Cert Stat] %08x\n", resp->cert.xid); - for (i = 2; i < ARRAY_SIZE(resp->raw_value); i++) { - ccprintf("\t"); - if (resp->raw_value[i]) - ccprintf("[%d] %08x ", i, resp->raw_value[i]); - } - ccprintf("\n"); - } - - if (pd_get_svid_count(port, TCPCI_MSG_SOP) < 1) { - ccprintf("No SVIDS discovered yet.\n"); - return; - } - - /* TODO(b/152418267): Display discovered SVIDs and modes for SOP' */ - for (i = 0; i < pd_get_svid_count(port, TCPCI_MSG_SOP); i++) { - ccprintf("SVID[%d]: %04x MODES:", i, disc->svids[i].svid); - for (j = 0; j < disc->svids[j].mode_cnt; j++) - ccprintf(" [%d] %08x", j + 1, - disc->svids[i].mode_vdo[j]); - ccprintf("\n"); - - modep = pd_get_amode_data(port, TCPCI_MSG_SOP, - disc->svids[i].svid); - if (modep) { - mode_caps = modep->data->mode_vdo[modep->opos - 1]; - ccprintf("MODE[%d]: svid:%04x caps:%08x\n", modep->opos, - modep->fx->svid, mode_caps); - } - } -} - -static int command_pe(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - /* command: pe <port> <subcmd> <args> */ - port = strtoi(argv[1], &e, 10); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - if (!strncasecmp(argv[2], "dump", 4)) - dump_pe(port); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pe, command_pe, - "<port> dump", - "USB PE"); -#endif /* CONFIG_CMD_USB_PD_PE */ - -#ifdef CONFIG_CMD_USB_PD_CABLE -static const char * const cable_type[] = { - [IDH_PTYPE_PCABLE] = "Passive", - [IDH_PTYPE_ACABLE] = "Active", -}; - -static const char * const cable_curr[] = { - [USB_VBUS_CUR_3A] = "3A", - [USB_VBUS_CUR_5A] = "5A", -}; - -static int command_cable(int argc, char **argv) -{ - int port; - char *e; - const struct pd_discovery *disc; - enum idh_ptype ptype; - int cable_rev; - union tbt_mode_resp_cable cable_mode_resp; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - port = strtoi(argv[1], &e, 0); - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - ptype = get_usb_pd_cable_type(port); - - ccprintf("Cable Type: "); - if (ptype != IDH_PTYPE_PCABLE && - ptype != IDH_PTYPE_ACABLE) { - ccprintf("Not Emark Cable\n"); - return EC_SUCCESS; - } - ccprintf("%s\n", cable_type[ptype]); - - cable_rev = pd_get_rev(port, TCPCI_MSG_SOP_PRIME); - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - - /* Cable revision */ - ccprintf("Cable Rev: %d.0\n", cable_rev + 1); - - /* - * For rev 2.0, rev 3.0 active and passive cables have same bits for - * connector type (Bit 19:18) and current handling capability bit 6:5 - */ - ccprintf("Connector Type: %d\n", - disc->identity.product_t1.p_rev20.connector); - - if (disc->identity.product_t1.p_rev20.vbus_cur) { - ccprintf("Cable Current: %s\n", - disc->identity.product_t1.p_rev20.vbus_cur > - ARRAY_SIZE(cable_curr) ? "Invalid" : - cable_curr[disc->identity.product_t1.p_rev20.vbus_cur]); - } else - ccprintf("Cable Current: Invalid\n"); - - /* - * For Rev 3.0 passive cables and Rev 2.0 active and passive cables, - * USB Superspeed Signaling support have same bits 2:0 - */ - if (ptype == IDH_PTYPE_PCABLE) - ccprintf("USB Superspeed Signaling support: %d\n", - disc->identity.product_t1.p_rev20.ss); - - /* - * For Rev 3.0 active cables and Rev 2.0 active and passive cables, - * SOP" controller preset have same bit 3 - */ - if (ptype == IDH_PTYPE_ACABLE) - ccprintf("SOP'' Controller: %s present\n", - disc->identity.product_t1.a_rev20.sop_p_p ? "" : "Not"); - - if (cable_rev == PD_REV30) { - /* - * For Rev 3.0 active and passive cables, Max Vbus vtg have - * same bits 10:9. - */ - ccprintf("Max vbus voltage: %d\n", - 20 + 10 * disc->identity.product_t1.p_rev30.vbus_max); - - /* For Rev 3.0 Active cables */ - if (ptype == IDH_PTYPE_ACABLE) { - ccprintf("SS signaling: USB_SS_GEN%u\n", - disc->identity.product_t2.a2_rev30.usb_gen ? - 2 : 1); - ccprintf("Number of SS lanes supported: %u\n", - disc->identity.product_t2.a2_rev30.usb_lanes); - } - } - - if (!cable_mode_resp.raw_value) - return EC_SUCCESS; - - ccprintf("Rounded support: %s\n", - cable_mode_resp.tbt_rounded == - TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED ? "Yes" : "No"); - - ccprintf("Optical cable: %s\n", - cable_mode_resp.tbt_cable == TBT_CABLE_OPTICAL ? "Yes" : "No"); - - ccprintf("Retimer support: %s\n", - cable_mode_resp.retimer_type == USB_RETIMER ? - "Yes" : "No"); - - ccprintf("Link training: %s-directional\n", - cable_mode_resp.lsrx_comm == BIDIR_LSRX_COMM ? "Bi" : "Uni"); - - ccprintf("Thunderbolt cable type: %s\n", - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE ? - "Active" : "Passive"); - - return EC_SUCCESS; -} - -DECLARE_CONSOLE_COMMAND(pdcable, command_cable, - "<port>", - "Cable Characteristics"); -#endif /* CONFIG_CMD_USB_PD_CABLE */ - -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ diff --git a/common/usb_pd_dual_role.c b/common/usb_pd_dual_role.c deleted file mode 100644 index 52042c5439..0000000000 --- a/common/usb_pd_dual_role.c +++ /dev/null @@ -1,473 +0,0 @@ -/* Copyright 2020 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. - * - * Dual Role (Source & Sink) USB-PD module. - */ - -#include "charge_manager.h" -#include "charge_state.h" -#include "dps.h" -#include "system.h" -#include "usb_common.h" -#include "usb_pd.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* The macro is used to prevent a DBZ exception while decoding PDOs. */ -#define PROCESS_ZERO_DIVISOR(x) ((x) == 0 ? 1 : (x)) - -#if defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) -/* - * As a sink, this is the max voltage (in millivolts) we can request - * before getting source caps - */ -static unsigned int max_request_mv = PD_MAX_VOLTAGE_MV; - -/* TODO(b:169532537): deprecate CONFIG_USB_PD_PREFER_MV */ -STATIC_IF_NOT(CONFIG_USB_PD_PREFER_MV) -struct pd_pref_config_t __maybe_unused pd_pref_config; - -void pd_set_max_voltage(unsigned int mv) -{ - max_request_mv = mv; -} - -unsigned int pd_get_max_voltage(void) -{ - return max_request_mv; -} - -/* - * Zinger implements a board specific usb policy that does not define - * PD_MAX_VOLTAGE_MV and PD_OPERATING_POWER_MW. And in turn, does not - * use the following functions. - */ -int pd_find_pdo_index(uint32_t src_cap_cnt, const uint32_t * const src_caps, - int max_mv, uint32_t *selected_pdo) -{ - int i, uw, mv; - int ret = 0; - int cur_uw = 0; - int has_preferred_pdo; - int prefer_cur; - int desired_uw = 0; - const int prefer_mv = pd_pref_config.mv; - const int type = pd_pref_config.type; - - int __attribute__((unused)) cur_mv = 0; - - if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) - desired_uw = charge_get_plt_plus_bat_desired_mw() * 1000; - - /* max voltage is always limited by this boards max request */ - max_mv = MIN(max_mv, PD_MAX_VOLTAGE_MV); - - /* Get max power that is under our max voltage input */ - for (i = 0; i < src_cap_cnt; i++) { - if (IS_ENABLED(CONFIG_USB_PD_ONLY_FIXED_PDOS) && - (src_caps[i] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - continue; - /* its an unsupported Augmented PDO (PD3.0) */ - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) - continue; - - mv = ((src_caps[i] >> 10) & 0x3FF) * 50; - /* Skip invalid voltage */ - if (!mv) - continue; - /* Skip any voltage not supported by this board */ - if (!pd_is_valid_input_voltage(mv)) - continue; - - if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - uw = 250000 * (src_caps[i] & 0x3FF); - } else { - int ma = (src_caps[i] & 0x3FF) * 10; - - ma = MIN(ma, PD_MAX_CURRENT_MA); - uw = ma * mv; - } - - if (mv > max_mv) - continue; - uw = MIN(uw, PD_MAX_POWER_MW * 1000); - prefer_cur = 0; - - /* Apply special rules in favor of voltage */ - if (IS_ENABLED(PD_PREFER_LOW_VOLTAGE)) { - if (uw == cur_uw && mv < cur_mv) - prefer_cur = 1; - } else if (IS_ENABLED(PD_PREFER_HIGH_VOLTAGE)) { - if (uw == cur_uw && mv > cur_mv) - prefer_cur = 1; - } else if (IS_ENABLED(CONFIG_USB_PD_PREFER_MV)) { - /* Pick if the PDO provides more than desired. */ - if (uw >= desired_uw) { - /* pick if cur_uw is less than desired watt */ - if (cur_uw < desired_uw) - prefer_cur = 1; - else if (type == PD_PREFER_BUCK) { - /* - * pick the smallest mV above prefer_mv - */ - if (mv >= prefer_mv && mv < cur_mv) - prefer_cur = 1; - /* - * pick if cur_mv is less than - * prefer_mv, and we have higher mV - */ - else if (cur_mv < prefer_mv && - mv > cur_mv) - prefer_cur = 1; - } else if (type == PD_PREFER_BOOST) { - /* - * pick the largest mV below prefer_mv - */ - if (mv <= prefer_mv && mv > cur_mv) - prefer_cur = 1; - /* - * pick if cur_mv is larger than - * prefer_mv, and we have lower mV - */ - else if (cur_mv > prefer_mv && - mv < cur_mv) - prefer_cur = 1; - } - /* - * pick the largest power if we don't see one staisfy - * desired power - */ - } else if (cur_uw == 0 || uw > cur_uw) { - prefer_cur = 1; - } - } - - /* Prefer higher power, except for tiebreaker */ - has_preferred_pdo = - prefer_cur || - (!IS_ENABLED(CONFIG_USB_PD_PREFER_MV) && uw > cur_uw); - - if (has_preferred_pdo) { - ret = i; - cur_uw = uw; - cur_mv = mv; - } - } - - if (selected_pdo) - *selected_pdo = src_caps[ret]; - - return ret; -} - -void pd_extract_pdo_power(uint32_t pdo, uint32_t *ma, uint32_t *max_mv, - uint32_t *min_mv) -{ - int max_ma, mw; - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) { - *max_mv = PDO_FIXED_VOLTAGE(pdo); - *min_mv = *max_mv; - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - *max_mv = PDO_AUG_MAX_VOLTAGE(pdo); - *min_mv = PDO_AUG_MIN_VOLTAGE(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) { - *max_mv = PDO_VAR_MAX_VOLTAGE(pdo); - *min_mv = PDO_VAR_MIN_VOLTAGE(pdo); - } else { - *max_mv = PDO_BATT_MAX_VOLTAGE(pdo); - *min_mv = PDO_BATT_MIN_VOLTAGE(pdo); - } - - if (*max_mv == 0) { - *ma = 0; - *min_mv = 0; - return; - } - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) { - max_ma = PDO_FIXED_CURRENT(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_AUGMENTED) { - max_ma = PDO_AUG_MAX_CURRENT(pdo); - } else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) { - max_ma = PDO_VAR_MAX_CURRENT(pdo); - } else { - mw = PDO_BATT_MAX_POWER(pdo); - max_ma = 1000 * mw / PROCESS_ZERO_DIVISOR(*min_mv); - } - max_ma = MIN(max_ma, - PD_MAX_POWER_MW * 1000 / PROCESS_ZERO_DIVISOR(*min_mv)); - *ma = MIN(max_ma, PD_MAX_CURRENT_MA); -} - -void pd_build_request(int32_t vpd_vdo, uint32_t *rdo, uint32_t *ma, - uint32_t *mv, int port) -{ - uint32_t pdo; - int pdo_index, flags = 0; - int uw; - int max_or_min_ma; - int max_or_min_mw; - int max_vbus; - int vpd_vbus_dcr; - int vpd_gnd_dcr; - uint32_t src_cap_cnt = pd_get_src_cap_cnt(port); - const uint32_t * const src_caps = pd_get_src_caps(port); - int charging_allowed; - int max_request_allowed; - uint32_t max_request_mv = pd_get_max_voltage(); - uint32_t unused; - - /* - * If this port is the current charge port, or if there isn't an active - * charge port, set this value to true. If CHARGE_PORT_NONE isn't - * considered, then there can be a race condition in PD negotiation and - * the charge manager which forces an incorrect request for - * vSafe5V. This can then lead to a brownout condition when the input - * current limit gets incorrectly set to 0.5A. - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - int chg_port = charge_manager_get_selected_charge_port(); - - charging_allowed = - (chg_port == port || chg_port == CHARGE_PORT_NONE); - } else { - charging_allowed = 1; - } - - if (IS_ENABLED(CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED)) - max_request_allowed = pd_is_max_request_allowed(); - else - max_request_allowed = 1; - - if (IS_ENABLED(CONFIG_USB_PD_DPS) && dps_is_enabled()) - max_request_mv = - MIN(max_request_mv, dps_get_dynamic_voltage()); - - /* - * If currently charging on a different port, or we are not allowed to - * request the max voltage, then select vSafe5V - */ - if (charging_allowed && max_request_allowed) { - /* find pdo index for max voltage we can request */ - pdo_index = pd_find_pdo_index(src_cap_cnt, src_caps, - max_request_mv, &pdo); - } else { - /* src cap 0 should be vSafe5V */ - pdo_index = 0; - pdo = src_caps[0]; - } - - pd_extract_pdo_power(pdo, ma, mv, &unused); - - /* - * Adjust VBUS current if CTVPD device was detected. - */ - if (vpd_vdo > 0) { - max_vbus = VPD_VDO_MAX_VBUS(vpd_vdo); - vpd_vbus_dcr = VPD_VDO_VBUS_IMP(vpd_vdo) << 1; - vpd_gnd_dcr = VPD_VDO_GND_IMP(vpd_vdo); - - /* - * Valid max_vbus values: - * 00b - 20000 mV - * 01b - 30000 mV - * 10b - 40000 mV - * 11b - 50000 mV - */ - max_vbus = 20000 + max_vbus * 10000; - if (*mv > max_vbus) - *mv = max_vbus; - - /* - * 5000 mA cable: 150 = 750000 / 50000 - * 3000 mA cable: 250 = 750000 / 30000 - */ - if (*ma > 3000) - *ma = 750000 / (150 + vpd_vbus_dcr + vpd_gnd_dcr); - else - *ma = 750000 / (250 + vpd_vbus_dcr + vpd_gnd_dcr); - } - - uw = *ma * *mv; - /* Mismatch bit set if less power offered than the operating power */ - if (uw < (1000 * PD_OPERATING_POWER_MW)) - flags |= RDO_CAP_MISMATCH; - -#ifdef CONFIG_USB_PD_GIVE_BACK - /* Tell source we are give back capable. */ - flags |= RDO_GIVE_BACK; - - /* - * BATTERY PDO: Inform the source that the sink will reduce - * power to this minimum level on receipt of a GotoMin Request. - */ - max_or_min_mw = PD_MIN_POWER_MW; - - /* - * FIXED or VARIABLE PDO: Inform the source that the sink will - * reduce current to this minimum level on receipt of a GotoMin - * Request. - */ - max_or_min_ma = PD_MIN_CURRENT_MA; -#else - /* - * Can't give back, so set maximum current and power to - * operating level. - */ - max_or_min_ma = *ma; - max_or_min_mw = uw / 1000; -#endif - - if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { - int mw = uw / 1000; - *rdo = RDO_BATT(pdo_index + 1, mw, max_or_min_mw, flags); - } else { - *rdo = RDO_FIXED(pdo_index + 1, *ma, max_or_min_ma, flags); - } - - /* - * Ref: USB Power Delivery Specification - * (Revision 3.0, Version 2.0 / Revision 2.0, Version 1.3) - * 6.4.2.4 USB Communications Capable - * 6.4.2.5 No USB Suspend - * - * If the port partner is capable of USB communication set the - * USB Communications Capable flag. - * If the port partner is sink device do not suspend USB as the - * power can be used for charging. - */ - if (pd_get_partner_usb_comm_capable(port)) { - *rdo |= RDO_COMM_CAP; - if (pd_get_power_role(port) == PD_ROLE_SINK) - *rdo |= RDO_NO_SUSPEND; - } -} - -void pd_process_source_cap(int port, int cnt, uint32_t *src_caps) -{ - pd_set_src_caps(port, cnt, src_caps); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - uint32_t ma, mv, pdo, unused; - uint32_t max_mv = pd_get_max_voltage(); - - if (IS_ENABLED(CONFIG_USB_PD_DPS) && dps_is_enabled()) - max_mv = MIN(max_mv, dps_get_dynamic_voltage()); - - /* Get max power info that we could request */ - pd_find_pdo_index(pd_get_src_cap_cnt(port), - pd_get_src_caps(port), - max_mv, &pdo); - pd_extract_pdo_power(pdo, &ma, &mv, &unused); - - /* Set max. limit, but apply 500mA ceiling */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, PD_MIN_MA); - pd_set_input_current_limit(port, ma, mv); - } -} -#endif /* defined(PD_MAX_VOLTAGE_MV) && defined(PD_OPERATING_POWER_MW) */ - -bool pd_is_battery_capable(void) -{ - bool capable; - - /* Battery is present and at some minimum percentage. */ - capable = (usb_get_battery_soc() >= - CONFIG_USB_PD_TRY_SRC_MIN_BATT_SOC); - -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - /* - * Not capable if the battery is in the disconnect state. The discharge - * FET may not be enabled and so attempting being a SRC may cut off - * our only power source at the time. - */ - capable &= (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED); -#elif defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * When battery is cutoff in ship mode it may not be reliable to - * check if battery is present with its state of charge. - * Also check if battery is initialized and ready to provide power. - */ - capable &= (battery_is_present() == BP_YES); -#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */ - - return capable; -} - -#ifdef CONFIG_USB_PD_TRY_SRC -bool pd_is_try_source_capable(void) -{ - int i; - uint8_t try_src = 0; - bool new_try_src; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - try_src |= (pd_get_dual_role(i) == PD_DRP_TOGGLE_ON); - - /* - * Enable try source when dual-role toggling AND battery is capable - * of powering the whole system. - */ - new_try_src = (try_src && pd_is_battery_capable()); - -#if CONFIG_DEDICATED_CHARGE_PORT_COUNT > 0 - /* - * If a dedicated supplier is present, power is not a concern and - * therefore allow Try.Src if we're toggling. - */ - new_try_src = try_src && (charge_manager_get_supplier() == - CHARGE_SUPPLIER_DEDICATED); -#endif /* CONFIG_DEDICATED_CHARGE_PORT_COUNT */ - - return new_try_src; -} -#endif /* CONFIG_USB_PD_TRY_SRC */ - -static int get_bbram_idx(uint8_t port) -{ - if (port < MAX_SYSTEM_BBRAM_IDX_PD_PORTS) - return (port + SYSTEM_BBRAM_IDX_PD0); - - return -1; -} - -int pd_get_saved_port_flags(int port, uint8_t *flags) -{ - if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - ccprintf("PD NVRAM FAIL"); -#endif - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -static void pd_set_saved_port_flags(int port, uint8_t flags) -{ - if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) { -#ifndef CHIP_HOST - ccprintf("PD NVRAM FAIL"); -#endif - } -} - -void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t do_set) -{ - uint8_t saved_flags; - - if (pd_get_saved_port_flags(port, &saved_flags) != EC_SUCCESS) - return; - - if (do_set) - saved_flags |= flag; - else - saved_flags &= ~flag; - - pd_set_saved_port_flags(port, saved_flags); -} diff --git a/common/usb_pd_host_cmd.c b/common/usb_pd_host_cmd.c deleted file mode 100644 index 4261e8c1f0..0000000000 --- a/common/usb_pd_host_cmd.c +++ /dev/null @@ -1,590 +0,0 @@ -/* Copyright 2020 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. - * - * Host commands for USB-PD module. - */ - -#include <string.h> - -#include "atomic.h" -#include "battery.h" -#include "charge_manager.h" -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "mkbp_event.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd_tcpm.h" -#include "usb_pd.h" -#ifdef CONFIG_COMMON_RUNTIME -struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif /* CONFIG_COMMON_RUNTIME */ - -#ifdef HAS_TASK_HOSTCMD - -static enum ec_status hc_pd_ports(struct host_cmd_handler_args *args) -{ - struct ec_response_usb_pd_ports *r = args->response; - - r->num_ports = board_get_usb_pd_port_count(); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_PORTS, - hc_pd_ports, - EC_VER_MASK(0)); - -#ifdef CONFIG_HOSTCMD_RWHASHPD -static enum ec_status -hc_remote_rw_hash_entry(struct host_cmd_handler_args *args) -{ - int i, idx = 0, found = 0; - const struct ec_params_usb_pd_rw_hash_entry *p = args->params; - static int rw_hash_next_idx; - - if (!p->dev_id) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < RW_HASH_ENTRIES; i++) { - if (p->dev_id == rw_hash_table[i].dev_id) { - idx = i; - found = 1; - break; - } - } - - if (!found) { - idx = rw_hash_next_idx; - rw_hash_next_idx = rw_hash_next_idx + 1; - if (rw_hash_next_idx == RW_HASH_ENTRIES) - rw_hash_next_idx = 0; - } - memcpy(&rw_hash_table[idx], p, sizeof(*p)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_RW_HASH_ENTRY, - hc_remote_rw_hash_entry, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_RWHASHPD */ - -#if defined(CONFIG_EC_CMD_PD_CHIP_INFO) && !defined(CONFIG_USB_PD_TCPC) -static enum ec_status hc_remote_pd_chip_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_pd_chip_info *p = args->params; - struct ec_response_pd_chip_info_v1 info; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (tcpm_get_chip_info(p->port, p->live, &info)) - return EC_RES_ERROR; - - /* - * Take advantage of the fact that v0 and v1 structs have the - * same layout for v0 data. (v1 just appends data) - */ - args->response_size = - args->version ? sizeof(struct ec_response_pd_chip_info_v1) - : sizeof(struct ec_response_pd_chip_info); - - memcpy(args->response, &info, args->response_size); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHIP_INFO, - hc_remote_pd_chip_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); -#endif /* CONFIG_EC_CMD_PD_CHIP_INFO && !CONFIG_USB_PD_TCPC */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static enum ec_status hc_remote_pd_set_amode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_set_mode_request *p = args->params; - - if ((p->port >= board_get_usb_pd_port_count()) || - (!p->svid) || (!p->opos)) - return EC_RES_INVALID_PARAM; - - switch (p->cmd) { - case PD_EXIT_MODE: - if (pd_dfp_exit_mode(p->port, TCPCI_MSG_SOP, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, - CMD_EXIT_MODE | VDO_OPOS(p->opos), NULL, 0); - else { - CPRINTF("Failed exit mode\n"); - return EC_RES_ERROR; - } - break; - case PD_ENTER_MODE: - if (pd_dfp_enter_mode(p->port, TCPCI_MSG_SOP, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, CMD_ENTER_MODE | - VDO_OPOS(p->opos), NULL, 0); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_SET_AMODE, - hc_remote_pd_set_amode, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_discovery(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_discovery_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->vid = pd_get_identity_vid(*port); - r->ptype = pd_get_product_type(*port); - - /* pid only included if vid is assigned */ - if (r->vid) - r->pid = pd_get_identity_pid(*port); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DISCOVERY, - hc_remote_pd_discovery, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_get_amode(struct host_cmd_handler_args *args) -{ - struct svdm_amode_data *modep; - const struct ec_params_usb_pd_get_mode_request *p = args->params; - struct ec_params_usb_pd_get_mode_response *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* no more to send */ - /* TODO(b/148528713): Use TCPMv2's separate storage for SOP'. */ - if (p->svid_idx >= pd_get_svid_count(p->port, TCPCI_MSG_SOP)) { - r->svid = 0; - args->response_size = sizeof(r->svid); - return EC_RES_SUCCESS; - } - - r->svid = pd_get_svid(p->port, p->svid_idx, TCPCI_MSG_SOP); - r->opos = 0; - memcpy(r->vdo, pd_get_mode_vdo(p->port, p->svid_idx, TCPCI_MSG_SOP), - sizeof(uint32_t) * PDO_MODES); - modep = pd_get_amode_data(p->port, TCPCI_MSG_SOP, r->svid); - - if (modep) - r->opos = pd_alt_mode(p->port, TCPCI_MSG_SOP, r->svid); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_GET_AMODE, - hc_remote_pd_get_amode, - EC_VER_MASK(0)); - -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - -#ifdef CONFIG_COMMON_RUNTIME -static enum ec_status hc_remote_pd_dev_info(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_rw_hash_entry *r = args->response; - uint16_t dev_id; - uint32_t current_image; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - pd_dev_get_rw_hash(*port, &dev_id, r->dev_rw_hash, ¤t_image); - - r->dev_id = dev_id; - r->current_image = current_image; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO, - hc_remote_pd_dev_info, - EC_VER_MASK(0)); - -static const enum pd_dual_role_states dual_role_map[USB_PD_CTRL_ROLE_COUNT] = { - [USB_PD_CTRL_ROLE_TOGGLE_ON] = PD_DRP_TOGGLE_ON, - [USB_PD_CTRL_ROLE_TOGGLE_OFF] = PD_DRP_TOGGLE_OFF, - [USB_PD_CTRL_ROLE_FORCE_SINK] = PD_DRP_FORCE_SINK, - [USB_PD_CTRL_ROLE_FORCE_SOURCE] = PD_DRP_FORCE_SOURCE, - [USB_PD_CTRL_ROLE_FREEZE] = PD_DRP_FREEZE, -}; - -static const mux_state_t typec_mux_map[USB_PD_CTRL_MUX_COUNT] = { - [USB_PD_CTRL_MUX_NONE] = USB_PD_MUX_NONE, - [USB_PD_CTRL_MUX_USB] = USB_PD_MUX_USB_ENABLED, - [USB_PD_CTRL_MUX_AUTO] = USB_PD_MUX_DP_ENABLED, - [USB_PD_CTRL_MUX_DP] = USB_PD_MUX_DP_ENABLED, - [USB_PD_CTRL_MUX_DOCK] = USB_PD_MUX_DOCK, -}; - -/* - * Combines the following information into a single byte - * Bit 0: Active/Passive cable - * Bit 1: Optical/Non-optical cable - * Bit 2: Legacy Thunderbolt adapter - * Bit 3: Active Link Uni-Direction/Bi-Direction - */ -static uint8_t get_pd_control_flags(int port) -{ - union tbt_mode_resp_cable cable_resp; - union tbt_mode_resp_device device_resp; - uint8_t control_flags = 0; - - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - return 0; - - cable_resp.raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - device_resp.raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP); - - /* - * Ref: USB Type-C Cable and Connector Specification - * Table F-11 TBT3 Cable Discover Mode VDO Responses - * For Passive cables, Active Cable Plug link training is set to 0 - */ - control_flags |= (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE || - cable_resp.tbt_active_passive == TBT_CABLE_ACTIVE) ? - USB_PD_CTRL_ACTIVE_CABLE : 0; - control_flags |= cable_resp.tbt_cable == TBT_CABLE_OPTICAL ? - USB_PD_CTRL_OPTICAL_CABLE : 0; - control_flags |= device_resp.tbt_adapter == TBT_ADAPTER_TBT2_LEGACY ? - USB_PD_CTRL_TBT_LEGACY_ADAPTER : 0; - control_flags |= cable_resp.lsrx_comm == UNIDIR_LSRX_COMM ? - USB_PD_CTRL_ACTIVE_LINK_UNIDIR : 0; - - return control_flags; -} - -static uint8_t pd_get_role_flags(int port) -{ - return (pd_get_power_role(port) == PD_ROLE_SOURCE ? - PD_CTRL_RESP_ROLE_POWER : 0) | - (pd_get_data_role(port) == PD_ROLE_DFP ? - PD_CTRL_RESP_ROLE_DATA : 0) | - (pd_get_vconn_state(port) ? - PD_CTRL_RESP_ROLE_VCONN : 0) | - (pd_get_partner_dual_role_power(port) ? - PD_CTRL_RESP_ROLE_DR_POWER : 0) | - (pd_get_partner_data_swap_capable(port) ? - PD_CTRL_RESP_ROLE_DR_DATA : 0) | - (pd_get_partner_usb_comm_capable(port) ? - PD_CTRL_RESP_ROLE_USB_COMM : 0) | - (pd_get_partner_unconstr_power(port) ? - PD_CTRL_RESP_ROLE_UNCONSTRAINED : 0); -} - -static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_control *p = args->params; - struct ec_response_usb_pd_control_v2 *r_v2 = args->response; - struct ec_response_usb_pd_control_v1 *r_v1 = args->response; - struct ec_response_usb_pd_control *r = args->response; - const char *task_state_name; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->role >= USB_PD_CTRL_ROLE_COUNT || - p->mux >= USB_PD_CTRL_MUX_COUNT) - return EC_RES_INVALID_PARAM; - - if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) { - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) - pd_set_dual_role(p->port, dual_role_map[p->role]); - else - return EC_RES_INVALID_PARAM; - } - - if (IS_ENABLED(CONFIG_USBC_SS_MUX) && - p->mux != USB_PD_CTRL_MUX_NO_CHANGE) - usb_mux_set(p->port, typec_mux_map[p->mux], - typec_mux_map[p->mux] == USB_PD_MUX_NONE ? - USB_SWITCH_DISCONNECT : - USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(p->port))); - - if (p->swap == USB_PD_CTRL_SWAP_DATA) { - pd_request_data_swap(p->port); - } else if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) { - if (p->swap == USB_PD_CTRL_SWAP_POWER) - pd_request_power_swap(p->port); - else if (IS_ENABLED(CONFIG_USBC_VCONN_SWAP) && - p->swap == USB_PD_CTRL_SWAP_VCONN) - pd_request_vconn_swap(p->port); - } - - switch (args->version) { - case 0: - r->enabled = pd_comm_is_enabled(p->port); - r->polarity = pd_get_polarity(p->port); - r->role = pd_get_power_role(p->port); - r->state = pd_get_task_state(p->port); - args->response_size = sizeof(*r); - break; - case 1: - case 2: - r_v2->enabled = - (pd_comm_is_enabled(p->port) ? - PD_CTRL_RESP_ENABLED_COMMS : 0) | - (pd_is_connected(p->port) ? - PD_CTRL_RESP_ENABLED_CONNECTED : 0) | - (pd_capable(p->port) ? - PD_CTRL_RESP_ENABLED_PD_CAPABLE : 0); - r_v2->role = pd_get_role_flags(p->port); - r_v2->polarity = pd_get_polarity(p->port); - - r_v2->cc_state = pd_get_task_cc_state(p->port); - task_state_name = pd_get_task_state_name(p->port); - if (task_state_name) - strzcpy(r_v2->state, task_state_name, - sizeof(r_v2->state)); - else - r_v2->state[0] = '\0'; - - r_v2->control_flags = get_pd_control_flags(p->port); - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - r_v2->dp_mode = get_dp_pin_mode(p->port); - r_v2->cable_speed = get_tbt_cable_speed(p->port); - r_v2->cable_gen = get_tbt_rounded_support(p->port); - } - - if (args->version == 1) - args->response_size = sizeof(*r_v1); - else - args->response_size = sizeof(*r_v2); - - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, - hc_usb_pd_control, - EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2)); -#endif /* CONFIG_COMMON_RUNTIME */ - -#if defined(CONFIG_HOSTCMD_FLASHPD) && defined(CONFIG_USB_PD_TCPMV2) -static enum ec_status hc_remote_flash(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_fw_update *p = args->params; - int port = p->port; - int rv = EC_RES_SUCCESS; - const uint32_t *data = &(p->size) + 1; - int i, size; - - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#if defined(CONFIG_CHARGE_MANAGER) && defined(CONFIG_BATTERY) && \ - (defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO)) - /* - * Do not allow PD firmware update if no battery and this port - * is sinking power, because we will lose power. - */ - if (battery_is_present() != BP_YES && - charge_manager_get_active_charge_port() == port) - return EC_RES_UNAVAILABLE; -#endif - - switch (p->cmd) { - case USB_PD_FW_REBOOT: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); - /* - * Return immediately to free pending i2c bus. Host needs to - * manage this delay. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_FLASH_ERASE: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); - /* - * Return immediately. Host needs to manage delays here which - * can be as long as 1.2 seconds on 64KB RW flash. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_ERASE_SIG: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_ERASE_SIG, NULL, 0); - break; - - case USB_PD_FW_FLASH_WRITE: - /* Data size must be a multiple of 4 */ - if (!p->size || p->size % 4) - return EC_RES_INVALID_PARAM; - - size = p->size / 4; - for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, - data + i, MIN(size - i, VDO_MAX_SIZE - 1)); - } - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - } - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, - hc_remote_flash, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_FLASHPD && CONFIG_USB_PD_TCPMV2 */ - -#ifdef CONFIG_MKBP_EVENT -__overridable void pd_notify_dp_alt_mode_entry(int port) -{ - (void)port; - CPRINTS("Notifying AP of DP Alt Mode Entry..."); - mkbp_send_event(EC_MKBP_EVENT_DP_ALT_MODE_ENTERED); -} -#endif /* CONFIG_MKBP_EVENT */ - -__overridable enum ec_pd_port_location board_get_pd_port_location(int port) -{ - (void)port; - return EC_PD_PORT_LOCATION_UNKNOWN; -} - -static enum ec_status hc_get_pd_port_caps(struct host_cmd_handler_args *args) -{ - const struct ec_params_get_pd_port_caps *p = args->params; - struct ec_response_get_pd_port_caps *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* Power Role */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) - r->pd_power_role_cap = EC_PD_POWER_ROLE_DUAL; - else - r->pd_power_role_cap = EC_PD_POWER_ROLE_SINK; - - /* Try-Power Role */ - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - r->pd_try_power_role_cap = EC_PD_TRY_POWER_ROLE_SOURCE; - else - r->pd_try_power_role_cap = EC_PD_TRY_POWER_ROLE_NONE; - - if (IS_ENABLED(CONFIG_USB_VPD) || - IS_ENABLED(CONFIG_USB_CTVPD)) - r->pd_data_role_cap = EC_PD_DATA_ROLE_UFP; - else - r->pd_data_role_cap = EC_PD_DATA_ROLE_DUAL; - - /* Allow boards to override the locations from UNKNOWN if desired */ - r->pd_port_location = board_get_pd_port_location(p->port); - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GET_PD_PORT_CAPS, - hc_get_pd_port_caps, - EC_VER_MASK(0)); - -#ifdef CONFIG_HOSTCMD_PD_CONTROL -static enum ec_status pd_control(struct host_cmd_handler_args *args) -{ - static int pd_control_disabled[CONFIG_USB_PD_PORT_MAX_COUNT]; - const struct ec_params_pd_control *cmd = args->params; - int enable = 0; - - if (cmd->chip >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* Always allow disable command */ - if (cmd->subcmd == PD_CONTROL_DISABLE) { - pd_control_disabled[cmd->chip] = 1; - return EC_RES_SUCCESS; - } - - if (pd_control_disabled[cmd->chip]) - return EC_RES_ACCESS_DENIED; - - if (cmd->subcmd == PD_SUSPEND) { - if (!pd_firmware_upgrade_check_power_readiness(cmd->chip)) - return EC_RES_BUSY; - enable = 0; - } else if (cmd->subcmd == PD_RESUME) { - enable = 1; - } else if (cmd->subcmd == PD_RESET) { -#ifdef HAS_TASK_PDCMD - board_reset_pd_mcu(); -#else - return EC_RES_INVALID_COMMAND; -#endif - } else if (cmd->subcmd == PD_CHIP_ON && board_set_tcpc_power_mode) { - board_set_tcpc_power_mode(cmd->chip, 1); - return EC_RES_SUCCESS; - } else { - return EC_RES_INVALID_COMMAND; - } - - pd_comm_enable(cmd->chip, enable); - pd_set_suspend(cmd->chip, !enable); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CONTROL, pd_control, EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_PD_CONTROL */ - -#if !defined(CONFIG_USB_PD_TCPM_STUB) && !defined(TEST_BUILD) -/* - * PD host event status for host command - * Note: this variable must be aligned on 4-byte boundary because we pass the - * address to atomic_ functions which use assembly to access them. - */ -static uint32_t pd_host_event_status __aligned(4); - -static enum ec_status -hc_pd_host_event_status(struct host_cmd_handler_args *args) -{ - struct ec_response_host_event_status *r = args->response; - - /* Read and clear the host event status to return to AP */ - r->status = atomic_clear(&pd_host_event_status); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_HOST_EVENT_STATUS, hc_pd_host_event_status, - EC_VER_MASK(0)); - -/* Send host event up to AP */ -void pd_send_host_event(int mask) -{ - /* mask must be set */ - if (!mask) - return; - - atomic_or(&pd_host_event_status, mask); - /* interrupt the AP */ - host_set_single_event(EC_HOST_EVENT_PD_MCU); -} -#endif /* ! CONFIG_USB_PD_TCPM_STUB && ! TEST_BUILD */ - -#endif /* HAS_TASK_HOSTCMD */ diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c deleted file mode 100644 index de6fc63a60..0000000000 --- a/common/usb_pd_policy.c +++ /dev/null @@ -1,969 +0,0 @@ -/* Copyright 2014 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 "atomic.h" -#include "charge_manager.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "flash.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "mkbp_event.h" -#include "registers.h" -#include "rsa.h" -#include "sha256.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "util.h" -#include "usb_api.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usbc_ppc.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ##args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ##args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -/* - * This file is currently only used for TCPMv1, and would need changes before - * being used for TCPMv2. One example: PD_FLAGS_* are TCPMv1 only. - */ -#ifndef CONFIG_USB_PD_TCPMV1 -#error This file must only be used with TCPMv1 -#endif - -static int rw_flash_changed = 1; - -__overridable void pd_check_pr_role(int port, enum pd_power_role pr_role, - int flags) -{ - /* - * If partner is dual-role power and dualrole toggling is on, consider - * if a power swap is necessary. - */ - if ((flags & PD_FLAGS_PARTNER_DR_POWER) && - pd_get_dual_role(port) == PD_DRP_TOGGLE_ON) { - /* - * If we are a sink and partner is not unconstrained, then - * swap to become a source. If we are source and partner is - * unconstrained, swap to become a sink. - */ - int partner_unconstrained = flags & PD_FLAGS_PARTNER_UNCONSTR; - - if ((!partner_unconstrained && pr_role == PD_ROLE_SINK) || - (partner_unconstrained && pr_role == PD_ROLE_SOURCE)) - pd_request_power_swap(port); - } -} - -__overridable void pd_check_dr_role(int port, enum pd_data_role dr_role, - int flags) -{ - /* If UFP, try to switch to DFP */ - if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_UFP) - pd_request_data_swap(port); -} - -#ifdef CONFIG_MKBP_EVENT -static int dp_alt_mode_entry_get_next_event(uint8_t *data) -{ - return EC_SUCCESS; -} -DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_DP_ALT_MODE_ENTERED, - dp_alt_mode_entry_get_next_event); -#endif /* CONFIG_MKBP_EVENT */ - -/* Last received source cap */ -static uint32_t pd_src_caps[CONFIG_USB_PD_PORT_MAX_COUNT][PDO_MAX_OBJECTS]; -static uint8_t pd_src_cap_cnt[CONFIG_USB_PD_PORT_MAX_COUNT]; - -const uint32_t * const pd_get_src_caps(int port) -{ - return pd_src_caps[port]; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ - int i; - - pd_src_cap_cnt[port] = cnt; - - for (i = 0; i < cnt; i++) - pd_src_caps[port][i] = *src_caps++; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - return pd_src_cap_cnt[port]; -} - -static struct pd_cable cable[CONFIG_USB_PD_PORT_MAX_COUNT]; - -enum pd_rev_type get_usb_pd_cable_revision(int port) -{ - return cable[port].rev; -} - -bool consume_sop_prime_repeat_msg(int port, uint8_t msg_id) -{ - if (cable[port].last_sop_p_msg_id != msg_id) { - cable[port].last_sop_p_msg_id = msg_id; - return false; - } - CPRINTF("C%d SOP Prime repeat msg_id %d\n", port, msg_id); - return true; -} - -bool consume_sop_prime_prime_repeat_msg(int port, uint8_t msg_id) -{ - if (cable[port].last_sop_p_p_msg_id != msg_id) { - cable[port].last_sop_p_p_msg_id = msg_id; - return false; - } - CPRINTF("C%d SOP Prime Prime repeat msg_id %d\n", port, msg_id); - return true; -} - -__maybe_unused static uint8_t is_sop_prime_ready(int port) -{ - /* - * Ref: USB PD 3.0 sec 2.5.4: When an Explicit Contract is in place the - * VCONN Source (either the DFP or the UFP) can communicate with the - * Cable Plug(s) using SOP’/SOP’’ Packets - * - * Ref: USB PD 2.0 sec 2.4.4: When an Explicit Contract is in place the - * DFP (either the Source or the Sink) can communicate with the - * Cable Plug(s) using SOP’/SOP” Packets. - * Sec 3.6.11 : Before communicating with a Cable Plug a Port Should - * ensure that it is the Vconn Source - */ - return (pd_get_vconn_state(port) && - (IS_ENABLED(CONFIG_USB_PD_REV30) || - (pd_get_data_role(port) == PD_ROLE_DFP))); -} - -void reset_pd_cable(int port) -{ - memset(&cable[port], 0, sizeof(cable[port])); - cable[port].last_sop_p_msg_id = INVALID_MSG_ID_COUNTER; - cable[port].last_sop_p_p_msg_id = INVALID_MSG_ID_COUNTER; -} - -bool should_enter_usb4_mode(int port) -{ - return IS_ENABLED(CONFIG_USB_PD_USB4) && - cable[port].flags & CABLE_FLAGS_ENTER_USB_MODE; -} - -void enable_enter_usb4_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - cable[port].flags |= CABLE_FLAGS_ENTER_USB_MODE; -} - -void disable_enter_usb4_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - cable[port].flags &= ~CABLE_FLAGS_ENTER_USB_MODE; -} - -#ifdef CONFIG_USB_PD_ALT_MODE - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - -static struct pd_discovery discovery[CONFIG_USB_PD_PORT_MAX_COUNT] - [DISCOVERY_TYPE_COUNT]; -static struct partner_active_modes partner_amodes[CONFIG_USB_PD_PORT_MAX_COUNT] - [AMODE_TYPE_COUNT]; - -static bool is_vdo_present(int cnt, int index) -{ - return cnt > index; -} - -static bool is_modal(int port, int cnt, const uint32_t *payload) -{ - return is_vdo_present(cnt, VDO_INDEX_IDH) && - PD_IDH_IS_MODAL(payload[VDO_INDEX_IDH]); -} - -static bool is_tbt_compat_mode(int port, int cnt, const uint32_t *payload) -{ - /* - * Ref: USB Type-C cable and connector specification - * F.2.5 TBT3 Device Discover Mode Responses - */ - return is_vdo_present(cnt, VDO_INDEX_IDH) && - PD_VDO_RESP_MODE_INTEL_TBT(payload[VDO_INDEX_IDH]); -} - -static bool cable_supports_tbt_speed(int port) -{ - enum tbt_compat_cable_speed tbt_cable_speed = get_tbt_cable_speed(port); - - return (tbt_cable_speed == TBT_SS_TBT_GEN3 || - tbt_cable_speed == TBT_SS_U32_GEN1_GEN2); -} - -static bool is_tbt_compat_enabled(int port) -{ - return (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - (cable[port].flags & CABLE_FLAGS_TBT_COMPAT_ENABLE)); -} - -static void enable_tbt_compat_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) - cable[port].flags |= CABLE_FLAGS_TBT_COMPAT_ENABLE; -} - -static inline void disable_tbt_compat_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) - cable[port].flags &= ~CABLE_FLAGS_TBT_COMPAT_ENABLE; -} - -static inline void limit_tbt_cable_speed(int port) -{ - /* Cable flags are cleared when cable reset is called */ - cable[port].flags |= CABLE_FLAGS_TBT_COMPAT_LIMIT_SPEED; -} - -static inline bool is_limit_tbt_cable_speed(int port) -{ - return !!(cable[port].flags & CABLE_FLAGS_TBT_COMPAT_LIMIT_SPEED); -} - -static bool is_intel_svid(int port, enum tcpci_msg_type type) -{ - int i; - - for (i = 0; i < discovery[port][type].svid_cnt; i++) { - if (pd_get_svid(port, i, type) == USB_VID_INTEL) - return true; - } - - return false; -} - -static inline bool is_usb4_mode_enabled(int port) -{ - return (IS_ENABLED(CONFIG_USB_PD_USB4) && - (cable[port].flags & CABLE_FLAGS_USB4_CAPABLE)); -} - -static inline void enable_usb4_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - cable[port].flags |= CABLE_FLAGS_USB4_CAPABLE; -} - -static inline void disable_usb4_mode(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - cable[port].flags &= ~CABLE_FLAGS_USB4_CAPABLE; -} - -/* - * Ref: USB Type-C Cable and Connector Specification - * Figure 5-1 USB4 Discovery and Entry Flow Model. - * - * Note: USB Type-C Cable and Connector Specification - * doesn't include details for Revision 2 cables. - * - * Passive Cable - * | - * ----------------------------------- - * | | - * Revision 2 Revision 3 - * USB Signalling USB Signalling - * | | - * ------------------ ------------------------- - * | | | | | | | - * USB2.0 USB3.1 USB3.1 USB3.2 USB4 USB3.2 USB2 - * | Gen1 Gen1 Gen2 Gen2 Gen3 Gen1 | - * | | | | | | Exit - * -------- ------------ -------- USB4 - * | | | Discovery. - * Exit Is DFP Gen3 Capable? Enter USB4 - * USB4 | with respective - * Discovery. --- No ---|--- Yes --- cable speed. - * | | - * Enter USB4 with Is Cable TBT3 - * respective cable | - * speed. --- No ---|--- Yes --- - * | | - * Enter USB4 with Enter USB4 with - * TBT Gen2 passive TBT Gen3 passive - * cable. cable. - * - */ -static bool is_cable_ready_to_enter_usb4(int port, int cnt) -{ - /* TODO: USB4 enter mode for Active cables */ - struct pd_discovery *disc = &discovery[port][TCPCI_MSG_SOP_PRIME]; - if (IS_ENABLED(CONFIG_USB_PD_USB4) && - (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) && - is_vdo_present(cnt, VDO_INDEX_PTYPE_CABLE1)) { - switch (cable[port].rev) { - case PD_REV30: - switch (disc->identity.product_t1.p_rev30.ss) { - case USB_R30_SS_U40_GEN3: - case USB_R30_SS_U32_U40_GEN1: - return true; - case USB_R30_SS_U32_U40_GEN2: - /* Check if DFP is Gen 3 capable */ - if (IS_ENABLED(CONFIG_USB_PD_TBT_GEN3_CAPABLE)) - return false; - return true; - default: - disable_usb4_mode(port); - return false; - } - case PD_REV20: - switch (disc->identity.product_t1.p_rev20.ss) { - case USB_R20_SS_U31_GEN1_GEN2: - /* Check if DFP is Gen 3 capable */ - if (IS_ENABLED(CONFIG_USB_PD_TBT_GEN3_CAPABLE)) - return false; - return true; - default: - disable_usb4_mode(port); - return false; - } - default: - disable_usb4_mode(port); - } - } - return false; -} - -void pd_dfp_discovery_init(int port) -{ - memset(&discovery[port], 0, sizeof(struct pd_discovery)); -} - -void pd_dfp_mode_init(int port) -{ - memset(&partner_amodes[port], 0, sizeof(partner_amodes[0])); -} - -static int dfp_discover_ident(uint32_t *payload) -{ - payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_IDENT); - return 1; -} - -static int dfp_discover_svids(uint32_t *payload) -{ - payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); - return 1; -} - -struct pd_discovery *pd_get_am_discovery_and_notify_access( - int port, enum tcpci_msg_type type) -{ - return (struct pd_discovery *)pd_get_am_discovery(port, type); -} - -const struct pd_discovery *pd_get_am_discovery(int port, - enum tcpci_msg_type type) -{ - return &discovery[port][type]; -} - -struct partner_active_modes * -pd_get_partner_active_modes(int port, enum tcpci_msg_type type) -{ - assert(type < AMODE_TYPE_COUNT); - return &partner_amodes[port][type]; -} - -/* Note: Enter mode flag is not needed by TCPMv1 */ -void pd_set_dfp_enter_mode_flag(int port, bool set) -{ -} - -/** - * Return the discover alternate mode payload data - * - * @param port USB-C port number - * @param payload Pointer to payload data to fill - * @return 1 if valid SVID present else 0 - */ -static int dfp_discover_modes(int port, uint32_t *payload) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - uint16_t svid = disc->svids[disc->svid_idx].svid; - - if (disc->svid_idx >= disc->svid_cnt) - return 0; - - payload[0] = VDO(svid, 1, CMD_DISCOVER_MODES); - - return 1; -} - -static bool is_usb4_vdo(int port, int cnt, uint32_t *payload) -{ - enum idh_ptype ptype = PD_IDH_PTYPE(payload[VDO_I(IDH)]); - - if (IS_PD_IDH_UFP_PTYPE(ptype)) { - /* - * Ref: USB Type-C Cable and Connector Specification - * Figure 5-1 USB4 Discovery and Entry Flow Model - * Device USB4 VDO detection. - */ - return IS_ENABLED(CONFIG_USB_PD_USB4) && - is_vdo_present(cnt, VDO_INDEX_PTYPE_UFP1_VDO) && - PD_PRODUCT_IS_USB4(payload[VDO_INDEX_PTYPE_UFP1_VDO]); - } - return false; -} - -static int process_am_discover_ident_sop(int port, int cnt, uint32_t head, - uint32_t *payload, - enum tcpci_msg_type *rtype) -{ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); - dfp_consume_identity(port, TCPCI_MSG_SOP, cnt, payload); - - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP) && is_sop_prime_ready(port) && - board_is_tbt_usb4_port(port)) { - /* Enable USB4 mode if USB4 VDO present and port partner - * supports USB Rev 3.0. - */ - if (is_usb4_vdo(port, cnt, payload) && - PD_HEADER_REV(head) == PD_REV30) - enable_usb4_mode(port); - - /* - * Enable Thunderbolt-compatible mode if the modal operation is - * supported. - */ - if (is_modal(port, cnt, payload)) - enable_tbt_compat_mode(port); - - if (is_modal(port, cnt, payload) || - is_usb4_vdo(port, cnt, payload)) { - *rtype = TCPCI_MSG_SOP_PRIME; - return dfp_discover_ident(payload); - } - } - - return dfp_discover_svids(payload); -} - -static int process_am_discover_ident_sop_prime(int port, int cnt, uint32_t head, - uint32_t *payload) -{ - dfp_consume_identity(port, TCPCI_MSG_SOP_PRIME, cnt, payload); - cable[port].rev = PD_HEADER_REV(head); - - /* - * Enter USB4 mode if the cable supports USB4 operation and has USB4 - * VDO. - */ - if (is_usb4_mode_enabled(port) && - is_cable_ready_to_enter_usb4(port, cnt)) { - enable_enter_usb4_mode(port); - usb_mux_set_safe_mode(port); - /* - * To change the mode of operation from USB4 the port needs to - * be reconfigured. - * Ref: USB Type-C Cable and Connectot Spec section 5.4.4. - */ - disable_tbt_compat_mode(port); - return 0; - } - - /* - * Disable Thunderbolt-compatible mode if the cable does not support - * superspeed. - */ - if (is_tbt_compat_enabled(port) && - get_tbt_cable_speed(port) < TBT_SS_U31_GEN1) - disable_tbt_compat_mode(port); - - return dfp_discover_svids(payload); -} - -static int process_am_discover_svids(int port, int cnt, uint32_t *payload, - enum tcpci_msg_type sop, - enum tcpci_msg_type *rtype) -{ - /* - * The pd_discovery structure stores SOP and SOP' discovery results - * separately, but TCPMv1 depends on one-dimensional storage of SVIDs - * and modes. Therefore, always use TCPCI_MSG_SOP in TCPMv1. - */ - dfp_consume_svids(port, sop, cnt, payload); - - /* - * Ref: USB Type-C Cable and Connector Specification, - * figure F-1: TBT3 Discovery Flow - * - * For USB4 mode if device or cable doesn't have Intel SVID, - * disable Thunderbolt-Compatible mode directly enter USB4 mode - * with USB3.2 Gen1/Gen2 speed. - * - * For Thunderbolt-compatible, check if 0x8087 is received for - * Discover SVID SOP. If not, disable Thunderbolt-compatible mode - * - * If 0x8087 is not received for Discover SVID SOP' limit to TBT - * passive Gen 2 cable. - */ - if (is_tbt_compat_enabled(port)) { - bool intel_svid = is_intel_svid(port, sop); - if (!intel_svid) { - if (is_usb4_mode_enabled(port)) { - disable_tbt_compat_mode(port); - cable[port].cable_mode_resp.tbt_cable_speed = - TBT_SS_U32_GEN1_GEN2; - enable_enter_usb4_mode(port); - usb_mux_set_safe_mode(port); - return 0; - } - - if (sop == TCPCI_MSG_SOP_PRIME) - limit_tbt_cable_speed(port); - else - disable_tbt_compat_mode(port); - } else if (sop == TCPCI_MSG_SOP) { - *rtype = TCPCI_MSG_SOP_PRIME; - return dfp_discover_svids(payload); - } - } - - return dfp_discover_modes(port, payload); -} - -static int process_tbt_compat_discover_modes(int port, - enum tcpci_msg_type sop, - uint32_t *payload, - enum tcpci_msg_type *rtype) -{ - int rsize; - - /* Initialize transmit type to SOP */ - *rtype = TCPCI_MSG_SOP; - - /* - * For active cables, Enter mode: SOP', SOP'', SOP - * Ref: USB Type-C Cable and Connector Specification, figure F-1: TBT3 - * Discovery Flow and Section F.2.7 TBT3 Cable Enter Mode Command. - */ - if (sop == TCPCI_MSG_SOP_PRIME) { - /* Store Discover Mode SOP' response */ - cable[port].cable_mode_resp.raw_value = payload[1]; - - if (is_usb4_mode_enabled(port)) { - /* - * If Cable is not Thunderbolt Gen 3 - * capable or Thunderbolt Gen1_Gen2 - * capable, disable USB4 mode and - * continue flow for - * Thunderbolt-compatible mode - */ - if (cable_supports_tbt_speed(port)) { - enable_enter_usb4_mode(port); - usb_mux_set_safe_mode(port); - return 0; - } - disable_usb4_mode(port); - } - - /* - * Send TBT3 Cable Enter Mode (SOP') for active cables, - * otherwise send TBT3 Device Enter Mode (SOP). - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - *rtype = TCPCI_MSG_SOP_PRIME; - - rsize = enter_tbt_compat_mode(port, *rtype, payload); - } else { - /* Store Discover Mode SOP response */ - cable[port].dev_mode_resp.raw_value = payload[1]; - - if (is_limit_tbt_cable_speed(port)) { - /* - * Passive cable has Nacked for Discover SVID. - * No need to do Discover modes of cable. - * Enter into device Thunderbolt-compatible mode. - */ - rsize = enter_tbt_compat_mode(port, *rtype, payload); - } else { - /* Discover modes for SOP' */ - discovery[port][TCPCI_MSG_SOP].svid_idx--; - rsize = dfp_discover_modes(port, payload); - *rtype = TCPCI_MSG_SOP_PRIME; - } - } - - return rsize; -} - -static int obj_cnt_enter_tbt_compat_mode(int port, enum tcpci_msg_type sop, - uint32_t *payload, - enum tcpci_msg_type *rtype) -{ - struct pd_discovery *disc = &discovery[port][TCPCI_MSG_SOP_PRIME]; - - /* Enter mode SOP' for active cables */ - if (sop == TCPCI_MSG_SOP_PRIME) { - /* Check if the cable has a SOP'' controller */ - if (disc->identity.product_t1.a_rev20.sop_p_p) - *rtype = TCPCI_MSG_SOP_PRIME_PRIME; - return enter_tbt_compat_mode(port, *rtype, payload); - } - - /* Enter Mode SOP'' for active cables with SOP'' controller */ - if (sop == TCPCI_MSG_SOP_PRIME_PRIME) - return enter_tbt_compat_mode(port, *rtype, payload); - - /* Update Mux state to Thunderbolt-compatible mode. */ - set_tbt_compat_mode_ready(port); - /* No response once device (and cable) acks */ - return 0; -} -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - -int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload, - uint32_t head, enum tcpci_msg_type *rtype) -{ - int cmd = PD_VDO_CMD(payload[0]); - int cmd_type = PD_VDO_CMDT(payload[0]); - int (*func)(int port, uint32_t *payload) = NULL; - - int rsize = 1; /* VDM header at a minimum */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - enum tcpci_msg_type sop = PD_HEADER_GET_SOP(head); -#endif - - /* Transmit SOP messages by default */ - *rtype = TCPCI_MSG_SOP; - - payload[0] &= ~VDO_CMDT_MASK; - *rpayload = payload; - - if (cmd_type == CMDT_INIT) { - switch (cmd) { - case CMD_DISCOVER_IDENT: - func = svdm_rsp.identity; - break; - case CMD_DISCOVER_SVID: - func = svdm_rsp.svids; - break; - case CMD_DISCOVER_MODES: - func = svdm_rsp.modes; - break; - case CMD_ENTER_MODE: - func = svdm_rsp.enter_mode; - break; - case CMD_DP_STATUS: - if (svdm_rsp.amode) - func = svdm_rsp.amode->status; - break; - case CMD_DP_CONFIG: - if (svdm_rsp.amode) - func = svdm_rsp.amode->config; - break; - case CMD_EXIT_MODE: - func = svdm_rsp.exit_mode; - break; -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_ATTENTION: - /* - * attention is only SVDM with no response - * (just goodCRC) return zero here. - */ - dfp_consume_attention(port, payload); - return 0; -#endif - default: - CPRINTF("ERR:CMD:%d\n", cmd); - rsize = 0; - } - if (func) - rsize = func(port, payload); - else /* not supported : NACK it */ - rsize = 0; - if (rsize >= 1) - payload[0] |= VDO_CMDT(CMDT_RSP_ACK); - else if (!rsize) { - payload[0] |= VDO_CMDT(CMDT_RSP_NAK); - rsize = 1; - } else { - payload[0] |= VDO_CMDT(CMDT_RSP_BUSY); - rsize = 1; - } - payload[0] |= - VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - } else if (cmd_type == CMDT_RSP_ACK) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - struct svdm_amode_data *modep; - - modep = pd_get_amode_data(port, TCPCI_MSG_SOP, - PD_VDO_VID(payload[0])); -#endif - switch (cmd) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_DISCOVER_IDENT: - /* Received a SOP' Discover Ident msg */ - if (sop == TCPCI_MSG_SOP_PRIME) { - rsize = process_am_discover_ident_sop_prime( - port, cnt, head, payload); - /* Received a SOP Discover Ident Message */ - } else { - rsize = process_am_discover_ident_sop( - port, cnt, head, payload, rtype); - } - break; - case CMD_DISCOVER_SVID: - rsize = process_am_discover_svids(port, cnt, payload, - sop, rtype); - break; - case CMD_DISCOVER_MODES: - dfp_consume_modes(port, sop, cnt, payload); - if (is_tbt_compat_enabled(port) && - is_tbt_compat_mode(port, cnt, payload)) { - rsize = process_tbt_compat_discover_modes( - port, sop, payload, rtype); - break; - } - - rsize = dfp_discover_modes(port, payload); - /* enter the default mode for DFP */ - if (!rsize) { - /* - * Disabling Thunderbolt-Compatible mode if - * discover mode response doesn't include Intel - * SVID. - */ - disable_tbt_compat_mode(port); - payload[0] = pd_dfp_enter_mode( - port, TCPCI_MSG_SOP, 0, 0); - if (payload[0]) - rsize = 1; - } - break; - case CMD_ENTER_MODE: - if (is_tbt_compat_enabled(port)) { - rsize = obj_cnt_enter_tbt_compat_mode( - port, sop, payload, rtype); - /* - * Continue with PD flow if - * Thunderbolt-compatible mode is disabled. - */ - } else if (!modep) { - rsize = 0; - } else { - if (!modep->opos) - pd_dfp_enter_mode(port, TCPCI_MSG_SOP, - 0, 0); - - if (modep->opos) { - rsize = modep->fx->status(port, - payload); - payload[0] |= PD_VDO_OPOS(modep->opos); - } - } - break; - case CMD_DP_STATUS: - /* DP status response & UFP's DP attention have same - payload */ - dfp_consume_attention(port, payload); - if (modep && modep->opos) - rsize = modep->fx->config(port, payload); - else - rsize = 0; - break; - case CMD_DP_CONFIG: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - /* no response after DFPs ack */ - rsize = 0; - break; - case CMD_EXIT_MODE: - /* no response after DFPs ack */ - rsize = 0; - break; -#endif - case CMD_ATTENTION: - /* no response after DFPs ack */ - rsize = 0; - break; - default: - CPRINTF("ERR:CMD:%d\n", cmd); - rsize = 0; - } - - payload[0] |= VDO_CMDT(CMDT_INIT); - payload[0] |= - VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - } else if (cmd_type == CMDT_RSP_BUSY) { - switch (cmd) { - case CMD_DISCOVER_IDENT: - case CMD_DISCOVER_SVID: - case CMD_DISCOVER_MODES: - /* resend if its discovery */ - rsize = 1; - break; - case CMD_ENTER_MODE: - /* Error */ - CPRINTF("ERR:ENTBUSY\n"); - rsize = 0; - break; - case CMD_EXIT_MODE: - rsize = 0; - break; - default: - rsize = 0; - } - } else if (cmd_type == CMDT_RSP_NAK) { - /* Passive cable Nacked for Discover SVID */ - if (cmd == CMD_DISCOVER_SVID && is_tbt_compat_enabled(port) && - sop == TCPCI_MSG_SOP_PRIME && - get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { - limit_tbt_cable_speed(port); - rsize = dfp_discover_modes(port, payload); - } else { - rsize = 0; - } -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ - } else { - CPRINTF("ERR:CMDT:%d\n", cmd); - /* do not answer */ - rsize = 0; - } - return rsize; -} - -#else - -int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload, - uint32_t head, enum tcpci_msg_type *rtype) -{ - return 0; -} - -#endif /* CONFIG_USB_PD_ALT_MODE */ - -#define FW_RW_END \ - (CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF + \ - CONFIG_RW_SIZE) - -uint8_t *flash_hash_rw(void) -{ - static struct sha256_ctx ctx; - - /* re-calculate RW hash when changed as its time consuming */ - if (rw_flash_changed) { - rw_flash_changed = 0; - SHA256_init(&ctx); - SHA256_update(&ctx, - (void *)CONFIG_PROGRAM_MEMORY_BASE + - CONFIG_RW_MEM_OFF, - CONFIG_RW_SIZE - RSANUMBYTES); - return SHA256_final(&ctx); - } else { - return ctx.buf; - } -} - -void pd_get_info(uint32_t *info_data) -{ - void *rw_hash = flash_hash_rw(); - - /* copy first 20 bytes of RW hash */ - memcpy(info_data, rw_hash, 5 * sizeof(uint32_t)); - /* copy other info into data msg */ -#if defined(CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR) && \ - defined(CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR) - info_data[5] = VDO_INFO(CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR, - CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR, - ver_get_num_commits(system_get_image_copy()), - (system_get_image_copy() != EC_IMAGE_RO)); -#else - info_data[5] = 0; -#endif -} - -int pd_custom_flash_vdm(int port, int cnt, uint32_t *payload) -{ - static int flash_offset; - int rsize = 1; /* default is just VDM header returned */ - - switch (PD_VDO_CMD(payload[0])) { - case VDO_CMD_VERSION: - memcpy(payload + 1, ¤t_image_data.version, 24); - rsize = 7; - break; - case VDO_CMD_REBOOT: - /* ensure the power supply is in a safe state */ - pd_power_supply_reset(0); - system_reset(0); - break; - case VDO_CMD_READ_INFO: - /* copy info into response */ - pd_get_info(payload + 1); - rsize = 7; - break; - case VDO_CMD_FLASH_ERASE: - /* do not kill the code under our feet */ - if (system_get_image_copy() != EC_IMAGE_RO) - break; - pd_log_event(PD_EVENT_ACC_RW_ERASE, 0, 0, NULL); - flash_offset = - CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF; - crec_flash_physical_erase(CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF, - CONFIG_RW_SIZE); - rw_flash_changed = 1; - break; - case VDO_CMD_FLASH_WRITE: - /* do not kill the code under our feet */ - if ((system_get_image_copy() != EC_IMAGE_RO) || - (flash_offset < - CONFIG_EC_WRITABLE_STORAGE_OFF + CONFIG_RW_STORAGE_OFF)) - break; - crec_flash_physical_write(flash_offset, 4 * (cnt - 1), - (const char *)(payload + 1)); - flash_offset += 4 * (cnt - 1); - rw_flash_changed = 1; - break; - case VDO_CMD_ERASE_SIG: - /* this is not touching the code area */ - { - uint32_t zero = 0; - int offset; - /* zeroes the area containing the RSA signature */ - for (offset = FW_RW_END - RSANUMBYTES; - offset < FW_RW_END; offset += 4) - crec_flash_physical_write(offset, 4, - (const char *)&zero); - } - break; - default: - /* Unknown : do not answer */ - return 0; - } - return rsize; -} diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c deleted file mode 100644 index abf75e8004..0000000000 --- a/common/usb_pd_protocol.c +++ /dev/null @@ -1,5449 +0,0 @@ -/* Copyright 2014 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 "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "printf.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpci.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_flags.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_tcpc.h" -#include "usbc_ocp.h" -#include "usbc_ppc.h" -#include "vboot.h" - -/* Flags to clear on a disconnect */ -#define PD_FLAGS_RESET_ON_DISCONNECT_MASK (PD_FLAGS_PARTNER_DR_POWER | \ - PD_FLAGS_PARTNER_DR_DATA | \ - PD_FLAGS_CHECK_IDENTITY | \ - PD_FLAGS_SNK_CAP_RECVD | \ - PD_FLAGS_TCPC_DRP_TOGGLE | \ - PD_FLAGS_EXPLICIT_CONTRACT | \ - PD_FLAGS_PREVIOUS_PD_CONN | \ - PD_FLAGS_CHECK_PR_ROLE | \ - PD_FLAGS_CHECK_DR_ROLE | \ - PD_FLAGS_PARTNER_UNCONSTR | \ - PD_FLAGS_VCONN_ON | \ - PD_FLAGS_TRY_SRC | \ - PD_FLAGS_PARTNER_USB_COMM | \ - PD_FLAGS_UPDATE_SRC_CAPS | \ - PD_FLAGS_TS_DTS_PARTNER | \ - PD_FLAGS_SNK_WAITING_BATT | \ - PD_FLAGS_CHECK_VCONN_STATE) - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -static int tcpc_prints(const char *string, int port) -{ - return CPRINTS("TCPC p%d %s", port, string); -} - -BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT <= EC_USB_PD_MAX_PORTS); - -/* - * Debug log level - higher number == more log - * Level 0: Log state transitions - * Level 1: Level 0, plus state name - * Level 2: Level 1, plus packet info - * Level 3: Level 2, plus ping packet and packet dump on error - * - * Note that higher log level causes timing changes and thus may affect - * performance. - * - * Can be limited to constant debug_level by CONFIG_USB_PD_DEBUG_LEVEL - */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const int debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static int debug_level; -#endif - -/* - * PD communication enabled flag. When false, PD state machine still - * detects source/sink connection and disconnection, and will still - * provide VBUS, but never sends any PD communication. - */ -static uint8_t pd_comm_enabled[CONFIG_USB_PD_PORT_MAX_COUNT]; -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#define tcpc_prints(string, port) -static const int debug_level; -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE -#define DUAL_ROLE_IF_ELSE(port, sink_clause, src_clause) \ - (pd[port].power_role == PD_ROLE_SINK ? (sink_clause) : (src_clause)) -#else -#define DUAL_ROLE_IF_ELSE(port, sink_clause, src_clause) (src_clause) -#endif - -#define READY_RETURN_STATE(port) DUAL_ROLE_IF_ELSE(port, PD_STATE_SNK_READY, \ - PD_STATE_SRC_READY) - -/* Type C supply voltage (mV) */ -#define TYPE_C_VOLTAGE 5000 /* mV */ - -/* PD counter definitions */ -#define PD_MESSAGE_ID_COUNT 7 -#define PD_HARD_RESET_COUNT 2 -#define PD_CAPS_COUNT 50 -#define PD_SNK_CAP_RETRIES 3 - -/* - * The time that we allow the port partner to send any messages after an - * explicit contract is established. 200ms was chosen somewhat arbitrarily as - * it should be long enough for sources to decide to send a message if they were - * going to, but not so long that a "low power charger connected" notification - * would be shown in the chrome OS UI. - */ -#define SNK_READY_HOLD_OFF_US (200 * MSEC) -/* - * For the same purpose as SNK_READY_HOLD_OFF_US, but this delay can be longer - * since the concern over "low power charger" is not relevant when connected as - * a source and the additional delay avoids a race condition where the partner - * port sends a power role swap request close to when the VDM discover identity - * message gets sent. - */ -#define SRC_READY_HOLD_OFF_US (400 * MSEC) - -enum ams_seq { - AMS_START, - AMS_RESPONSE, -}; - -enum vdm_states { - VDM_STATE_ERR_BUSY = -3, - VDM_STATE_ERR_SEND = -2, - VDM_STATE_ERR_TMOUT = -1, - VDM_STATE_DONE = 0, - /* Anything >0 represents an active state */ - VDM_STATE_READY = 1, - VDM_STATE_BUSY = 2, - VDM_STATE_WAIT_RSP_BUSY = 3, -}; - -#ifdef CONFIG_USB_PD_DUAL_ROLE -/* Port dual-role state */ -enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = - CONFIG_USB_PD_INITIAL_DRP_STATE}; - -/* Enable variable for Try.SRC states */ -static bool pd_try_src_enable; -#endif - -#ifdef CONFIG_USB_PD_REV30 -/* - * The spec. revision is the argument for this macro. - * Rev 0 (PD 1.0) - return PD_CTRL_REJECT - * Rev 1 (PD 2.0) - return PD_CTRL_REJECT - * Rev 2 (PD 3.0) - return PD_CTRL_NOT_SUPPORTED - * - * Note: this should only be used in locations where responding on a lower - * revision with a Reject is valid (ex. a source refusing a PR_Swap). For - * other uses of Not_Supported, use PD_CTRL_NOT_SUPPORTED directly. - */ -#define NOT_SUPPORTED(r) (r < 2 ? PD_CTRL_REJECT : PD_CTRL_NOT_SUPPORTED) -#else -#define NOT_SUPPORTED(r) PD_CTRL_REJECT -#endif - -#ifdef CONFIG_USB_PD_REV30 -/* - * The spec. revision is used to index into this array. - * Rev 0 (VDO 1.0) - return VDM_VER10 - * Rev 1 (VDO 1.0) - return VDM_VER10 - * Rev 2 (VDO 2.0) - return VDM_VER20 - */ -static const uint8_t vdo_ver[] = { - VDM_VER10, VDM_VER10, VDM_VER20}; -#define VDO_VER(v) vdo_ver[v] -#else -#define VDO_VER(v) VDM_VER10 -#endif - -static struct pd_protocol { - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* 3-bit rolling message ID counter */ - uint8_t msg_id; - /* port polarity */ - enum tcpc_cc_polarity polarity; - /* PD state for port */ - enum pd_states task_state; - /* PD state when we run state handler the last time */ - enum pd_states last_state; - /* bool: request state change to SUSPENDED */ - uint8_t req_suspend_state; - /* The state to go to after timeout */ - enum pd_states timeout_state; - /* port flags, see PD_FLAGS_* */ - uint32_t flags; - /* Timeout for the current state. Set to 0 for no timeout. */ - uint64_t timeout; - /* Time for source recovery after hard reset */ - uint64_t src_recover; - /* Time for CC debounce end */ - uint64_t cc_debounce; - /* The cc state */ - enum pd_cc_states cc_state; - /* status of last transmit */ - uint8_t tx_status; - - /* Last received */ - uint8_t last_msg_id; - - /* last requested voltage PDO index */ - int requested_idx; -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* Current limit / voltage based on the last request message */ - uint32_t curr_limit; - uint32_t supply_voltage; - /* Signal charging update that affects the port */ - int new_power_request; - /* Store previously requested voltage request */ - int prev_request_mv; - /* Time for Try.SRC states */ - uint64_t try_src_marker; - uint64_t try_timeout; -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* Time to enter low power mode */ - uint64_t low_power_time; - /* Time to debounce exit low power mode */ - uint64_t low_power_exit_time; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#endif - - /* - * Time to ignore Vbus absence due to external IC debounce detection - * logic immediately after a power role swap. - */ - uint64_t vbus_debounce_time; - - /* PD state for Vendor Defined Messages */ - enum vdm_states vdm_state; - /* Timeout for the current vdm state. Set to 0 for no timeout. */ - timestamp_t vdm_timeout; - /* next Vendor Defined Message to send */ - uint32_t vdo_data[VDO_MAX_SIZE]; - /* type of transmit message (SOP/SOP'/SOP'') */ - enum tcpci_msg_type xmit_type; - uint8_t vdo_count; - /* VDO to retry if UFP responder replied busy. */ - uint32_t vdo_retry; - - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_image current_image; -#ifdef CONFIG_USB_PD_REV30 - /* protocol revision */ - uint8_t rev; -#endif - /* - * Some port partners are really chatty after an explicit contract is - * established. Therefore, we allow this time for the port partner to - * send any messages in order to avoid a collision of sending messages - * of our own. - */ - uint64_t ready_state_holdoff_timer; - /* - * PD 2.0 spec, section 6.5.11.1 - * When we can give up on a HARD_RESET transmission. - */ - uint64_t hard_reset_complete_timer; -} pd[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#ifdef CONFIG_USB_PD_TCPMV1_DEBUG -static const char * const pd_state_names[] = { - "DISABLED", "SUSPENDED", - "SNK_DISCONNECTED", "SNK_DISCONNECTED_DEBOUNCE", - "SNK_HARD_RESET_RECOVER", - "SNK_DISCOVERY", "SNK_REQUESTED", "SNK_TRANSITION", "SNK_READY", - "SNK_SWAP_INIT", "SNK_SWAP_SNK_DISABLE", - "SNK_SWAP_SRC_DISABLE", "SNK_SWAP_STANDBY", "SNK_SWAP_COMPLETE", - "SRC_DISCONNECTED", "SRC_DISCONNECTED_DEBOUNCE", - "SRC_HARD_RESET_RECOVER", "SRC_STARTUP", - "SRC_DISCOVERY", "SRC_NEGOCIATE", "SRC_ACCEPTED", "SRC_POWERED", - "SRC_TRANSITION", "SRC_READY", "SRC_GET_SNK_CAP", "DR_SWAP", - "SRC_SWAP_INIT", "SRC_SWAP_SNK_DISABLE", "SRC_SWAP_SRC_DISABLE", - "SRC_SWAP_STANDBY", - "VCONN_SWAP_SEND", "VCONN_SWAP_INIT", "VCONN_SWAP_READY", - "SOFT_RESET", "HARD_RESET_SEND", "HARD_RESET_EXECUTE", "BIST_RX", - "BIST_TX", - "DRP_AUTO_TOGGLE", - "ENTER_USB", -}; -BUILD_ASSERT(ARRAY_SIZE(pd_state_names) == PD_STATE_COUNT); -#endif - -int pd_comm_is_enabled(int port) -{ -#ifdef CONFIG_COMMON_RUNTIME - return pd_comm_enabled[port]; -#else - return 1; -#endif -} - -bool pd_alt_mode_capable(int port) -{ - /* - * PD is alternate mode capable only if PD communication is enabled and - * the port is not suspended. - */ - return pd_comm_is_enabled(port) && - !(pd[port].task_state == PD_STATE_SUSPENDED); -} - -static inline void set_state_timeout(int port, - uint64_t timeout, - enum pd_states timeout_state) -{ - pd[port].timeout = timeout; - pd[port].timeout_state = timeout_state; -} - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ -#ifdef CONFIG_USB_PD_REV30 - /* TCPMv1 Only stores PD revision for SOP and SOP' types */ - ASSERT(type < NUM_SOP_STAR_TYPES - 1); - - if (type == TCPCI_MSG_SOP_PRIME) - return get_usb_pd_cable_revision(port); - - return pd[port].rev; -#else - return PD_REV20; -#endif -} - -int pd_get_vdo_ver(int port, enum tcpci_msg_type type) -{ -#ifdef CONFIG_USB_PD_REV30 - if (type == TCPCI_MSG_SOP_PRIME) - return vdo_ver[get_usb_pd_cable_revision(port)]; - - return vdo_ver[pd[port].rev]; -#else - return VDM_VER10; -#endif -} - -/* Return flag for pd state is connected */ -int pd_is_connected(int port) -{ - if (pd[port].task_state == PD_STATE_DISABLED) - return 0; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - if (pd[port].task_state == PD_STATE_DRP_AUTO_TOGGLE) - return 0; -#endif - - return DUAL_ROLE_IF_ELSE(port, - /* sink */ - pd[port].task_state != PD_STATE_SNK_DISCONNECTED && - pd[port].task_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE, - /* source */ - pd[port].task_state != PD_STATE_SRC_DISCONNECTED && - pd[port].task_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE); -} - -/* Return true if partner port is known to be PD capable. */ -bool pd_capable(int port) -{ - return !!(pd[port].flags & PD_FLAGS_PREVIOUS_PD_CONN); -} - -/* - * For TCPMv1, this routine always returns false so that the USB3 signals - * are connected without delay when the initial connection is UFP. - */ -bool pd_waiting_on_partner_src_caps(int port) -{ - return false; -} - -/* - * Return true if partner port is capable of communication over USB data - * lines. - */ -bool pd_get_partner_usb_comm_capable(int port) -{ - return !!(pd[port].flags & PD_FLAGS_PARTNER_USB_COMM); -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_vbus_low(int port) -{ - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; -} -#endif - - -#ifdef CONFIG_USBC_VCONN -static void set_vconn(int port, int enable) -{ - /* - * Disable PPC Vconn first then TCPC in case the voltage feeds back - * to TCPC and damages. - */ - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && !enable) - ppc_set_vconn(port, 0); - - /* - * Some TCPCs/PPC combinations can trigger OVP if the TCPC doesn't - * source VCONN. This happens if the TCPC will trip OVP with 5V, and the - * PPC doesn't isolate the TCPC from VCONN when sourcing. But, some PPCs - * which do isolate the TCPC can't handle 5V on its host-side CC pins, - * so the TCPC shouldn't source VCONN in those cases. - * - * In the first case, both TCPC and PPC will potentially source Vconn, - * but that should be okay since Vconn has "make before break" - * electrical requirements when swapping anyway. - * - * See b/72961003 and b/180973460 - */ - tcpm_set_vconn(port, enable); - - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && enable) - ppc_set_vconn(port, 1); -} -#endif /* defined(CONFIG_USBC_VCONN) */ - -#ifdef CONFIG_USB_PD_REV30 -/* Note: rp should be set to either SINK_TX_OK or SINK_TX_NG */ -static void sink_can_xmit(int port, int rp) -{ - tcpm_select_rp_value(port, rp); - tcpm_set_cc(port, TYPEC_CC_RP); - - /* We must wait tSinkTx before sending a message */ - if (rp == SINK_TX_NG) - usleep(PD_T_SINK_TX); -} -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - -/* 10 ms is enough time for any TCPC transaction to complete. */ -#define PD_LPM_DEBOUNCE_US (10 * MSEC) -/* 25 ms on LPM exit to ensure TCPC is settled */ -#define PD_LPM_EXIT_DEBOUNCE_US (25 * MSEC) - -/* This is only called from the PD tasks that owns the port. */ -static void handle_device_access(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - pd[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; - if (pd[port].flags & PD_FLAGS_LPM_ENGAGED) { - tcpc_prints("Exit Low Power Mode", port); - pd[port].flags &= ~(PD_FLAGS_LPM_ENGAGED | - PD_FLAGS_LPM_REQUESTED); - pd[port].flags |= PD_FLAGS_LPM_EXIT; - - pd[port].low_power_exit_time = get_time().val - + PD_LPM_EXIT_DEBOUNCE_US; - /* - * Wake to ensure we make another pass through the main task - * loop after clearing the flags. - */ - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -static int pd_device_in_low_power(int port) -{ - /* - * If we are actively waking the device up in the PD task, do not - * let TCPC operation wait or retry because we are in low power mode. - */ - if (port == TASK_ID_TO_PD_PORT(task_get_current()) && - (pd[port].flags & PD_FLAGS_LPM_TRANSITION)) - return 0; - - return pd[port].flags & PD_FLAGS_LPM_ENGAGED; -} - -static int reset_device_and_notify(int port) -{ - int rv; - int task, waiting_tasks; - - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - pd[port].flags |= PD_FLAGS_LPM_TRANSITION; - rv = tcpm_init(port); - pd[port].flags &= ~PD_FLAGS_LPM_TRANSITION; - - if (rv == EC_SUCCESS) - tcpc_prints("init ready", port); - else - tcpc_prints("init failed!", port); - - /* - * Before getting the other tasks that are waiting, clear the reset - * event from this PD task to prevent multiple reset/init events - * occurring. - * - * The double reset event happens when the higher priority PD interrupt - * task gets an interrupt during the above tcpm_init function. When that - * occurs, the higher priority task waits correctly for us to finish - * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which - * would result in a second, unnecessary init. - */ - atomic_clear_bits(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_clear(&pd[port].tasks_waiting_on_reset); - - /* - * Now that we are done waking up the device, handle device access - * manually because we ignored it while waking up device. - */ - handle_device_access(port); - - /* Clear SW LPM state; the state machine will set it again if needed */ - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; - - /* Wake up all waiting tasks. */ - while (waiting_tasks) { - task = __fls(waiting_tasks); - waiting_tasks &= ~BIT(task); - task_set_event(task, TASK_EVENT_PD_AWAKE); - } - - return rv; -} - -static void pd_wait_for_wakeup(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - /* If we are in the PD task, we can directly reset */ - reset_device_and_notify(port); - } else { - /* Otherwise, we need to wait for the TCPC reset to complete */ - atomic_or(&pd[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset event while - * it is already processing the reset event. If that occurs, - * then we will reset the TCPC multiple times, which is - * undesirable but most likely benign. Empirically, this doesn't - * happen much, but it if starts occurring, we can add a guard - * to prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TCPC_RESET); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } -} - -void pd_wait_exit_low_power(int port) -{ - if (pd_device_in_low_power(port)) - pd_wait_for_wakeup(port); -} - -/* - * This can be called from any task. If we are in the PD task, we can handle - * immediately. Otherwise, we need to notify the PD task via event. - */ -void pd_device_accessed(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - /* Ignore any access to device while it is waking up */ - if (pd[port].flags & PD_FLAGS_LPM_TRANSITION) - return; - - handle_device_access(port); - } else { - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_DEVICE_ACCESSED); - } -} - -void pd_prevent_low_power_mode(int port, int prevent) -{ - const int current_task_mask = (1 << task_get_current()); - - if (prevent) - atomic_or(&pd[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear_bits(&pd[port].tasks_preventing_lpm, - current_task_mask); -} - -/* This is only called from the PD tasks that owns the port. */ -static void exit_low_power_mode(int port) -{ - if (pd[port].flags & PD_FLAGS_LPM_ENGAGED) - reset_device_and_notify(port); - else - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; -} - -#else /* !CONFIG_USB_PD_TCPC_LOW_POWER */ - -/* We don't need to notify anyone if low power mode isn't involved. */ -static int reset_device_and_notify(int port) -{ - const int rv = tcpm_init(port); - - if (rv == EC_SUCCESS) - tcpc_prints("init ready", port); - else - tcpc_prints("init failed!", port); - - return rv; -} - -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -/** - * Invalidate last message received at the port when the port gets disconnected - * or reset(soft/hard). This is used to identify and handle the duplicate - * messages. - * - * @param port USB PD TCPC port number - */ -static void invalidate_last_message_id(int port) -{ - pd[port].last_msg_id = INVALID_MSG_ID_COUNTER; -} - -static bool consume_sop_repeat_message(int port, uint8_t msg_id) -{ - if (pd[port].last_msg_id != msg_id) { - pd[port].last_msg_id = msg_id; - return false; - } - CPRINTF("C%d Repeat msg_id %d\n", port, msg_id); - return true; -} - -/** - * Identify and drop any duplicate messages received at the port. - * - * @param port USB PD TCPC port number - * @param msg_header Message Header containing the RX message ID - * @return True if the received message is a duplicate one, False otherwise. - * - * From USB PD version 1.3 section 6.7.1, the port which communicates - * using SOP* Packets Shall maintain copies of the last MessageID for - * each type of SOP* it uses. - */ -static bool consume_repeat_message(int port, uint32_t msg_header) -{ - uint8_t msg_id = PD_HEADER_ID(msg_header); - enum tcpci_msg_type sop = PD_HEADER_GET_SOP(msg_header); - - /* If repeat message ignore, except softreset control request. */ - if (PD_HEADER_TYPE(msg_header) == PD_CTRL_SOFT_RESET && - PD_HEADER_CNT(msg_header) == 0) { - return false; - } else if (sop == TCPCI_MSG_SOP_PRIME) { - return consume_sop_prime_repeat_msg(port, msg_id); - } else if (sop == TCPCI_MSG_SOP_PRIME_PRIME) { - return consume_sop_prime_prime_repeat_msg(port, msg_id); - } else { - return consume_sop_repeat_message(port, msg_id); - } - -} - -/** - * Returns true if the port is currently in the try src state. - */ -static inline int is_try_src(int port) -{ - return pd[port].flags & PD_FLAGS_TRY_SRC; -} - -static inline void set_state(int port, enum pd_states next_state) -{ - enum pd_states last_state = pd[port].task_state; -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_PD_TCPC_ON_CHIP) - int i; -#endif - int not_auto_toggling = 1; - - set_state_timeout(port, 0, 0); - pd[port].task_state = next_state; - - if (last_state == next_state) - return; - -#if defined(CONFIG_USBC_PPC) && defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) - /* If we're entering DRP_AUTO_TOGGLE, there is no sink connected. */ - if (next_state == PD_STATE_DRP_AUTO_TOGGLE) { - ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED); - /* Disable Auto Discharge Disconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - usbc_ocp_snk_is_connected(port, false); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - usbc_ocp_clear_event_counter(port); - } - } -#endif /* CONFIG_USBC_PPC && CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifdef CONFIG_USB_PD_DUAL_ROLE -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - if (last_state != PD_STATE_DRP_AUTO_TOGGLE) - /* Clear flag to allow DRP auto toggle when possible */ - pd[port].flags &= ~PD_FLAGS_TCPC_DRP_TOGGLE; - else - /* This is an auto toggle instead of disconnect */ - not_auto_toggling = 0; -#endif - - /* Ignore dual-role toggling between sink and source */ - if ((last_state == PD_STATE_SNK_DISCONNECTED && - next_state == PD_STATE_SRC_DISCONNECTED) || - (last_state == PD_STATE_SRC_DISCONNECTED && - next_state == PD_STATE_SNK_DISCONNECTED)) - return; - - if (next_state == PD_STATE_SRC_DISCONNECTED || - next_state == PD_STATE_SNK_DISCONNECTED) { -#ifdef CONFIG_USBC_PPC - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - /* - * Neither a debug accessory nor UFP attached. - * Tell the PPC module that there is no device connected. - */ - if (!cc_is_at_least_one_rd(cc1, cc2)) { - ppc_dev_is_connected(port, PPC_DEV_DISCONNECTED); - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - usbc_ocp_snk_is_connected(port, false); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - usbc_ocp_clear_event_counter(port); - } - } -#endif /* CONFIG_USBC_PPC */ - - /* Clear the holdoff timer since the port is disconnected. */ - pd[port].ready_state_holdoff_timer = 0; - - /* - * We should not clear any flags when transitioning back to the - * disconnected state from the debounce state as the two states - * here are really the same states in the state diagram. - */ - if (last_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE && - last_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE) { - pd[port].flags &= ~PD_FLAGS_RESET_ON_DISCONNECT_MASK; - reset_pd_cable(port); - } - - /* Clear the input current limit */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif -#ifdef CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_CC_OPEN); -#endif /* CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER */ -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* defined(CONFIG_USBC_VCONN) */ - pd_update_saved_port_flags(port, PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); -#else /* CONFIG_USB_PD_DUAL_ROLE */ - if (next_state == PD_STATE_SRC_DISCONNECTED) { -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* CONFIG_USBC_VCONN */ -#endif /* !CONFIG_USB_PD_DUAL_ROLE */ - /* If we are source, make sure VBUS is off and restore RP */ - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* Restore non-active ports to CONFIG_USB_PD_PULLUP */ - pd_power_supply_reset(port); - tcpm_set_cc(port, TYPEC_CC_RP); - } -#ifdef CONFIG_USB_PD_REV30 - /* Adjust rev to highest level*/ - pd[port].rev = PD_REV30; -#endif - pd[port].dev_id = 0; -#ifdef CONFIG_CHARGE_MANAGER - charge_manager_update_dualrole(port, CAP_UNKNOWN); -#endif -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - if (pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0)) - usb_mux_set_safe_mode(port); -#endif - /* - * Indicate that the port is disconnected by setting role to - * DFP as SoCs have special signals when they are the UFP ports - * (e.g. OTG signals) - */ - pd_execute_data_swap(port, PD_ROLE_DFP); -#ifdef CONFIG_USBC_SS_MUX - usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_DISCONNECT, - pd[port].polarity); -#endif - /* Disable TCPC RX */ - tcpm_set_rx_enable(port, 0); - - /* Invalidate message IDs. */ - invalidate_last_message_id(port); - - if (not_auto_toggling) - /* Disable Auto Discharge Disconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* detect USB PD cc disconnect */ - if (IS_ENABLED(CONFIG_COMMON_RUNTIME)) - hook_notify(HOOK_USB_PD_DISCONNECT); - } - -#ifdef CONFIG_USB_PD_REV30 - /* Upon entering SRC_READY, it is safe for the sink to transmit */ - if (next_state == PD_STATE_SRC_READY) { - if (pd[port].rev == PD_REV30 && - pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT) - sink_can_xmit(port, SINK_TX_OK); - } -#endif - -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_PD_TCPC_ON_CHIP) - /* If a PD device is attached then disable deep sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd_capable(i)) - break; - } - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - else - disable_sleep(SLEEP_MASK_USB_PD); -#endif - -#ifdef CONFIG_USB_PD_TCPMV1_DEBUG - if (debug_level > 0) - CPRINTF("C%d st%d %s\n", port, next_state, - pd_state_names[next_state]); - else -#endif - CPRINTF("C%d st%d\n", port, next_state); -} - -/* increment message ID counter */ -static void inc_id(int port) -{ - pd[port].msg_id = (pd[port].msg_id + 1) & PD_MESSAGE_ID_COUNT; -} - -void pd_transmit_complete(int port, int status) -{ - if (status == TCPC_TX_COMPLETE_SUCCESS) - inc_id(port); - - pd[port].tx_status = status; - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX); -} - -static int pd_transmit(int port, enum tcpci_msg_type type, - uint16_t header, const uint32_t *data, enum ams_seq ams) -{ - int evt; - int res; -#ifdef CONFIG_USB_PD_REV30 - int sink_ng = 0; -#endif - - /* If comms are disabled, do not transmit, return error */ - if (!pd_comm_is_enabled(port)) - return -1; - - /* Don't try to transmit anything until we have processed - * all RX messages. - */ - if (tcpm_has_pending_message(port)) - return -1; - -#ifdef CONFIG_USB_PD_REV30 - /* Source-coordinated collision avoidance */ - /* - * USB PD Rev 3.0, Version 2.0: Section 2.7.3.2 - * Collision Avoidance - Protocol Layer - * - * In order to avoid message collisions due to asynchronous Messaging - * sent from the Sink, the Source sets Rp to SinkTxOk (3A) to indicate - * to the Sink that it is ok to initiate an AMS. When the Source wishes - * to initiate an AMS, it sets Rp to SinkTxNG (1.5A). - * When the Sink detects that Rp is set to SinkTxOk, it May initiate an - * AMS. When the Sink detects that Rp is set to SinkTxNG it Shall Not - * initiate an AMS and Shall only send Messages that are part of an AMS - * the Source has initiated. - * Note that this restriction applies to SOP* AMS’s i.e. for both Port - * to Port and Port to Cable Plug communications. - * - * This starts after an Explicit Contract is in place (see section 2.5.2 - * SOP* Collision Avoidance). - * - * Note: a Sink can still send Hard Reset signaling at any time. - */ - if ((pd[port].rev == PD_REV30) && ams == AMS_START && - (pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT)) { - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* - * Inform Sink that it can't transmit. If a sink - * transmission is in progress and a collision occurs, - * a reset is generated. This should be rare because - * all extended messages are chunked. This effectively - * defaults to PD REV 2.0 collision avoidance. - */ - sink_can_xmit(port, SINK_TX_NG); - sink_ng = 1; - } else if (type != TCPCI_MSG_TX_HARD_RESET) { - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - if (cc1 == TYPEC_CC_VOLT_RP_1_5 || - cc2 == TYPEC_CC_VOLT_RP_1_5) { - /* Sink can't transmit now. */ - /* Return failure, pd_task can retry later */ - return -1; - } - } - } -#endif - tcpm_transmit(port, type, header, data); - - /* Wait until TX is complete */ - evt = task_wait_event_mask(PD_EVENT_TX, PD_T_TCPC_TX_TIMEOUT); - - if (evt & TASK_EVENT_TIMER) - return -1; - - /* TODO: give different error condition for failed vs discarded */ - res = pd[port].tx_status == TCPC_TX_COMPLETE_SUCCESS ? 1 : -1; - -#ifdef CONFIG_USB_PD_REV30 - /* If the AMS transaction failed to start, reset CC to OK */ - if (res < 0 && sink_ng) - sink_can_xmit(port, SINK_TX_OK); -#endif - return res; -} - -static void pd_update_roles(int port) -{ - /* Notify TCPC of role update */ - tcpm_set_msg_header(port, pd[port].power_role, pd[port].data_role); -} - -static int send_control(int port, int type) -{ - int bit_len; - uint16_t header = PD_HEADER(type, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 0, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - /* - * For PD 3.0, collision avoidance logic needs to know if this message - * will begin a new Atomic Message Sequence (AMS) - */ - enum ams_seq ams = ((1 << type) & PD_CTRL_AMS_START_MASK) - ? AMS_START : AMS_RESPONSE; - - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, NULL, ams); - if (debug_level >= 2) - CPRINTF("C%d CTRL[%d]>%d\n", port, type, bit_len); - - return bit_len; -} - -/* - * Note: Source capabilities may either be in an existing AMS (ex. as a - * response to Get_Source_Cap), or the beginning of an AMS for a power - * negotiation. - */ -static int send_source_cap(int port, enum ams_seq ams) -{ - int bit_len; -#if defined(CONFIG_USB_PD_DYNAMIC_SRC_CAP) || \ - defined(CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT) - const uint32_t *src_pdo; - const int src_pdo_cnt = charge_manager_get_source_pdo(&src_pdo, port); -#else - const uint32_t *src_pdo = pd_src_pdo; - const int src_pdo_cnt = pd_src_pdo_cnt; -#endif - uint16_t header; - - if (src_pdo_cnt == 0) - /* No source capabilities defined, sink only */ - header = PD_HEADER(PD_CTRL_REJECT, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 0, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - else - header = PD_HEADER(PD_DATA_SOURCE_CAP, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, src_pdo_cnt, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, src_pdo, ams); - if (debug_level >= 2) - CPRINTF("C%d srcCAP>%d\n", port, bit_len); - - return bit_len; -} - -#ifdef CONFIG_USB_PD_REV30 -static int send_battery_cap(int port, uint32_t *payload) -{ - int bit_len; - uint16_t msg[6] = {0, 0, 0, 0, 0, 0}; - uint16_t header = PD_HEADER(PD_EXT_BATTERY_CAP, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - 3, /* Number of Data Objects */ - pd[port].rev, - 1 /* This is an exteded message */ - ); - - /* Set extended header */ - msg[0] = PD_EXT_HEADER(0, /* Chunk Number */ - 0, /* Request Chunk */ - 9 /* Data Size in bytes */ - ); - /* Set VID */ - msg[1] = USB_VID_GOOGLE; - - /* Set PID */ - msg[2] = CONFIG_USB_PID; - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload[0]) != 0) { - /* Invalid battery reference */ - msg[5] = 1; - } else { - uint32_t v; - uint32_t c; - - /* - * The Battery Design Capacity field shall return the - * Battery’s design capacity in tenths of Wh. If the - * Battery is Hot Swappable and is not present, the - * Battery Design Capacity field shall be set to 0. If - * the Battery is unable to report its Design Capacity, - * it shall return 0xFFFF - */ - msg[3] = 0xffff; - - /* - * The Battery Last Full Charge Capacity field shall - * return the Battery’s last full charge capacity in - * tenths of Wh. If the Battery is Hot Swappable and - * is not present, the Battery Last Full Charge Capacity - * field shall be set to 0. If the Battery is unable to - * report its Design Capacity, the Battery Last Full - * Charge Capacity field shall be set to 0xFFFF. - */ - msg[4] = 0xffff; - - if (battery_design_voltage(&v) == 0) { - if (battery_design_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[3] = DIV_ROUND_NEAREST((c * v), - 100000); - } - - if (battery_full_charge_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[4] = DIV_ROUND_NEAREST((c * v), - 100000); - } - } - } - } - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, (uint32_t *)msg, - AMS_RESPONSE); - if (debug_level >= 2) - CPRINTF("C%d batCap>%d\n", port, bit_len); - return bit_len; -} - -static int send_battery_status(int port, uint32_t *payload) -{ - int bit_len; - uint32_t msg = 0; - uint16_t header = PD_HEADER(PD_DATA_BATTERY_STATUS, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - 1, /* Number of Data Objects */ - pd[port].rev, - 0 /* This is NOT an extended message */ - ); - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - */ - if (BATT_CAP_REF(payload[0]) != 0) { - /* Invalid battery reference */ - msg |= BSDO_INVALID; - } else { - uint32_t v; - uint32_t c; - - if (battery_design_voltage(&v) != 0 || - battery_remaining_capacity(&c) != 0) { - msg |= BSDO_CAP(BSDO_CAP_UNKNOWN); - } else { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg |= BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } - - /* Battery is present */ - msg |= BSDO_PRESENT; - - /* - * For drivers that are not smart battery compliant, - * battery_status() returns EC_ERROR_UNIMPLEMENTED and - * the battery is assumed to be idle. - */ - if (battery_status(&c) != 0) { - msg |= BSDO_IDLE; /* assume idle */ - } else { - if (c & STATUS_FULLY_CHARGED) - /* Fully charged */ - msg |= BSDO_IDLE; - else if (c & STATUS_DISCHARGING) - /* Discharging */ - msg |= BSDO_DISCHARGING; - /* else battery is charging.*/ - } - } - } else { - msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - } - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, &msg, AMS_RESPONSE); - if (debug_level >= 2) - CPRINTF("C%d batStat>%d\n", port, bit_len); - - return bit_len; -} -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static void send_sink_cap(int port) -{ - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_SINK_CAP, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, pd_snk_pdo_cnt, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, pd_snk_pdo, - AMS_RESPONSE); - if (debug_level >= 2) - CPRINTF("C%d snkCAP>%d\n", port, bit_len); -} - -static int send_request(int port, uint32_t rdo) -{ - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_REQUEST, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 1, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - - /* Note: ams will need to be AMS_START if used for PPS keep alive */ - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, &rdo, AMS_RESPONSE); - if (debug_level >= 2) - CPRINTF("C%d REQ>%d\n", port, bit_len); - - return bit_len; -} - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -#ifdef CONFIG_COMMON_RUNTIME -static int send_bist_cmd(int port) -{ - /* currently only support sending bist carrier 2 */ - uint32_t bdo = BDO(BDO_MODE_CARRIER2, 0); - int bit_len; - uint16_t header = PD_HEADER(PD_DATA_BIST, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, 1, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - - bit_len = pd_transmit(port, TCPCI_MSG_SOP, header, &bdo, AMS_START); - CPRINTF("C%d BIST>%d\n", port, bit_len); - - return bit_len; -} -#endif - -static void queue_vdm(int port, uint32_t *header, const uint32_t *data, - int data_cnt, enum tcpci_msg_type type) -{ - pd[port].vdo_count = data_cnt + 1; - pd[port].vdo_data[0] = header[0]; - pd[port].xmit_type = type; - memcpy(&pd[port].vdo_data[1], data, - sizeof(uint32_t) * data_cnt); - /* Set ready, pd task will actually send */ - pd[port].vdm_state = VDM_STATE_READY; -} - -static void handle_vdm_request(int port, int cnt, uint32_t *payload, - uint32_t head) -{ - int rlen = 0; - uint32_t *rdata; - enum tcpci_msg_type rtype = TCPCI_MSG_SOP; - - if (pd[port].vdm_state == VDM_STATE_BUSY) { - /* If UFP responded busy retry after timeout */ - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) { - pd[port].vdm_timeout.val = get_time().val + - PD_T_VDM_BUSY; - pd[port].vdm_state = VDM_STATE_WAIT_RSP_BUSY; - pd[port].vdo_retry = (payload[0] & ~VDO_CMDT_MASK) | - CMDT_INIT; - return; - } else { - pd[port].vdm_state = VDM_STATE_DONE; -#ifdef CONFIG_USB_PD_REV30 - if (pd[port].rev == PD_REV30 && - pd[port].power_role == PD_ROLE_SOURCE && - pd[port].flags & PD_FLAGS_EXPLICIT_CONTRACT) - sink_can_xmit(port, SINK_TX_OK); -#endif - } - } - - if (PD_VDO_SVDM(payload[0])) - rlen = pd_svdm(port, cnt, payload, &rdata, head, &rtype); - else - rlen = pd_custom_vdm(port, cnt, payload, &rdata); - - if (rlen > 0) { - queue_vdm(port, rdata, &rdata[1], rlen - 1, rtype); - return; - } - - if (debug_level >= 2) - CPRINTF("C%d Unhandled VDM VID %04x CMD %04x\n", - port, PD_VDO_VID(payload[0]), payload[0] & 0xFFFF); -} - -bool pd_is_disconnected(int port) -{ - return pd[port].task_state == PD_STATE_SRC_DISCONNECTED -#ifdef CONFIG_USB_PD_DUAL_ROLE - || pd[port].task_state == PD_STATE_SNK_DISCONNECTED -#endif - ; -} - -static void pd_set_data_role(int port, enum pd_data_role role) -{ - pd[port].data_role = role; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role); -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ - pd_execute_data_swap(port, role); - - set_usb_mux_with_current_data_role(port); - pd_update_roles(port); -#ifdef CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER - /* - * For BC1.2 detection that is triggered on data role change events - * instead of VBUS changes, need to set an event to wake up the USB_CHG - * task and indicate the current data role. - */ - if (role == PD_ROLE_UFP) - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_DR_UFP); - else if (role == PD_ROLE_DFP) - task_set_event(USB_CHG_PORT_TO_TASK_ID(port), - USB_CHG_EVENT_DR_DFP); -#endif /* CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER */ -} - -#ifdef CONFIG_USBC_VCONN -static void pd_set_vconn_role(int port, int role) -{ - if (role == PD_ROLE_VCONN_ON) - pd[port].flags |= PD_FLAGS_VCONN_ON; - else - pd[port].flags &= ~PD_FLAGS_VCONN_ON; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_VCONN_ROLE, role); -#endif -} -#endif /* CONFIG_USBC_VCONN */ - -void pd_execute_hard_reset(int port) -{ - int hard_rst_tx = pd[port].last_state == PD_STATE_HARD_RESET_SEND; - - CPRINTF("C%d HARD RST %cX\n", port, hard_rst_tx ? 'T' : 'R'); - - pd[port].msg_id = 0; - invalidate_last_message_id(port); - tcpm_set_rx_enable(port, 0); -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - if (pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0)) - usb_mux_set_safe_mode(port); -#endif - -#ifdef CONFIG_USB_PD_REV30 - pd[port].rev = PD_REV30; -#endif - /* - * Fake set last state to hard reset to make sure that the next - * state to run knows that we just did a hard reset. - */ - pd[port].last_state = PD_STATE_HARD_RESET_EXECUTE; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If we are swapping to a source and have changed to Rp, restore back - * to Rd and turn off vbus to match our power_role. - */ - if (pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY || - pd[port].task_state == PD_STATE_SNK_SWAP_COMPLETE) { - tcpm_set_cc(port, TYPEC_CC_RD); - pd_power_supply_reset(port); - } - - if (pd[port].power_role == PD_ROLE_SINK) { - /* Initial data role for sink is UFP */ - pd_set_data_role(port, PD_ROLE_UFP); - - /* Clear the input current limit */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif /* CONFIG_CHARGE_MANAGER */ - -#ifdef CONFIG_USBC_VCONN - /* - * Sink must turn off Vconn after a hard reset if it was being - * sourced previously - */ - if (pd[port].flags & PD_FLAGS_VCONN_ON) { - set_vconn(port, 0); - pd_set_vconn_role(port, PD_ROLE_VCONN_OFF); - } -#endif - - set_state(port, PD_STATE_SNK_HARD_RESET_RECOVER); - return; - } else { - /* Initial data role for source is DFP */ - pd_set_data_role(port, PD_ROLE_DFP); - } - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - - if (!hard_rst_tx) - usleep(PD_T_PS_HARD_RESET); - - /* We are a source, cut power */ - pd_power_supply_reset(port); - pd[port].src_recover = get_time().val + PD_T_SRC_RECOVER; -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif - set_state(port, PD_STATE_SRC_HARD_RESET_RECOVER); -} - -static void execute_soft_reset(int port) -{ - invalidate_last_message_id(port); - set_state(port, DUAL_ROLE_IF_ELSE(port, PD_STATE_SNK_DISCOVERY, - PD_STATE_SRC_DISCOVERY)); - CPRINTF("C%d Soft Rst\n", port); -} - -void pd_soft_reset(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (pd_is_connected(i)) { - set_state(i, PD_STATE_SOFT_RESET); - task_wake(PD_PORT_TO_TASK_ID(i)); - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -/* - * Request desired charge voltage from source. - * Returns EC_SUCCESS on success or non-zero on failure. - */ -static int pd_send_request_msg(int port, int always_send_request) -{ - uint32_t rdo, curr_limit, supply_voltage; - int res; - - /* Clear new power request */ - pd[port].new_power_request = 0; - - /* Build and send request RDO */ - pd_build_request(0, &rdo, &curr_limit, &supply_voltage, port); - - if (!always_send_request) { - /* Don't re-request the same voltage */ - if (pd[port].prev_request_mv == supply_voltage) - return EC_SUCCESS; -#ifdef CONFIG_CHARGE_MANAGER - /* Limit current to PD_MIN_MA during transition */ - else - charge_manager_force_ceil(port, PD_MIN_MA); -#endif - } - - CPRINTF("C%d Req [%d] %dmV %dmA", port, RDO_POS(rdo), - supply_voltage, curr_limit); - if (rdo & RDO_CAP_MISMATCH) - CPRINTF(" Mismatch"); - CPRINTF("\n"); - - pd[port].curr_limit = curr_limit; - pd[port].supply_voltage = supply_voltage; - pd[port].prev_request_mv = supply_voltage; - res = send_request(port, rdo); - if (res < 0) - return res; - set_state(port, PD_STATE_SNK_REQUESTED); - return EC_SUCCESS; -} -#endif - -static void pd_update_pdo_flags(int port, int pdo_cnt, uint32_t *pdos) -{ - /* can only parse PDO flags if type is fixed */ - if ((pdos[0] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pdos[0] & PDO_FIXED_DUAL_ROLE) - pd[port].flags |= PD_FLAGS_PARTNER_DR_POWER; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_DR_POWER; - - if (pdos[0] & PDO_FIXED_UNCONSTRAINED) - pd[port].flags |= PD_FLAGS_PARTNER_UNCONSTR; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_UNCONSTR; - - if (pdos[0] & PDO_FIXED_COMM_CAP) - pd[port].flags |= PD_FLAGS_PARTNER_USB_COMM; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_USB_COMM; -#endif - - if (pdos[0] & PDO_FIXED_DATA_SWAP) - pd[port].flags |= PD_FLAGS_PARTNER_DR_DATA; - else - pd[port].flags &= ~PD_FLAGS_PARTNER_DR_DATA; - - /* - * Treat device as a dedicated charger (meaning we should charge - * from it) if: - * - it does not support power swap, or - * - it is unconstrained power, or - * - it presents at least 27 W of available power - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - uint32_t max_ma, max_mv, max_pdo, max_mw, unused; - - /* - * Get max power that the partner offers (not necessarily what - * this board will request) - */ - pd_find_pdo_index(pdo_cnt, pdos, PD_REV3_MAX_VOLTAGE, - &max_pdo); - pd_extract_pdo_power(max_pdo, &max_ma, &max_mv, &unused); - max_mw = max_ma * max_mv / 1000; - - if (!(pdos[0] & PDO_FIXED_DUAL_ROLE) || - (pdos[0] & PDO_FIXED_UNCONSTRAINED) || - max_mw >= PD_DRP_CHARGE_POWER_MIN) - charge_manager_update_dualrole(port, CAP_DEDICATED); - else - charge_manager_update_dualrole(port, CAP_DUALROLE); - } -} - -static void handle_data_request(int port, uint32_t head, - uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - int cnt = PD_HEADER_CNT(head); - - switch (type) { -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_DATA_SOURCE_CAP: - if ((pd[port].task_state == PD_STATE_SNK_DISCOVERY) - || (pd[port].task_state == PD_STATE_SNK_TRANSITION) - || (pd[port].task_state == PD_STATE_SNK_REQUESTED) - || ((get_usb_pd_vbus_detect() == - USB_PD_VBUS_DETECT_NONE) - && (pd[port].task_state == - PD_STATE_SNK_HARD_RESET_RECOVER)) - || (pd[port].task_state == PD_STATE_SNK_READY)) { -#ifdef CONFIG_USB_PD_REV30 - /* - * Only adjust sink rev if source rev is higher. - */ - if (PD_HEADER_REV(head) < pd[port].rev) - pd[port].rev = PD_HEADER_REV(head); -#endif - /* Port partner is now known to be PD capable */ - pd[port].flags |= PD_FLAGS_PREVIOUS_PD_CONN; - - /* src cap 0 should be fixed PDO */ - pd_update_pdo_flags(port, cnt, payload); - - pd_process_source_cap(port, cnt, payload); - - /* Source will resend source cap on failure */ - pd_send_request_msg(port, 1); - } - break; -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - case PD_DATA_REQUEST: - if ((pd[port].power_role == PD_ROLE_SOURCE) && (cnt == 1)) { -#ifdef CONFIG_USB_PD_REV30 - /* - * Adjust the rev level to what the sink supports. If - * they're equal, no harm done. - */ - pd[port].rev = PD_HEADER_REV(head); -#endif - if (!pd_check_requested_voltage(payload[0], port)) { - if (send_control(port, PD_CTRL_ACCEPT) < 0) - /* - * if we fail to send accept, do - * nothing and let sink timeout and - * send hard reset - */ - return; - - /* explicit contract is now in place */ - pd[port].flags |= PD_FLAGS_EXPLICIT_CONTRACT; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags( - port, PD_BBRMFLG_EXPLICIT_CONTRACT, 1); -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - pd[port].requested_idx = RDO_POS(payload[0]); - set_state(port, PD_STATE_SRC_ACCEPTED); - return; - } - } - /* the message was incorrect or cannot be satisfied */ - send_control(port, PD_CTRL_REJECT); - /* keep last contract in place (whether implicit or explicit) */ - set_state(port, PD_STATE_SRC_READY); - break; - case PD_DATA_BIST: - /* If not in READY state, then don't start BIST */ - if (DUAL_ROLE_IF_ELSE(port, - pd[port].task_state == PD_STATE_SNK_READY, - pd[port].task_state == PD_STATE_SRC_READY)) { - /* currently only support sending bist carrier mode 2 */ - if ((payload[0] >> 28) == 5) { - /* bist data object mode is 2 */ - pd_transmit(port, TCPCI_MSG_TX_BIST_MODE_2, 0, - NULL, AMS_RESPONSE); - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - } - } - break; - case PD_DATA_SINK_CAP: - pd[port].flags |= PD_FLAGS_SNK_CAP_RECVD; - /* snk cap 0 should be fixed PDO */ - pd_update_pdo_flags(port, cnt, payload); - if (pd[port].task_state == PD_STATE_SRC_GET_SINK_CAP) - set_state(port, PD_STATE_SRC_READY); - break; -#ifdef CONFIG_USB_PD_REV30 - case PD_DATA_BATTERY_STATUS: - break; - /* TODO : Add case PD_DATA_RESET for exiting USB4 */ - - /* - * TODO : Add case PD_DATA_ENTER_USB to accept or reject - * Enter_USB request from port partner. - */ -#endif - case PD_DATA_VENDOR_DEF: - handle_vdm_request(port, cnt, payload, head); - break; - default: - CPRINTF("C%d Unhandled data message type %d\n", port, type); - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_request_power_swap(int port) -{ - if (pd[port].task_state == PD_STATE_SRC_READY) - set_state(port, PD_STATE_SRC_SWAP_INIT); - else if (pd[port].task_state == PD_STATE_SNK_READY) - set_state(port, PD_STATE_SNK_SWAP_INIT); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef CONFIG_USBC_VCONN_SWAP -void pd_request_vconn_swap(int port) -{ - if (pd[port].task_state == PD_STATE_SRC_READY || - pd[port].task_state == PD_STATE_SNK_READY) - set_state(port, PD_STATE_VCONN_SWAP_SEND); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_try_vconn_src(int port) -{ - /* - * If we don't currently provide vconn, and we can supply it, send - * a vconn swap request. - */ - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - if (pd_check_vconn_swap(port)) - pd_request_vconn_swap(port); - } -} -#endif -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -void pd_request_data_swap(int port) -{ - if (DUAL_ROLE_IF_ELSE(port, - pd[port].task_state == PD_STATE_SNK_READY, - pd[port].task_state == PD_STATE_SRC_READY)) - set_state(port, PD_STATE_DR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -static void pd_set_power_role(int port, enum pd_power_role role) -{ - pd[port].power_role = role; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_saved_port_flags(port, PD_BBRMFLG_POWER_ROLE, role); -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ -} - -static void pd_dr_swap(int port) -{ - pd_set_data_role(port, !pd[port].data_role); - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; -} - -static void handle_ctrl_request(int port, uint32_t head, - uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - int res; - - switch (type) { - case PD_CTRL_GOOD_CRC: - /* should not get it */ - break; - case PD_CTRL_PING: - /* Nothing else to do */ - break; - case PD_CTRL_GET_SOURCE_CAP: - if (pd[port].task_state == PD_STATE_SRC_READY) - set_state(port, PD_STATE_SRC_DISCOVERY); - else { - res = send_source_cap(port, AMS_RESPONSE); - if ((res >= 0) && - (pd[port].task_state == PD_STATE_SRC_DISCOVERY)) - set_state(port, PD_STATE_SRC_NEGOCIATE); - } - break; - case PD_CTRL_GET_SINK_CAP: -#ifdef CONFIG_USB_PD_DUAL_ROLE - send_sink_cap(port); -#else - send_control(port, NOT_SUPPORTED(pd[port].rev)); -#endif - break; -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_CTRL_GOTO_MIN: -#ifdef CONFIG_USB_PD_GIVE_BACK - if (pd[port].task_state == PD_STATE_SNK_READY) { - /* - * Reduce power consumption now! - * - * The source will restore power to this sink - * by sending a new source cap message at a - * later time. - */ - pd_snk_give_back(port, &pd[port].curr_limit, - &pd[port].supply_voltage); - set_state(port, PD_STATE_SNK_TRANSITION); - } -#endif - - break; - case PD_CTRL_PS_RDY: - if (pd[port].task_state == PD_STATE_SNK_SWAP_SRC_DISABLE) { - set_state(port, PD_STATE_SNK_SWAP_STANDBY); - } else if (pd[port].task_state == PD_STATE_SRC_SWAP_STANDBY) { - /* reset message ID and swap roles */ - pd[port].msg_id = 0; - invalidate_last_message_id(port); - pd_set_power_role(port, PD_ROLE_SINK); - pd_update_roles(port); - /* - * Give the state machine time to read VBUS as high. - * Note: This is empirically determined, not strictly - * part of the USB PD spec. - */ - pd[port].vbus_debounce_time = - get_time().val + PD_T_DEBOUNCE; - set_state(port, PD_STATE_SNK_DISCOVERY); -#ifdef CONFIG_USBC_VCONN_SWAP - } else if (pd[port].task_state == PD_STATE_VCONN_SWAP_INIT) { - /* - * If VCONN is on, then this PS_RDY tells us it's - * ok to turn VCONN off - */ - if (pd[port].flags & PD_FLAGS_VCONN_ON) - set_state(port, PD_STATE_VCONN_SWAP_READY); -#endif - } else if (pd[port].task_state == PD_STATE_SNK_DISCOVERY) { - /* Don't know what power source is ready. Reset. */ - set_state(port, PD_STATE_HARD_RESET_SEND); - } else if (pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY) { - /* Do nothing, assume this is a redundant PD_RDY */ - } else if (pd[port].power_role == PD_ROLE_SINK) { - /* - * Give the source some time to send any messages before - * we start our interrogation. Add some jitter of up to - * ~192ms to prevent multiple collisions. - */ - if (pd[port].task_state == PD_STATE_SNK_TRANSITION) - pd[port].ready_state_holdoff_timer = - get_time().val + SNK_READY_HOLD_OFF_US - + (get_time().le.lo & 0xf) * 12 * MSEC; - - set_state(port, PD_STATE_SNK_READY); - pd_set_input_current_limit(port, pd[port].curr_limit, - pd[port].supply_voltage); -#ifdef CONFIG_CHARGE_MANAGER - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - pd[port].curr_limit); -#endif - } - break; -#endif - case PD_CTRL_REJECT: - if (pd[port].task_state == PD_STATE_ENTER_USB) { - if (!IS_ENABLED(CONFIG_USBC_SS_MUX)) - break; - - /* - * Since Enter USB sets the mux state to SAFE mode, - * resetting the mux state back to USB mode on - * recieveing a NACK. - */ - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, - USB_SWITCH_CONNECT, pd[port].polarity); - - set_state(port, READY_RETURN_STATE(port)); - break; - } - case PD_CTRL_WAIT: - if (pd[port].task_state == PD_STATE_DR_SWAP) { - if (type == PD_CTRL_WAIT) /* try again ... */ - pd[port].flags |= PD_FLAGS_CHECK_DR_ROLE; - set_state(port, READY_RETURN_STATE(port)); - } -#ifdef CONFIG_USBC_VCONN_SWAP - else if (pd[port].task_state == PD_STATE_VCONN_SWAP_SEND) - set_state(port, READY_RETURN_STATE(port)); -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE - else if (pd[port].task_state == PD_STATE_SRC_SWAP_INIT) - set_state(port, PD_STATE_SRC_READY); - else if (pd[port].task_state == PD_STATE_SNK_SWAP_INIT) - set_state(port, PD_STATE_SNK_READY); - else if (pd[port].task_state == PD_STATE_SNK_REQUESTED) { - /* - * On reception of a WAIT message, transition to - * PD_STATE_SNK_READY after PD_T_SINK_REQUEST ms to - * send another request. - * - * On reception of a REJECT message, transition to - * PD_STATE_SNK_READY but don't resend the request if - * we already have a contract in place. - * - * On reception of a REJECT message without a contract, - * transition to PD_STATE_SNK_DISCOVERY instead. - */ - if (type == PD_CTRL_WAIT) { - /* - * Trigger a new power request when - * we enter PD_STATE_SNK_READY - */ - pd[port].new_power_request = 1; - - /* - * After the request is triggered, - * make sure the request is sent. - */ - pd[port].prev_request_mv = 0; - - /* - * Transition to PD_STATE_SNK_READY - * after PD_T_SINK_REQUEST ms. - */ - set_state_timeout(port, - get_time().val + - PD_T_SINK_REQUEST, - PD_STATE_SNK_READY); - } else { - /* The request was rejected */ - const int in_contract = - pd[port].flags & - PD_FLAGS_EXPLICIT_CONTRACT; - set_state(port, - in_contract ? PD_STATE_SNK_READY - : PD_STATE_SNK_DISCOVERY); - } - } -#endif - break; - case PD_CTRL_ACCEPT: - if (pd[port].task_state == PD_STATE_ENTER_USB) { - if (!IS_ENABLED(CONFIG_USBC_SS_MUX)) - break; - - /* Connect the SBU and USB lines to the connector */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 1); - - /* Set usb mux to USB4 mode */ - usb_mux_set(port, USB_PD_MUX_USB4_ENABLED, - USB_SWITCH_CONNECT, pd[port].polarity); - - set_state(port, READY_RETURN_STATE(port)); - } else if (pd[port].task_state == PD_STATE_SOFT_RESET) { - /* - * For the case that we sent soft reset in SNK_DISCOVERY - * on startup due to VBUS never low, clear the flag. - */ - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; - execute_soft_reset(port); - } else if (pd[port].task_state == PD_STATE_DR_SWAP) { - /* switch data role */ - pd_dr_swap(port); - set_state(port, READY_RETURN_STATE(port)); -#ifdef CONFIG_USB_PD_DUAL_ROLE -#ifdef CONFIG_USBC_VCONN_SWAP - } else if (pd[port].task_state == PD_STATE_VCONN_SWAP_SEND) { - /* switch vconn */ - set_state(port, PD_STATE_VCONN_SWAP_INIT); -#endif - } else if (pd[port].task_state == PD_STATE_SRC_SWAP_INIT) { - /* explicit contract goes away for power swap */ - pd[port].flags &= ~PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); - set_state(port, PD_STATE_SRC_SWAP_SNK_DISABLE); - } else if (pd[port].task_state == PD_STATE_SNK_SWAP_INIT) { - /* explicit contract goes away for power swap */ - pd[port].flags &= ~PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 0); - set_state(port, PD_STATE_SNK_SWAP_SNK_DISABLE); - } else if (pd[port].task_state == PD_STATE_SNK_REQUESTED) { - /* explicit contract is now in place */ - pd[port].flags |= PD_FLAGS_EXPLICIT_CONTRACT; - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, - 1); - set_state(port, PD_STATE_SNK_TRANSITION); -#endif - } - break; - case PD_CTRL_SOFT_RESET: - execute_soft_reset(port); - pd[port].msg_id = 0; - /* We are done, acknowledge with an Accept packet */ - send_control(port, PD_CTRL_ACCEPT); - break; - case PD_CTRL_PR_SWAP: -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd_check_power_swap(port)) { - send_control(port, PD_CTRL_ACCEPT); - /* - * Clear flag for checking power role to avoid - * immediately requesting another swap. - */ - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - set_state(port, - DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_SWAP_SNK_DISABLE, - PD_STATE_SRC_SWAP_SNK_DISABLE)); - } else { - send_control(port, PD_CTRL_REJECT); - } -#else - send_control(port, NOT_SUPPORTED(pd[port].rev)); -#endif - break; - case PD_CTRL_DR_SWAP: - if (pd_check_data_swap(port, pd[port].data_role)) { - /* - * Accept switch and perform data swap. Clear - * flag for checking data role to avoid - * immediately requesting another swap. - */ - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - if (send_control(port, PD_CTRL_ACCEPT) >= 0) - pd_dr_swap(port); - } else { - send_control(port, PD_CTRL_REJECT); - - } - break; - case PD_CTRL_VCONN_SWAP: -#ifdef CONFIG_USBC_VCONN_SWAP - if (pd[port].task_state == PD_STATE_SRC_READY || - pd[port].task_state == PD_STATE_SNK_READY) { - if (pd_check_vconn_swap(port)) { - if (send_control(port, PD_CTRL_ACCEPT) > 0) - set_state(port, - PD_STATE_VCONN_SWAP_INIT); - } else { - send_control(port, PD_CTRL_REJECT); - } - } -#else - send_control(port, NOT_SUPPORTED(pd[port].rev)); -#endif - break; - default: -#ifdef CONFIG_USB_PD_REV30 - send_control(port, PD_CTRL_NOT_SUPPORTED); -#endif - CPRINTF("C%d Unhandled ctrl message type %d\n", port, type); - } -} - -#ifdef CONFIG_USB_PD_REV30 -static void handle_ext_request(int port, uint16_t head, uint32_t *payload) -{ - int type = PD_HEADER_TYPE(head); - - switch (type) { - case PD_EXT_GET_BATTERY_CAP: - send_battery_cap(port, payload); - break; - case PD_EXT_GET_BATTERY_STATUS: - send_battery_status(port, payload); - break; - case PD_EXT_BATTERY_CAP: - break; - default: - send_control(port, PD_CTRL_NOT_SUPPORTED); - } -} -#endif - -static void handle_request(int port, uint32_t head, - uint32_t *payload) -{ - int cnt = PD_HEADER_CNT(head); - int data_role = PD_HEADER_DROLE(head); - int p; - - /* dump received packet content (only dump ping at debug level 3) */ - if ((debug_level == 2 && PD_HEADER_TYPE(head) != PD_CTRL_PING) || - debug_level >= 3) { - CPRINTF("C%d RECV %04x/%d ", port, head, cnt); - for (p = 0; p < cnt; p++) - CPRINTF("[%d]%08x ", p, payload[p]); - CPRINTF("\n"); - } - - /* - * If we are in disconnected state, we shouldn't get a request. Do - * a hard reset if we get one. - */ - if (!pd_is_connected(port)) - set_state(port, PD_STATE_HARD_RESET_SEND); - - /* - * When a data role conflict is detected, USB-C ErrorRecovery - * actions shall be performed, and transitioning to unattached state - * is one such legal action. - */ - if (pd[port].data_role == data_role) { - /* - * If the port doesn't support removing the terminations, just - * go to the unattached state. - */ - if (tcpm_set_cc(port, TYPEC_CC_OPEN) == EC_SUCCESS) { - /* Do not drive VBUS or VCONN. */ - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif /* defined(CONFIG_USBC_VCONN) */ - usleep(PD_T_ERROR_RECOVERY); - - /* Restore terminations. */ - tcpm_set_cc(port, DUAL_ROLE_IF_ELSE(port, TYPEC_CC_RD, - TYPEC_CC_RP)); - } - set_state(port, - DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - return; - } - -#ifdef CONFIG_USB_PD_REV30 - /* Check if this is an extended chunked data message. */ - if (pd[port].rev == PD_REV30 && PD_HEADER_EXT(head)) { - handle_ext_request(port, head, payload); - return; - } -#endif - if (cnt) - handle_data_request(port, head, payload); - else - handle_ctrl_request(port, head, payload); -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - if (count > VDO_MAX_SIZE - 1) { - CPRINTF("C%d VDM over max size\n", port); - return; - } - - /* set VDM header with VID & CMD */ - pd[port].vdo_data[0] = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd); -#ifdef CONFIG_USB_PD_REV30 - pd[port].vdo_data[0] |= VDO_SVDM_VERS(vdo_ver[pd[port].rev]); -#endif - queue_vdm(port, pd[port].vdo_data, data, count, TCPCI_MSG_SOP); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -static inline int pdo_busy(int port) -{ - /* - * Note, main PDO state machine (pd_task) uses READY state exclusively - * to denote port partners have successfully negociated a contract. All - * other protocol actions force state transitions. - */ - int rv = (pd[port].task_state != PD_STATE_SRC_READY); -#ifdef CONFIG_USB_PD_DUAL_ROLE - rv &= (pd[port].task_state != PD_STATE_SNK_READY); -#endif - return rv; -} - -static uint64_t vdm_get_ready_timeout(uint32_t vdm_hdr) -{ - uint64_t timeout; - int cmd = PD_VDO_CMD(vdm_hdr); - - /* its not a structured VDM command */ - if (!PD_VDO_SVDM(vdm_hdr)) - return 500*MSEC; - - switch (PD_VDO_CMDT(vdm_hdr)) { - case CMDT_INIT: - if ((cmd == CMD_ENTER_MODE) || (cmd == CMD_EXIT_MODE)) - timeout = PD_T_VDM_WAIT_MODE_E; - else - timeout = PD_T_VDM_SNDR_RSP; - break; - default: - if ((cmd == CMD_ENTER_MODE) || (cmd == CMD_EXIT_MODE)) - timeout = PD_T_VDM_E_MODE; - else - timeout = PD_T_VDM_RCVR_RSP; - break; - } - return timeout; -} - -static void exit_tbt_mode_sop_prime(int port) -{ - /* Exit Thunderbolt-Compatible mode SOP' */ - uint16_t header; - int opos; - - if (!IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) - return; - - opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL); - if (opos <= 0) - return; - - CPRINTS("C%d Cable exiting TBT Compat mode", port); - /* - * Note: TCPMv2 contemplates separate discovery structures for each SOP - * type. TCPMv1 only uses one discovery structure, so all accesses - * specify TCPCI_MSG_SOP. - */ - if (pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL, opos)) - usb_mux_set_safe_mode(port); - else - return; - - header = PD_HEADER(PD_DATA_VENDOR_DEF, pd[port].power_role, - pd[port].data_role, pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - - pd[port].vdo_data[0] = VDO(USB_VID_INTEL, 1, - CMD_EXIT_MODE | VDO_OPOS(opos)); - - pd_transmit(port, TCPCI_MSG_SOP_PRIME, header, pd[port].vdo_data, - AMS_START); - - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); -} - -static void pd_vdm_send_state_machine(int port) -{ - int res; - uint16_t header; - enum tcpci_msg_type msg_type = pd[port].xmit_type; - - switch (pd[port].vdm_state) { - case VDM_STATE_READY: - /* Only transmit VDM if connected. */ - if (!pd_is_connected(port)) { - pd[port].vdm_state = VDM_STATE_ERR_BUSY; - break; - } - - /* - * if there's traffic or we're not in PDO ready state don't send - * a VDM. - */ - if (pdo_busy(port)) - break; - - /* - * To communicate with the cable plug, an explicit contract - * should be established, VCONN should be enabled and data role - * that can communicate with the cable plug should be in place. - * For USB3.0, UFP/DFP can communicate whereas in case of - * USB2.0 only DFP can talk to the cable plug. - * - * For communication between USB2.0 UFP and cable plug, - * data role swap takes place during source and sink - * negotiation and in case of failure, a soft reset is issued. - */ - if ((msg_type == TCPCI_MSG_SOP_PRIME) || - (msg_type == TCPCI_MSG_SOP_PRIME_PRIME)) { - /* Prepare SOP'/SOP'' header and send VDM */ - header = PD_HEADER( - PD_DATA_VENDOR_DEF, - PD_PLUG_FROM_DFP_UFP, - 0, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port, TCPCI_MSG_SOP), - 0); - res = pd_transmit(port, msg_type, header, - pd[port].vdo_data, AMS_START); - /* - * In the case of SOP', if there is no response from - * the cable, it's a non-emark cable and therefore the - * pd flow should continue irrespective of cable - * response, sending discover_identity so the pd flow - * remains intact. - * - * In the case of SOP'', if there is no response from - * the cable, exit Thunderbolt-Compatible mode - * discovery, reset the mux state since, the mux will - * be set to a safe state before entering - * Thunderbolt-Compatible mode and enter the default - * mode. - */ - if (res < 0) { - header = PD_HEADER(PD_DATA_VENDOR_DEF, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev - (port, TCPCI_MSG_SOP), - 0); - - if ((msg_type == TCPCI_MSG_SOP_PRIME_PRIME) && - IS_ENABLED(CONFIG_USBC_SS_MUX)) { - exit_tbt_mode_sop_prime(port); - } else if (msg_type == TCPCI_MSG_SOP_PRIME) { - pd[port].vdo_data[0] = VDO(USB_SID_PD, - 1, CMD_DISCOVER_SVID); - } - res = pd_transmit(port, TCPCI_MSG_SOP, header, - pd[port].vdo_data, AMS_START); - reset_pd_cable(port); - } - } else { - /* Prepare SOP header and send VDM */ - header = PD_HEADER(PD_DATA_VENDOR_DEF, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - (int)pd[port].vdo_count, - pd_get_rev(port, TCPCI_MSG_SOP), 0); - res = pd_transmit(port, TCPCI_MSG_SOP, header, - pd[port].vdo_data, AMS_START); - } - - if (res < 0) { - pd[port].vdm_state = VDM_STATE_ERR_SEND; - } else { - pd[port].vdm_state = VDM_STATE_BUSY; - pd[port].vdm_timeout.val = get_time().val + - vdm_get_ready_timeout(pd[port].vdo_data[0]); - } - break; - case VDM_STATE_WAIT_RSP_BUSY: - /* wait and then initiate request again */ - if (get_time().val > pd[port].vdm_timeout.val) { - pd[port].vdo_data[0] = pd[port].vdo_retry; - pd[port].vdo_count = 1; - pd[port].vdm_state = VDM_STATE_READY; - } - break; - case VDM_STATE_BUSY: - /* Wait for VDM response or timeout */ - if (pd[port].vdm_timeout.val && - (get_time().val > pd[port].vdm_timeout.val)) { - pd[port].vdm_state = VDM_STATE_ERR_TMOUT; - } - break; - case VDM_STATE_ERR_SEND: - /* Sending the VDM failed, so try again. */ - CPRINTF("C%d VDMretry\n", port); - pd[port].vdm_state = VDM_STATE_READY; - break; - default: - break; - } -} - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint8_t *hash) -{ - int j; - ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id)); - for (j = 0; j < PD_RW_HASH_SIZE; j += 4) { - ccprintf(" 0x%02x%02x%02x%02x", hash[j + 3], hash[j + 2], - hash[j + 1], hash[j]); - } - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ -#ifdef CONFIG_COMMON_RUNTIME - int i; -#endif - - pd[port].dev_id = dev_id; - memcpy(pd[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - if (debug_level >= 2) - pd_dev_dump_info(dev_id, (uint8_t *)rw_hash); -#endif - pd[port].current_image = current_image; - -#ifdef CONFIG_COMMON_RUNTIME - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); -#endif - return 0; -} - -void pd_dev_get_rw_hash(int port, uint16_t *dev_id, uint8_t *rw_hash, - uint32_t *current_image) -{ - *dev_id = pd[port].dev_id; - *current_image = pd[port].current_image; - if (*dev_id) - memcpy(rw_hash, pd[port].dev_rw_hash, PD_RW_HASH_SIZE); -} - -__maybe_unused static void exit_supported_alt_mode(int port) -{ - int i; - - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - return; - - for (i = 0; i < supported_modes_cnt; i++) { - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, - supported_modes[i].svid); - - if (opos > 0 && pd_dfp_exit_mode(port, TCPCI_MSG_SOP, - supported_modes[i].svid, opos)) { - CPRINTS("C%d Exiting ALT mode with SVID = 0x%x", port, - supported_modes[i].svid); - usb_mux_set_safe_mode(port); - pd_send_vdm(port, supported_modes[i].svid, - CMD_EXIT_MODE | VDO_OPOS(opos), NULL, 0); - /* Wait for an ACK from port-partner */ - pd_vdm_send_state_machine(port); - } - } -} - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port) -{ - - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { - /* - * The SoC will negotiate the alternate mode again when - * it boots up. - */ - exit_supported_alt_mode(port); - } -#ifdef CONFIG_USBC_VCONN_SWAP - else { - /* Request for Vconn Swap */ - pd_try_vconn_src(port); - } -#endif - /* Ensure mux is set properly after chipset transition */ - set_usb_mux_with_current_data_role(port); -} -#endif /* CONFIG_POWER_COMMON */ - -#ifdef CONFIG_USB_PD_DUAL_ROLE -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -#ifdef CONFIG_USB_PD_TRY_SRC -static void pd_update_try_source(void) -{ - int i; - - pd_try_src_enable = pd_is_try_source_capable(); - - /* - * Clear this flag to cover case where a TrySrc - * mode went from enabled to disabled and trying_source - * was active at that time. - */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd[i].flags &= ~PD_FLAGS_TRY_SRC; -} -#endif /* CONFIG_USB_PD_TRY_SRC */ - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC -static void pd_update_snk_reset(void) -{ - int i; - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd[i].flags & PD_FLAGS_SNK_WAITING_BATT) { - /* - * Battery has gained sufficient charge to kick off PD - * negotiation and withstand a hard reset. Clear the - * flag and let reset begin if task is waiting in - * SNK_DISCOVERY. - */ - pd[i].flags &= ~PD_FLAGS_SNK_WAITING_BATT; - - if (pd[i].task_state == PD_STATE_SNK_DISCOVERY) { - CPRINTS("C%d: Starting soft reset timer", i); - set_state_timeout(i, - get_time().val + PD_T_SINK_WAIT_CAP, - PD_STATE_SOFT_RESET); - } - } - } -} -#endif - -#if defined(CONFIG_USB_PD_TRY_SRC) || defined(CONFIG_USB_PD_RESET_MIN_BATT_SOC) -static void pd_update_battery_soc_change(void) -{ -#ifdef CONFIG_USB_PD_TRY_SRC - pd_update_try_source(); -#endif - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - pd_update_snk_reset(); -#endif -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_battery_soc_change, - HOOK_PRIO_DEFAULT); -#endif /* CONFIG_USB_PD_TRY_SRC || CONFIG_USB_PD_RESET_MIN_BATT_SOC */ - -static inline void pd_set_dual_role_no_wakeup(int port, - enum pd_dual_role_states state) -{ - drp_state[port] = state; - -#ifdef CONFIG_USB_PD_TRY_SRC - pd_update_try_source(); -#endif -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_no_wakeup(port, state); - - /* Wake task up to process change */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_UPDATE_DUAL_ROLE); -} - -static int pd_is_power_swapping(int port) -{ - /* return true if in the act of swapping power roles */ - return pd[port].task_state == PD_STATE_SNK_SWAP_SNK_DISABLE || - pd[port].task_state == PD_STATE_SNK_SWAP_SRC_DISABLE || - pd[port].task_state == PD_STATE_SNK_SWAP_STANDBY || - pd[port].task_state == PD_STATE_SNK_SWAP_COMPLETE || - pd[port].task_state == PD_STATE_SRC_SWAP_SNK_DISABLE || - pd[port].task_state == PD_STATE_SRC_SWAP_SRC_DISABLE || - pd[port].task_state == PD_STATE_SRC_SWAP_STANDBY; -} - -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is toggle off and we are in the - * source disconnected state). - */ - if (pd[port].power_role == PD_ROLE_SOURCE && - (drp_state[port] == PD_DRP_FORCE_SINK - || (drp_state[port] == PD_DRP_TOGGLE_OFF - && pd[port].task_state == PD_STATE_SRC_DISCONNECTED))) { - pd_set_power_role(port, PD_ROLE_SINK); - set_state(port, PD_STATE_SNK_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RD); - /* Make sure we're not sourcing VBUS. */ - pd_power_supply_reset(port); - } - - /* - * Change to source if port is currently a sink and the - * new DRP state is force source. If we are performing - * power swap we won't change anything because - * changing state will disrupt power swap process - * and we are power swapping to desired power role. - */ - if (pd[port].power_role == PD_ROLE_SINK && - drp_state[port] == PD_DRP_FORCE_SOURCE && - !pd_is_power_swapping(port)) { - pd_set_power_role(port, PD_ROLE_SOURCE); - set_state(port, PD_STATE_SRC_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RP); - } -} - -/* - * Provide Rp to ensure the partner port is in a known state (eg. not - * PD negotiated, not sourcing 20V). - */ -static void pd_partner_port_reset(int port) -{ - uint64_t timeout; - uint8_t flags; - - /* - * If there is no contract in place (or if we fail to read the BBRAM - * flags), there is no need to reset the partner. - */ - if (pd_get_saved_port_flags(port, &flags) != EC_SUCCESS || - !(flags & PD_BBRMFLG_EXPLICIT_CONTRACT)) - return; - - /* - * If we reach here, an explicit contract is in place. - * - * If PD communications are allowed, don't apply Rp. We'll issue a - * SoftReset later on and renegotiate our contract. This particular - * condition only applies to unlocked RO images with an explicit - * contract in place. - */ - if (pd_comm_is_enabled(port)) - return; - - /* If we just lost power, don't apply Rp. */ - if (system_get_reset_flags() & - (EC_RESET_FLAG_BROWNOUT | EC_RESET_FLAG_POWER_ON)) - return; - - /* - * Clear the active contract bit before we apply Rp in case we - * intentionally brown out because we cut off our only power supply. - */ - pd_update_saved_port_flags(port, PD_BBRMFLG_EXPLICIT_CONTRACT, 0); - - /* Provide Rp for 200 msec. or until we no longer have VBUS. */ - CPRINTF("C%d Apply Rp!\n", port); - cflush(); - tcpm_set_cc(port, TYPEC_CC_RP); - timeout = get_time().val + 200 * MSEC; - - while (get_time().val < timeout && pd_is_vbus_present(port)) - msleep(10); -} -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -enum pd_power_role pd_get_power_role(int port) -{ - return pd[port].power_role; -} - -enum pd_data_role pd_get_data_role(int port) -{ - return pd[port].data_role; -} - -enum pd_cc_states pd_get_task_cc_state(int port) -{ - return pd[port].cc_state; -} - -uint8_t pd_get_task_state(int port) -{ - return pd[port].task_state; -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -uint32_t pd_get_requested_voltage(int port) -{ - return pd[port].supply_voltage; -} - -uint32_t pd_get_requested_current(int port) -{ - return pd[port].curr_limit; -} -#endif - -const char *pd_get_task_state_name(int port) -{ -#ifdef CONFIG_USB_PD_TCPMV1_DEBUG - if (debug_level > 0) - return pd_state_names[pd[port].task_state]; -#endif - return ""; -} - -bool pd_get_vconn_state(int port) -{ - return !!(pd[port].flags & PD_FLAGS_VCONN_ON); -} - -bool pd_get_partner_dual_role_power(int port) -{ - return !!(pd[port].flags & PD_FLAGS_PARTNER_DR_POWER); -} - -bool pd_get_partner_unconstr_power(int port) -{ - return !!(pd[port].flags & PD_FLAGS_PARTNER_UNCONSTR); -} - -enum tcpc_cc_polarity pd_get_polarity(int port) -{ - return pd[port].polarity; -} - -bool pd_get_partner_data_swap_capable(int port) -{ - /* return data swap capable status of port partner */ - return !!(pd[port].flags & PD_FLAGS_PARTNER_DR_DATA); -} - -#ifdef CONFIG_COMMON_RUNTIME -void pd_comm_enable(int port, int enable) -{ - /* We don't check port >= CONFIG_USB_PD_PORT_MAX_COUNT deliberately */ - pd_comm_enabled[port] = enable; - - /* If type-C connection, then update the TCPC RX enable */ - if (pd_is_connected(port)) - tcpm_set_rx_enable(port, enable); - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If communications are enabled, start hard reset timer for - * any port in PD_SNK_DISCOVERY. - */ - if (enable && pd[port].task_state == PD_STATE_SNK_DISCOVERY) - set_state_timeout(port, - get_time().val + PD_T_SINK_WAIT_CAP, - PD_STATE_HARD_RESET_SEND); -#endif -} -#endif - -void pd_ping_enable(int port, int enable) -{ - if (enable) - pd[port].flags |= PD_FLAGS_PING_ENABLED; - else - pd[port].flags &= ~PD_FLAGS_PING_ENABLED; -} - -__overridable uint8_t board_get_src_dts_polarity(int port) -{ - /* - * If the port in SRC DTS, the polarity is determined by the board, - * i.e. what Rp impedance the CC lines are pulled. If this function - * is not overridden, assume CC1 is primary. - */ - return 0; -} - -#if defined(CONFIG_CHARGE_MANAGER) - -/** - * Signal power request to indicate a charger update that affects the port. - */ -void pd_set_new_power_request(int port) -{ - pd[port].new_power_request = 1; - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_CHARGE_MANAGER */ - -#if defined(CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP) && defined(CONFIG_USBC_SS_MUX) -/* - * Backwards compatible DFP does not support USB SS because it applies VBUS - * before debouncing CC and setting USB SS muxes, but SS detection will fail - * before we are done debouncing CC. - */ -#error "Backwards compatible DFP does not support USB" -#endif - -#ifdef CONFIG_COMMON_RUNTIME - -/* Initialize globals based on system state. */ -static void pd_init_tasks(void) -{ - static int initialized; - int enable = 1; - int i; - - /* Initialize globals once, for all PD tasks. */ - if (initialized) - return; - -#if defined(HAS_TASK_CHIPSET) && defined(CONFIG_USB_PD_DUAL_ROLE) - /* Set dual-role state based on chipset power state */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_FORCE_SINK; - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_TOGGLE_OFF; - else /* CHIPSET_STATE_ON */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - drp_state[i] = PD_DRP_TOGGLE_ON; -#endif - -#if defined(CONFIG_USB_PD_COMM_DISABLED) - enable = 0; -#elif defined(CONFIG_USB_PD_COMM_LOCKED) - /* Disable PD communication if we're in RO, WP is enabled, and EFS - * didn't register NO_BOOT. */ - if (!system_is_in_rw() && system_is_locked() && !vboot_allow_usb_pd()) - enable = 0; -#endif - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd_comm_enabled[i] = enable; - CPRINTS("PD comm %sabled", enable ? "en" : "dis"); - - initialized = 1; -} -#endif /* CONFIG_COMMON_RUNTIME */ - -#if !defined(CONFIG_USB_PD_TCPC) && defined(CONFIG_USB_PD_DUAL_ROLE) -static int pd_restart_tcpc(int port) -{ - if (board_set_tcpc_power_mode) { - /* force chip reset */ - board_set_tcpc_power_mode(port, 0); - } - return tcpm_init(port); -} -#endif - -static void pd_send_enter_usb(int port, int *timeout) -{ - uint32_t usb4_payload; - uint16_t header; - int res; - - /* - * TODO: Enable Enter USB for cables (SOP'). - * This is needed for active cables - */ - if (!IS_ENABLED(CONFIG_USBC_SS_MUX) || - !IS_ENABLED(CONFIG_USB_PD_USB4) || - !IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - return; - - usb4_payload = get_enter_usb_msg_payload(port); - - header = PD_HEADER(PD_DATA_ENTER_USB, - pd[port].power_role, - pd[port].data_role, - pd[port].msg_id, - 1, - PD_REV30, - 0); - - res = pd_transmit(port, TCPCI_MSG_SOP, header, &usb4_payload, - AMS_START); - if (res < 0) { - *timeout = 10*MSEC; - /* - * If failed to get goodCRC, send soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - READY_RETURN_STATE(port)); - return; - } - - /* Disable Enter USB4 mode prevent re-entry */ - disable_enter_usb4_mode(port); - - set_state(port, PD_STATE_ENTER_USB); -} - -void pd_task(void *u) -{ - uint32_t head; - int port = TASK_ID_TO_PD_PORT(task_get_current()); - uint32_t payload[7]; - int timeout = 10*MSEC; - enum tcpc_cc_voltage_status cc1, cc2; - int res, incoming_packet = 0; - int hard_reset_count = 0; -#ifdef CONFIG_USB_PD_DUAL_ROLE - uint64_t next_role_swap = PD_T_DRP_SNK; - uint8_t saved_flgs = 0; -#ifndef CONFIG_USB_PD_VBUS_DETECT_NONE - int snk_hard_reset_vbus_off = 0; -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - const int auto_toggle_supported = tcpm_auto_toggle_supported(port); -#endif -#if defined(CONFIG_CHARGE_MANAGER) - typec_current_t typec_curr = 0, typec_curr_change = 0; -#endif /* CONFIG_CHARGE_MANAGER */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - enum pd_states this_state; - enum pd_cc_states new_cc_state; - timestamp_t now; - uint64_t next_src_cap = 0; - int caps_count = 0, hard_reset_sent = 0; - int snk_cap_count = 0; - int evt; - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * Set the ports in Low Power Mode so that other tasks wait until - * TCPC is initialized and ready. - */ - pd[port].flags |= PD_FLAGS_LPM_ENGAGED; -#endif - -#ifdef CONFIG_COMMON_RUNTIME - pd_init_tasks(); -#endif - - /* - * Ensure the power supply is in the default state and ensure we are not - * sourcing Vconn - */ - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If we were previously a sink but also the VCONN source, we should - * still continue to source VCONN. Otherwise, we should turn off VCONN - * since we are also going to turn off VBUS. - */ - if (pd_comm_is_enabled(port) && - (pd_get_saved_port_flags(port, &saved_flgs) == EC_SUCCESS) && - ((saved_flgs & PD_BBRMFLG_POWER_ROLE) == PD_ROLE_SINK) && - (saved_flgs & PD_BBRMFLG_EXPLICIT_CONTRACT) && - (saved_flgs & PD_BBRMFLG_VCONN_ROLE)) - set_vconn(port, 1); - else -#endif - set_vconn(port, 0); -#endif - -#ifdef CONFIG_USB_PD_TCPC_BOARD_INIT - /* Board specific TCPC init */ - board_tcpc_init(); -#endif - - /* Initialize TCPM driver and wait for TCPC to be ready */ - res = reset_device_and_notify(port); - invalidate_last_message_id(port); - -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_partner_port_reset(port); -#endif - - this_state = res ? PD_STATE_SUSPENDED : PD_DEFAULT_STATE(port); -#ifndef CONFIG_USB_PD_TCPC - if (!res) { - struct ec_response_pd_chip_info_v1 info; - - if (tcpm_get_chip_info(port, 0, &info) == - EC_SUCCESS) { - CPRINTS("TCPC p%d VID:0x%x PID:0x%x DID:0x%x " - "FWV:0x%" PRIx64, - port, info.vendor_id, info.product_id, - info.device_id, info.fw_version_number); - } - } -#endif - -#ifdef CONFIG_USB_PD_REV30 - /* Set Revision to highest */ - pd[port].rev = PD_REV30; -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If VBUS is high, then initialize flag for VBUS has always been - * present. This flag is used to maintain a PD connection after a - * reset by sending a soft reset. - */ - pd[port].flags |= - pd_is_vbus_present(port) ? PD_FLAGS_VBUS_NEVER_LOW : 0; -#endif - - /* Disable TCPC RX until connection is established */ - tcpm_set_rx_enable(port, 0); - -#ifdef CONFIG_USBC_SS_MUX - /* Initialize USB mux to its default state */ - usb_mux_init(port); -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If there's an explicit contract in place, let's restore the data and - * power roles such that any messages we send to the port partner will - * still be valid. - */ - if (pd_comm_is_enabled(port) && - (pd_get_saved_port_flags(port, &saved_flgs) == EC_SUCCESS) && - (saved_flgs & PD_BBRMFLG_EXPLICIT_CONTRACT)) { - /* Only attempt to maintain previous sink contracts */ - if ((saved_flgs & PD_BBRMFLG_POWER_ROLE) == PD_ROLE_SINK) { - pd_set_power_role(port, - (saved_flgs & PD_BBRMFLG_POWER_ROLE) ? - PD_ROLE_SOURCE : PD_ROLE_SINK); - pd_set_data_role(port, - (saved_flgs & PD_BBRMFLG_DATA_ROLE) ? - PD_ROLE_DFP : PD_ROLE_UFP); -#ifdef CONFIG_USBC_VCONN - pd_set_vconn_role(port, - (saved_flgs & PD_BBRMFLG_VCONN_ROLE) ? - PD_ROLE_VCONN_ON : PD_ROLE_VCONN_OFF); -#endif /* CONFIG_USBC_VCONN */ - - /* - * Since there is an explicit contract in place, let's - * issue a SoftReset such that we can renegotiate with - * our port partner in order to synchronize our state - * machines. - */ - this_state = PD_STATE_SOFT_RESET; - - /* - * Re-discover any alternate modes we may have been - * using with this port partner. - */ - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; - } else { - /* - * Vbus was turned off during the power supply reset - * earlier, so clear the contract flag and re-start as - * default role - */ - pd_update_saved_port_flags(port, - PD_BBRMFLG_EXPLICIT_CONTRACT, 0); - - } - /* - * Set the TCPC reset event such that we can set our CC - * terminations, determine polarity, and enable RX so we - * can hear back from our port partner if maintaining our old - * connection. - */ - task_set_event(task_get_current(), PD_EVENT_TCPC_RESET); - } -#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */ - /* Set the power role if we haven't already. */ - if (this_state != PD_STATE_SOFT_RESET) - pd_set_power_role(port, PD_ROLE_DEFAULT(port)); - - /* Initialize PD protocol state variables for each port. */ - pd[port].vdm_state = VDM_STATE_DONE; - set_state(port, this_state); - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If we're not in an explicit contract, set our terminations to match - * our default power role. - */ - if (!(saved_flgs & PD_BBRMFLG_EXPLICIT_CONTRACT)) -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - tcpm_set_cc(port, PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE ? - TYPEC_CC_RP : TYPEC_CC_RD); - -#ifdef CONFIG_USBC_PPC - /* - * Wait to initialize the PPC after setting the correct Rd values in - * the TCPC otherwise the TCPC might not be pulling the CC lines down - * when the PPC connects the CC lines from the USB connector to the - * TCPC cause the source to drop Vbus causing a brown out. - */ - ppc_init(port); -#endif - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - /* Initialize PD Policy engine */ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); -#endif - -#ifdef CONFIG_CHARGE_MANAGER - /* Initialize PD and type-C supplier current limits to 0 */ - pd_set_input_current_limit(port, 0, 0); - typec_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); -#endif - - /* - * Since most boards configure the TCPC interrupt as edge - * and it is possible that the interrupt line was asserted between init - * and calling set_state, we need to process any pending interrupts now. - * Otherwise future interrupts will never fire because another edge - * never happens. Note this needs to happen after set_state() is called. - */ - if (IS_ENABLED(CONFIG_HAS_TASK_PD_INT)) - schedule_deferred_pd_interrupt(port); - - while (1) { - /* process VDM messages last */ - pd_vdm_send_state_machine(port); - - /* Verify board specific health status : current, voltages... */ - res = pd_board_checks(); - if (res != EC_SUCCESS) { - /* cut the power */ - pd_execute_hard_reset(port); - /* notify the other side of the issue */ - pd_transmit(port, TCPCI_MSG_TX_HARD_RESET, 0, NULL, - AMS_START); - } - - /* wait for next event/packet or timeout expiration */ - evt = task_wait_event(timeout); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - if (evt & (PD_EXIT_LOW_POWER_EVENT_MASK | TASK_EVENT_WAKE)) - exit_low_power_mode(port); - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); -#endif -#ifdef CONFIG_POWER_COMMON - if (evt & PD_EVENT_POWER_STATE_CHANGE) - handle_new_power_state(port); -#endif - -#if defined(CONFIG_USB_PD_ALT_MODE_DFP) - if (evt & PD_EVENT_SYSJUMP) { - exit_supported_alt_mode(port); - notify_sysjump_ready(); - } -#endif - -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -#endif - -#ifdef CONFIG_USB_PD_TCPC - /* - * run port controller task to check CC and/or read incoming - * messages - */ - tcpc_run(port, evt); -#else - /* if TCPC has reset, then need to initialize it again */ - if (evt & PD_EVENT_TCPC_RESET) { - reset_device_and_notify(port); -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - } - - if ((evt & PD_EVENT_TCPC_RESET) && - (pd[port].task_state != PD_STATE_DRP_AUTO_TOGGLE)) { -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd[port].task_state == PD_STATE_SOFT_RESET) { - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * Set the terminations to match our power - * role. - */ - tcpm_set_cc(port, pd[port].power_role ? - TYPEC_CC_RP : TYPEC_CC_RD); - - /* Determine the polarity. */ - tcpm_get_cc(port, &cc1, &cc2); - if (pd[port].power_role == PD_ROLE_SINK) { - pd[port].polarity = - get_snk_polarity(cc1, cc2); - } else if (cc_is_snk_dbg_acc(cc1, cc2)) { - pd[port].polarity = - board_get_src_dts_polarity( - port); - } else { - pd[port].polarity = - get_src_polarity(cc1, cc2); - } - } else -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - { - /* Ensure CC termination is default */ - tcpm_set_cc(port, PD_ROLE_DEFAULT(port) == - PD_ROLE_SOURCE ? TYPEC_CC_RP : - TYPEC_CC_RD); - } - - /* - * If we have a stable contract in the default role, - * then simply update TCPC with some missing info - * so that we can continue without resetting PD comms. - * Otherwise, go to the default disconnected state - * and force renegotiation. - */ - if (pd[port].vdm_state == VDM_STATE_DONE && ( -#ifdef CONFIG_USB_PD_DUAL_ROLE - (PD_ROLE_DEFAULT(port) == PD_ROLE_SINK && - pd[port].task_state == PD_STATE_SNK_READY) || - (pd[port].task_state == PD_STATE_SOFT_RESET) || -#endif - (PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE && - pd[port].task_state == PD_STATE_SRC_READY))) { - pd_set_polarity(port, pd[port].polarity); - tcpm_set_msg_header(port, pd[port].power_role, - pd[port].data_role); - tcpm_set_rx_enable(port, 1); - } else { - /* Ensure state variables are at default */ - pd_set_power_role(port, PD_ROLE_DEFAULT(port)); - pd[port].vdm_state = VDM_STATE_DONE; - set_state(port, PD_DEFAULT_STATE(port)); -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_update_dual_role_config(port); -#endif - } - } -#endif - -#ifdef CONFIG_USBC_PPC - /* - * TODO: Useful for non-PPC cases as well, but only needed - * for PPC cases right now. Revisit later. - */ - if (evt & PD_EVENT_SEND_HARD_RESET) - set_state(port, PD_STATE_HARD_RESET_SEND); -#endif /* defined(CONFIG_USBC_PPC) */ - - if (evt & PD_EVENT_RX_HARD_RESET) - pd_execute_hard_reset(port); - - /* process any potential incoming message */ - incoming_packet = tcpm_has_pending_message(port); - if (incoming_packet) { - /* Dequeue and consume duplicate message ID. */ - if (tcpm_dequeue_message(port, payload, &head) == - EC_SUCCESS - && !consume_repeat_message(port, head) - ) - handle_request(port, head, payload); - - /* Check if there are any more messages */ - if (tcpm_has_pending_message(port)) - task_set_event(PD_PORT_TO_TASK_ID(port), - TASK_EVENT_WAKE); - } - - if (pd[port].req_suspend_state) - set_state(port, PD_STATE_SUSPENDED); - - /* if nothing to do, verify the state of the world in 500ms */ - this_state = pd[port].task_state; - timeout = 500*MSEC; - switch (this_state) { - case PD_STATE_DISABLED: - /* Nothing to do */ - break; - case PD_STATE_SRC_DISCONNECTED: - timeout = 10*MSEC; - pd_set_src_caps(port, 0, NULL); -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) - break; -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - - tcpm_get_cc(port, &cc1, &cc2); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling and not try.src - */ - if (auto_toggle_supported && - !(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && - !is_try_src(port) && - cc_is_open(cc1, cc2)) { - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - timeout = 2*MSEC; - break; - } -#endif - /* - * Transition to DEBOUNCE if we detect appropriate - * signals - * - * (from 4.5.2.2.10.2 Exiting from Try.SRC State) - * If try_src -and- - * have only one Rd (not both) => DEBOUNCE - * - * (from 4.5.2.2.7.2 Exiting from Unattached.SRC State) - * If not try_src -and- - * have at least one Rd => DEBOUNCE -or- - * have audio access => DEBOUNCE - * - * try_src should not exit if both pins are Rd - */ - if ((is_try_src(port) && cc_is_only_one_rd(cc1, cc2)) || - (!is_try_src(port) && - (cc_is_at_least_one_rd(cc1, cc2) || - cc_is_audio_acc(cc1, cc2)))) { -#ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) - break; -#endif - pd[port].cc_state = PD_CC_NONE; - set_state(port, - PD_STATE_SRC_DISCONNECTED_DEBOUNCE); - break; - } -#if defined(CONFIG_USB_PD_DUAL_ROLE) - now = get_time(); - /* - * Try.SRC state is embedded here. The port - * shall transition to TryWait.SNK after - * tDRPTry (PD_T_DRP_TRY) and Vbus is within - * vSafe0V, or after tTryTimeout - * (PD_T_TRY_TIMEOUT). Otherwise we should stay - * within Try.SRC (break). - */ - if (is_try_src(port)) { - if (now.val < pd[port].try_src_marker) { - break; - } else if (now.val < pd[port].try_timeout) { - if (pd_is_vbus_present(port)) - break; - } - - /* - * Transition to TryWait.SNK now, so set - * state and update src marker time. - */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - pd[port].try_src_marker = - get_time().val + PD_T_DEBOUNCE; - timeout = 2 * MSEC; - break; - } - - /* - * If Try.SRC state is not active, then handle - * the normal DRP toggle from SRC->SNK. - */ - if (now.val < next_role_swap || - drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_FREEZE) - break; - - /* - * Transition to SNK now, so set state and - * update next role swap time. - */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - next_role_swap = get_time().val + PD_T_DRP_SNK; - /* Swap states quickly */ - timeout = 2 * MSEC; -#endif - break; - case PD_STATE_SRC_DISCONNECTED_DEBOUNCE: - timeout = 20*MSEC; - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_snk_dbg_acc(cc1, cc2)) { - /* Debug accessory */ - new_cc_state = PD_CC_UFP_DEBUG_ACC; - } else if (cc_is_at_least_one_rd(cc1, cc2)) { - /* UFP attached */ - new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc_is_audio_acc(cc1, cc2)) { - /* Audio accessory */ - new_cc_state = PD_CC_UFP_AUDIO_ACC; - } else { - /* No UFP */ - set_state(port, PD_STATE_SRC_DISCONNECTED); - timeout = 5*MSEC; - break; - } - - /* Set debounce timer */ - if (new_cc_state != pd[port].cc_state) { - pd[port].cc_debounce = - get_time().val + - (is_try_src(port) ? PD_T_DEBOUNCE - : PD_T_CC_DEBOUNCE); - pd[port].cc_state = new_cc_state; - break; - } - - /* Debounce the cc state */ - if (get_time().val < pd[port].cc_debounce) - break; - - /* Debounce complete */ - if (IS_ENABLED(CONFIG_COMMON_RUNTIME)) - hook_notify(HOOK_USB_PD_CONNECT); - -#ifdef CONFIG_USBC_PPC - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (usbc_ocp_is_port_latched_off(port)) - break; -#endif /* CONFIG_USBC_PPC */ - - /* UFP is attached */ - if (new_cc_state == PD_CC_UFP_ATTACHED || - new_cc_state == PD_CC_UFP_DEBUG_ACC) { -#ifdef CONFIG_USBC_PPC - /* Inform PPC that a sink is connected. */ - ppc_dev_is_connected(port, PPC_DEV_SNK); -#endif /* CONFIG_USBC_PPC */ - if (IS_ENABLED(CONFIG_USBC_OCP)) - usbc_ocp_snk_is_connected(port, true); - if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { - pd[port].polarity = - board_get_src_dts_polarity( - port); - } else { - pd[port].polarity = - get_src_polarity(cc1, cc2); - } - pd_set_polarity(port, pd[port].polarity); - - /* initial data role for source is DFP */ - pd_set_data_role(port, PD_ROLE_DFP); - - /* Enable Auto Discharge Disconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - - if (new_cc_state == PD_CC_UFP_DEBUG_ACC) - pd[port].flags |= - PD_FLAGS_TS_DTS_PARTNER; - -#ifdef CONFIG_USBC_VCONN - /* - * Do not source Vconn when debug accessory is - * detected. Section 4.5.2.2.17.1 in USB spec - * v1-3 - */ - if (new_cc_state != PD_CC_UFP_DEBUG_ACC) { - /* - * Start sourcing Vconn before Vbus to - * ensure we are within USB Type-C - * Spec 1.3 tVconnON. - */ - set_vconn(port, 1); - pd_set_vconn_role(port, - PD_ROLE_VCONN_ON); - } -#endif - -#ifndef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { -#ifdef CONFIG_USBC_VCONN - /* Stop sourcing Vconn if Vbus failed */ - set_vconn(port, 0); - pd_set_vconn_role(port, - PD_ROLE_VCONN_OFF); -#endif /* CONFIG_USBC_VCONN */ -#ifdef CONFIG_USBC_SS_MUX - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, - pd[port].polarity); -#endif /* CONFIG_USBC_SS_MUX */ - break; - } - /* - * Set correct Rp value determined during - * pd_set_power_supply_ready. This should be - * safe because Vconn is being sourced, - * preventing incorrect CCD detection. - */ - tcpm_set_cc(port, TYPEC_CC_RP); -#endif /* CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP */ - /* If PD comm is enabled, enable TCPC RX */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); - - pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE; - hard_reset_count = 0; - timeout = 5*MSEC; - - set_state(port, PD_STATE_SRC_STARTUP); - } - /* - * AUDIO_ACC will remain in this state indefinitely - * until disconnect. - */ - break; - case PD_STATE_SRC_HARD_RESET_RECOVER: - /* Do not continue until hard reset recovery time */ - if (get_time().val < pd[port].src_recover) { - timeout = 50*MSEC; - break; - } - -#ifdef CONFIG_USBC_VCONN - /* - * Start sourcing Vconn again and set the flag, in case - * it was 0 due to a previous swap - */ - set_vconn(port, 1); - pd_set_vconn_role(port, PD_ROLE_VCONN_ON); -#endif - - /* Enable VBUS */ - timeout = 10*MSEC; - if (pd_set_power_supply_ready(port)) { - set_state(port, PD_STATE_SRC_DISCONNECTED); - break; - } -#if defined(CONFIG_USB_PD_TCPM_TCPCI) || defined(CONFIG_USB_PD_TCPM_STUB) - /* - * After transmitting hard reset, TCPM writes - * to RECEIVE_DETECT register to enable - * PD message passing. - */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); -#endif /* CONFIG_USB_PD_TCPM_TCPCI || CONFIG_USB_PD_TCPM_STUB */ - - pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE; - set_state(port, PD_STATE_SRC_STARTUP); - break; - case PD_STATE_SRC_STARTUP: - /* Reset cable attributes and flags */ - reset_pd_cable(port); - /* Wait for power source to enable */ - if (pd[port].last_state != pd[port].task_state) { - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; - /* reset various counters */ - caps_count = 0; - pd[port].msg_id = 0; - snk_cap_count = 0; - set_state_timeout( - port, -#ifdef CONFIG_USBC_BACKWARDS_COMPATIBLE_DFP - /* - * delay for power supply to start up. - * subtract out debounce time if coming - * from debounce state since vbus is - * on during debounce. - */ - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY - - (pd[port].last_state == - PD_STATE_SRC_DISCONNECTED_DEBOUNCE - ? PD_T_CC_DEBOUNCE : 0), -#else - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, -#endif - PD_STATE_SRC_DISCOVERY); - } - break; - case PD_STATE_SRC_DISCOVERY: - now = get_time(); - if (pd[port].last_state != pd[port].task_state) { - caps_count = 0; - next_src_cap = now.val; - /* - * If we have had PD connection with this port - * partner, then start NoResponseTimer. - */ - if (pd_capable(port)) - set_state_timeout(port, - get_time().val + - PD_T_NO_RESPONSE, - hard_reset_count < - PD_HARD_RESET_COUNT ? - PD_STATE_HARD_RESET_SEND : - PD_STATE_SRC_DISCONNECTED); - } - - /* Send source cap some minimum number of times */ - if (caps_count < PD_CAPS_COUNT && - next_src_cap <= now.val) { - /* Query capabilities of the other side */ - res = send_source_cap(port, AMS_START); - /* packet was acked => PD capable device) */ - if (res >= 0) { - set_state(port, - PD_STATE_SRC_NEGOCIATE); - timeout = 10*MSEC; - hard_reset_count = 0; - caps_count = 0; - /* Port partner is PD capable */ - pd[port].flags |= - PD_FLAGS_PREVIOUS_PD_CONN; - } else { /* failed, retry later */ - timeout = PD_T_SEND_SOURCE_CAP; - next_src_cap = now.val + - PD_T_SEND_SOURCE_CAP; - caps_count++; - } - } else if (caps_count < PD_CAPS_COUNT) { - timeout = next_src_cap - now.val; - } - break; - case PD_STATE_SRC_NEGOCIATE: - /* wait for a "Request" message */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SRC_ACCEPTED: - /* Accept sent, wait for enabling the new voltage */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout( - port, - get_time().val + - PD_T_SINK_TRANSITION, - PD_STATE_SRC_POWERED); - break; - case PD_STATE_SRC_POWERED: - /* Switch to the new requested voltage */ - if (pd[port].last_state != pd[port].task_state) { - pd[port].flags |= PD_FLAGS_CHECK_VCONN_STATE; - pd_transition_voltage(pd[port].requested_idx); - set_state_timeout( - port, - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_STATE_SRC_TRANSITION); - } - break; - case PD_STATE_SRC_TRANSITION: - /* the voltage output is good, notify the source */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res >= 0) { - timeout = 10*MSEC; - - /* - * Give the sink some time to send any messages - * before we may send messages of our own. Add - * some jitter of up to ~192ms, to prevent - * multiple collisions. This delay also allows - * the sink device to request power role swap - * and allow the the accept message to be sent - * prior to CMD_DISCOVER_IDENT being sent in the - * SRC_READY state. - */ - pd[port].ready_state_holdoff_timer = - get_time().val + SRC_READY_HOLD_OFF_US - + (get_time().le.lo & 0xf) * 12 * MSEC; - - /* it's time to ping regularly the sink */ - set_state(port, PD_STATE_SRC_READY); - } else { - /* The sink did not ack, cut the power... */ - set_state(port, PD_STATE_SRC_DISCONNECTED); - } - break; - case PD_STATE_SRC_READY: - timeout = PD_T_SOURCE_ACTIVITY; - - /* - * Don't send any traffic yet until our holdoff timer - * has expired. Some devices are chatty once we reach - * the SRC_READY state and we may end up in a collision - * of messages if we try to immediately send our - * interrogations. - */ - if (get_time().val <= - pd[port].ready_state_holdoff_timer) - break; - - /* - * Don't send any PD traffic if we woke up due to - * incoming packet or if VDO response pending to avoid - * collisions. - */ - if (incoming_packet || - (pd[port].vdm_state == VDM_STATE_BUSY)) - break; - - /* Send updated source capabilities to our partner */ - if (pd[port].flags & PD_FLAGS_UPDATE_SRC_CAPS) { - res = send_source_cap(port, AMS_START); - if (res >= 0) { - set_state(port, - PD_STATE_SRC_NEGOCIATE); - pd[port].flags &= - ~PD_FLAGS_UPDATE_SRC_CAPS; - } - break; - } - - /* Send get sink cap if haven't received it yet */ - if (!(pd[port].flags & PD_FLAGS_SNK_CAP_RECVD)) { - if (++snk_cap_count <= PD_SNK_CAP_RETRIES) { - /* Get sink cap to know if dual-role device */ - send_control(port, PD_CTRL_GET_SINK_CAP); - set_state(port, PD_STATE_SRC_GET_SINK_CAP); - break; - } else if (debug_level >= 2 && - snk_cap_count == PD_SNK_CAP_RETRIES+1) { - CPRINTF("C%d ERR SNK_CAP\n", port); - } - } - - /* Check power role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_PR_ROLE) { - pd_check_pr_role(port, PD_ROLE_SOURCE, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - } - - - /* Check data role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_DR_ROLE) { - pd_check_dr_role(port, pd[port].data_role, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - break; - } - - /* Check for Vconn source, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_VCONN_STATE) { - /* - * Ref: Section 2.6.1 of both - * USB-PD Spec Revision 2.0, Version 1.3 & - * USB-PD Spec Revision 3.0, Version 2.0 - * During Explicit contract the Sink can - * initiate or receive a request an exchange - * of VCONN Source. - */ - pd_try_execute_vconn_swap(port, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_VCONN_STATE; - break; - } - - /* Send discovery SVDMs last */ - if (pd[port].data_role == PD_ROLE_DFP && - (pd[port].flags & PD_FLAGS_CHECK_IDENTITY)) { -#ifndef CONFIG_USB_PD_SIMPLE_DFP - pd_send_vdm(port, USB_SID_PD, - CMD_DISCOVER_IDENT, NULL, 0); -#endif - pd[port].flags &= ~PD_FLAGS_CHECK_IDENTITY; - break; - } - - /* - * Enter_USB if port partner and cable are - * USB4 compatible. - */ - if (should_enter_usb4_mode(port)) { - pd_send_enter_usb(port, &timeout); - break; - } - - if (!(pd[port].flags & PD_FLAGS_PING_ENABLED)) - break; - - /* Verify that the sink is alive */ - res = send_control(port, PD_CTRL_PING); - if (res >= 0) - break; - - /* Ping dropped. Try soft reset. */ - set_state(port, PD_STATE_SOFT_RESET); - timeout = 10 * MSEC; - break; - case PD_STATE_SRC_GET_SINK_CAP: - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SRC_READY); - break; - case PD_STATE_DR_SWAP: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_DR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - READY_RETURN_STATE(port)); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - READY_RETURN_STATE(port)); - } - break; -#ifdef CONFIG_USB_PD_DUAL_ROLE - case PD_STATE_SRC_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_PR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - PD_STATE_SRC_READY); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SRC_READY); - } - break; - case PD_STATE_SRC_SWAP_SNK_DISABLE: - /* Give time for sink to stop drawing current */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_SINK_TRANSITION, - PD_STATE_SRC_SWAP_SRC_DISABLE); - break; - case PD_STATE_SRC_SWAP_SRC_DISABLE: - if (pd[port].last_state != pd[port].task_state) { - /* Turn power off */ - pd_power_supply_reset(port); - - /* - * Switch to Rd and swap roles to sink - * - * The reason we do this as early as possible is - * to help prevent CC disconnection cases where - * both partners are applying an Rp. Certain PD - * stacks (e.g. qualcomm), reflexively apply - * their Rp once VBUS falls beneath - * ~3.67V. (b/77827528). - */ - tcpm_set_cc(port, TYPEC_CC_RD); - pd_set_power_role(port, PD_ROLE_SINK); - - /* Inform TCPC of power role update. */ - pd_update_roles(port); - - set_state_timeout(port, - get_time().val + - PD_POWER_SUPPLY_TURN_OFF_DELAY, - PD_STATE_SRC_SWAP_STANDBY); - } - break; - case PD_STATE_SRC_SWAP_STANDBY: - /* Send PS_RDY to let sink know our power is off */ - if (pd[port].last_state != pd[port].task_state) { - /* Send PS_RDY */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res < 0) { - timeout = 10*MSEC; - set_state(port, - PD_STATE_SRC_DISCONNECTED); - break; - } - /* Wait for PS_RDY from new source */ - set_state_timeout(port, - get_time().val + - PD_T_PS_SOURCE_ON, - PD_STATE_SNK_DISCONNECTED); - } - break; - case PD_STATE_SUSPENDED: { -#ifndef CONFIG_USB_PD_TCPC - int rstatus; -#endif - tcpc_prints("suspended!", port); - pd[port].req_suspend_state = 0; -#ifdef CONFIG_USB_PD_TCPC - pd_rx_disable_monitoring(port); - pd_hw_release(port); - pd_power_supply_reset(port); -#else - pd_power_supply_reset(port); -#ifdef CONFIG_USBC_VCONN - set_vconn(port, 0); -#endif - rstatus = tcpm_release(port); - if (rstatus != 0 && rstatus != EC_ERROR_UNIMPLEMENTED) - tcpc_prints("release failed!", port); -#endif - /* Drain any outstanding software message queues. */ - tcpm_clear_pending_messages(port); - - /* Wait for resume */ - while (pd[port].task_state == PD_STATE_SUSPENDED) { -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - int evt = task_wait_event(-1); - - if (evt & PD_EVENT_SYSJUMP) - /* Nothing to do for sysjump prep */ - notify_sysjump_ready(); -#else - task_wait_event(-1); -#endif - } -#ifdef CONFIG_USB_PD_TCPC - pd_hw_init(port, PD_ROLE_DEFAULT(port)); - tcpc_prints("resumed!", port); -#else - if (rstatus != EC_ERROR_UNIMPLEMENTED && - pd_restart_tcpc(port) != 0) { - /* stay in PD_STATE_SUSPENDED */ - tcpc_prints("restart failed!", port); - break; - } - /* Set the CC termination and state back to default */ - tcpm_set_cc(port, - PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE ? - TYPEC_CC_RP : - TYPEC_CC_RD); - set_state(port, PD_DEFAULT_STATE(port)); - tcpc_prints("resumed!", port); -#endif - break; - } - case PD_STATE_SNK_DISCONNECTED: -#ifdef CONFIG_USB_PD_LOW_POWER - timeout = (drp_state[port] != - PD_DRP_TOGGLE_ON ? SECOND : 10*MSEC); -#else - timeout = 10*MSEC; -#endif - pd_set_src_caps(port, 0, NULL); -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) - break; -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - - tcpm_get_cc(port, &cc1, &cc2); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is not already - * auto toggling and not try.src, and dual role toggling - * is allowed. - */ - if (auto_toggle_supported && - !(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && - !is_try_src(port) && - cc_is_open(cc1, cc2) && - (drp_state[port] == PD_DRP_TOGGLE_ON)) { - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - timeout = 2*MSEC; - break; - } -#endif - - /* Source connection monitoring */ - if (!cc_is_open(cc1, cc2)) { - pd[port].cc_state = PD_CC_NONE; - hard_reset_count = 0; - new_cc_state = PD_CC_NONE; - pd[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - set_state(port, - PD_STATE_SNK_DISCONNECTED_DEBOUNCE); - timeout = 10*MSEC; - break; - } - - /* - * If Try.SRC is active and failed to detect a SNK, - * then it transitions to TryWait.SNK. Need to prevent - * normal dual role toggle until tDRPTryWait timer - * expires. - */ - if (pd[port].flags & PD_FLAGS_TRY_SRC) { - if (get_time().val > pd[port].try_src_marker) - pd[port].flags &= ~PD_FLAGS_TRY_SRC; - break; - } - - /* If no source detected, check for role toggle. */ - if (drp_state[port] == PD_DRP_TOGGLE_ON && - get_time().val >= next_role_swap) { - /* Swap roles to source */ - pd_set_power_role(port, PD_ROLE_SOURCE); - set_state(port, PD_STATE_SRC_DISCONNECTED); - tcpm_set_cc(port, TYPEC_CC_RP); - next_role_swap = get_time().val + PD_T_DRP_SRC; - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * Clear low power mode flag as we are swapping - * states quickly. - */ - pd[port].flags &= ~PD_FLAGS_LPM_REQUESTED; -#endif - - /* Swap states quickly */ - timeout = 2*MSEC; - break; - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If we are remaining in the SNK_DISCONNECTED state, - * let's go into low power mode and wait for a change on - * CC status. - */ - pd[port].flags |= PD_FLAGS_LPM_REQUESTED; -#endif/* CONFIG_USB_PD_TCPC_LOW_POWER */ - break; - - case PD_STATE_SNK_DISCONNECTED_DEBOUNCE: - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_rp(cc1) && cc_is_rp(cc2)) { - /* Debug accessory */ - new_cc_state = PD_CC_DFP_DEBUG_ACC; - } else if (cc_is_rp(cc1) || cc_is_rp(cc2)) { - new_cc_state = PD_CC_DFP_ATTACHED; - } else { - /* No connection any more */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - timeout = 5*MSEC; - break; - } - - timeout = 20*MSEC; - - /* Debounce the cc state */ - if (new_cc_state != pd[port].cc_state) { - pd[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - pd[port].cc_state = new_cc_state; - break; - } - /* Wait for CC debounce and VBUS present */ - if (get_time().val < pd[port].cc_debounce || - !pd_is_vbus_present(port)) - break; - - if (pd_try_src_enable && - !(pd[port].flags & PD_FLAGS_TRY_SRC)) { - /* - * If TRY_SRC is enabled, but not active, - * then force attempt to connect as source. - */ - pd[port].try_src_marker = get_time().val - + PD_T_DRP_TRY; - pd[port].try_timeout = get_time().val - + PD_T_TRY_TIMEOUT; - /* Swap roles to source */ - pd_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_cc(port, TYPEC_CC_RP); - timeout = 2*MSEC; - set_state(port, PD_STATE_SRC_DISCONNECTED); - /* Set flag after the state change */ - pd[port].flags |= PD_FLAGS_TRY_SRC; - break; - } - - /* We are attached */ - if (IS_ENABLED(CONFIG_COMMON_RUNTIME)) - hook_notify(HOOK_USB_PD_CONNECT); - pd[port].polarity = get_snk_polarity(cc1, cc2); - pd_set_polarity(port, pd[port].polarity); - /* reset message ID on connection */ - pd[port].msg_id = 0; - /* initial data role for sink is UFP */ - pd_set_data_role(port, PD_ROLE_UFP); - /* Enable Auto Discharge Disconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); -#if defined(CONFIG_CHARGE_MANAGER) - typec_curr = usb_get_typec_current_limit( - pd[port].polarity, cc1, cc2); - typec_set_input_current_limit( - port, typec_curr, TYPE_C_VOLTAGE); -#endif - -#ifdef CONFIG_USBC_PPC - /* Inform PPC that a source is connected. */ - ppc_dev_is_connected(port, PPC_DEV_SRC); -#endif /* CONFIG_USBC_PPC */ - if (IS_ENABLED(CONFIG_USBC_OCP)) - usbc_ocp_snk_is_connected(port, false); - - /* If PD comm is enabled, enable TCPC RX */ - if (pd_comm_is_enabled(port)) - tcpm_set_rx_enable(port, 1); - - /* DFP is attached */ - if (new_cc_state == PD_CC_DFP_ATTACHED || - new_cc_state == PD_CC_DFP_DEBUG_ACC) { - pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE | - PD_FLAGS_CHECK_IDENTITY; - /* Reset cable attributes and flags */ - reset_pd_cable(port); - - if (new_cc_state == PD_CC_DFP_DEBUG_ACC) - pd[port].flags |= - PD_FLAGS_TS_DTS_PARTNER; - set_state(port, PD_STATE_SNK_DISCOVERY); - timeout = 10*MSEC; - hook_call_deferred( - &pd_usb_billboard_deferred_data, - PD_T_AME); - } - break; - case PD_STATE_SNK_HARD_RESET_RECOVER: - if (pd[port].last_state != pd[port].task_state) - pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; - - if (get_usb_pd_vbus_detect() == - USB_PD_VBUS_DETECT_NONE) { - /* - * Can't measure vbus state so this is the - * maximum recovery time for the source. - */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, get_time().val + - PD_T_SAFE_0V + - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON, - PD_STATE_SNK_DISCONNECTED); - } else { -#ifndef CONFIG_USB_PD_VBUS_DETECT_NONE - /* Wait for VBUS to go low and then high*/ - if (pd[port].last_state != - pd[port].task_state) { - snk_hard_reset_vbus_off = 0; - set_state_timeout(port, - get_time().val + - PD_T_SAFE_0V, - hard_reset_count < - PD_HARD_RESET_COUNT ? - PD_STATE_HARD_RESET_SEND : - PD_STATE_SNK_DISCOVERY); - } - - if (!pd_is_vbus_present(port) && - !snk_hard_reset_vbus_off) { - /* VBUS has gone low, reset timeout */ - snk_hard_reset_vbus_off = 1; - set_state_timeout(port, - get_time().val + - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON, - PD_STATE_SNK_DISCONNECTED); - } - if (pd_is_vbus_present(port) && - snk_hard_reset_vbus_off) { - /* VBUS went high again */ - set_state(port, PD_STATE_SNK_DISCOVERY); - timeout = 10*MSEC; - } - - /* - * Don't need to set timeout because VBUS - * changing will trigger an interrupt and - * wake us up. - */ -#endif - } - break; - case PD_STATE_SNK_DISCOVERY: - /* Wait for source cap expired only if we are enabled */ - if ((pd[port].last_state != pd[port].task_state) - && pd_comm_is_enabled(port)) { -#if defined(CONFIG_USB_PD_TCPM_TCPCI) || defined(CONFIG_USB_PD_TCPM_STUB) - /* - * If we come from hard reset recover state, - * then we can process the source capabilities - * form partner now, so enable PHY layer - * receiving function. - */ - if (pd[port].last_state == - PD_STATE_SNK_HARD_RESET_RECOVER) - tcpm_set_rx_enable(port, 1); -#endif /* CONFIG_USB_PD_TCPM_TCPCI || CONFIG_USB_PD_TCPM_STUB */ -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - /* - * If the battery has not met a configured safe - * level for hard resets, refrain from starting - * reset timers as a hard reset could brown out - * the board. Note this may mean that - * high-power chargers will stay at 15W until a - * reset is sent, depending on boot timing. - */ - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != - BATTERY_NOT_DISCONNECTED) - pd[port].flags |= - PD_FLAGS_SNK_WAITING_BATT; - else - pd[port].flags &= - ~PD_FLAGS_SNK_WAITING_BATT; -#endif - - if (pd[port].flags & - PD_FLAGS_SNK_WAITING_BATT) { -#ifdef CONFIG_CHARGE_MANAGER - /* - * Configure this port as dedicated for - * now, so it won't be de-selected by - * the charge manager leaving safe mode. - */ - charge_manager_update_dualrole(port, - CAP_DEDICATED); -#endif - CPRINTS("C%d: Battery low. " - "Hold reset timer", port); - /* - * If VBUS has never been low, and we timeout - * waiting for source cap, try a soft reset - * first, in case we were already in a stable - * contract before this boot. - */ - } else if (pd[port].flags & - PD_FLAGS_VBUS_NEVER_LOW) { - set_state_timeout(port, - get_time().val + - PD_T_SINK_WAIT_CAP, - PD_STATE_SOFT_RESET); - /* - * If we haven't passed hard reset counter, - * start SinkWaitCapTimer, otherwise start - * NoResponseTimer. - */ - } else if (hard_reset_count < - PD_HARD_RESET_COUNT) { - set_state_timeout(port, - get_time().val + - PD_T_SINK_WAIT_CAP, - PD_STATE_HARD_RESET_SEND); - } else if (pd_capable(port)) { - /* ErrorRecovery */ - set_state_timeout(port, - get_time().val + - PD_T_NO_RESPONSE, - PD_STATE_SNK_DISCONNECTED); - } -#if defined(CONFIG_CHARGE_MANAGER) - /* - * If we didn't come from disconnected, must - * have come from some path that did not set - * typec current limit. So, set to 0 so that - * we guarantee this is revised below. - */ - if (pd[port].last_state != - PD_STATE_SNK_DISCONNECTED_DEBOUNCE) - typec_curr = 0; -#endif - } - -#if defined(CONFIG_CHARGE_MANAGER) - timeout = PD_T_SINK_ADJ - PD_T_DEBOUNCE; - - /* Check if CC pull-up has changed */ - tcpm_get_cc(port, &cc1, &cc2); - if (typec_curr != usb_get_typec_current_limit( - pd[port].polarity, cc1, cc2)) { - /* debounce signal by requiring two reads */ - if (typec_curr_change) { - /* set new input current limit */ - typec_curr = - usb_get_typec_current_limit( - pd[port].polarity, - cc1, cc2); - typec_set_input_current_limit( - port, typec_curr, TYPE_C_VOLTAGE); - } else { - /* delay for debounce */ - timeout = PD_T_DEBOUNCE; - } - typec_curr_change = !typec_curr_change; - } else { - typec_curr_change = 0; - } -#endif - break; - case PD_STATE_SNK_REQUESTED: - /* Wait for ACCEPT or REJECT */ - if (pd[port].last_state != pd[port].task_state) { - pd[port].flags |= PD_FLAGS_CHECK_VCONN_STATE; - hard_reset_count = 0; - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - } - break; - case PD_STATE_SNK_TRANSITION: - /* Wait for PS_RDY */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_PS_TRANSITION, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SNK_READY: - timeout = 20*MSEC; - - /* - * Don't send any traffic yet until our holdoff timer - * has expired. Some devices are chatty once we reach - * the SNK_READY state and we may end up in a collision - * of messages if we try to immediately send our - * interrogations. - */ - if (get_time().val <= - pd[port].ready_state_holdoff_timer) - break; - - /* - * Don't send any PD traffic if we woke up due to - * incoming packet or if VDO response pending to avoid - * collisions. - */ - if (incoming_packet || - (pd[port].vdm_state == VDM_STATE_BUSY)) - break; - - /* Check for new power to request */ - if (pd[port].new_power_request) { - if (pd_send_request_msg(port, 0) != EC_SUCCESS) - set_state(port, PD_STATE_SOFT_RESET); - break; - } - - /* Check power role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_PR_ROLE) { - pd_check_pr_role(port, PD_ROLE_SINK, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_PR_ROLE; - break; - } - - /* Check data role policy, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_DR_ROLE) { - pd_check_dr_role(port, pd[port].data_role, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_DR_ROLE; - break; - } - - /* Check for Vconn source, which may trigger a swap */ - if (pd[port].flags & PD_FLAGS_CHECK_VCONN_STATE) { - /* - * Ref: Section 2.6.2 of both - * USB-PD Spec Revision 2.0, Version 1.3 & - * USB-PD Spec Revision 3.0, Version 2.0 - * During Explicit contract the Sink can - * initiate or receive a request an exchange - * of VCONN Source. - */ - pd_try_execute_vconn_swap(port, - pd[port].flags); - pd[port].flags &= ~PD_FLAGS_CHECK_VCONN_STATE; - break; - } - - /* If DFP, send discovery SVDMs */ - if (pd[port].data_role == PD_ROLE_DFP && - (pd[port].flags & PD_FLAGS_CHECK_IDENTITY)) { - pd_send_vdm(port, USB_SID_PD, - CMD_DISCOVER_IDENT, NULL, 0); - pd[port].flags &= ~PD_FLAGS_CHECK_IDENTITY; - break; - } - - /* - * Enter_USB if port partner and cable are - * USB4 compatible. - */ - if (should_enter_usb4_mode(port)) { - pd_send_enter_usb(port, &timeout); - break; - } - - /* Sent all messages, don't need to wake very often */ - timeout = 200*MSEC; - break; - case PD_STATE_SNK_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_PR_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - PD_STATE_SNK_READY); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - PD_STATE_SNK_READY); - } - break; - case PD_STATE_SNK_SWAP_SNK_DISABLE: - /* Stop drawing power */ - pd_set_input_current_limit(port, 0, 0); -#ifdef CONFIG_CHARGE_MANAGER - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -#endif - set_state(port, PD_STATE_SNK_SWAP_SRC_DISABLE); - timeout = 10*MSEC; - break; - case PD_STATE_SNK_SWAP_SRC_DISABLE: - /* Wait for PS_RDY */ - if (pd[port].last_state != pd[port].task_state) - set_state_timeout(port, - get_time().val + - PD_T_PS_SOURCE_OFF, - PD_STATE_HARD_RESET_SEND); - break; - case PD_STATE_SNK_SWAP_STANDBY: - if (pd[port].last_state != pd[port].task_state) { - /* Switch to Rp and enable power supply. */ - tcpm_set_cc(port, TYPEC_CC_RP); - if (pd_set_power_supply_ready(port)) { - /* Restore Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - timeout = 10*MSEC; - set_state(port, - PD_STATE_SNK_DISCONNECTED); - break; - } - /* Wait for power supply to turn on */ - set_state_timeout( - port, - get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_STATE_SNK_SWAP_COMPLETE); - } - break; - case PD_STATE_SNK_SWAP_COMPLETE: - /* Send PS_RDY and change to source role */ - res = send_control(port, PD_CTRL_PS_RDY); - if (res < 0) { - /* Restore Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - pd_power_supply_reset(port); - timeout = 10 * MSEC; - set_state(port, PD_STATE_SNK_DISCONNECTED); - break; - } - - /* Don't send GET_SINK_CAP on swap */ - snk_cap_count = PD_SNK_CAP_RETRIES+1; - caps_count = 0; - pd[port].msg_id = 0; - pd_set_power_role(port, PD_ROLE_SOURCE); - pd_update_roles(port); - set_state(port, PD_STATE_SRC_DISCOVERY); - timeout = 10*MSEC; - break; -#ifdef CONFIG_USBC_VCONN_SWAP - case PD_STATE_VCONN_SWAP_SEND: - if (pd[port].last_state != pd[port].task_state) { - res = send_control(port, PD_CTRL_VCONN_SWAP); - if (res < 0) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, send - * soft reset, otherwise ignore - * failure. - */ - set_state(port, res == -1 ? - PD_STATE_SOFT_RESET : - READY_RETURN_STATE(port)); - break; - } - /* Wait for accept or reject */ - set_state_timeout(port, - get_time().val + - PD_T_SENDER_RESPONSE, - READY_RETURN_STATE(port)); - } - break; - case PD_STATE_VCONN_SWAP_INIT: - if (pd[port].last_state != pd[port].task_state) { - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - /* Turn VCONN on and wait for it */ - set_vconn(port, 1); - set_state_timeout(port, - get_time().val + - CONFIG_USBC_VCONN_SWAP_DELAY_US, - PD_STATE_VCONN_SWAP_READY); - } else { - set_state_timeout(port, - get_time().val + - PD_T_VCONN_SOURCE_ON, - READY_RETURN_STATE(port)); - } - } - break; - case PD_STATE_VCONN_SWAP_READY: - if (pd[port].last_state != pd[port].task_state) { - if (!(pd[port].flags & PD_FLAGS_VCONN_ON)) { - /* VCONN is now on, send PS_RDY */ - pd_set_vconn_role(port, - PD_ROLE_VCONN_ON); - res = send_control(port, - PD_CTRL_PS_RDY); - if (res == -1) { - timeout = 10*MSEC; - /* - * If failed to get goodCRC, - * send soft reset - */ - set_state(port, - PD_STATE_SOFT_RESET); - break; - } - set_state(port, - READY_RETURN_STATE(port)); - } else { - /* Turn VCONN off and wait for it */ - set_vconn(port, 0); - pd_set_vconn_role(port, - PD_ROLE_VCONN_OFF); - set_state_timeout(port, - get_time().val + - CONFIG_USBC_VCONN_SWAP_DELAY_US, - READY_RETURN_STATE(port)); - } - } - break; -#endif /* CONFIG_USBC_VCONN_SWAP */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - case PD_STATE_SOFT_RESET: - if (pd[port].last_state != pd[port].task_state) { - /* Message ID of soft reset is always 0 */ - invalidate_last_message_id(port); - pd[port].msg_id = 0; - res = send_control(port, PD_CTRL_SOFT_RESET); - - /* if soft reset failed, try hard reset. */ - if (res < 0) { - set_state(port, - PD_STATE_HARD_RESET_SEND); - timeout = 5*MSEC; - break; - } - - set_state_timeout( - port, - get_time().val + PD_T_SENDER_RESPONSE, - PD_STATE_HARD_RESET_SEND); - } - break; - case PD_STATE_HARD_RESET_SEND: - hard_reset_count++; - if (pd[port].last_state != pd[port].task_state) { - hard_reset_sent = 0; - pd[port].hard_reset_complete_timer = 0; - } -#ifdef CONFIG_CHARGE_MANAGER - if (pd[port].last_state == PD_STATE_SNK_DISCOVERY || - (pd[port].last_state == PD_STATE_SOFT_RESET && - (pd[port].flags & PD_FLAGS_VBUS_NEVER_LOW))) { - pd[port].flags &= ~PD_FLAGS_VBUS_NEVER_LOW; - /* - * If discovery timed out, assume that we - * have a dedicated charger attached. This - * may not be a correct assumption according - * to the specification, but it generally - * works in practice and the harmful - * effects of a wrong assumption here - * are minimal. - */ - charge_manager_update_dualrole(port, - CAP_DEDICATED); - } -#endif - - if (hard_reset_sent) - break; - - if (pd_transmit(port, TCPCI_MSG_TX_HARD_RESET, 0, NULL, - AMS_START) < 0) { - /* - * likely a non-idle channel - * TCPCI r2.0 v1.0 4.4.15: - * the TCPC does not retry HARD_RESET - * but we can try periodically until the timer - * expires. - */ - now = get_time(); - if (pd[port].hard_reset_complete_timer == 0) { - pd[port].hard_reset_complete_timer = - now.val + - PD_T_HARD_RESET_COMPLETE; - timeout = PD_T_HARD_RESET_RETRY; - break; - } - if (now.val < - pd[port].hard_reset_complete_timer) { - CPRINTS("C%d: Retrying hard reset", - port); - timeout = PD_T_HARD_RESET_RETRY; - break; - } - /* - * PD 2.0 spec, section 6.5.11.1 - * Pretend TX_HARD_RESET succeeded after - * timeout. - */ - } - - hard_reset_sent = 1; - /* - * If we are source, delay before cutting power - * to allow sink time to get hard reset. - */ - if (pd[port].power_role == PD_ROLE_SOURCE) { - set_state_timeout(port, - get_time().val + PD_T_PS_HARD_RESET, - PD_STATE_HARD_RESET_EXECUTE); - } else { - set_state(port, PD_STATE_HARD_RESET_EXECUTE); - timeout = 10 * MSEC; - } - break; - case PD_STATE_HARD_RESET_EXECUTE: -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If hard reset while in the last stages of power - * swap, then we need to restore our CC resistor. - */ - if (pd[port].last_state == PD_STATE_SNK_SWAP_STANDBY) - tcpm_set_cc(port, TYPEC_CC_RD); -#endif - - /* reset our own state machine */ - pd_execute_hard_reset(port); - timeout = 10*MSEC; - break; -#ifdef CONFIG_COMMON_RUNTIME - case PD_STATE_BIST_RX: - send_bist_cmd(port); - /* Delay at least enough for partner to finish BIST */ - timeout = PD_T_BIST_RECEIVE + 20*MSEC; - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - break; - case PD_STATE_BIST_TX: - pd_transmit(port, TCPCI_MSG_TX_BIST_MODE_2, 0, NULL, - AMS_START); - /* Delay at least enough to finish sending BIST */ - timeout = PD_T_BIST_TRANSMIT + 20*MSEC; - /* Set to appropriate port disconnected state */ - set_state(port, DUAL_ROLE_IF_ELSE(port, - PD_STATE_SNK_DISCONNECTED, - PD_STATE_SRC_DISCONNECTED)); - break; -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - case PD_STATE_DRP_AUTO_TOGGLE: - { - enum pd_drp_next_states next_state; - - assert(auto_toggle_supported); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(evt & PD_EVENT_CC)) - break; - - /* - * Debounce low power mode exit. Some TCPCs need time - * for the CC_STATUS register to be stable after exiting - * low power mode. - */ - if (pd[port].flags & PD_FLAGS_LPM_EXIT) { - uint64_t now; - - now = get_time().val; - if (now < pd[port].low_power_exit_time) - break; - - CPRINTS("TCPC p%d Exit Low Power Mode done", - port); - pd[port].flags &= ~PD_FLAGS_LPM_EXIT; - } -#endif - - /* - * Check for connection - * - * Send FALSE for supports_auto_toggle to not change - * the current return value of UNATTACHED instead of - * the auto-toggle ATTACHED_WAIT response for TCPMv1. - */ - tcpm_get_cc(port, &cc1, &cc2); - - next_state = drp_auto_toggle_next_state( - &pd[port].drp_sink_time, - pd[port].power_role, - drp_state[port], - cc1, cc2, false); - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* - * The next state is not determined just by what is - * attached, but also depends on DRP_STATE. Regardless - * of next state, if nothing is attached, then always - * request low power mode. - */ - if (cc_is_open(cc1, cc2)) - pd[port].flags |= PD_FLAGS_LPM_REQUESTED; -#endif - if (next_state == DRP_TC_DEFAULT) { - if (PD_DEFAULT_STATE(port) == - PD_STATE_SNK_DISCONNECTED) - next_state = DRP_TC_UNATTACHED_SNK; - else - next_state = DRP_TC_UNATTACHED_SRC; - } - - if (next_state == DRP_TC_UNATTACHED_SNK) { - /* - * The TCPCI comes out of auto toggle with - * a prospective connection. It is expecting - * us to set the CC lines to what it is - * thinking is best or it goes direct back to - * unattached. So get the SNK polarity to - * be able to setup the CC lines to avoid this. - */ - pd[port].polarity = get_snk_polarity(cc1, cc2); - - tcpm_set_cc(port, TYPEC_CC_RD); - pd_set_power_role(port, PD_ROLE_SINK); - timeout = 2*MSEC; - set_state(port, PD_STATE_SNK_DISCONNECTED); - } else if (next_state == DRP_TC_UNATTACHED_SRC) { - /* - * The TCPCI comes out of auto toggle with - * a prospective connection. It is expecting - * us to set the CC lines to what it is - * thinking is best or it goes direct back to - * unattached. So get the SNK polarity to - * be able to setup the CC lines to avoid this. - */ - pd[port].polarity = get_src_polarity(cc1, cc2); - - tcpm_set_cc(port, TYPEC_CC_RP); - pd_set_power_role(port, PD_ROLE_SOURCE); - timeout = 2*MSEC; - set_state(port, PD_STATE_SRC_DISCONNECTED); - } else { - /* - * We are staying in PD_STATE_DRP_AUTO_TOGGLE, - * therefore enable auto-toggle. - */ - tcpm_enable_drp_toggle(port); - pd[port].flags |= PD_FLAGS_TCPC_DRP_TOGGLE; - set_state(port, PD_STATE_DRP_AUTO_TOGGLE); - } - - break; - } -#endif - case PD_STATE_ENTER_USB: - if (pd[port].last_state != pd[port].task_state) { - set_state_timeout(port, - get_time().val + PD_T_SENDER_RESPONSE, - READY_RETURN_STATE(port)); - } - break; - default: - break; - } - - pd[port].last_state = this_state; - - /* - * Check for state timeout, and if not check if need to adjust - * timeout value to wake up on the next state timeout. - */ - now = get_time(); - if (pd[port].timeout) { - if (now.val >= pd[port].timeout) { - set_state(port, pd[port].timeout_state); - /* On a state timeout, run next state soon */ - timeout = timeout < 10*MSEC ? timeout : 10*MSEC; - } else if (pd[port].timeout - now.val < timeout) { - timeout = pd[port].timeout - now.val; - } - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - /* Determine if we need to put the TCPC in low power mode */ - if (pd[port].flags & PD_FLAGS_LPM_REQUESTED && - !(pd[port].flags & PD_FLAGS_LPM_ENGAGED)) { - int64_t time_left; - - /* If any task prevents LPM, wait another debounce */ - if (pd[port].tasks_preventing_lpm) { - pd[port].low_power_time = - PD_LPM_DEBOUNCE_US + now.val; - } - - time_left = pd[port].low_power_time - now.val; - if (time_left <= 0) { - pd[port].flags |= PD_FLAGS_LPM_ENGAGED; - pd[port].flags |= PD_FLAGS_LPM_TRANSITION; - tcpm_enter_low_power_mode(port); - pd[port].flags &= ~PD_FLAGS_LPM_TRANSITION; - tcpc_prints("Enter Low Power Mode", port); - timeout = -1; - } else if (timeout < 0 || timeout > time_left) { - timeout = time_left; - } - } -#endif - - /* Check for disconnection if we're connected */ - if (!pd_is_connected(port)) - continue; -#ifdef CONFIG_USB_PD_DUAL_ROLE - if (pd_is_power_swapping(port)) - continue; -#endif - if (pd[port].power_role == PD_ROLE_SOURCE) { - /* Source: detect disconnect by monitoring CC */ - tcpm_get_cc(port, &cc1, &cc2); - if (polarity_rm_dts(pd[port].polarity)) - cc1 = cc2; - if (cc1 == TYPEC_CC_VOLT_OPEN) { - set_state(port, PD_STATE_SRC_DISCONNECTED); - /* Debouncing */ - timeout = 10*MSEC; -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * If Try.SRC is configured, then ATTACHED_SRC - * needs to transition to TryWait.SNK. Change - * power role to SNK and start state timer. - */ - if (pd_try_src_enable) { - /* Swap roles to sink */ - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - /* Set timer for TryWait.SNK state */ - pd[port].try_src_marker = get_time().val - + PD_T_DEBOUNCE; - /* Advance to TryWait.SNK state */ - set_state(port, - PD_STATE_SNK_DISCONNECTED); - /* Mark state as TryWait.SNK */ - pd[port].flags |= PD_FLAGS_TRY_SRC; - } -#endif - } - } -#ifdef CONFIG_USB_PD_DUAL_ROLE - /* - * Sink disconnect if VBUS is low and - * 1) we are not waiting for VBUS to debounce after a power - * role swap. - * 2) we are not recovering from a hard reset. - */ - if (pd[port].power_role == PD_ROLE_SINK && - pd[port].vbus_debounce_time < get_time().val && - !pd_is_vbus_present(port) && - pd[port].task_state != PD_STATE_SNK_HARD_RESET_RECOVER && - pd[port].task_state != PD_STATE_HARD_RESET_EXECUTE) { - /* Sink: detect disconnect by monitoring VBUS */ - set_state(port, PD_STATE_SNK_DISCONNECTED); - /* set timeout small to reconnect fast */ - timeout = 5*MSEC; - } -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - } -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { -#ifdef CONFIG_CHARGE_MANAGER - if (charge_manager_get_active_charge_port() != i) -#endif - pd[i].flags |= PD_FLAGS_CHECK_PR_ROLE | - PD_FLAGS_CHECK_DR_ROLE; - pd_set_dual_role(i, PD_DRP_TOGGLE_ON); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd_set_dual_role(i, PD_DRP_TOGGLE_OFF); - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF); - pd[i].flags |= PD_FLAGS_CHECK_IDENTITY; - /* Reset cable attributes and flags */ - reset_pd_cable(i); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE); - } - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE); - } - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -#ifdef CONFIG_COMMON_RUNTIME - -static void pd_control_resume(int port) -{ - if (pd[port].task_state != PD_STATE_SUSPENDED) - return; - - set_state(port, PD_DEFAULT_STATE(port)); - /* - * Since we did not service interrupts while we were suspended, - * see if there is a waiting interrupt to be serviced. If the - * interrupt line isn't asserted, we won't communicate with the - * TCPC. - */ - if (IS_ENABLED(HAS_TASK_PD_INT_C0)) - schedule_deferred_pd_interrupt(port); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* - * (suspend=1) request pd_task transition to the suspended state. hang - * around for a while until we observe the state change. this can - * take a while (like 300ms) on startup when pd_task is sleeping in - * tcpci_tcpm_init. - * - * (suspend=0) force pd_task out of the suspended state and into the - * port's default state. - */ - -void pd_set_suspend(int port, int suspend) -{ - int tries = 300; - - if (suspend) { - pd[port].req_suspend_state = 1; - do { - task_wake(PD_PORT_TO_TASK_ID(port)); - if (pd[port].task_state == PD_STATE_SUSPENDED) - break; - msleep(1); - } while (--tries != 0); - if (!tries) - tcpc_prints("set_suspend failed!", port); - } else { - pd_control_resume(port); - } -} - -int pd_is_port_enabled(int port) -{ - switch (pd[port].task_state) { - case PD_STATE_DISABLED: - case PD_STATE_SUSPENDED: - return 0; - default: - return 1; - } -} - -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) -void pd_send_hpd(int port, enum hpd_event hpd) -{ - uint32_t data[1]; - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - if (!opos) - return; - - data[0] = VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */ - (hpd != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 0, /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - pd_send_vdm(port, USB_SID_DISPLAYPORT, - VDO_OPOS(opos) | CMD_ATTENTION, data, 1); - /* Wait until VDM is done. */ - while (pd[0].vdm_state > 0) - task_wait_event(USB_PD_RX_TMOUT_US * - (CONFIG_PD_RETRY_COUNT + 1)); -} -#endif - -int pd_fetch_acc_log_entry(int port) -{ - timestamp_t timeout; - - /* Cannot send a VDM now, the host should retry */ - if (pd[port].vdm_state > 0) - return pd[port].vdm_state == VDM_STATE_BUSY ? - EC_RES_BUSY : EC_RES_UNAVAILABLE; - - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0); - timeout.val = get_time().val + 75*MSEC; - - /* Wait until VDM is done */ - while ((pd[port].vdm_state > 0) && - (get_time().val < timeout.val)) - task_wait_event(10*MSEC); - - if (pd[port].vdm_state > 0) - return EC_RES_TIMEOUT; - else if (pd[port].vdm_state < 0) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE -void pd_request_source_voltage(int port, int mv) -{ - pd_set_max_voltage(mv); - - if (pd[port].task_state == PD_STATE_SNK_READY || - pd[port].task_state == PD_STATE_SNK_TRANSITION) { - /* Set flag to send new power request in pd_task */ - pd[port].new_power_request = 1; - } else { - pd_set_power_role(port, PD_ROLE_SINK); - tcpm_set_cc(port, TYPEC_CC_RD); - set_state(port, PD_STATE_SNK_DISCONNECTED); - } - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_set_external_voltage_limit(int port, int mv) -{ - pd_set_max_voltage(mv); - - if (pd[port].task_state == PD_STATE_SNK_READY || - pd[port].task_state == PD_STATE_SNK_TRANSITION) { - /* Set flag to send new power request in pd_task */ - pd[port].new_power_request = 1; - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_update_contract(int port) -{ - if ((pd[port].task_state >= PD_STATE_SRC_NEGOCIATE) && - (pd[port].task_state <= PD_STATE_SRC_GET_SINK_CAP)) { - pd[port].flags |= PD_FLAGS_UPDATE_SRC_CAPS; - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - -static int command_pd(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - if (argc >= 3) { -#ifdef CONFIG_USB_PD_DEBUG_LEVEL - return EC_ERROR_PARAM2; -#else - int level = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - debug_level = level; -#endif - } - ccprintf("debug=%d\n", debug_level); - - return EC_SUCCESS; - } - -#ifdef CONFIG_CMD_PD -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - else if (!strncasecmp(argv[1], "rwhashtable", 3)) { - int i; - struct ec_params_usb_pd_rw_hash_entry *p; - for (i = 0; i < RW_HASH_ENTRIES; i++) { - p = &rw_hash_table[i]; - pd_dev_dump_info(p->dev_id, p->dev_rw_hash); - } - return EC_SUCCESS; - } -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ -#ifdef CONFIG_USB_PD_TRY_SRC - else if (!strncasecmp(argv[1], "trysrc", 6)) { - int enable; - - if (argc >= 3) { - enable = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM3; - pd_try_src_enable = enable ? 1 : 0; - } - - ccprintf("Try.SRC %s\n", pd_try_src_enable ? "on" : "off"); - return EC_SUCCESS; - } -#endif -#endif - else if (!strcasecmp(argv[1], "version")) { - ccprintf("%d\n", PD_STACK_VERSION); - return EC_SUCCESS; - } - - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; -#if defined(CONFIG_CMD_PD) && defined(CONFIG_USB_PD_DUAL_ROLE) - - if (!strcasecmp(argv[2], "tx")) { - set_state(port, PD_STATE_SNK_DISCOVERY); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "bist_rx")) { - set_state(port, PD_STATE_BIST_RX); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "bist_tx")) { - if (*e) - return EC_ERROR_PARAM3; - set_state(port, PD_STATE_BIST_TX); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strcasecmp(argv[2], "charger")) { - pd_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_cc(port, TYPEC_CC_RP); - set_state(port, PD_STATE_SRC_DISCONNECTED); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "dev", 3)) { - int max_volt; - if (argc >= 4) - max_volt = strtoi(argv[3], &e, 10) * 1000; - else - max_volt = pd_get_max_voltage(); - - pd_request_source_voltage(port, max_volt); - ccprintf("max req: %dmV\n", max_volt); - } else if (!strcasecmp(argv[2], "disable")) { - pd_comm_enable(port, 0); - ccprintf("Port C%d disable\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "enable")) { - pd_comm_enable(port, 1); - ccprintf("Port C%d enabled\n", port); - return EC_SUCCESS; - } else if (!strncasecmp(argv[2], "hard", 4)) { - set_state(port, PD_STATE_HARD_RESET_SEND); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "info", 4)) { - int i; - ccprintf("Hash "); - for (i = 0; i < PD_RW_HASH_SIZE / 4; i++) - ccprintf("%08x ", pd[port].dev_rw_hash[i]); - ccprintf("\nImage %s\n", - ec_image_to_string(pd[port].current_image)); - } else if (!strncasecmp(argv[2], "soft", 4)) { - set_state(port, PD_STATE_SOFT_RESET); - task_wake(PD_PORT_TO_TASK_ID(port)); - } else if (!strncasecmp(argv[2], "swap", 4)) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strncasecmp(argv[3], "power", 5)) - pd_request_power_swap(port); - else if (!strncasecmp(argv[3], "data", 4)) - pd_request_data_swap(port); -#ifdef CONFIG_USBC_VCONN_SWAP - else if (!strncasecmp(argv[3], "vconn", 5)) - pd_request_vconn_swap(port); -#endif - else - return EC_ERROR_PARAM3; - } else if (!strncasecmp(argv[2], "srccaps", 7)) { - pd_srccaps_dump(port); - } else if (!strncasecmp(argv[2], "ping", 4)) { - int enable; - - if (argc > 3) { - enable = strtoi(argv[3], &e, 10); - if (*e) - return EC_ERROR_PARAM3; - pd_ping_enable(port, enable); - } - - ccprintf("Pings %s\n", - (pd[port].flags & PD_FLAGS_PING_ENABLED) ? - "on" : "off"); - } else if (!strncasecmp(argv[2], "vdm", 3)) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strncasecmp(argv[3], "ping", 4)) { - uint32_t enable; - if (argc < 5) - return EC_ERROR_PARAM_COUNT; - enable = strtoi(argv[4], &e, 10); - if (*e) - return EC_ERROR_PARAM4; - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_PING_ENABLE, - &enable, 1); - } else if (!strncasecmp(argv[3], "curr", 4)) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_CURRENT, - NULL, 0); - } else if (!strncasecmp(argv[3], "vers", 4)) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_VERSION, - NULL, 0); - } else { - return EC_ERROR_PARAM_COUNT; - } -#if defined(CONFIG_CMD_PD) && defined(CONFIG_CMD_PD_FLASH) - } else if (!strncasecmp(argv[2], "flash", 4)) { - return remote_flashing(argc, argv); -#endif -#if defined(CONFIG_CMD_PD) && defined(CONFIG_USB_PD_DUAL_ROLE) - } else if (!strcasecmp(argv[2], "dualrole")) { - if (argc < 4) { - ccprintf("dual-role toggling: "); - switch (drp_state[port]) { - case PD_DRP_TOGGLE_ON: - ccprintf("on\n"); - break; - case PD_DRP_TOGGLE_OFF: - ccprintf("off\n"); - break; - case PD_DRP_FREEZE: - ccprintf("freeze\n"); - break; - case PD_DRP_FORCE_SINK: - ccprintf("force sink\n"); - break; - case PD_DRP_FORCE_SOURCE: - ccprintf("force source\n"); - break; - } - } else { - if (!strcasecmp(argv[3], "on")) - pd_set_dual_role(port, PD_DRP_TOGGLE_ON); - else if (!strcasecmp(argv[3], "off")) - pd_set_dual_role(port, PD_DRP_TOGGLE_OFF); - else if (!strcasecmp(argv[3], "freeze")) - pd_set_dual_role(port, PD_DRP_FREEZE); - else if (!strcasecmp(argv[3], "sink")) - pd_set_dual_role(port, PD_DRP_FORCE_SINK); - else if (!strcasecmp(argv[3], "source")) - pd_set_dual_role(port, - PD_DRP_FORCE_SOURCE); - else - return EC_ERROR_PARAM4; - } - return EC_SUCCESS; -#endif - } else -#endif - if (!strncasecmp(argv[2], "state", 5)) { - ccprintf("Port C%d CC%d, %s - Role: %s-%s%s " - "State: %d(%s), Flags: 0x%04x\n", - port, pd[port].polarity + 1, - pd_comm_is_enabled(port) ? "Ena" : "Dis", - pd[port].power_role == PD_ROLE_SOURCE ? "SRC" : "SNK", - pd[port].data_role == PD_ROLE_DFP ? "DFP" : "UFP", - (pd[port].flags & PD_FLAGS_VCONN_ON) ? "-VC" : "", - pd[port].task_state, - debug_level > 0 ? pd_get_task_state_name(port) : "", - pd[port].flags); - } else { - return EC_ERROR_PARAM1; - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pd, command_pd, - "version" - "|dump" -#ifdef CONFIG_USB_PD_TRY_SRC - "|trysrc" -#endif - " [0|1|2]" -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - "|rwhashtable" -#endif - "\n\t<port> state" -#ifdef CONFIG_USB_PD_DUAL_ROLE - "|tx|bist_rx|bist_tx|charger|dev" - "\n\t<port> disable|enable|soft|info|hard|ping" - "\n\t<port> dualrole [on|off|freeze|sink|source]" - "\n\t<port> swap [power|data|vconn]" - "\n\t<port> vdm [ping|curr|vers]" -#ifdef CONFIG_CMD_PD_FLASH - "\n\t<port> flash [erase|reboot|signature|info|version]" -#endif /* CONFIG_CMD_PD_FLASH */ -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - "\n\t<port> srccaps", - "USB PD"); - -#ifdef HAS_TASK_HOSTCMD - -#ifdef CONFIG_HOSTCMD_FLASHPD -static enum ec_status hc_remote_flash(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_fw_update *p = args->params; - int port = p->port; - const uint32_t *data = &(p->size) + 1; - int i, size, rv = EC_RES_SUCCESS; - timestamp_t timeout; - - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#if defined(CONFIG_BATTERY) && \ - (defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO)) - /* - * Do not allow PD firmware update if no battery and this port - * is sinking power, because we will lose power. - */ - if (battery_is_present() != BP_YES && - charge_manager_get_active_charge_port() == port) - return EC_RES_UNAVAILABLE; -#endif - - /* - * Busy still with a VDM that host likely generated. 1 deep VDM queue - * so just return for retry logic on host side to deal with. - */ - if (pd[port].vdm_state > 0) - return EC_RES_BUSY; - - switch (p->cmd) { - case USB_PD_FW_REBOOT: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); - - /* - * Return immediately to free pending i2c bus. Host needs to - * manage this delay. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_FLASH_ERASE: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); - - /* - * Return immediately. Host needs to manage delays here which - * can be as long as 1.2 seconds on 64KB RW flash. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_ERASE_SIG: - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_ERASE_SIG, NULL, 0); - timeout.val = get_time().val + 500*MSEC; - break; - - case USB_PD_FW_FLASH_WRITE: - /* Data size must be a multiple of 4 */ - if (!p->size || p->size % 4) - return EC_RES_INVALID_PARAM; - - size = p->size / 4; - for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, - data + i, MIN(size - i, VDO_MAX_SIZE - 1)); - timeout.val = get_time().val + 500*MSEC; - - /* Wait until VDM is done */ - while ((pd[port].vdm_state > 0) && - (get_time().val < timeout.val)) - task_wait_event(10*MSEC); - - if (pd[port].vdm_state > 0) - return EC_RES_TIMEOUT; - } - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - break; - } - - /* Wait until VDM is done or timeout */ - while ((pd[port].vdm_state > 0) && (get_time().val < timeout.val)) - task_wait_event(50*MSEC); - - if ((pd[port].vdm_state > 0) || - (pd[port].vdm_state == VDM_STATE_ERR_TMOUT)) - rv = EC_RES_TIMEOUT; - else if (pd[port].vdm_state < 0) - rv = EC_RES_ERROR; - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, - hc_remote_flash, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_FLASHPD */ - -#endif /* HAS_TASK_HOSTCMD */ - - -#endif /* CONFIG_COMMON_RUNTIME */ diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c deleted file mode 100644 index 1aaee29abc..0000000000 --- a/common/usb_pd_tcpc.c +++ /dev/null @@ -1,1468 +0,0 @@ -/* Copyright 2015 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 "adc.h" -#include "common.h" -#include "config.h" -#include "console.h" -#include "crc.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpci.h" -#include "tcpm/tcpm.h" -#include "timer.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_config.h" -#include "usb_pd_tcpm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* - * Debug log level - higher number == more log - * Level 0: Log state transitions - * Level 1: Level 0, plus packet info - * Level 2: Level 1, plus ping packet and packet dump on error - * - * Note that higher log level causes timing changes and thus may affect - * performance. - */ -static int debug_level; - -static struct mutex pd_crc_lock; -#else -#define CPRINTF(format, args...) -static const int debug_level; -#endif - -/* Encode 5 bits using Biphase Mark Coding */ -#define BMC(x) ((x & 1 ? 0x001 : 0x3FF) \ - ^ (x & 2 ? 0x004 : 0x3FC) \ - ^ (x & 4 ? 0x010 : 0x3F0) \ - ^ (x & 8 ? 0x040 : 0x3C0) \ - ^ (x & 16 ? 0x100 : 0x300)) - -/* 4b/5b + Bimark Phase encoding */ -static const uint16_t bmc4b5b[] = { -/* 0 = 0000 */ BMC(0x1E) /* 11110 */, -/* 1 = 0001 */ BMC(0x09) /* 01001 */, -/* 2 = 0010 */ BMC(0x14) /* 10100 */, -/* 3 = 0011 */ BMC(0x15) /* 10101 */, -/* 4 = 0100 */ BMC(0x0A) /* 01010 */, -/* 5 = 0101 */ BMC(0x0B) /* 01011 */, -/* 6 = 0110 */ BMC(0x0E) /* 01110 */, -/* 7 = 0111 */ BMC(0x0F) /* 01111 */, -/* 8 = 1000 */ BMC(0x12) /* 10010 */, -/* 9 = 1001 */ BMC(0x13) /* 10011 */, -/* A = 1010 */ BMC(0x16) /* 10110 */, -/* B = 1011 */ BMC(0x17) /* 10111 */, -/* C = 1100 */ BMC(0x1A) /* 11010 */, -/* D = 1101 */ BMC(0x1B) /* 11011 */, -/* E = 1110 */ BMC(0x1C) /* 11100 */, -/* F = 1111 */ BMC(0x1D) /* 11101 */, -/* Sync-1 K-code 11000 Startsynch #1 */ -/* Sync-2 K-code 10001 Startsynch #2 */ -/* RST-1 K-code 00111 Hard Reset #1 */ -/* RST-2 K-code 11001 Hard Reset #2 */ -/* EOP K-code 01101 EOP End Of Packet */ -/* Reserved Error 00000 */ -/* Reserved Error 00001 */ -/* Reserved Error 00010 */ -/* Reserved Error 00011 */ -/* Reserved Error 00100 */ -/* Reserved Error 00101 */ -/* Reserved Error 00110 */ -/* Reserved Error 01000 */ -/* Reserved Error 01100 */ -/* Reserved Error 10000 */ -/* Reserved Error 11111 */ -}; - -static const uint8_t dec4b5b[] = { -/* Error */ 0x10 /* 00000 */, -/* Error */ 0x10 /* 00001 */, -/* Error */ 0x10 /* 00010 */, -/* Error */ 0x10 /* 00011 */, -/* Error */ 0x10 /* 00100 */, -/* Error */ 0x10 /* 00101 */, -/* Error */ 0x10 /* 00110 */, -/* RST-1 */ 0x13 /* 00111 K-code: Hard Reset #1 */, -/* Error */ 0x10 /* 01000 */, -/* 1 = 0001 */ 0x01 /* 01001 */, -/* 4 = 0100 */ 0x04 /* 01010 */, -/* 5 = 0101 */ 0x05 /* 01011 */, -/* Error */ 0x10 /* 01100 */, -/* EOP */ 0x15 /* 01101 K-code: EOP End Of Packet */, -/* 6 = 0110 */ 0x06 /* 01110 */, -/* 7 = 0111 */ 0x07 /* 01111 */, -/* Error */ 0x10 /* 10000 */, -/* Sync-2 */ 0x12 /* 10001 K-code: Startsynch #2 */, -/* 8 = 1000 */ 0x08 /* 10010 */, -/* 9 = 1001 */ 0x09 /* 10011 */, -/* 2 = 0010 */ 0x02 /* 10100 */, -/* 3 = 0011 */ 0x03 /* 10101 */, -/* A = 1010 */ 0x0A /* 10110 */, -/* B = 1011 */ 0x0B /* 10111 */, -/* Sync-1 */ 0x11 /* 11000 K-code: Startsynch #1 */, -/* RST-2 */ 0x14 /* 11001 K-code: Hard Reset #2 */, -/* C = 1100 */ 0x0C /* 11010 */, -/* D = 1101 */ 0x0D /* 11011 */, -/* E = 1110 */ 0x0E /* 11100 */, -/* F = 1111 */ 0x0F /* 11101 */, -/* 0 = 0000 */ 0x00 /* 11110 */, -/* Error */ 0x10 /* 11111 */, -}; - -/* Start of Packet sequence : three Sync-1 K-codes, then one Sync-2 K-code */ -#define PD_SOP (PD_SYNC1 | (PD_SYNC1<<5) | (PD_SYNC1<<10) | (PD_SYNC2<<15)) -#define PD_SOP_PRIME (PD_SYNC1 | (PD_SYNC1<<5) | \ - (PD_SYNC3<<10) | (PD_SYNC3<<15)) -#define PD_SOP_PRIME_PRIME (PD_SYNC1 | (PD_SYNC3<<5) | \ - (PD_SYNC1<<10) | (PD_SYNC3<<15)) - -/* Hard Reset sequence : three RST-1 K-codes, then one RST-2 K-code */ -#define PD_HARD_RESET (PD_RST1 | (PD_RST1 << 5) |\ - (PD_RST1 << 10) | (PD_RST2 << 15)) - -/* - * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector - * Specification) - * - * CC1 CC2 STATE POSITION - * ---------------------------------------- - * open open NC N/A - * Rd open UFP attached 1 - * open Rd UFP attached 2 - * open Ra pwr cable no UFP N/A - * Ra open pwr cable no UFP N/A - * Rd Ra pwr cable & UFP 1 - * Ra Rd pwr cable & UFP 2 - * Rd Rd dbg accessory N/A - * Ra Ra audio accessory N/A - * - * Note, V(Rd) > V(Ra) - */ -#ifndef PD_SRC_RD_THRESHOLD -#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV -#endif -#ifndef PD_SRC_VNC -#define PD_SRC_VNC PD_SRC_DEF_VNC_MV -#endif - -#ifndef CC_RA -#define CC_RA(port, cc, sel) (cc < PD_SRC_RD_THRESHOLD) -#endif -#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC)) -#ifndef CC_NC -#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC) -#endif - -/* - * Polarity based on 'UFP Perspective'. - * - * CC1 CC2 STATE POSITION - * ---------------------------------------- - * open open NC N/A - * Rp open DFP attached 1 - * open Rp DFP attached 2 - * Rp Rp Accessory attached N/A - */ -#ifndef PD_SNK_VA -#define PD_SNK_VA PD_SNK_VA_MV -#endif - -#define CC_RP(cc) (cc >= PD_SNK_VA) - -/* - * Type C power source charge current limits are identified by their cc - * voltage (set by selecting the proper Rd resistor). Any voltage below - * TYPE_C_SRC_500_THRESHOLD will not be identified as a type C charger. - */ -#define TYPE_C_SRC_500_THRESHOLD PD_SRC_RD_THRESHOLD -#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */ -#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */ - -/* Convert TCPC Alert register to index into pd.alert[] */ -#define ALERT_REG_TO_INDEX(reg) (reg - TCPC_REG_ALERT) - -/* PD transmit errors */ -enum pd_tx_errors { - PD_TX_ERR_GOODCRC = -1, /* Failed to receive goodCRC */ - PD_TX_ERR_DISABLED = -2, /* Attempted transmit even though disabled */ - PD_TX_ERR_INV_ACK = -4, /* Received different packet instead of gCRC */ - PD_TX_ERR_COLLISION = -5 /* Collision detected during transmit */ -}; - -/* PD Header with SOP* encoded in bits 31 - 28 */ -union pd_header_sop { - uint16_t pd_header; - uint32_t head; -}; - -/* - * If TCPM is not on this chip, and PD low power is defined, then use low - * power task delay logic. - */ -#if !defined(CONFIG_USB_POWER_DELIVERY) && defined(CONFIG_USB_PD_LOW_POWER) -#define TCPC_LOW_POWER -#endif - -/* - * Receive message buffer size. Buffer physical size is RX_BUFFER_SIZE + 1, - * but only RX_BUFFER_SIZE of that memory is used to store messages that can - * be retrieved from TCPM. The last slot is a temporary buffer for collecting - * a message before deciding whether or not to keep it. - */ -#ifdef CONFIG_USB_POWER_DELIVERY -#define RX_BUFFER_SIZE 1 -#else -#define RX_BUFFER_SIZE 2 -#endif - -static struct pd_port_controller { - /* current port power role (SOURCE or SINK) */ - uint8_t power_role; - /* current port data role (DFP or UFP) */ - uint8_t data_role; - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - uint8_t polarity; - /* Our CC pull resistor setting */ - uint8_t cc_pull; - /* CC status */ - uint8_t cc_status[2]; - /* TCPC alert status */ - uint16_t alert; - uint16_t alert_mask; - /* RX enabled */ - uint8_t rx_enabled; - /* Power status */ - uint8_t power_status; - uint8_t power_status_mask; - -#ifdef TCPC_LOW_POWER - /* Timestamp beyond which we allow low power task sampling */ - timestamp_t low_power_ts; -#endif - - /* Last received */ - int rx_head[RX_BUFFER_SIZE+1]; - uint32_t rx_payload[RX_BUFFER_SIZE+1][7]; - int rx_buf_head, rx_buf_tail; - - /* Next transmit */ - enum tcpci_msg_type tx_type; - uint16_t tx_head; - uint32_t tx_payload[7]; - const uint32_t *tx_data; -} pd[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static int rx_buf_is_full(int port) -{ - /* - * TODO: Refactor these to use the incrementing-counter idiom instead of - * the wrapping-counter idiom to reclaim the last buffer entry. - * - * Buffer is full if the tail is 1 ahead of head. - */ - int diff = pd[port].rx_buf_tail - pd[port].rx_buf_head; - return (diff == 1) || (diff == -RX_BUFFER_SIZE); -} - -int rx_buf_is_empty(int port) -{ - /* Buffer is empty if the head and tail are the same */ - return pd[port].rx_buf_tail == pd[port].rx_buf_head; -} - -void rx_buf_clear(int port) -{ - pd[port].rx_buf_tail = pd[port].rx_buf_head; -} - -static void rx_buf_increment(int port, int *buf_ptr) -{ - *buf_ptr = *buf_ptr == RX_BUFFER_SIZE ? 0 : *buf_ptr + 1; -} - -static inline int encode_short(int port, int off, uint16_t val16) -{ - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 0) & 0xF]); - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 4) & 0xF]); - off = pd_write_sym(port, off, bmc4b5b[(val16 >> 8) & 0xF]); - return pd_write_sym(port, off, bmc4b5b[(val16 >> 12) & 0xF]); -} - -int encode_word(int port, int off, uint32_t val32) -{ - off = encode_short(port, off, (val32 >> 0) & 0xFFFF); - return encode_short(port, off, (val32 >> 16) & 0xFFFF); -} - -/* prepare a 4b/5b-encoded PD message to send */ -int prepare_message(int port, uint16_t header, uint8_t cnt, - const uint32_t *data) -{ - int off, i; - /* 64-bit preamble */ - off = pd_write_preamble(port); -#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD) - /* Start Of Packet Prime: 2x Sync-1 + 2x Sync-3 */ - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC3)); - off = pd_write_sym(port, off, BMC(PD_SYNC3)); -#else - /* Start Of Packet: 3x Sync-1 + 1x Sync-2 */ - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC1)); - off = pd_write_sym(port, off, BMC(PD_SYNC2)); -#endif - /* header */ - off = encode_short(port, off, header); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_lock(&pd_crc_lock); -#endif - - crc32_init(); - crc32_hash16(header); - /* data payload */ - for (i = 0; i < cnt; i++) { - off = encode_word(port, off, data[i]); - crc32_hash32(data[i]); - } - /* CRC */ - off = encode_word(port, off, crc32_result()); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_unlock(&pd_crc_lock); -#endif - - /* End Of Packet */ - off = pd_write_sym(port, off, BMC(PD_EOP)); - /* Ensure that we have a final edge */ - return pd_write_last_edge(port, off); -} - -static int send_hard_reset(int port) -{ - int off; - - if (debug_level >= 1) - CPRINTF("C%d Send hard reset\n", port); - - /* 64-bit preamble */ - off = pd_write_preamble(port); - /* Hard-Reset: 3x RST-1 + 1x RST-2 */ - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST1)); - off = pd_write_sym(port, off, BMC(PD_RST2)); - /* Ensure that we have a final edge */ - off = pd_write_last_edge(port, off); - /* Transmit the packet */ - if (pd_start_tx(port, pd[port].polarity, off) < 0) - return PD_TX_ERR_COLLISION; - pd_tx_done(port, pd[port].polarity); - /* Keep RX monitoring on */ - pd_rx_enable_monitoring(port); - return 0; -} - -static int send_validate_message(int port, uint16_t header, - const uint32_t *data) -{ - int r; - static uint32_t payload[7]; - uint8_t expected_msg_id = PD_HEADER_ID(header); - uint8_t cnt = PD_HEADER_CNT(header); - int retries = PD_HEADER_TYPE(header) == PD_DATA_SOURCE_CAP ? - 0 : - CONFIG_PD_RETRY_COUNT; - - /* retry 3 times if we are not getting a valid answer */ - for (r = 0; r <= retries; r++) { - int bit_len, head; - /* write the encoded packet in the transmission buffer */ - bit_len = prepare_message(port, header, cnt, data); - /* Transmit the packet */ - if (pd_start_tx(port, pd[port].polarity, bit_len) < 0) { - /* - * Collision detected, return immediately so we can - * respond to what we have received. - */ - return PD_TX_ERR_COLLISION; - } - pd_tx_done(port, pd[port].polarity); - /* - * If this is the first attempt, leave RX monitoring off, - * and do a blocking read of the channel until timeout or - * packet received. If we failed the first try, enable - * interrupt and yield to other tasks, so that we don't - * starve them. - */ - if (r) { - pd_rx_enable_monitoring(port); - /* Wait for message receive timeout */ - if (task_wait_event(USB_PD_RX_TMOUT_US) == - TASK_EVENT_TIMER) - continue; - /* - * Make sure we woke up due to rx recd, otherwise - * we need to manually start - */ - if (!pd_rx_started(port)) { - pd_rx_disable_monitoring(port); - pd_rx_start(port); - } - } else { - /* starting waiting for GoodCrc */ - pd_rx_start(port); - } - /* read the incoming packet if any */ - head = pd_analyze_rx(port, payload); - pd_rx_complete(port); - /* keep RX monitoring on to avoid collisions */ - pd_rx_enable_monitoring(port); - if (head > 0) { /* we got a good packet, analyze it */ - int type = PD_HEADER_TYPE(head); - int nb = PD_HEADER_CNT(head); - uint8_t id = PD_HEADER_ID(head); - if (type == PD_CTRL_GOOD_CRC && nb == 0 && - id == expected_msg_id) { - /* got the GoodCRC we were expecting */ - /* do not catch last edges as a new packet */ - udelay(20); - return bit_len; - } else { - /* - * we have received a good packet - * but not the expected GoodCRC, - * the other side is trying to contact us, - * bail out immediately so we can get the retry. - */ - return PD_TX_ERR_INV_ACK; - } - } - } - /* we failed all the re-transmissions */ - if (debug_level >= 1) - CPRINTF("TX NOACK%d %04x/%d\n", port, header, cnt); - return PD_TX_ERR_GOODCRC; -} - -static void send_goodcrc(int port, int id) -{ - uint16_t header = PD_HEADER(PD_CTRL_GOOD_CRC, pd[port].power_role, - pd[port].data_role, id, 0, 0, 0); - int bit_len = prepare_message(port, header, 0, NULL); - - if (pd_start_tx(port, pd[port].polarity, bit_len) < 0) - /* another packet recvd before we could send goodCRC */ - return; - pd_tx_done(port, pd[port].polarity); - /* Keep RX monitoring on */ - pd_rx_enable_monitoring(port); -} - -#if 0 -/* TODO: when/how do we trigger this ? */ -static int analyze_rx_bist(int port); - -void bist_mode_2_rx(int port) -{ - int analyze_bist = 0; - int num_bits; - timestamp_t start_time; - - /* monitor for incoming packet */ - pd_rx_enable_monitoring(port); - - /* loop until we start receiving data */ - start_time.val = get_time().val; - while ((get_time().val - start_time.val) < (500*MSEC)) { - task_wait_event(10*MSEC); - /* incoming packet ? */ - if (pd_rx_started(port)) { - analyze_bist = 1; - break; - } - } - - if (analyze_bist) { - /* - * once we start receiving bist data, analyze 40 bytes - * every 10 msec. Continue analyzing until BIST data - * is no longer received. The standard limits the max - * BIST length to 60 msec. - */ - start_time.val = get_time().val; - while ((get_time().val - start_time.val) - < (PD_T_BIST_RECEIVE)) { - num_bits = analyze_rx_bist(port); - pd_rx_complete(port); - /* - * If no data was received, then analyze_rx_bist() - * will return a -1 and there is no need to stay - * in this mode - */ - if (num_bits == -1) - break; - msleep(10); - pd_rx_enable_monitoring(port); - } - } else { - CPRINTF("BIST RX TO\n"); - } -} -#endif - -static void bist_mode_2_tx(int port) -{ - int bit; - - CPRINTF("BIST 2: p%d\n", port); - /* - * build context buffer with 5 bytes, where the data is - * alternating 1's and 0's. - */ - bit = pd_write_sym(port, 0, BMC(0x15)); - bit = pd_write_sym(port, bit, BMC(0x0a)); - bit = pd_write_sym(port, bit, BMC(0x15)); - bit = pd_write_sym(port, bit, BMC(0x0a)); - - /* start a circular DMA transfer */ - pd_tx_set_circular_mode(port); - pd_start_tx(port, pd[port].polarity, bit); - - task_wait_event(PD_T_BIST_TRANSMIT); - - /* clear dma circular mode, will also stop dma */ - pd_tx_clear_circular_mode(port); - /* finish and cleanup transmit */ - pd_tx_done(port, pd[port].polarity); -} - -static inline int decode_short(int port, int off, uint16_t *val16) -{ - uint32_t w; - int end; - - end = pd_dequeue_bits(port, off, 20, &w); - -#if 0 /* DEBUG */ - CPRINTS("%d-%d: %05x %x:%x:%x:%x", - off, end, w, - dec4b5b[(w >> 15) & 0x1f], dec4b5b[(w >> 10) & 0x1f], - dec4b5b[(w >> 5) & 0x1f], dec4b5b[(w >> 0) & 0x1f]); -#endif - *val16 = dec4b5b[w & 0x1f] | - (dec4b5b[(w >> 5) & 0x1f] << 4) | - (dec4b5b[(w >> 10) & 0x1f] << 8) | - (dec4b5b[(w >> 15) & 0x1f] << 12); - return end; -} - -static inline int decode_word(int port, int off, uint32_t *val32) -{ - off = decode_short(port, off, (uint16_t *)val32); - return decode_short(port, off, ((uint16_t *)val32 + 1)); -} - -#ifdef CONFIG_COMMON_RUNTIME -#if 0 -/* - * TODO: when/how do we trigger this ? Could add custom vendor command - * to TCPCI to enter bist verification? Is there an easier way? - */ -static int count_set_bits(int n) -{ - int count = 0; - while (n) { - n &= (n - 1); - count++; - } - return count; -} - -static int analyze_rx_bist(int port) -{ - int i = 0, bit = -1; - uint32_t w, match; - int invalid_bits = 0; - int bits_analyzed = 0; - static int total_invalid_bits; - - /* dequeue bits until we see a full byte of alternating 1's and 0's */ - while (i < 10 && (bit < 0 || (w != 0xaa && w != 0x55))) - bit = pd_dequeue_bits(port, i++, 8, &w); - - /* if we didn't find any bytes that match criteria, display error */ - if (i == 10) { - CPRINTF("invalid pattern\n"); - return -1; - } - /* - * now we know what matching byte we are looking for, dequeue a bunch - * more data and count how many bits differ from expectations. - */ - match = w; - bit = i - 1; - for (i = 0; i < 40; i++) { - bit = pd_dequeue_bits(port, bit, 8, &w); - if (i && (i % 20 == 0)) - CPRINTF("\n"); - CPRINTF("%02x ", w); - bits_analyzed += 8; - invalid_bits += count_set_bits(w ^ match); - } - - total_invalid_bits += invalid_bits; - - CPRINTF("\nInvalid: %d/%d\n", - invalid_bits, total_invalid_bits); - return bits_analyzed; -} -#endif -#endif - -int pd_analyze_rx(int port, uint32_t *payload) -{ - int bit; - char *msg = "---"; - uint32_t val = 0; - union pd_header_sop phs; - uint32_t pcrc, ccrc; - int p, cnt; - uint32_t eop; - - pd_init_dequeue(port); - - /* Detect preamble */ - bit = pd_find_preamble(port); - if (bit == PD_RX_ERR_HARD_RESET || bit == PD_RX_ERR_CABLE_RESET) { - /* Hard reset or cable reset */ - return bit; - } else if (bit < 0) { - msg = "Preamble"; - goto packet_err; - } - - /* Find the Start Of Packet sequence */ - while (bit > 0) { - bit = pd_dequeue_bits(port, bit, 20, &val); -#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD) - if (val == PD_SOP_PRIME) { - break; - } else if (val == PD_SOP) { - CPRINTF("SOP\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } else if (val == PD_SOP_PRIME_PRIME) { - CPRINTF("SOP''\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } -#else /* CONFIG_USB_VPD || CONFIG_USB_CTVPD */ -#ifdef CONFIG_USB_PD_DECODE_SOP - if (val == PD_SOP || val == PD_SOP_PRIME || - val == PD_SOP_PRIME_PRIME) - break; -#else - if (val == PD_SOP) { - break; - } else if (val == PD_SOP_PRIME) { - CPRINTF("SOP'\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } else if (val == PD_SOP_PRIME_PRIME) { - CPRINTF("SOP''\n"); - return PD_RX_ERR_UNSUPPORTED_SOP; - } -#endif /* CONFIG_USB_PD_DECODE_SOP */ -#endif /* CONFIG_USB_VPD || CONFIG_USB_CTVPD */ - } - if (bit < 0) { -#ifdef CONFIG_USB_PD_DECODE_SOP - if (val == PD_SOP) - msg = "SOP"; - else if (val == PD_SOP_PRIME) - msg = "SOP'"; - else if (val == PD_SOP_PRIME_PRIME) - msg = "SOP''"; - else - msg = "SOP*"; -#else - msg = "SOP"; -#endif - goto packet_err; - } - - phs.head = 0; - - /* read header */ - bit = decode_short(port, bit, &phs.pd_header); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_lock(&pd_crc_lock); -#endif - - crc32_init(); - crc32_hash16(phs.pd_header); - cnt = PD_HEADER_CNT(phs.pd_header); - -#ifdef CONFIG_USB_PD_DECODE_SOP - /* Encode message address */ - if (val == PD_SOP) { - phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP); - } else if (val == PD_SOP_PRIME) { - phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP_PRIME); - } else if (val == PD_SOP_PRIME_PRIME) { - phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP_PRIME_PRIME); - } else { - msg = "SOP*"; - goto packet_err; - } -#endif - - /* read payload data */ - for (p = 0; p < cnt && bit > 0; p++) { - bit = decode_word(port, bit, payload+p); - crc32_hash32(payload[p]); - } - ccrc = crc32_result(); - -#ifdef CONFIG_COMMON_RUNTIME - mutex_unlock(&pd_crc_lock); -#endif - - if (bit < 0) { - msg = "len"; - goto packet_err; - } - - /* check transmitted CRC */ - bit = decode_word(port, bit, &pcrc); - if (bit < 0 || pcrc != ccrc) { - msg = "CRC"; - if (pcrc != ccrc) - bit = PD_RX_ERR_CRC; - if (debug_level >= 1) - CPRINTF("CRC%d %08x <> %08x\n", port, pcrc, ccrc); - goto packet_err; - } - - /* - * Check EOP. EOP is 5 bits, but last bit may not be able to - * be dequeued, depending on ending state of CC line, so stop - * at 4 bits (assumes last bit is 0). - */ - bit = pd_dequeue_bits(port, bit, 4, &eop); - if (bit < 0 || eop != PD_EOP) { - msg = "EOP"; - goto packet_err; - } - - return phs.head; -packet_err: - if (debug_level >= 2) - pd_dump_packet(port, msg); - else - CPRINTF("RXERR%d %s\n", port, msg); - return bit; -} - -static void handle_request(int port, uint16_t head) -{ - int cnt = PD_HEADER_CNT(head); - - if (PD_HEADER_TYPE(head) != PD_CTRL_GOOD_CRC || cnt) - send_goodcrc(port, PD_HEADER_ID(head)); - else - /* keep RX monitoring on to avoid collisions */ - pd_rx_enable_monitoring(port); -} - -/* Convert CC voltage to CC status */ -static int cc_voltage_to_status(int port, int cc_volt, int cc_sel) -{ - /* If we have a pull-up, then we are source, check for Rd. */ - if (pd[port].cc_pull == TYPEC_CC_RP) { - if (CC_NC(port, cc_volt, cc_sel)) - return TYPEC_CC_VOLT_OPEN; - else if (CC_RA(port, cc_volt, cc_sel)) - return TYPEC_CC_VOLT_RA; - else - return TYPEC_CC_VOLT_RD; - /* If we have a pull-down, then we are sink, check for Rp. */ - } -#ifdef CONFIG_USB_PD_DUAL_ROLE - else if (pd[port].cc_pull == TYPEC_CC_RD) { - if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD) - return TYPEC_CC_VOLT_RP_3_0; - else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD) - return TYPEC_CC_VOLT_RP_1_5; - else if (CC_RP(cc_volt)) - return TYPEC_CC_VOLT_RP_DEF; - else - return TYPEC_CC_VOLT_OPEN; - } -#endif - /* If we are open, then always return 0 */ - else - return 0; -} - -static void alert(int port, int mask) -{ - /* Always update the Alert status register */ - pd[port].alert |= mask; - /* - * Only send interrupt to TCPM if corresponding - * bit in the alert_enable register is set. - */ - if (pd[port].alert_mask & mask) - tcpc_alert(port); -} - -int tcpc_run(int port, int evt) -{ - int cc, i, res; - - /* Don't do anything when port is not available */ - if (port >= board_get_usb_pd_port_count()) - return -1; - - /* incoming packet ? */ - if (pd_rx_started(port) && pd[port].rx_enabled) { - /* Get message and place at RX buffer head */ - res = pd[port].rx_head[pd[port].rx_buf_head] = - pd_analyze_rx(port, - pd[port].rx_payload[pd[port].rx_buf_head]); - pd_rx_complete(port); - - /* - * If there is space in buffer, then increment head to keep - * the message and send goodCRC. If this is a hard reset, - * send alert regardless of rx buffer status. Else if there is - * no space in buffer, then do not send goodCRC and drop - * message. - */ - if (res > 0 && !rx_buf_is_full(port)) { - rx_buf_increment(port, &pd[port].rx_buf_head); - handle_request(port, res); - alert(port, TCPC_REG_ALERT_RX_STATUS); - } else if (res == PD_RX_ERR_HARD_RESET) { - alert(port, TCPC_REG_ALERT_RX_HARD_RST); - } - } - - /* outgoing packet ? */ - if ((evt & PD_EVENT_TX) && pd[port].rx_enabled) { - switch (pd[port].tx_type) { -#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD) - case TCPCI_MSG_SOP_PRIME: -#else - case TCPCI_MSG_SOP: -#endif - res = send_validate_message(port, - pd[port].tx_head, - pd[port].tx_data); - break; - case TCPCI_MSG_TX_BIST_MODE_2: - bist_mode_2_tx(port); - res = 0; - break; - case TCPCI_MSG_TX_HARD_RESET: - res = send_hard_reset(port); - break; - default: - res = PD_TX_ERR_DISABLED; - break; - } - - /* send appropriate alert for tx completion */ - if (res >= 0) - alert(port, TCPC_REG_ALERT_TX_SUCCESS); - else if (res == PD_TX_ERR_GOODCRC) - alert(port, TCPC_REG_ALERT_TX_FAILED); - else - alert(port, TCPC_REG_ALERT_TX_DISCARDED); - } else { - /* If we have nothing to transmit, then sample CC lines */ - - /* CC pull changed, wait 1ms for CC voltage to stabilize */ - if (evt & PD_EVENT_CC) - usleep(MSEC); - - /* check CC lines */ - for (i = 0; i < 2; i++) { - /* read CC voltage */ - cc = pd_adc_read(port, i); - - /* convert voltage to status, and check status change */ - cc = cc_voltage_to_status(port, cc, i); - if (pd[port].cc_status[i] != cc) { - pd[port].cc_status[i] = cc; - alert(port, TCPC_REG_ALERT_CC_STATUS); - } - } - } - - /* make sure PD monitoring is enabled to wake on PD RX */ - if (pd[port].rx_enabled) - pd_rx_enable_monitoring(port); - -#ifdef TCPC_LOW_POWER - /* - * If we are presenting Rd with no connection, and timestamp is - * past the low power timestamp, then we don't need to sample - * CC lines as often. In this case, our connection delay should not - * actually increased because we will get an interrupt on VBUS detect. - */ - return (get_time().val >= pd[port].low_power_ts.val && - pd[port].cc_pull == TYPEC_CC_RD && - cc_is_open(pd[port].cc_status[0], pd[port].cc_status[1])) - ? 200 * MSEC - : 10 * MSEC; -#else - return 10*MSEC; -#endif -} - -#if !defined(CONFIG_USB_POWER_DELIVERY) -void pd_task(void *u) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); - int timeout = 10*MSEC; - int evt; - - /* initialize phy task */ - tcpc_init(port); - - /* we are now initialized */ - pd[port].power_status &= ~TCPC_REG_POWER_STATUS_UNINIT; - - while (1) { - /* wait for next event/packet or timeout expiration */ - evt = task_wait_event(timeout); - - /* run phy task once */ - timeout = tcpc_run(port, evt); - } -} -#endif - -void pd_rx_event(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE); -} - -int tcpc_alert_status(int port, int *alert) -{ - /* return the value of the TCPC Alert register */ - uint16_t ret = pd[port].alert; - *alert = ret; - return EC_SUCCESS; -} - -int tcpc_alert_status_clear(int port, uint16_t mask) -{ - /* - * If the RX status alert is attempting to be cleared, then increment - * rx buffer tail pointer. if the RX buffer is not empty, then keep - * the RX status alert active. - */ - if (mask & TCPC_REG_ALERT_RX_STATUS) { - if (!rx_buf_is_empty(port)) { - rx_buf_increment(port, &pd[port].rx_buf_tail); - if (!rx_buf_is_empty(port)) - /* buffer is not empty, keep alert active */ - mask &= ~TCPC_REG_ALERT_RX_STATUS; - } - } - - /* clear only the bits specified by the TCPM */ - pd[port].alert &= ~mask; -#ifndef CONFIG_USB_POWER_DELIVERY - /* Set Alert# inactive if all alert bits clear */ - if (!pd[port].alert) - tcpc_alert_clear(port); -#endif - return EC_SUCCESS; -} - -int tcpc_alert_mask_set(int port, uint16_t mask) -{ - /* Update the alert mask as specificied by the TCPM */ - pd[port].alert_mask = mask; - return EC_SUCCESS; -} - -int tcpc_set_cc(int port, int pull) -{ - /* If CC pull resistor not changing, then nothing to do */ - if (pd[port].cc_pull == pull) - return EC_SUCCESS; - - /* Change CC pull resistor */ - pd[port].cc_pull = pull; -#ifdef CONFIG_USB_PD_DUAL_ROLE - pd_set_host_mode(port, pull == TYPEC_CC_RP); -#endif - -#ifdef TCPC_LOW_POWER - /* - * Reset the low power timestamp every time CC termination toggles, - * because we only want to go into low power mode when we are not - * dual-role toggling. - */ - pd[port].low_power_ts.val = get_time().val + - 2*(PD_T_DRP_SRC + PD_T_DRP_SNK); -#endif - - /* - * Before CC pull can be changed and the task can read the new - * status, we should set the CC status to open, in case TCPM - * asks before it is known for sure. - */ - pd[port].cc_status[0] = TYPEC_CC_VOLT_OPEN; - pd[port].cc_status[1] = pd[port].cc_status[0]; - - /* Wake the PD phy task with special CC event mask */ - /* TODO: use top case if no TCPM on same CPU */ -#ifdef CONFIG_USB_POWER_DELIVERY - tcpc_run(port, PD_EVENT_CC); -#else - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC); -#endif - return EC_SUCCESS; -} - -int tcpc_get_cc(int port, enum tcpc_cc_voltage_status *cc1, - enum tcpc_cc_voltage_status *cc2) -{ - *cc2 = pd[port].cc_status[1]; - *cc1 = pd[port].cc_status[0]; - - return EC_SUCCESS; -} - -int board_select_rp_value(int port, int rp) __attribute__((weak)); - -int tcpc_select_rp_value(int port, int rp) -{ - if (board_select_rp_value) - return board_select_rp_value(port, rp); - else - return EC_ERROR_UNIMPLEMENTED; -} - -int tcpc_set_polarity(int port, int polarity) -{ - pd[port].polarity = polarity; - pd_select_polarity(port, pd[port].polarity); - - return EC_SUCCESS; -} - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -static int tcpc_set_power_status(int port, int vbus_present) -{ - /* Update VBUS present bit */ - if (vbus_present) - pd[port].power_status |= TCPC_REG_POWER_STATUS_VBUS_PRES; - else - pd[port].power_status &= ~TCPC_REG_POWER_STATUS_VBUS_PRES; - - /* Set bit Port Power Status bit in Alert register */ - if (pd[port].power_status_mask & TCPC_REG_POWER_STATUS_VBUS_PRES) - alert(port, TCPC_REG_ALERT_POWER_STATUS); - - return EC_SUCCESS; -} -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - -int tcpc_set_power_status_mask(int port, uint8_t mask) -{ - pd[port].power_status_mask = mask; - return EC_SUCCESS; -} - -int tcpc_set_vconn(int port, int enable) -{ -#ifdef CONFIG_USBC_VCONN - pd_set_vconn(port, pd[port].polarity, enable); -#endif - return EC_SUCCESS; -} - -int tcpc_set_rx_enable(int port, int enable) -{ -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY) - int i; -#endif - pd[port].rx_enabled = enable; - - if (!enable) - pd_rx_disable_monitoring(port); - -#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY) - /* If any PD port is connected, then disable deep sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); ++i) - if (pd[i].rx_enabled) - break; - - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - else - disable_sleep(SLEEP_MASK_USB_PD); -#endif - return EC_SUCCESS; -} - -int tcpc_transmit(int port, enum tcpci_msg_type type, uint16_t header, - const uint32_t *data) -{ - /* Store data to transmit and wake task to send it */ - pd[port].tx_type = type; - pd[port].tx_head = header; - pd[port].tx_data = data; - /* TODO: use top case if no TCPM on same CPU */ -#ifdef CONFIG_USB_POWER_DELIVERY - tcpc_run(port, PD_EVENT_TX); -#else - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX); -#endif - return EC_SUCCESS; -} - -int tcpc_set_msg_header(int port, int power_role, int data_role) -{ - pd[port].power_role = power_role; - pd[port].data_role = data_role; - - return EC_SUCCESS; -} - -int tcpc_get_message(int port, uint32_t *payload, int *head) -{ - /* Get message at tail of RX buffer */ - int idx = pd[port].rx_buf_tail; - - memcpy(payload, pd[port].rx_payload[idx], - sizeof(pd[port].rx_payload[idx])); - *head = pd[port].rx_head[idx]; - return EC_SUCCESS; -} - -void tcpc_pre_init(void) -{ - int i; - - /* Mark as uninitialized */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) - pd[i].power_status |= TCPC_REG_POWER_STATUS_UNINIT | - TCPC_REG_POWER_STATUS_VBUS_DET; -} -/* Must be prioritized above i2c init */ -DECLARE_HOOK(HOOK_INIT, tcpc_pre_init, HOOK_PRIO_INIT_I2C - 1); - -void tcpc_init(int port) -{ - int i; - - if (port >= board_get_usb_pd_port_count()) - return; - - /* Initialize physical layer */ - pd_hw_init(port, PD_ROLE_DEFAULT(port)); - pd[port].cc_pull = PD_ROLE_DEFAULT(port) == - PD_ROLE_SOURCE ? TYPEC_CC_RP : TYPEC_CC_RD; -#ifdef TCPC_LOW_POWER - /* Don't use low power immediately after boot */ - pd[port].low_power_ts.val = get_time().val + SECOND; -#endif - - /* make sure PD monitoring is disabled initially */ - pd[port].rx_enabled = 0; - - /* make initial readings of CC voltages */ - for (i = 0; i < 2; i++) { - pd[port].cc_status[i] = cc_voltage_to_status(port, - pd_adc_read(port, i), - i); - } - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 - tcpc_set_power_status(port, !gpio_get_level(port ? - GPIO_USB_C1_VBUS_WAKE_L : - GPIO_USB_C0_VBUS_WAKE_L)); -#else - tcpc_set_power_status(port, !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L)); -#endif /* CONFIG_USB_PD_PORT_MAX_COUNT >= 2 */ -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - - /* set default alert and power mask register values */ - pd[port].alert_mask = TCPC_REG_ALERT_MASK_ALL; - pd[port].power_status_mask = TCPC_REG_POWER_STATUS_MASK_ALL; - - /* set power status alert since the UNINIT bit has been set */ - alert(port, TCPC_REG_ALERT_POWER_STATUS); -} - -#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS -void pd_vbus_evt_p0(enum gpio_signal signal) -{ - tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C0), - !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L)); - task_wake(TASK_ID_PD_C0); -} - -#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2 -void pd_vbus_evt_p1(enum gpio_signal signal) -{ - if (board_get_usb_pd_port_count() == 1) - return; - - tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C1), - !gpio_get_level(GPIO_USB_C1_VBUS_WAKE_L)); - task_wake(TASK_ID_PD_C1); -} -#endif /* PD_PORT_COUNT >= 2 */ -#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */ - -#ifndef CONFIG_USB_POWER_DELIVERY -static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload) -{ - uint16_t alert; - - /* If we are not yet initialized, ignore any write command */ - if (pd[port].power_status & TCPC_REG_POWER_STATUS_UNINIT) - return; - - switch (reg) { - case TCPC_REG_ROLE_CTRL: - tcpc_set_cc(port, TCPC_REG_ROLE_CTRL_CC1(payload[1])); - break; - case TCPC_REG_POWER_CTRL: - tcpc_set_vconn(port, TCPC_REG_POWER_CTRL_VCONN(payload[1])); - break; - case TCPC_REG_TCPC_CTRL: - tcpc_set_polarity(port, - TCPC_REG_TCPC_CTRL_POLARITY(payload[1])); - break; - case TCPC_REG_MSG_HDR_INFO: - tcpc_set_msg_header(port, - TCPC_REG_MSG_HDR_INFO_PROLE(payload[1]), - TCPC_REG_MSG_HDR_INFO_DROLE(payload[1])); - break; - case TCPC_REG_ALERT: - alert = payload[1]; - alert |= (payload[2] << 8); - /* clear alert bits specified by the TCPM */ - tcpc_alert_status_clear(port, alert); - break; - case TCPC_REG_ALERT_MASK: - alert = payload[1]; - alert |= (payload[2] << 8); - tcpc_alert_mask_set(port, alert); - break; - case TCPC_REG_RX_DETECT: - tcpc_set_rx_enable(port, payload[1] & - TCPC_REG_RX_DETECT_SOP_HRST_MASK); - break; - case TCPC_REG_POWER_STATUS_MASK: - tcpc_set_power_status_mask(port, payload[1]); - break; - case TCPC_REG_TX_HDR: - pd[port].tx_head = (payload[2] << 8) | payload[1]; - break; - case TCPC_REG_TX_DATA: - memcpy(pd[port].tx_payload, &payload[1], len - 1); - break; - case TCPC_REG_TRANSMIT: - tcpc_transmit(port, TCPC_REG_TRANSMIT_TYPE(payload[1]), - pd[port].tx_head, pd[port].tx_payload); - break; - } -} - -static int tcpc_i2c_read(int port, int reg, uint8_t *payload) -{ - enum tcpc_cc_voltage_status cc1, cc2; - int alert; - - switch (reg) { - case TCPC_REG_VENDOR_ID: - *(uint16_t *)payload = USB_VID_GOOGLE; - return 2; - case TCPC_REG_CC_STATUS: - tcpc_get_cc(port, &cc1, &cc2); - payload[0] = TCPC_REG_CC_STATUS_SET( - pd[port].cc_pull == TYPEC_CC_RD, - pd[port].cc_status[0], pd[port].cc_status[1]); - return 1; - case TCPC_REG_ROLE_CTRL: - payload[0] = TCPC_REG_ROLE_CTRL_SET(0, 0, - pd[port].cc_pull, - pd[port].cc_pull); - return 1; - case TCPC_REG_TCPC_CTRL: - payload[0] = TCPC_REG_TCPC_CTRL_SET(pd[port].polarity); - return 1; - case TCPC_REG_MSG_HDR_INFO: - payload[0] = TCPC_REG_MSG_HDR_INFO_SET(pd[port].data_role, - pd[port].power_role); - return 1; - case TCPC_REG_RX_DETECT: - payload[0] = pd[port].rx_enabled ? - TCPC_REG_RX_DETECT_SOP_HRST_MASK : 0; - return 1; - case TCPC_REG_ALERT: - tcpc_alert_status(port, &alert); - payload[0] = alert & 0xff; - payload[1] = (alert >> 8) & 0xff; - return 2; - case TCPC_REG_ALERT_MASK: - payload[0] = pd[port].alert_mask & 0xff; - payload[1] = (pd[port].alert_mask >> 8) & 0xff; - return 2; - case TCPC_REG_RX_BYTE_CNT: - payload[0] = 3 + 4 * - PD_HEADER_CNT(pd[port].rx_head[pd[port].rx_buf_tail]); - return 1; - case TCPC_REG_RX_HDR: - payload[0] = pd[port].rx_head[pd[port].rx_buf_tail] & 0xff; - payload[1] = - (pd[port].rx_head[pd[port].rx_buf_tail] >> 8) & 0xff; - return 2; - case TCPC_REG_RX_DATA: - memcpy(payload, pd[port].rx_payload[pd[port].rx_buf_tail], - sizeof(pd[port].rx_payload[pd[port].rx_buf_tail])); - return sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]); - case TCPC_REG_POWER_STATUS: - payload[0] = pd[port].power_status; - return 1; - case TCPC_REG_POWER_STATUS_MASK: - payload[0] = pd[port].power_status_mask; - return 1; - case TCPC_REG_TX_HDR: - payload[0] = pd[port].tx_head & 0xff; - payload[1] = (pd[port].tx_head >> 8) & 0xff; - return 2; - case TCPC_REG_TX_DATA: - memcpy(payload, pd[port].tx_payload, - sizeof(pd[port].tx_payload)); - return sizeof(pd[port].tx_payload); - default: - return 0; - } -} - -void tcpc_i2c_process(int read, int port, int len, uint8_t *payload, - void (*send_response)(int)) -{ - int i, reg; - - if (debug_level >= 1) { - CPRINTF("tcpci p%d: ", port); - for (i = 0; i < len; i++) - CPRINTF("0x%02x ", payload[i]); - CPRINTF("\n"); - } - - /* length must always be at least 1 */ - if (len == 0) { - /* - * if this is a read, we must call send_response() for - * i2c transaction to finishe properly - */ - if (read) - (*send_response)(0); - } - - /* if this is a write, length must be at least 2 */ - if (!read && len < 2) - return; - - /* register is always first byte */ - reg = payload[0]; - - /* perform read or write */ - if (read) { - len = tcpc_i2c_read(port, reg, payload); - (*send_response)(len); - } else { - tcpc_i2c_write(port, reg, len, payload); - } -} -#endif - -#ifdef CONFIG_COMMON_RUNTIME -static int command_tcpc(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - int level; - - if (argc < 3) - ccprintf("lvl: %d\n", debug_level); - else { - level = strtoi(argv[2], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - debug_level = level; - } - return EC_SUCCESS; - } - - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - if (*e || port >= board_get_usb_pd_port_count()) - return EC_ERROR_PARAM2; - - if (!strcasecmp(argv[2], "clock")) { - int freq; - - if (argc < 4) - return EC_ERROR_PARAM2; - - freq = strtoi(argv[3], &e, 10); - if (*e) - return EC_ERROR_PARAM2; - pd_set_clock(port, freq); - ccprintf("set TX frequency to %d Hz\n", freq); - return EC_SUCCESS; - } else if (!strncasecmp(argv[2], "state", 5)) { - ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d\n" - "Alert: 0x%02x Mask: 0x%04x\n" - "Power Status: 0x%02x Mask: 0x%02x\n", port, - pd[port].rx_enabled ? "Ena" : "Dis", - pd[port].cc_pull, - pd[port].cc_status[0], pd[port].cc_status[1], - pd[port].alert, pd[port].alert_mask, - pd[port].power_status, pd[port].power_status_mask); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(tcpc, command_tcpc, - "dump [0|1]\n\t<port> [clock|state]", - "Type-C Port Controller"); -#endif diff --git a/common/usb_port_power_dumb.c b/common/usb_port_power_dumb.c deleted file mode 100644 index 09c7e29033..0000000000 --- a/common/usb_port_power_dumb.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright 2012 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. - */ - -/* USB charging control module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "usb_charge.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr) -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -static uint8_t charge_mode[USB_PORT_COUNT]; - -static void usb_port_set_enabled(int port_id, int en) -{ - gpio_set_level(usb_port_enable[port_id], en); - charge_mode[port_id] = en; -} - -__maybe_unused static void usb_port_all_ports_on(void) -{ - int i; - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, 1); -} - -static void usb_port_all_ports_off(void) -{ - int i; - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, 0); -} - -/*****************************************************************************/ -/* Host commands */ - -int usb_charge_set_mode(int port_id, enum usb_charge_mode mode, - enum usb_suspend_charge inhibit_charge) -{ - CPRINTS("USB port p%d %d", port_id, mode); - - if (port_id < 0 || port_id >= USB_PORT_COUNT) - return EC_ERROR_INVAL; - - switch (mode) { - case USB_CHARGE_MODE_DISABLED: - usb_port_set_enabled(port_id, 0); - break; - case USB_CHARGE_MODE_ENABLED: - usb_port_set_enabled(port_id, 1); - break; - default: - return EC_ERROR_UNKNOWN; - } - - return EC_SUCCESS; -} - -static enum ec_status -usb_port_command_set_mode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_charge_set_mode *p = args->params; - - if (usb_charge_set_mode(p->usb_port_id, p->mode, - p->inhibit_charge) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_CHARGE_SET_MODE, - usb_port_command_set_mode, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Console commands */ - -static int command_set_mode(int argc, char **argv) -{ - int port_id = -1; - int mode = -1; - int i; - char *e; - - switch (argc) { - case 3: - port_id = strtoi(argv[1], &e, 0); - if (*e || port_id < 0 || port_id >= USB_PORT_COUNT) - return EC_ERROR_PARAM1; - - if (!parse_bool(argv[2], &mode)) - return EC_ERROR_PARAM2; - - usb_port_set_enabled(port_id, mode); - /* fallthrough */ - case 1: - for (i = 0; i < USB_PORT_COUNT; i++) - ccprintf("Port %d: %s\n", - i, charge_mode[i] ? "on" : "off"); - return EC_SUCCESS; - } - - return EC_ERROR_PARAM_COUNT; -} -DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode, - "[<port> <on | off>]", - "Set USB charge mode"); - - -/*****************************************************************************/ -/* Hooks */ - -static void usb_port_preserve_state(void) -{ - system_add_jump_tag(USB_SYSJUMP_TAG, USB_HOOK_VERSION, - sizeof(charge_mode), charge_mode); -} -DECLARE_HOOK(HOOK_SYSJUMP, usb_port_preserve_state, HOOK_PRIO_DEFAULT); - -static void usb_port_init(void) -{ - const uint8_t *prev; - int version, size, i; - - prev = (const uint8_t *)system_get_jump_tag(USB_SYSJUMP_TAG, - &version, &size); - if (!prev || version != USB_HOOK_VERSION || - size != sizeof(charge_mode)) { - usb_port_all_ports_off(); - return; - } - - for (i = 0; i < USB_PORT_COUNT; i++) - usb_port_set_enabled(i, prev[i]); -} -DECLARE_HOOK(HOOK_INIT, usb_port_init, HOOK_PRIO_DEFAULT); - -#ifndef CONFIG_USB_PORT_POWER_DUMB_CUSTOM_HOOK -static void usb_port_startup(void) -{ - /* Turn on USB ports on as we go into S0 from S5. */ - usb_port_all_ports_on(); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, usb_port_startup, HOOK_PRIO_DEFAULT); - -static void usb_port_shutdown(void) -{ - /* Turn on USB ports off as we go back to S5. */ - usb_port_all_ports_off(); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_port_shutdown, HOOK_PRIO_DEFAULT); -#endif /* CONFIG_USB_PORT_POWER_DUMB_CUSTOM_HOOK */ diff --git a/common/usb_port_power_smart.c b/common/usb_port_power_smart.c deleted file mode 100644 index 170180cbab..0000000000 --- a/common/usb_port_power_smart.c +++ /dev/null @@ -1,257 +0,0 @@ -/* Copyright 2012 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. - */ - -/* USB charging control module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "usb_charge.h" -#include "util.h" - -#define CPUTS(outstr) cputs(CC_USBCHARGE, outstr) -#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) - -#ifndef CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE -#define CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE USB_CHARGE_MODE_SDP2 -#endif - -struct charge_mode_t { - uint8_t mode:7; - uint8_t inhibit_charging_in_suspend:1; -} __pack; - -static struct charge_mode_t charge_mode[CONFIG_USB_PORT_POWER_SMART_PORT_COUNT]; - -#ifdef CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY -/* - * If we only support CDP and SDP, the control signals are hard-wired so - * there's nothing to do. The only to do is set ILIM_SEL. - */ -static void usb_charge_set_control_mode(int port_id, int mode) {} -#else /* !defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ -static void usb_charge_set_control_mode(int port_id, int mode) -{ -#ifdef CONFIG_USB_PORT_POWER_SMART_SIMPLE - /* - * One single shared control signal, so the last mode set to either - * port wins. Also, only CTL1 can be set; the other pins are - * hard-wired. - */ - gpio_or_ioex_set_level(GPIO_USB_CTL1, mode & 0x4); -#else - if (port_id == 0) { - gpio_or_ioex_set_level(GPIO_USB1_CTL1, mode & 0x4); - gpio_or_ioex_set_level(GPIO_USB1_CTL2, mode & 0x2); - gpio_or_ioex_set_level(GPIO_USB1_CTL3, mode & 0x1); - } else { - gpio_or_ioex_set_level(GPIO_USB2_CTL1, mode & 0x4); - gpio_or_ioex_set_level(GPIO_USB2_CTL2, mode & 0x2); - gpio_or_ioex_set_level(GPIO_USB2_CTL3, mode & 0x1); - } -#endif /* defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) */ -} -#endif /* defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ - -static void usb_charge_set_enabled(int port_id, int en) -{ - ASSERT(port_id < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT); - gpio_or_ioex_set_level(usb_port_enable[port_id], en); -} - -static void usb_charge_set_ilim(int port_id, int sel) -{ - int ilim_sel; - -#if defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) || \ - defined(CONFIG_USB_PORT_POWER_SMART_INVERTED) - /* ILIM_SEL is inverted. */ - sel = !sel; -#endif - - ilim_sel = GPIO_USB1_ILIM_SEL; -#if !defined(CONFIG_USB_PORT_POWER_SMART_SIMPLE) && \ - CONFIG_USB_PORT_POWER_SMART_PORT_COUNT == 2 - if (port_id != 0) - ilim_sel = GPIO_USB2_ILIM_SEL; -#endif - - gpio_or_ioex_set_level(ilim_sel, sel); -} - -static void usb_charge_all_ports_ctrl(enum usb_charge_mode mode) -{ - int i; - - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, mode, USB_ALLOW_SUSPEND_CHARGE); -} - -int usb_charge_set_mode(int port_id, enum usb_charge_mode mode, - enum usb_suspend_charge inhibit_charge) -{ - CPRINTS("USB charge p%d m%d i%d", port_id, mode, inhibit_charge); - - if (port_id >= CONFIG_USB_PORT_POWER_SMART_PORT_COUNT) - return EC_ERROR_INVAL; - - if (mode == USB_CHARGE_MODE_DEFAULT) - mode = CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE; - - switch (mode) { - case USB_CHARGE_MODE_DISABLED: - usb_charge_set_enabled(port_id, 0); - break; - case USB_CHARGE_MODE_SDP2: - usb_charge_set_control_mode(port_id, 7); - usb_charge_set_ilim(port_id, 0); - usb_charge_set_enabled(port_id, 1); - break; - case USB_CHARGE_MODE_CDP: - usb_charge_set_control_mode(port_id, 7); - usb_charge_set_ilim(port_id, 1); - usb_charge_set_enabled(port_id, 1); - break; -#ifndef CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY - case USB_CHARGE_MODE_DCP_SHORT: - usb_charge_set_control_mode(port_id, 4); - usb_charge_set_enabled(port_id, 1); - break; -#endif /* !defined(CONFIG_USB_PORT_POWER_SMART_CDP_SDP_ONLY) */ - default: - return EC_ERROR_UNKNOWN; - } - - charge_mode[port_id].mode = mode; - charge_mode[port_id].inhibit_charging_in_suspend = inhibit_charge; - - return EC_SUCCESS; -} - -/*****************************************************************************/ -/* Console commands */ - -static int command_set_mode(int argc, char **argv) -{ - int port_id = -1; - int mode = -1, inhibit_charge = 0; - char *e; - int i; - - if (argc == 1) { - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - ccprintf("Port %d: %d,%d\n", i, charge_mode[i].mode, - charge_mode[i].inhibit_charging_in_suspend); - return EC_SUCCESS; - } - - if (argc != 3 && argc != 4) - return EC_ERROR_PARAM_COUNT; - - port_id = strtoi(argv[1], &e, 0); - if (*e || port_id < 0 || - port_id >= CONFIG_USB_PORT_POWER_SMART_PORT_COUNT) - return EC_ERROR_PARAM1; - - mode = strtoi(argv[2], &e, 0); - if (*e || mode < 0 || mode >= USB_CHARGE_MODE_COUNT) - return EC_ERROR_PARAM2; - - if (argc == 4) { - inhibit_charge = strtoi(argv[3], &e, 0); - if (*e || (inhibit_charge != 0 && inhibit_charge != 1)) - return EC_ERROR_PARAM3; - } - - return usb_charge_set_mode(port_id, mode, inhibit_charge); -} -DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode, - "[<port> <0 | 1 | 2 | 3> [<0 | 1>]]", - "Set USB charge mode"); - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -usb_charge_command_set_mode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_charge_set_mode *p = args->params; - - if (usb_charge_set_mode(p->usb_port_id, p->mode, - p->inhibit_charge) != EC_SUCCESS) - return EC_RES_ERROR; - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_CHARGE_SET_MODE, - usb_charge_command_set_mode, - EC_VER_MASK(0)); - -/*****************************************************************************/ -/* Hooks */ - -static void usb_charge_preserve_state(void) -{ - system_add_jump_tag(USB_SYSJUMP_TAG, USB_HOOK_VERSION, - sizeof(charge_mode), charge_mode); -} -DECLARE_HOOK(HOOK_SYSJUMP, usb_charge_preserve_state, HOOK_PRIO_DEFAULT); - -static void usb_charge_init(void) -{ - const struct charge_mode_t *prev; - int version, size, i; - - prev = (const struct charge_mode_t *)system_get_jump_tag(USB_SYSJUMP_TAG, - &version, &size); - - if (!prev || version != USB_HOOK_VERSION || - size != sizeof(charge_mode)) { - usb_charge_all_ports_ctrl(USB_CHARGE_MODE_DISABLED); - return; - } - - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, prev[i].mode, - prev[i].inhibit_charging_in_suspend); -} -DECLARE_HOOK(HOOK_INIT, usb_charge_init, HOOK_PRIO_DEFAULT); - -static void usb_charge_resume(void) -{ - int i; - - /* Turn on USB ports on as we go into S0 from S3 or S5. */ - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - usb_charge_set_mode(i, - CONFIG_USB_PORT_POWER_SMART_DEFAULT_MODE, - charge_mode[i].inhibit_charging_in_suspend); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, usb_charge_resume, HOOK_PRIO_DEFAULT); - -static void usb_charge_suspend(void) -{ - int i; - - /* - * Inhibit charging during suspend if the inhibit_charging_in_suspend - * is set to 1. - */ - for (i = 0; i < CONFIG_USB_PORT_POWER_SMART_PORT_COUNT; i++) - if (charge_mode[i].inhibit_charging_in_suspend) - usb_charge_set_enabled(i, 0 /* disabled */); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, usb_charge_suspend, HOOK_PRIO_DEFAULT); - -static void usb_charge_shutdown(void) -{ - /* Turn on USB ports off as we go back to S5. */ - usb_charge_all_ports_ctrl(USB_CHARGE_MODE_DISABLED); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, usb_charge_shutdown, HOOK_PRIO_DEFAULT); diff --git a/common/usb_update.c b/common/usb_update.c deleted file mode 100644 index 3b307ede9a..0000000000 --- a/common/usb_update.c +++ /dev/null @@ -1,594 +0,0 @@ -/* Copyright 2016 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 "byteorder.h" -#include "common.h" -#include "console.h" -#include "consumer.h" -#include "curve25519.h" -#include "flash.h" -#include "queue_policies.h" -#include "host_command.h" -#include "rollback.h" -#include "rwsig.h" -#include "sha256.h" -#include "system.h" -#include "uart.h" -#include "update_fw.h" -#include "usb-stream.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) - -/* - * This file is an adaptation layer between the USB interface and the firmware - * update engine. The engine expects to receive long blocks of data, 1K or so - * in size, prepended by the offset where the data needs to be programmed into - * the flash and a 4 byte integrity check value. - * - * The USB transfer, on the other hand, operates on much shorter chunks of - * data, typically 64 bytes in this case. This module reassembles firmware - * programming blocks from the USB chunks, and invokes the programmer passing - * it the full block. - * - * The programmer reports results by putting the return value into the same - * buffer where the block was passed in. This wrapper retrieves the - * programmer's return value, and sends it back to the host. The return value - * is usually one byte in size, the only exception is the connection - * establishment phase where the return value is 16 bytes in size. - * - * In the end of the successful image transfer and programming, the host sends - * the reset command, and the device reboots itself. - */ - -struct consumer const update_consumer; -struct usb_stream_config const usb_update; - -static struct queue const update_to_usb = QUEUE_DIRECT(64, uint8_t, - null_producer, - usb_update.consumer); -static struct queue const usb_to_update = QUEUE_DIRECT(64, uint8_t, - usb_update.producer, - update_consumer); - -USB_STREAM_CONFIG_FULL(usb_update, - USB_IFACE_UPDATE, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_GOOGLE_UPDATE, - USB_PROTOCOL_GOOGLE_UPDATE, - USB_STR_UPDATE_NAME, - USB_EP_UPDATE, - USB_MAX_PACKET_SIZE, - USB_MAX_PACKET_SIZE, - usb_to_update, - update_to_usb) - - -/* The receiver can be in one of the states below. */ -enum rx_state { - rx_idle, /* Nothing happened yet. */ - rx_inside_block, /* Assembling a block to pass to the programmer. */ - rx_outside_block, /* Waiting for the next block to start or for the - reset command. */ -}; - -enum rx_state rx_state_ = rx_idle; -static uint8_t block_buffer[sizeof(struct update_command) + - CONFIG_UPDATE_PDU_SIZE]; -static uint32_t block_size; -static uint32_t block_index; - -#ifdef CONFIG_USB_PAIRING -#define KEY_CONTEXT "device-identity" - -static int pair_challenge(struct pair_challenge *challenge) -{ - uint8_t response; - - /* Scratchpad for device secret and x25519 public/shared key. */ - uint8_t tmp[32]; - BUILD_ASSERT(sizeof(tmp) >= X25519_PUBLIC_VALUE_LEN); - BUILD_ASSERT(sizeof(tmp) >= X25519_PRIVATE_KEY_LEN); - BUILD_ASSERT(sizeof(tmp) >= CONFIG_ROLLBACK_SECRET_SIZE); - - /* Scratchpad for device_private and authenticator. */ - uint8_t tmp2[32]; - BUILD_ASSERT(sizeof(tmp2) >= X25519_PRIVATE_KEY_LEN); - BUILD_ASSERT(sizeof(tmp2) >= SHA256_DIGEST_SIZE); - - /* tmp = device_secret */ - if (rollback_get_secret(tmp) != EC_SUCCESS) { - response = EC_RES_UNAVAILABLE; - QUEUE_ADD_UNITS(&update_to_usb, &response, sizeof(response)); - return 1; - } - - /* - * Nothing can fail from now on, let's push data to the queue as soon as - * possible to save some temporary variables. - */ - response = EC_RES_SUCCESS; - QUEUE_ADD_UNITS(&update_to_usb, &response, sizeof(response)); - - /* - * tmp2 = device_private - * = HMAC_SHA256(device_secret, "device-identity") - */ - hmac_SHA256(tmp2, tmp, CONFIG_ROLLBACK_SECRET_SIZE, - KEY_CONTEXT, sizeof(KEY_CONTEXT) - 1); - - /* tmp = device_public = x25519(device_private, x25519_base_point) */ - X25519_public_from_private(tmp, tmp2); - QUEUE_ADD_UNITS(&update_to_usb, tmp, sizeof(tmp)); - - /* tmp = shared_secret = x25519(device_private, host_public) */ - X25519(tmp, tmp2, challenge->host_public); - - /* tmp2 = authenticator = HMAC_SHA256(shared_secret, nonce) */ - hmac_SHA256(tmp2, tmp, sizeof(tmp), - challenge->nonce, sizeof(challenge->nonce)); - QUEUE_ADD_UNITS(&update_to_usb, tmp2, - member_size(struct pair_challenge_response, authenticator)); - return 1; -} -#endif - -/* - * Fetches a transfer start frame from the queue. This can be either an update - * start frame (block_size = 0, all of cmd = 0), or the beginning of a frame - * (block_size > 0, valid block_base in cmd). - */ -static int fetch_transfer_start(struct consumer const *consumer, size_t count, - struct update_frame_header *pupfr) -{ - int i; - - /* - * Let's just make sure we drain the queue no matter what the contents - * are. This way they won't be in the way during next callback, even - * if these contents are not what's expected. - * - * Note: If count > sizeof(*pupfr), pupfr will be corrupted. This is - * ok as we will immediately fail after this. - */ - i = count; - while (i > 0) { - QUEUE_REMOVE_UNITS(consumer->queue, pupfr, - MIN(i, sizeof(*pupfr))); - i -= sizeof(*pupfr); - } - - if (count != sizeof(struct update_frame_header)) { - CPRINTS("FW update: wrong first block, size %d", count); - return 0; - } - - return 1; -} - -static int try_vendor_command(struct consumer const *consumer, size_t count) -{ - char buffer[USB_MAX_PACKET_SIZE]; - struct update_frame_header *cmd_buffer = (void *)buffer; - int rv = 0; - - /* Validate count (too short, or too long). */ - if (count < sizeof(*cmd_buffer) || count > sizeof(buffer)) - return 0; - - /* - * Let's copy off the queue the update frame header, to see if this - * is a channeled vendor command. - */ - queue_peek_units(consumer->queue, cmd_buffer, 0, sizeof(*cmd_buffer)); - if (be32toh(cmd_buffer->cmd.block_base) != UPDATE_EXTRA_CMD) - return 0; - - if (be32toh(cmd_buffer->block_size) != count) { - CPRINTS("%s: problem: block size and count mismatch (%d != %d)", - __func__, be32toh(cmd_buffer->block_size), count); - return 0; - } - - /* Get the entire command, don't remove it from the queue just yet. */ - queue_peek_units(consumer->queue, cmd_buffer, 0, count); - - /* Looks like this is a vendor command, let's verify it. */ - if (update_pdu_valid(&cmd_buffer->cmd, - count - offsetof(struct update_frame_header, cmd))) { - enum update_extra_command subcommand; - uint8_t response; - size_t response_size = sizeof(response); - int __attribute__((unused)) header_size; - int __attribute__((unused)) data_count; - - /* looks good, let's process it. */ - rv = 1; - - /* Now remove it from the queue. */ - queue_advance_head(consumer->queue, count); - - subcommand = be16toh(*((uint16_t *)(cmd_buffer + 1))); - - /* - * header size: update frame header + 2 bytes for subcommand - * data_count: Some commands take in extra data as parameter - */ - header_size = sizeof(*cmd_buffer) + sizeof(uint16_t); - data_count = count - header_size; - - switch (subcommand) { - case UPDATE_EXTRA_CMD_IMMEDIATE_RESET: - CPRINTS("Rebooting!"); - CPRINTF("\n\n"); - cflush(); - system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED); - /* Unreachable, unless something bad happens. */ - response = EC_RES_ERROR; - break; - case UPDATE_EXTRA_CMD_JUMP_TO_RW: -#ifdef CONFIG_RWSIG - /* - * Tell rwsig task to jump to RW. This does nothing if - * verification failed, and will only jump later on if - * verification is still in progress. - */ - rwsig_continue(); - - switch (rwsig_get_status()) { - case RWSIG_VALID: - response = EC_RES_SUCCESS; - break; - case RWSIG_INVALID: - response = EC_RES_INVALID_CHECKSUM; - break; - case RWSIG_IN_PROGRESS: - response = EC_RES_IN_PROGRESS; - break; - default: - response = EC_RES_ERROR; - } -#else - system_run_image_copy(EC_IMAGE_RW); -#endif - break; -#ifdef CONFIG_RWSIG - case UPDATE_EXTRA_CMD_STAY_IN_RO: - rwsig_abort(); - response = EC_RES_SUCCESS; - break; -#endif - case UPDATE_EXTRA_CMD_UNLOCK_RW: - crec_flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, 0); - response = EC_RES_SUCCESS; - break; -#ifdef CONFIG_ROLLBACK - case UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK: - crec_flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_AT_BOOT - , 0); - response = EC_RES_SUCCESS; - break; -#ifdef CONFIG_ROLLBACK_SECRET_SIZE -#ifdef CONFIG_ROLLBACK_UPDATE - case UPDATE_EXTRA_CMD_INJECT_ENTROPY: { - if (data_count < CONFIG_ROLLBACK_SECRET_SIZE) { - CPRINTS("Entropy too short"); - response = EC_RES_INVALID_PARAM; - break; - } - - CPRINTS("Adding %db of entropy", data_count); - /* Add the entropy to secret. */ - rollback_add_entropy(buffer + header_size, data_count); - break; - } -#endif /* CONFIG_ROLLBACK_UPDATE */ -#ifdef CONFIG_USB_PAIRING - case UPDATE_EXTRA_CMD_PAIR_CHALLENGE: { - if (data_count < sizeof(struct pair_challenge)) { - CPRINTS("Challenge data too short"); - response = EC_RES_INVALID_PARAM; - break; - } - - /* pair_challenge takes care of answering */ - return pair_challenge((struct pair_challenge *) - (buffer + header_size)); - } -#endif -#endif /* CONFIG_ROLLBACK_SECRET_SIZE */ -#endif /* CONFIG_ROLLBACK */ -#ifdef CONFIG_TOUCHPAD - case UPDATE_EXTRA_CMD_TOUCHPAD_INFO: { - struct touchpad_info tp = { 0 }; - - if (data_count != 0) { - response = EC_RES_INVALID_PARAM; - break; - } - - response_size = touchpad_get_info(&tp); - if (response_size < 1) { - response = EC_RES_ERROR; - break; - } - -#ifdef CONFIG_TOUCHPAD_VIRTUAL_OFF - tp.fw_address = CONFIG_TOUCHPAD_VIRTUAL_OFF; - tp.fw_size = CONFIG_TOUCHPAD_VIRTUAL_SIZE; - -#ifdef CONFIG_TOUCHPAD_HASH_FW - memcpy(tp.allowed_fw_hash, touchpad_fw_full_hash, - sizeof(tp.allowed_fw_hash)); -#endif -#endif /* CONFIG_TOUCHPAD_VIRTUAL_OFF */ - QUEUE_ADD_UNITS(&update_to_usb, - &tp, response_size); - return 1; - } - case UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG: { - uint8_t *data = NULL; - unsigned int write_count = 0; - - /* - * Let the touchpad driver decide what it wants to do - * with the payload data, and put the response in data. - */ - response = touchpad_debug(buffer + header_size, - data_count, &data, &write_count); - - /* - * On error, or if there is no data to write back, just - * write back response. - */ - if (response != EC_RES_SUCCESS || write_count == 0) - break; - - /* Check that we can write all the data to the queue. */ - if (write_count > queue_space(&update_to_usb)) - return EC_RES_BUSY; - - QUEUE_ADD_UNITS(&update_to_usb, data, write_count); - return 1; - } -#endif -#ifdef CONFIG_USB_CONSOLE_READ - /* - * TODO(b/112877237): move this to a new interface, so we can - * support reading log and other commands at the same time? - */ - case UPDATE_EXTRA_CMD_CONSOLE_READ_INIT: - response = uart_console_read_buffer_init(); - break; - case UPDATE_EXTRA_CMD_CONSOLE_READ_NEXT: { - uint8_t *data = buffer + header_size; - uint8_t output[64]; - uint16_t write_count = 0; - - if (data_count != 1) { - response = EC_RES_INVALID_PARAM; - break; - } - - response = uart_console_read_buffer( - data[0], - (char *)output, - MIN(sizeof(output), - queue_space(&update_to_usb)), - &write_count); - if (response != EC_RES_SUCCESS || write_count == 0) - break; - - QUEUE_ADD_UNITS(&update_to_usb, output, write_count); - return 1; - } -#endif - default: - response = EC_RES_INVALID_COMMAND; - } - - QUEUE_ADD_UNITS(&update_to_usb, &response, response_size); - } - - return rv; -} - -/* - * When was last time a USB callback was called, in microseconds, free running - * timer. - */ -static uint64_t prev_activity_timestamp; - -/* - * A flag indicating that at least one valid PDU containing flash update block - * has been received in the current transfer session. - */ -static uint8_t data_was_transferred; - -/* Reply with an error to remote side, reset state. */ -static void send_error_reset(uint8_t resp_value) -{ - QUEUE_ADD_UNITS(&update_to_usb, &resp_value, 1); - rx_state_ = rx_idle; - data_was_transferred = 0; -} - -/* Called to deal with data from the host */ -static void update_out_handler(struct consumer const *consumer, size_t count) -{ - struct update_frame_header upfr; - size_t resp_size; - uint8_t resp_value; - uint64_t delta_time; - - /* How much time since the previous USB callback? */ - delta_time = get_time().val - prev_activity_timestamp; - prev_activity_timestamp += delta_time; - - /* If timeout exceeds 5 seconds - let's start over. */ - if ((delta_time > 5000000) && (rx_state_ != rx_idle)) { - rx_state_ = rx_idle; - CPRINTS("FW update: recovering after timeout"); - } - - if (rx_state_ == rx_idle) { - /* - * The payload must be an update initiating PDU. - * - * The size of the response returned in the same buffer will - * exceed the received frame size; Let's make sure there is - * enough room for the response in the buffer. - */ - union { - struct update_frame_header upfr; - struct { - uint32_t unused; - struct first_response_pdu startup_resp; - }; - } u; - - /* Check is this is a channeled TPM extension command. */ - if (try_vendor_command(consumer, count)) - return; - - /* - * An update start PDU is a command without any payload, with - * digest = 0, and base = 0. - */ - if (!fetch_transfer_start(consumer, count, &u.upfr) || - be32toh(u.upfr.block_size) != - sizeof(struct update_frame_header) || - u.upfr.cmd.block_digest != 0 || - u.upfr.cmd.block_base != 0) { - /* - * Something is wrong, this payload is not a valid - * update start PDU. Let'w indicate this by returning - * a single byte error code. - */ - CPRINTS("FW update: invalid start."); - send_error_reset(UPDATE_GEN_ERROR); - return; - } - - CPRINTS("FW update: starting..."); - fw_update_command_handler(&u.upfr.cmd, count - - offsetof(struct update_frame_header, - cmd), - &resp_size); - - if (!u.startup_resp.return_value) { - rx_state_ = rx_outside_block; /* We're in business. */ - data_was_transferred = 0; /* No data received yet. */ - } - - /* Let the host know what updater had to say. */ - QUEUE_ADD_UNITS(&update_to_usb, &u.startup_resp, resp_size); - return; - } - - if (rx_state_ == rx_outside_block) { - /* - * Expecting to receive the beginning of the block or the - * reset command if all data blocks have been processed. - */ - if (count == 4) { - uint32_t command; - - QUEUE_REMOVE_UNITS(consumer->queue, &command, - sizeof(command)); - command = be32toh(command); - if (command == UPDATE_DONE) { - CPRINTS("FW update: done"); - - if (data_was_transferred) { - fw_update_complete(); - data_was_transferred = 0; - } - - resp_value = 0; - QUEUE_ADD_UNITS(&update_to_usb, - &resp_value, 1); - rx_state_ = rx_idle; - return; - } - } - - /* - * At this point we expect a block start message. It is - * sizeof(upfr) bytes in size. - */ - if (!fetch_transfer_start(consumer, count, &upfr)) { - CPRINTS("Invalid block start."); - send_error_reset(UPDATE_GEN_ERROR); - return; - } - - /* Let's allocate a large enough buffer. */ - block_size = be32toh(upfr.block_size) - - offsetof(struct update_frame_header, cmd); - - /* - * Only update start PDU is allowed to have a size 0 payload. - */ - if (block_size <= sizeof(struct update_command) || - block_size > sizeof(block_buffer)) { - CPRINTS("Invalid block size (%d).", block_size); - send_error_reset(UPDATE_GEN_ERROR); - return; - } - - /* - * Copy the rest of the message into the block buffer to pass - * to the updater. - */ - block_index = sizeof(upfr) - - offsetof(struct update_frame_header, cmd); - memcpy(block_buffer, &upfr.cmd, block_index); - block_size -= block_index; - rx_state_ = rx_inside_block; - return; - } - - /* Must be inside block. */ - QUEUE_REMOVE_UNITS(consumer->queue, block_buffer + block_index, count); - block_index += count; - block_size -= count; - - if (block_size) { - if (count <= sizeof(upfr)) { - /* - * A block header size instead of chunk size message - * has been received, let's abort the transfer. - */ - CPRINTS("Unexpected header"); - send_error_reset(UPDATE_GEN_ERROR); - return; - } - return; /* More to come. */ - } - - /* - * Ok, the entire block has been received and reassembled, pass it to - * the updater for verification and programming. - */ - fw_update_command_handler(block_buffer, block_index, &resp_size); - - /* - * There was at least an attempt to program the flash, set the - * flag. - */ - data_was_transferred = 1; - resp_value = block_buffer[0]; - QUEUE_ADD_UNITS(&update_to_usb, &resp_value, sizeof(resp_value)); - rx_state_ = rx_outside_block; -} - -struct consumer const update_consumer = { - .queue = &usb_to_update, - .ops = &((struct consumer_ops const) { - .written = update_out_handler, - }), -}; diff --git a/common/usbc/build.mk b/common/usbc/build.mk deleted file mode 100644 index 48ab5351b8..0000000000 --- a/common/usbc/build.mk +++ /dev/null @@ -1,51 +0,0 @@ -# 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. - -# Build for USB Type-C and Power Delivery - -# Note that this variable includes the trailing "/" -_usbc_dir:=$(dir $(lastword $(MAKEFILE_LIST))) - -ifneq ($(CONFIG_USB_PD_TCPMV2),) -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_pd_timer.o -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_sm.o -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usbc_task.o - -# Type-C state machines -ifneq ($(CONFIG_USB_TYPEC_SM),) -all-obj-$(CONFIG_USB_VPD)+=$(_usbc_dir)usb_tc_vpd_sm.o -all-obj-$(CONFIG_USB_CTVPD)+=$(_usbc_dir)usb_tc_ctvpd_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_tc_drp_acc_trysrc_sm.o -endif # CONFIG_USB_TYPEC_SM - -# Protocol state machine -ifneq ($(CONFIG_USB_PRL_SM),) -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_prl_sm.o -endif # CONFIG_USB_PRL_SM - -# Policy Engine state machines -ifneq ($(CONFIG_USB_PE_SM),) -all-obj-$(CONFIG_USB_VPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_CTVPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usbc_pd_policy.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pe_drp_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pd_dpm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)dp_alt_mode.o -all-obj-$(CONFIG_USB_PD_TBT_COMPAT_MODE)+=$(_usbc_dir)tbt_alt_mode.o -all-obj-$(CONFIG_USB_PD_USB4)+=$(_usbc_dir)usb_mode.o -all-obj-$(CONFIG_CMD_PD)+=$(_usbc_dir)usb_pd_console.o -all-obj-$(CONFIG_USB_PD_HOST_CMD)+=$(_usbc_dir)usb_pd_host.o -endif # CONFIG_USB_PE_SM - -# Retimer firmware update -all-obj-$(CONFIG_USBC_RETIMER_FW_UPDATE)+=$(_usbc_dir)usb_retimer_fw_update.o - -# ALT-DP mode for UFP ports -all-obj-$(CONFIG_USB_PD_ALT_MODE_UFP_DP)+=$(_usbc_dir)usb_pd_dp_ufp.o -endif # CONFIG_USB_PD_TCPMV2 - -# For testing -all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usbc_pd_policy.o -all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usb_pe_drp_sm.o -all-obj-$(CONFIG_TEST_SM)+=$(_usbc_dir)usb_sm.o diff --git a/common/usbc/dp_alt_mode.c b/common/usbc/dp_alt_mode.c deleted file mode 100644 index 9a3493c6e1..0000000000 --- a/common/usbc/dp_alt_mode.c +++ /dev/null @@ -1,292 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * DisplayPort alternate mode support - * Refer to VESA DisplayPort Alt Mode on USB Type-C Standard, version 2.0, - * section 5.2 - */ - -#include <stdbool.h> -#include <stdint.h> -#include "assert.h" -#include "usb_common.h" -#include "usb_dp_alt_mode.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* The state of the DP negotiation */ -enum dp_states { - DP_START = 0, - DP_ENTER_ACKED, - DP_ENTER_NAKED, - DP_STATUS_ACKED, - DP_ACTIVE, - DP_ENTER_RETRY, - DP_INACTIVE, - DP_STATE_COUNT -}; -static enum dp_states dp_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Map of states to expected VDM commands in responses. - * Default of 0 indicates no command expected. - */ -static const uint8_t state_vdm_cmd[DP_STATE_COUNT] = { - [DP_START] = CMD_ENTER_MODE, - [DP_ENTER_ACKED] = CMD_DP_STATUS, - [DP_STATUS_ACKED] = CMD_DP_CONFIG, - [DP_ACTIVE] = CMD_EXIT_MODE, - [DP_ENTER_NAKED] = CMD_EXIT_MODE, - [DP_ENTER_RETRY] = CMD_ENTER_MODE, -}; - -bool dp_is_active(int port) -{ - return dp_state[port] == DP_ACTIVE; -} - -void dp_init(int port) -{ - dp_state[port] = DP_START; -} - -bool dp_entry_is_done(int port) -{ - return dp_state[port] == DP_ACTIVE || - dp_state[port] == DP_INACTIVE; -} - -static void dp_entry_failed(int port) -{ - CPRINTS("C%d: DP alt mode protocol failed!", port); - dp_state[port] = DP_INACTIVE; -} - -static bool dp_response_valid(int port, enum tcpci_msg_type type, - char *cmdt, int vdm_cmd) -{ - enum dp_states st = dp_state[port]; - - /* - * Check for an unexpected response. - * If DP is inactive, ignore the command. - */ - if (type != TCPCI_MSG_SOP || - (st != DP_INACTIVE && state_vdm_cmd[st] != vdm_cmd)) { - CPRINTS("C%d: Received unexpected DP VDM %s (cmd %d) from" - " %s in state %d", port, cmdt, vdm_cmd, - type == TCPCI_MSG_SOP ? "port partner" : "cable plug", - st); - dp_entry_failed(port); - return false; - } - return true; -} - -static void dp_exit_to_usb_mode(int port) -{ - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT, opos); - set_usb_mux_with_current_data_role(port); - - CPRINTS("C%d: Exited DP mode", port); - /* - * If the EC exits an alt mode autonomously, don't try to enter it again. If - * the AP commands the EC to exit DP mode, it might command the EC to enter - * again later, so leave the state machine ready for that possibility. - */ - dp_state[port] = IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) - ? DP_START : DP_INACTIVE; -} - -void dp_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const struct svdm_amode_data *modep = - pd_get_amode_data(port, type, USB_SID_DISPLAYPORT); - const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); - - if (!dp_response_valid(port, type, "ACK", vdm_cmd)) - return; - - /* TODO(b/155890173): Validate VDO count for specific commands */ - - switch (dp_state[port]) { - case DP_START: - case DP_ENTER_RETRY: - dp_state[port] = DP_ENTER_ACKED; - /* Inform PE layer that alt mode is now active */ - pd_set_dfp_enter_mode_flag(port, true); - break; - case DP_ENTER_ACKED: - /* DP status response & UFP's DP attention have same payload. */ - dfp_consume_attention(port, vdm); - dp_state[port] = DP_STATUS_ACKED; - break; - case DP_STATUS_ACKED: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - dp_state[port] = DP_ACTIVE; - CPRINTS("C%d: Entered DP mode", port); - break; - case DP_ACTIVE: - /* - * Request to exit mode successful, so put the module in an - * inactive state. - */ - dp_exit_to_usb_mode(port); - break; - case DP_ENTER_NAKED: - /* - * The request to exit the mode was successful, - * so try to enter the mode again. - */ - dp_state[port] = DP_ENTER_RETRY; - break; - case DP_INACTIVE: - /* - * This can occur if the mode is shutdown because - * the CPU is being turned off, and an exit mode - * command has been sent. - */ - break; - default: - /* Invalid or unexpected negotiation state */ - CPRINTF("%s called with invalid state %d\n", - __func__, dp_state[port]); - dp_entry_failed(port); - break; - } -} - -void dp_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd) -{ - if (!dp_response_valid(port, type, "NAK", vdm_cmd)) - return; - - switch (dp_state[port]) { - case DP_START: - /* - * If a request to enter DP mode is NAK'ed, this likely - * means the partner is already in DP alt mode, so - * request to exit the mode first before retrying - * the enter command. This can happen if the EC - * is restarted (e.g to go into recovery mode) while - * DP alt mode is active. - */ - dp_state[port] = DP_ENTER_NAKED; - break; - case DP_ENTER_RETRY: - /* - * Another NAK on the second attempt to enter DP mode. - * Give up. - */ - dp_entry_failed(port); - break; - case DP_ACTIVE: - /* Treat an Exit Mode NAK the same as an Exit Mode ACK. */ - dp_exit_to_usb_mode(port); - break; - default: - CPRINTS("C%d: NAK for cmd %d in state %d", port, - vdm_cmd, dp_state[port]); - dp_entry_failed(port); - break; - } -} - -int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm) -{ - const struct svdm_amode_data *modep = pd_get_amode_data(port, - TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - int vdo_count_ret; - - if (vdo_count < VDO_MAX_SIZE) - return -1; - - switch (dp_state[port]) { - case DP_START: - case DP_ENTER_RETRY: - /* Enter the first supported mode for DisplayPort. */ - vdm[0] = pd_dfp_enter_mode(port, TCPCI_MSG_SOP, - USB_SID_DISPLAYPORT, 0); - if (vdm[0] == 0) - return -1; - /* CMDT_INIT is 0, so this is a no-op */ - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - if (dp_state[port] == DP_START) - CPRINTS("C%d: Attempting to enter DP mode", port); - break; - case DP_ENTER_ACKED: - if (!(modep && modep->opos)) - return -1; - - vdo_count_ret = modep->fx->status(port, vdm); - if (vdo_count_ret == 0) - return -1; - vdm[0] |= PD_VDO_OPOS(modep->opos); - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - break; - case DP_STATUS_ACKED: - if (!(modep && modep->opos)) - return -1; - - vdo_count_ret = modep->fx->config(port, vdm); - if (vdo_count_ret == 0) - return -1; - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - break; - case DP_ENTER_NAKED: - case DP_ACTIVE: - /* - * Called to exit DP alt mode, either when the mode - * is active and the system is shutting down, or - * when an initial request to enter the mode is NAK'ed. - * This can happen if the EC is restarted (e.g to go - * into recovery mode) while DP alt mode is active. - * It would be good to invoke modep->fx->exit but - * this doesn't set up the VDM, it clears state. - * TODO(b/159856063): Clean up the API to the fx functions. - */ - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_SID_DISPLAYPORT, - 1, /* structured */ - CMD_EXIT_MODE); - - vdm[0] |= VDO_OPOS(modep->opos); - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - break; - case DP_INACTIVE: - /* - * DP mode is inactive. - */ - return -1; - default: - CPRINTF("%s called with invalid state %d\n", - __func__, dp_state[port]); - return -1; - } - return vdo_count_ret; -} diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c deleted file mode 100644 index 73e2796345..0000000000 --- a/common/usbc/tbt_alt_mode.c +++ /dev/null @@ -1,579 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Thunderbolt alternate mode support - * Refer to USB Type-C Cable and Connector Specification Release 2.0 Section F - */ - -#include "atomic.h" -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tbt.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_tbt_alt_mode.h" - -/* - * Enter/Exit TBT mode with active cable - * - * - * TBT_START |------------ - * retry_done = false | | - * | v | - * |<------------------| Exit Mode SOP | - * | retry_done = true | | | - * v | | ACK/NAK | - * Enter Mode SOP' | --------|--------- | - * ACK | NAK | Exit Mode SOP'' | - * |------|------| | | | - * | | | | ACK/NAK | - * v | | --------|--------- | - * Enter Mode SOP'' | | Exit Mode SOP' | - * | | | | | - * ACK | NAK | | | ACK/NAK | - * |------|------| | | ------------------ | - * | | | | retry_done == true? | - * v | | | | | - * Enter Mode SOP | | | No | | - * | | | |----------- | - * ACK | NAK | | |Yes | - * |-------|------| | | v | - * | | | | TBT_INACTIVE | - * v | | | retry_done = false | - * TBT_ACTIVE | | | | - * retry_done = true | | | | - * | | | | | - * v v v v | - * -----------------------------------------------------------------| - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* - * If a partner sends an Enter Mode NAK, Exit Mode and try again. This has - * happened when the EC loses state after previously entering an alt mode - * with a partner. It may be fixed in b/159495742, in which case this - * logic is unneeded. - */ -#define TBT_FLAG_RETRY_DONE BIT(0) -#define TBT_FLAG_EXIT_DONE BIT(1) -#define TBT_FLAG_CABLE_ENTRY_DONE BIT(2) - -static uint8_t tbt_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define TBT_SET_FLAG(port, flag) (tbt_flags[port] |= (flag)) -#define TBT_CLR_FLAG(port, flag) (tbt_flags[port] &= (~flag)) -#define TBT_CHK_FLAG(port, flag) (tbt_flags[port] & (flag)) - -static int tbt_prints(const char *string, int port) -{ - return CPRINTS("C%d: TBT %s", port, string); -} - -/* The states of Thunderbolt negotiation */ -enum tbt_states { - TBT_START = 0, - TBT_ENTER_SOP, - TBT_ACTIVE, - TBT_EXIT_SOP, - TBT_INACTIVE, - /* Active cable only */ - TBT_ENTER_SOP_PRIME, - TBT_ENTER_SOP_PRIME_PRIME, - TBT_EXIT_SOP_PRIME, - TBT_EXIT_SOP_PRIME_PRIME, - TBT_STATE_COUNT, -}; -static enum tbt_states tbt_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static const uint8_t state_vdm_cmd[TBT_STATE_COUNT] = { - [TBT_ENTER_SOP] = CMD_ENTER_MODE, - [TBT_ACTIVE] = CMD_EXIT_MODE, - [TBT_EXIT_SOP] = CMD_EXIT_MODE, - /* Active cable only */ - [TBT_ENTER_SOP_PRIME] = CMD_ENTER_MODE, - [TBT_ENTER_SOP_PRIME_PRIME] = CMD_ENTER_MODE, - [TBT_EXIT_SOP_PRIME] = CMD_EXIT_MODE, - [TBT_EXIT_SOP_PRIME_PRIME] = CMD_EXIT_MODE, -}; - -void tbt_init(int port) -{ - tbt_state[port] = TBT_START; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -bool tbt_is_active(int port) -{ - return tbt_state[port] != TBT_INACTIVE && - tbt_state[port] != TBT_START; -} - -bool tbt_entry_is_done(int port) -{ - return tbt_state[port] == TBT_ACTIVE || - tbt_state[port] == TBT_INACTIVE; -} - -bool tbt_cable_entry_is_done(int port) -{ - return TBT_CHK_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -static void tbt_exit_done(int port) -{ - /* - * If the EC exits an alt mode autonomously, don't try to enter it again. If - * the AP commands the EC to exit DP mode, it might command the EC to enter - * again later, so leave the state machine ready for that possibility. - */ - tbt_state[port] = IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) - ? TBT_START : TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - - if (!TBT_CHK_FLAG(port, TBT_FLAG_EXIT_DONE)) { - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - tbt_prints("Exited alternate mode", port); - return; - } - - tbt_prints("alt mode protocol failed!", port); -} - -void tbt_exit_mode_request(int port) -{ - union tbt_mode_resp_cable cable_mode_resp; - - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_EXIT_DONE); - /* - * If the port has entered USB4 mode with Thunderbolt mode for the - * cable, on request to exit, only exit Thunderbolt mode for the - * cable. - * TODO (b/156749387): Remove once data reset feature is in place. - */ - if (tbt_state[port] == TBT_ENTER_SOP) { - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* - * For Linear re-driver cables, the port enters USB4 mode - * with Thunderbolt mode for SOP prime. Hence, on request to - * exit, only exit Thunderbolt mode SOP prime - */ - tbt_state[port] = - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE ? - TBT_EXIT_SOP_PRIME : TBT_EXIT_SOP_PRIME_PRIME; - } -} - -static bool tbt_response_valid(int port, enum tcpci_msg_type type, - char *cmdt, int vdm_cmd) -{ - enum tbt_states st = tbt_state[port]; - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME) }; - - /* - * Check for an unexpected response. - * 1. invalid command - * 2. invalid Tx type for passive cable - * If Thunderbolt is inactive, ignore the command. - */ - if ((st != TBT_INACTIVE && state_vdm_cmd[st] != vdm_cmd) || - (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && - cable_mode_resp.tbt_active_passive == TBT_CABLE_PASSIVE && - type != TCPCI_MSG_SOP)) { - tbt_exit_done(port); - return false; - } - return true; -} - -/* Exit Mode process is complete, but retry Enter Mode process */ -static void tbt_retry_enter_mode(int port) -{ - tbt_state[port] = TBT_START; - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); -} - -/* Send Exit Mode to SOP''(if supported), or SOP' */ -static void tbt_active_cable_exit_mode(int port) -{ - const struct pd_discovery *disc; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - if (disc->identity.product_t1.a_rev20.sop_p_p) - tbt_state[port] = TBT_EXIT_SOP_PRIME_PRIME; - else - tbt_state[port] = TBT_EXIT_SOP_PRIME; -} - -bool tbt_cable_entry_required_for_usb4(int port) -{ - const struct pd_discovery *disc_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - /* Request to enter Thunderbolt mode for the cable prior to entering - * USB4 mode if - - * 1. Thunderbolt Mode SOP' VDO active/passive bit (B25) is - * TBT_CABLE_ACTIVE or - * 2. It's an active cable with VDM version < 2.0 or - * VDO version < 1.3 - */ - if (tbt_cable_entry_is_done(port)) - return false; - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - if (cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) - return true; - - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - disc_sop_prime = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < - VDO_VERSION_1_3) - return true; - } - return false; -} - -void intel_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const struct pd_discovery *disc; - const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); - int opos_sop, opos_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - if (!tbt_response_valid(port, type, "ACK", vdm_cmd)) - return; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - tbt_prints("enter mode SOP'", port); - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - /* For LRD cables, Enter mode SOP' -> Enter mode SOP */ - if (disc->identity.product_t1.a_rev20.sop_p_p && - cable_mode_resp.tbt_active_passive != TBT_CABLE_ACTIVE) { - tbt_state[port] = TBT_ENTER_SOP_PRIME_PRIME; - } else { - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME_PRIME: - tbt_prints("enter mode SOP''", port); - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - break; - case TBT_ENTER_SOP: - set_tbt_compat_mode_ready(port); - tbt_state[port] = TBT_ACTIVE; - tbt_prints("enter mode SOP", port); - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - /* Indicate to PE layer that alt mode is active */ - pd_set_dfp_enter_mode_flag(port, true); - break; - case TBT_ACTIVE: - tbt_prints("exit mode SOP", port); - opos_sop = pd_alt_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL, opos_sop); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - tbt_active_cable_exit_mode(port); - } else { - /* - * Exit Mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - } - break; - case TBT_EXIT_SOP: - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - tbt_prints("exit mode SOP''", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - set_usb_mux_with_current_data_role(port); - break; - case TBT_EXIT_SOP_PRIME: - tbt_prints("exit mode SOP'", port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - opos_sop_prime = - pd_alt_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL, opos_sop_prime); - set_usb_mux_with_current_data_role(port); - } else { - tbt_retry_enter_mode(port); - } - break; - case TBT_INACTIVE: - /* - * This can occur if the mode is shutdown because - * the CPU is being turned off, and an exit mode - * command has been sent. - */ - break; - default: - /* Invalid or unexpected negotiation state */ - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -void intel_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd) -{ - if (!tbt_response_valid(port, type, "NAK", vdm_cmd)) - return; - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - case TBT_ENTER_SOP_PRIME_PRIME: - case TBT_ENTER_SOP: - /* - * If a request to enter Thunderbolt mode is NAK'ed, this - * likely means the partner is already in Thunderbolt alt mode, - * so request to exit the mode first before retrying the enter - * command. This can happen if the EC is restarted - */ - tbt_state[port] = TBT_EXIT_SOP; - break; - case TBT_ACTIVE: - /* Exit SOP got NAK'ed */ - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - tbt_prints("exit mode SOP failed", port); - tbt_state[port] = TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - } - break; - case TBT_EXIT_SOP: - /* Exit SOP got NAK'ed */ - tbt_prints("exit mode SOP failed", port); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* Retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - set_usb_mux_with_current_data_role(port); - tbt_prints("exit mode SOP'' failed", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - set_usb_mux_with_current_data_role(port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_prints("exit mode SOP' failed", port); - tbt_exit_done(port); - } else { - tbt_retry_enter_mode(port); - } - break; - default: - CPRINTS("C%d: NAK for cmd %d in state %d", port, - vdm_cmd, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -static bool tbt_mode_is_supported(int port, int vdo_count) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - if (!disc->identity.idh.modal_support) - return false; - - if (get_tbt_cable_speed(port) < TBT_SS_U31_GEN1) - return false; - - /* - * TBT4 PD Discovery Flow Application Notes Revision 0.9: - * Figure 2: for active cable, SOP' should support - * SVID USB_VID_INTEL to enter Thunderbolt alt mode - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE && - !pd_is_mode_discovered_for_svid( - port, TCPCI_MSG_SOP_PRIME, USB_VID_INTEL)) - return false; - - return true; -} - -int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm, - enum tcpci_msg_type *tx_type) -{ - struct svdm_amode_data *modep; - int vdo_count_ret = 0; - union tbt_mode_resp_cable cable_mode_resp; - - *tx_type = TCPCI_MSG_SOP; - - if (vdo_count < VDO_MAX_SIZE) - return -1; - - switch (tbt_state[port]) { - case TBT_START: - if (!tbt_mode_is_supported(port, vdo_count)) - return 0; - - if (!TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - tbt_prints("attempt to enter mode", port); - else - tbt_prints("retry to enter mode", port); - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* Active cable and LRD cables send Enter Mode SOP' first */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE || - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) { - vdo_count_ret = enter_tbt_compat_mode(port, - TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - tbt_state[port] = TBT_ENTER_SOP_PRIME; - } else { - /* Passive cable send Enter Mode SOP */ - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_ENTER_SOP_PRIME_PRIME: - vdo_count_ret = - enter_tbt_compat_mode( - port, TCPCI_MSG_SOP_PRIME_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_ENTER_SOP: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - break; - case TBT_EXIT_SOP: - case TBT_ACTIVE: - /* - * Called to exit Thunderbolt alt mode, either when the mode is - * active and the system is shutting down, or when an initial - * request to enter the mode is NAK'ed. This can happen if EC - * is restarted while Thunderbolt mode is active. - */ - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS( - pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - break; - case TBT_EXIT_SOP_PRIME_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_INACTIVE: - /* Thunderbolt mode is inactive */ - return 0; - default: - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - return -1; - } - - return vdo_count_ret; -} diff --git a/common/usbc/usb_mode.c b/common/usbc/usb_mode.c deleted file mode 100644 index b9dc4973bc..0000000000 --- a/common/usbc/usb_mode.c +++ /dev/null @@ -1,311 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * USB4 mode support - * Refer USB Type-C Cable and Connector Specification Release 2.0 Section 5 and - * USB Power Delivery Specification Revision 3.0, Version 2.0 Section 6.4.8 - */ - -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mode.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usbc_ppc.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -enum usb4_mode_status { - USB4_MODE_FAILURE, - USB4_MODE_SUCCESS, -}; - -enum usb4_states { - USB4_START, - USB4_ENTER_SOP, - USB4_ENTER_SOP_PRIME, - USB4_ENTER_SOP_PRIME_PRIME, - USB4_ACTIVE, - USB4_INACTIVE, - USB4_STATE_COUNT, -}; - -/* - * USB4 PD flow: - * - * Cable type - * | - * |-------- Passive ---|---- Active -----| - * | | - * USB Highest Speed Structured VDM version - * | (cable revision)-- <2.0---->| - * --------|--------|------| | | - * | | | | >=2.0 | - * >=Gen3 Gen2 Gen1 USB2.0 | | - * | | | | VDO version--- <1.3 ---> Modal op? -- N --| - * Enter USB | | | (B21:23 of | | - * SOP with | | | Discover ID SOP'- y | - * Gen3 cable | | Skip Active cable VDO1) | | - * speed | | USB4 | TBT SVID? -- N --| - * | | mode >=1.3 | | - * Is modal op? | entry | y | - * | | Cable USB4 - N | | - * y | support? | Gen4 cable? - N - Skip - * | | | Skip USB4 | USB4 - * Is TBT SVID? -N- Enter | mode entry | mode - * | USB4 SOP | | entry - * y with Gen2 y | - * | cable speed | | - * | | | - * Is Discover mode | | - * SOP' B25? - N - Enter Enter USB4 mode | - * | USB4 SOP (SOP, SOP', SOP'') | - * | with speed | - * y from TBT mode | - * | SOP' VDO | - * | |<-- NAK -- Enter mode TBT SOP'<---| - * |---->Enter TBT SOP'-------NAK------>| | | | - * | | | | ACK | - * | ACK | | | | - * | | | |<-- NAK -- Enter mode TBT SOP'' | - * | Enter USB4 SOP | | | | - * | with speed from Exit TBT mode SOP ACK | - * | TBT mode SOP' VDO | | | | - * | ACK/NAK Enter USB4 SOP | - * | | | with speed from | - * | Exit TBT mode SOP'' TBT mode SOP' VDO | - * | | | | - * | ACK/NAK | - * | | | | - * | Exit TBT mode SOP' | - * | | | | - * | ACK/NAK | - * | | | | - * |---- N ----Retry done? -------------| |--------Retry done? ---- N -------| - * | | - * y y - * | | - * Skip USB4 mode entry Skip USB4 mode entry - */ - -static enum usb4_states usb4_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static void usb4_debug_prints(int port, enum usb4_mode_status usb4_status) -{ - CPRINTS("C%d: USB4: State:%d Status:%d", port, usb4_state[port], - usb4_status); -} - -bool enter_usb_entry_is_done(int port) -{ - return usb4_state[port] == USB4_ACTIVE || - usb4_state[port] == USB4_INACTIVE; -} - -void usb4_exit_mode_request(int port) -{ - usb4_state[port] = USB4_START; - usb_mux_set_safe_mode_exit(port); - set_usb_mux_with_current_data_role(port); -} - -void enter_usb_init(int port) -{ - usb4_state[port] = USB4_START; -} - -void enter_usb_failed(int port) -{ - /* - * Since Enter USB sets the mux state to SAFE mode, fall back - * to USB mode on receiving a NAK. - */ - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - usb4_debug_prints(port, USB4_MODE_FAILURE); - usb4_state[port] = USB4_INACTIVE; -} - -static bool enter_usb_response_valid(int port, enum tcpci_msg_type type) -{ - /* - * Check for an unexpected response. - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && - type != TCPCI_MSG_SOP) { - enter_usb_failed(port); - return false; - } - return true; -} - -bool enter_usb_port_partner_is_capable(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - if (usb4_state[port] == USB4_INACTIVE) - return false; - - if (!PD_PRODUCT_IS_USB4(disc->identity.product_t1.raw_value)) - return false; - - return true; -} - -bool enter_usb_cable_is_capable(int port) -{ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { - if (get_usb4_cable_speed(port) < USB_R30_SS_U32_U40_GEN1) - return false; - } else if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - const struct pd_discovery *disc_sop_prime = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) >= VDM_VER20 && - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver >= - VDO_VERSION_1_3) { - union active_cable_vdo2_rev30 a2_rev30 = - disc_sop_prime->identity.product_t2.a2_rev30; - /* - * For VDM version >= 2.0 and VD0 version is >= 1.3, - * do not enter USB4 mode if the cable isn't USB4 - * capable. - */ - if (a2_rev30.usb_40_support == USB4_NOT_SUPPORTED) - return false; - /* - * For VDM version < 2.0 or VDO version < 1.3, do not enter USB4 - * mode if the cable - - * doesn't support modal operation or - * doesn't support Intel SVID or - * doesn't have rounded support. - */ - } else { - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, - TCPCI_MSG_SOP_PRIME) }; - - if (!disc->identity.idh.modal_support || - !pd_is_mode_discovered_for_svid(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL) || - cable_mode_resp.tbt_rounded != - TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED) - return false; - } - } else { - /* Not Emark cable */ - return false; - } - - return true; -} - -void enter_usb_accepted(int port, enum tcpci_msg_type type) -{ - const struct pd_discovery *disc; - - if (!enter_usb_response_valid(port, type)) - return; - - switch (usb4_state[port]) { - case USB4_ENTER_SOP_PRIME: - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - if (disc->identity.product_t1.a_rev20.sop_p_p) - usb4_state[port] = USB4_ENTER_SOP_PRIME_PRIME; - else - usb4_state[port] = USB4_ENTER_SOP; - break; - case USB4_ENTER_SOP_PRIME_PRIME: - usb4_state[port] = USB4_ENTER_SOP; - break; - case USB4_ENTER_SOP: - /* Connect the SBU and USB lines to the connector */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 1); - - usb4_state[port] = USB4_ACTIVE; - - /* Set usb mux to USB4 mode */ - usb_mux_set(port, USB_PD_MUX_USB4_ENABLED, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - usb4_debug_prints(port, USB4_MODE_SUCCESS); - break; - case USB4_ACTIVE: - break; - default: - enter_usb_failed(port); - } -} - -void enter_usb_rejected(int port, enum tcpci_msg_type type) -{ - if (!enter_usb_response_valid(port, type) || - usb4_state[port] == USB4_ACTIVE) - return; - - enter_usb_failed(port); -} - -uint32_t enter_usb_setup_next_msg(int port, enum tcpci_msg_type *type) -{ - const struct pd_discovery *disc_sop_prime; - - switch (usb4_state[port]) { - case USB4_START: - disc_sop_prime = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - /* - * Ref: Tiger Lake Platform PD Controller Interface Requirements - * for Integrated USBC, section A.2.2: USB4 as DFP. - * Enter safe mode before sending Enter USB SOP/SOP'/SOP'' - * TODO (b/156749387): Remove once data reset feature is in - * place. - */ - usb_mux_set_safe_mode(port); - - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < - VDO_VERSION_1_3 || - get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { - usb4_state[port] = USB4_ENTER_SOP; - } else { - usb4_state[port] = USB4_ENTER_SOP_PRIME; - *type = TCPCI_MSG_SOP_PRIME; - } - break; - case USB4_ENTER_SOP_PRIME: - *type = TCPCI_MSG_SOP_PRIME; - break; - case USB4_ENTER_SOP_PRIME_PRIME: - *type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case USB4_ENTER_SOP: - *type = TCPCI_MSG_SOP; - break; - case USB4_ACTIVE: - return -1; - default: - return 0; - } - return get_enter_usb_msg_payload(port); -} diff --git a/common/usbc/usb_pd_console.c b/common/usbc/usb_pd_console.c deleted file mode 100644 index bbee776611..0000000000 --- a/common/usbc/usb_pd_console.c +++ /dev/null @@ -1,212 +0,0 @@ -/* Copyright 2020 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 "common.h" -#include "console.h" -#include "usb_common.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_pd.h" -#include "util.h" - -test_export_static int command_pd(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - if (argc >= 3) { - int level = strtoi(argv[2], &e, 10); - - if (*e) - return EC_ERROR_PARAM2; - - if (level < DEBUG_DISABLE) - level = DEBUG_DISABLE; - else if (level > DEBUG_LEVEL_MAX) - level = DEBUG_LEVEL_MAX; - - prl_set_debug_level(level); - pe_set_debug_level(level); - tc_set_debug_level(level); - ccprintf("debug=%d\n", level); - return EC_SUCCESS; - } - } else if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) && - !strcasecmp(argv[1], "trysrc")) { - enum try_src_override_t ov = tc_get_try_src_override(); - - if (argc >= 3) { - ov = strtoi(argv[2], &e, 10); - if (*e || ov > TRY_SRC_NO_OVERRIDE) - return EC_ERROR_PARAM3; - tc_try_src_override(ov); - } - - if (ov == TRY_SRC_NO_OVERRIDE) - ccprintf("Try.SRC System controlled\n"); - else - ccprintf("Try.SRC Forced %s\n", ov ? "ON" : "OFF"); - - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "version")) { - ccprintf("%d\n", PD_STACK_VERSION); - return EC_SUCCESS; - } - - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - if (*e || port >= CONFIG_USB_PD_PORT_MAX_COUNT) - return EC_ERROR_PARAM2; - - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) { - if (!strcasecmp(argv[2], "tx")) { - pd_dpm_request(port, DPM_REQUEST_SNK_STARTUP); - } else if (!strcasecmp(argv[2], "charger")) { - pd_dpm_request(port, DPM_REQUEST_SRC_STARTUP); - } else if (!strcasecmp(argv[2], "dev")) { - int max_volt; - - if (argc >= 4) { - max_volt = strtoi(argv[3], &e, 10) * 1000; - if (*e) - return EC_ERROR_PARAM3; - } else { - max_volt = pd_get_max_voltage(); - } - pd_request_source_voltage(port, max_volt); - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - ccprintf("max req: %dmV\n", max_volt); - } else if (!strcasecmp(argv[2], "disable")) { - pd_comm_enable(port, 0); - ccprintf("Port C%d disable\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "enable")) { - pd_comm_enable(port, 1); - ccprintf("Port C%d enabled\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "hard")) { - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } else if (!strcasecmp(argv[2], "soft")) { - pd_dpm_request(port, DPM_REQUEST_SOFT_RESET_SEND); - } else if (!strcasecmp(argv[2], "swap")) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[3], "power")) - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); - else if (!strcasecmp(argv[3], "data")) - pd_dpm_request(port, DPM_REQUEST_DR_SWAP); - else if (IS_ENABLED(CONFIG_USBC_VCONN_SWAP) && - !strcasecmp(argv[3], "vconn")) - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); - else - return EC_ERROR_PARAM3; - } else if (!strcasecmp(argv[2], "dualrole")) { - if (argc < 4) { - cflush(); - ccprintf("dual-role toggling: "); - switch (pd_get_dual_role(port)) { - case PD_DRP_TOGGLE_ON: - ccprintf("on\n"); - break; - case PD_DRP_TOGGLE_OFF: - ccprintf("off\n"); - break; - case PD_DRP_FREEZE: - ccprintf("freeze\n"); - break; - case PD_DRP_FORCE_SINK: - ccprintf("force sink\n"); - break; - case PD_DRP_FORCE_SOURCE: - ccprintf("force source\n"); - break; - cflush(); - } - } else { - if (!strcasecmp(argv[3], "on")) - pd_set_dual_role(port, - PD_DRP_TOGGLE_ON); - else if (!strcasecmp(argv[3], "off")) - pd_set_dual_role(port, - PD_DRP_TOGGLE_OFF); - else if (!strcasecmp(argv[3], "freeze")) - pd_set_dual_role(port, PD_DRP_FREEZE); - else if (!strcasecmp(argv[3], "sink")) - pd_set_dual_role(port, - PD_DRP_FORCE_SINK); - else if (!strcasecmp(argv[3], "source")) - pd_set_dual_role(port, - PD_DRP_FORCE_SOURCE); - else - return EC_ERROR_PARAM4; - } - return EC_SUCCESS; - } - } - - if (!strcasecmp(argv[2], "state")) { - cflush(); - ccprintf("Port C%d CC%d, %s - Role: %s-%s", - port, pd_get_polarity(port) + 1, - pd_comm_is_enabled(port) ? "Enable" : "Disable", - pd_get_power_role(port) == - PD_ROLE_SOURCE ? "SRC" : "SNK", - pd_get_data_role(port) == PD_ROLE_DFP ? "DFP" : "UFP"); - - if (IS_ENABLED(CONFIG_USBC_VCONN)) - ccprintf("%s ", tc_is_vconn_src(port) ? "-VC" : ""); - - ccprintf("TC State: %s, Flags: 0x%04x", - tc_get_current_state(port), - tc_get_flags(port)); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) - ccprintf(" PE State: %s, Flags: 0x%04x\n", - pe_get_current_state(port), - pe_get_flags(port)); - else - ccprintf("\n"); - - cflush(); - } else if (!strcasecmp(argv[2], "srccaps")) { - pd_srccaps_dump(port); - } - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER) && - !strcasecmp(argv[2], "timer")) { - pd_timer_dump(port); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pd, command_pd, - "version" - "\ndump [0|1|2|3]" -#ifdef CONFIG_USB_PD_TRY_SRC - "\ntrysrc [0|1|2]" -#endif - "\n\t<port> state" - "\n\t<port> srccaps" -#ifdef CONFIG_CMD_PD_TIMER - "\n\t<port> timer" -#endif /* CONFIG_CMD_PD_TIMER */ -#ifdef CONFIG_USB_PD_DUAL_ROLE - "|tx|charger|dev" - "\n\t<port> disable|enable|soft|hard" - "\n\t<port> dualrole [on|off|freeze|sink|source]" - "\n\t<port> swap [power|data|vconn]" -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - , - "USB PD"); diff --git a/common/usbc/usb_pd_dp_ufp.c b/common/usbc/usb_pd_dp_ufp.c deleted file mode 100644 index 0009b5c710..0000000000 --- a/common/usbc/usb_pd_dp_ufp.c +++ /dev/null @@ -1,448 +0,0 @@ -/* 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. - */ - -/* - * Functions required for UFP_D operation - */ - -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "usb_pd.h" -#include "usb_pd_dp_ufp.h" - - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -enum hpd_state { - LOW_WAIT, - HIGH_CHECK, - HIGH_WAIT, - LOW_CHECK, - IRQ_CHECK, -}; - -#define EDGE_QUEUE_DEPTH BIT(3) -#define EDGE_QUEUE_MASK (EDGE_QUEUE_DEPTH - 1) -#define HPD_QUEUE_DEPTH BIT(2) -#define HPD_QUEUE_MASK (HPD_QUEUE_DEPTH - 1) -#define HPD_T_IRQ_MIN_PULSE 250 -#define HPD_T_IRQ_MAX_PULSE (2 * MSEC) -#define HPD_T_MIN_DP_ATTEN (10 * MSEC) - -struct hpd_mark { - int level; - uint64_t ts; -}; - -struct hpd_edge { - int overflow; - uint32_t head; - uint32_t tail; - struct hpd_mark buffer[EDGE_QUEUE_DEPTH]; -}; - -struct hpd_info { - enum hpd_state state; - int count; - int send_enable; - uint64_t timer; - uint64_t last_send_ts; - enum hpd_event queue[HPD_QUEUE_DEPTH]; - struct hpd_edge edges; -}; - -static struct hpd_info hpd; -static struct mutex hpd_mutex; - -static int alt_dp_mode_opos[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void pd_ufp_set_dp_opos(int port, int opos) -{ - alt_dp_mode_opos[port] = opos; -} - -int pd_ufp_get_dp_opos(int port) -{ - return alt_dp_mode_opos[port]; -} - -void pd_ufp_enable_hpd_send(int port) -{ - /* - * This control is used ensure that a DP_ATTENTION message is not sent - * to the DFP-D before a DP_CONFIG messaage has been received. This - * control is not strictly required by the spec, but some port partners - * will get confused if DP_ATTENTION is sent prior to DP_CONFIG. - */ - hpd.send_enable = 1; -} - -static void hpd_to_dp_attention(void) -{ - int port = hpd_config.port; - int evt_index = hpd.count - 1; - uint32_t vdm[2]; - uint32_t svdm_header; - enum hpd_event evt; - int opos = pd_ufp_get_dp_opos(port); - - if (!opos) - return; - - /* Get the next hpd event from the queue */ - evt = hpd.queue[evt_index]; - /* Save timestamp of when most recent DP attention message was sent */ - hpd.last_send_ts = get_time().val; - - /* - * Construct DP Attention message. This consists of the VDM header and - * the DP_STATUS VDO. - */ - svdm_header = VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)) | - VDO_OPOS(opos) | CMD_ATTENTION; - vdm[0] = VDO(USB_SID_DISPLAYPORT, 1, svdm_header); - - vdm[1] = VDO_DP_STATUS((evt == hpd_irq), /* IRQ_HPD */ - (evt != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - dock_get_mf_preference(), /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - - /* Send request to DPM to send an attention VDM */ - pd_request_vdm_attention(port, vdm, ARRAY_SIZE(vdm)); - - /* If there are still events, need to shift the buffer */ - if (--hpd.count) { - int i; - - for (i = 0; i < hpd.count; i++) - hpd.queue[i] = hpd.queue[i + 1]; - } -} - -static void hpd_queue_event(enum hpd_event evt) -{ - /* - * HPD events are put into a queue. However, this queue is not a typical - * FIFO queue. Instead there are special rules based on which type of - * event is being added. - * HPD_LOW -> always resets the queue and must be in slot 0 - * HPD_HIGH -> must follow a HPD_LOW, so can only be in slot 0 or - * slot 1. - * HPD_IRQ -> There shall never be more than 2 HPD_IRQ events - * stored in the queue and HPD_IRQ must follow HPD_HIGH - * - * Worst case for queueing HPD events is 4 events in the queue: - * 0 - HPD_LOW - * 1 - HPD_HIGH - * 2 - HPD_IRQ - * 3 - HPD_IRQ - * - * The above rules mean that HPD_LOW and HPD_HIGH events can always be - * added to the queue since high must follow low and a low event resets - * the queue. HPD_IRQ events are checked to make sure that they don't - * overflow the queue and to ensure that no more than 2 hpd_irq events - * are kept in the queue. - */ - if (evt == hpd_irq) { - if ((hpd.count >= HPD_QUEUE_DEPTH) || ((hpd.count >= 2) && - (hpd.queue[hpd.count - 2] == hpd_irq))) { - CPRINTS("hpd: discard hpd: count - %d", - hpd.count); - return; - } - } - - if (evt == hpd_low) { - hpd.count = 0; - } - - /* Add event to the queue */ - hpd.queue[hpd.count++] = evt; -} - -static void hpd_to_pd_converter(int level, uint64_t ts) -{ - /* - * HPD edges are marked in the irq routine. The converter state machine - * runs in the hooks task and so there will be some delay between when - * the edge was captured and when that edge is processed here in the - * state machine. This means that the delitch timer (250 uSec) may have - * already expired or is about to expire. - * - * If transitioning to timing dependent state, need to ensure the state - * machine is executed again. All timers are relative to the ts value - * passed into this routine. The timestamps passed into this routine - * are either the values latched in the irq routine, or the current - * time latched by the calling function. From the perspective of the - * state machine, ts represents the current time. - * - * Note that all hpd queue events are contingent on detecting edges - * on the incoming hpd gpio signal. The hpd->dp attention converter is - * enabled/disabled as part of the svdm dp enter/exit response handler - * functions. When the converter is disabled, gpio interrupts for the - * hpd gpio signal are disabled so it will never execute, unless the - * converter is enabled, and the converter is only enabled when the - * UFP-D is actively in ALT-DP mode. - */ - switch (hpd.state) { - case LOW_WAIT: - /* - * In this state only expected event is a level change from low - * to high. - */ - if (level) { - hpd.state = HIGH_CHECK; - hpd.timer = ts + HPD_T_IRQ_MIN_PULSE; - } - break; - case HIGH_CHECK: - /* - * In this state if level is high and deglitch timer is - * exceeded, then state advances to HIGH_WAIT, otherwise return - * to LOW_WAIT state. - */ - if (!level || (ts <= hpd.timer)) { - hpd.state = LOW_WAIT; - } else { - hpd.state = HIGH_WAIT; - hpd_queue_event(hpd_high); - } - break; - case HIGH_WAIT: - /* - * In this state, only expected event is a level change from - * high to low. If current level is low, then advance to - * LOW_CHECK for deglitch checking. - */ - if (!level) { - hpd.state = LOW_CHECK; - hpd.timer = ts + HPD_T_IRQ_MIN_PULSE; - } - break; - case LOW_CHECK: - /* - * This state is used to deglitch high->low level - * change. However, due to processing latency, it's possible to - * detect hpd_irq event if level is high and low pulse width was - * valid. - */ - if (!level) { - /* Still low, now wait for IRQ or LOW determination */ - hpd.timer = ts + (HPD_T_IRQ_MAX_PULSE - - HPD_T_IRQ_MIN_PULSE); - hpd.state = IRQ_CHECK; - - } else { - uint64_t irq_ts = hpd.timer + HPD_T_IRQ_MAX_PULSE - - HPD_T_IRQ_MIN_PULSE; - /* - * If hpd is high now, this must have been an edge - * event, but still need to determine if the pulse width - * is longer than hpd_irq min pulse width. State will - * advance to HIGH_WAIT, but if pulse width is < 2 msec, - * must send hpd_irq event. - */ - if ((ts >= hpd.timer) && (ts <= irq_ts)) { - /* hpd irq detected */ - hpd_queue_event(hpd_irq); - } - hpd.state = HIGH_WAIT; - } - break; - case IRQ_CHECK: - /* - * In this state deglitch time has already passed. If current - * level is low and hpd_irq timer has expired, then go to - * LOW_WAIT as hpd_low event has been detected. If level is high - * and low pulse is < hpd_irq, hpd_irq event has been detected. - */ - if (level) { - hpd.state = HIGH_WAIT; - if (ts <= hpd.timer) { - hpd_queue_event(hpd_irq); - } - } else if (ts > hpd.timer) { - hpd.state = LOW_WAIT; - hpd_queue_event(hpd_low); - } - break; - } -} - -static void manage_hpd(void); -DECLARE_DEFERRED(manage_hpd); - -static void manage_hpd(void) -{ - int level; - uint64_t ts = get_time().val; - uint32_t num_hpd_events = (hpd.edges.head - hpd.edges.tail) & - EDGE_QUEUE_MASK; - - /* - * HPD edges are detected via GPIO interrupts. The ISR routine adds edge - * info to a queue and scheudles this routine. If this routine is called - * without a new edge detected, then it is being called due to a timer - * event. - */ - - /* First check to see overflow condition has occurred */ - if (hpd.edges.overflow) { - /* Disable hpd interrupts */ - usb_pd_hpd_converter_enable(0); - /* Re-enable hpd converter */ - usb_pd_hpd_converter_enable(1); - } - - if (num_hpd_events) { - while(num_hpd_events-- > 0) { - int idx = hpd.edges.tail; - - level = hpd.edges.buffer[idx].level; - ts = hpd.edges.buffer[idx].ts; - - hpd_to_pd_converter(level, ts); - hpd.edges.tail = (hpd.edges.tail + 1) & EDGE_QUEUE_MASK; - } - } else { - /* no new edge event, so get current time and level */ - level = gpio_get_level(hpd_config.signal); - ts = get_time().val; - hpd_to_pd_converter(level, ts); - } - - /* - * If min time spacing requirement is exceeded and a hpd_event is - * queued, then send DP_ATTENTION message. - */ - if (hpd.count > 0) { - /* - * If at least one hpd event is pending in the queue, send - * a DP_ATTENTION message if a DP_CONFIG message has been - * received and have passed the minimum spacing interval. - */ - if (hpd.send_enable && - ((get_time().val - hpd.last_send_ts) > - HPD_T_MIN_DP_ATTEN)) { - /* Generate DP_ATTENTION event pending in queue */ - hpd_to_dp_attention(); - } else { - uint32_t callback_us; - - /* - * Need to wait until until min spacing requirement of - * DP attention messages. Set callback time to the min - * value required. This callback time could be changed - * based on hpd interrupts. - * - * This wait is also used to prevent a DP_ATTENTION - * message from being sent before at least one DP_CONFIG - * message has been received. If DP_ATTENTION messages - * need to be delayed for this reason, then just wait - * the minimum time spacing. - */ - callback_us = HPD_T_MIN_DP_ATTEN - - (get_time().val - hpd.last_send_ts); - if (callback_us <= 0 || - callback_us > HPD_T_MIN_DP_ATTEN) - callback_us = HPD_T_MIN_DP_ATTEN; - hook_call_deferred(&manage_hpd_data, callback_us); - } - } - - /* - * Because of the delay between gpio edge irq, and when those edge - * events are processed here, all timers must be done relative to the - * timing marker stored in the hpd edge queue. If the state machine - * required a new timer, then hpd.timer will be advanced relative to the - * ts that was passed into the state machine. - * - * If the deglitch timer is active, then it can likely already have been - * expired when the edge gets processed. So if the timer is active the - * deferred callback must be requested. - *. - */ - if (hpd.timer > ts) { - uint64_t callback_us = 0; - uint64_t now = get_time().val; - - /* If timer is in the future, adjust the callback timer */ - if (now < hpd.timer) - callback_us = (hpd.timer - now) & 0xffffffff; - - hook_call_deferred(&manage_hpd_data, callback_us); - } -} - -void usb_pd_hpd_converter_enable(int enable) -{ - /* - * The hpd converter should be enabled as part of the UFP-D enter mode - * response function. Likewise, the converter should be disabled by the - * exit mode function. In addition, the coverter may get disabled so - * that it can be reset in the case that the input gpio edges queue - * overflows. A muxtex must be used here since this function may be - * called from the PD task (enter/exit response mode functions) or from - * the hpd event handler state machine (hook task). - */ - mutex_lock(&hpd_mutex); - - if (enable) { - gpio_disable_interrupt(hpd_config.signal); - /* Reset HPD event queue */ - hpd.state = LOW_WAIT; - hpd.count = 0; - hpd.timer = 0; - hpd.last_send_ts = 0; - hpd.send_enable = 0; - - /* Reset hpd signal edges queue */ - hpd.edges.head = 0; - hpd.edges.tail = 0; - hpd.edges.overflow = 0; - - /* If signal is high, need to ensure state machine executes */ - if (gpio_get_level(hpd_config.signal)) - hook_call_deferred(&manage_hpd_data, 0); - - /* Enable hpd edge detection */ - gpio_enable_interrupt(hpd_config.signal); - } else { - gpio_disable_interrupt(hpd_config.signal); - hook_call_deferred(&manage_hpd_data, -1); - } - - mutex_unlock(&hpd_mutex); -} - -void usb_pd_hpd_edge_event(int signal) -{ - int next_head = (hpd.edges.head + 1) & EDGE_QUEUE_MASK; - struct hpd_mark mark; - - /* Get current timestamp and level */ - mark.ts = get_time().val; - mark.level = gpio_get_level(hpd_config.signal); - - /* Add this edge to the buffer if there is space */ - if (next_head != hpd.edges.tail) { - hpd.edges.buffer[hpd.edges.head].ts = mark.ts; - hpd.edges.buffer[hpd.edges.head].level = mark.level; - hpd.edges.head = next_head; - } else { - /* Edge queue is overflowing, need to reset the converter */ - hpd.edges.overflow = 1; - } - /* Schedule HPD state machine to run ASAP */ - hook_call_deferred(&manage_hpd_data, 0); -} diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c deleted file mode 100644 index eb2dfc52c0..0000000000 --- a/common/usbc/usb_pd_dpm.c +++ /dev/null @@ -1,704 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Device Policy Manager implementation - * Refer to USB PD 3.0 spec, version 2.0, sections 8.2 and 8.3 - */ - -#include "charge_state.h" -#include "compile_time_macros.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_dp_alt_mode.h" -#include "usb_mode.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_tbt_alt_mode.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Max Attention length is header + 1 VDO */ -#define DPM_ATTENION_MAX_VDO 2 - -static struct { - uint32_t flags; - uint32_t vdm_attention[DPM_ATTENION_MAX_VDO]; - int vdm_cnt; - mutex_t vdm_attention_mutex; -} dpm[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define DPM_SET_FLAG(port, flag) atomic_or(&dpm[(port)].flags, (flag)) -#define DPM_CLR_FLAG(port, flag) atomic_clear_bits(&dpm[(port)].flags, (flag)) -#define DPM_CHK_FLAG(port, flag) (dpm[(port)].flags & (flag)) - -/* Flags for internal DPM state */ -#define DPM_FLAG_MODE_ENTRY_DONE BIT(0) -#define DPM_FLAG_EXIT_REQUEST BIT(1) -#define DPM_FLAG_ENTER_DP BIT(2) -#define DPM_FLAG_ENTER_TBT BIT(3) -#define DPM_FLAG_ENTER_USB4 BIT(4) -#define DPM_FLAG_SEND_ATTENTION BIT(5) - -#ifdef CONFIG_ZEPHYR -static int init_vdm_attention_mutex(const struct device *dev) -{ - int port; - - ARG_UNUSED(dev); - - for (port = 0; port < CONFIG_USB_PD_PORT_MAX_COUNT; port++) - k_mutex_init(&dpm[port].vdm_attention_mutex); - - return 0; -} -SYS_INIT(init_vdm_attention_mutex, POST_KERNEL, 50); -#endif /* CONFIG_ZEPHYR */ - -enum ec_status pd_request_vdm_attention(int port, const uint32_t *data, - int vdo_count) -{ - mutex_lock(&dpm[port].vdm_attention_mutex); - - /* Only one Attention message may be pending */ - if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) { - mutex_unlock(&dpm[port].vdm_attention_mutex); - return EC_RES_UNAVAILABLE; - } - - /* SVDM Attention message must be 1 or 2 VDOs in length */ - if (!vdo_count || (vdo_count > DPM_ATTENION_MAX_VDO)) { - mutex_unlock(&dpm[port].vdm_attention_mutex); - return EC_RES_INVALID_PARAM; - } - - /* Save contents of Attention message */ - memcpy(dpm[port].vdm_attention, data, vdo_count * sizeof(uint32_t)); - dpm[port].vdm_cnt = vdo_count; - - /* - * Indicate to DPM that an Attention message needs to be sent. This flag - * will be cleared when the Attention message is sent to the policy - * engine. - */ - DPM_SET_FLAG(port, DPM_FLAG_SEND_ATTENTION); - - mutex_unlock(&dpm[port].vdm_attention_mutex); - - return EC_RES_SUCCESS; -} - -enum ec_status pd_request_enter_mode(int port, enum typec_mode mode) -{ - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* Only one enter request may be active at a time. */ - if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4)) - return EC_RES_BUSY; - - switch (mode) { - case TYPEC_MODE_DP: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_DP); - break; -#ifdef CONFIG_USB_PD_TBT_COMPAT_MODE - case TYPEC_MODE_TBT: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_TBT); - break; -#endif /* CONFIG_USB_PD_TBT_COMPAT_MODE */ -#ifdef CONFIG_USB_PD_USB4 - case TYPEC_MODE_USB4: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_USB4); - break; -#endif - default: - return EC_RES_INVALID_PARAM; - } - - DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); - DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); - - return EC_RES_SUCCESS; -} - -void dpm_init(int port) -{ - dpm[port].flags = 0; -} - -static void dpm_set_mode_entry_done(int port) -{ - DPM_SET_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); - DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4); -} - -void dpm_set_mode_exit_request(int port) -{ - DPM_SET_FLAG(port, DPM_FLAG_EXIT_REQUEST); -} - -static void dpm_clear_mode_exit_request(int port) -{ - DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); -} - -/* - * Returns true if the current policy requests that the EC try to enter this - * mode on this port. If the EC is in charge of policy, the answer is always - * yes. - */ -static bool dpm_mode_entry_requested(int port, enum typec_mode mode) -{ - /* If the AP isn't controlling policy, the EC is. */ - if (!IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY)) - return true; - - switch (mode) { - case TYPEC_MODE_DP: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP); - case TYPEC_MODE_TBT: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_TBT); - case TYPEC_MODE_USB4: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_USB4); - default: - return false; - } -} - -void dpm_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const uint16_t svid = PD_VDO_VID(vdm[0]); - - assert(vdo_count >= 1); - - switch (svid) { - case USB_SID_DISPLAYPORT: - dp_vdm_acked(port, type, vdo_count, vdm); - break; - case USB_VID_INTEL: - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { - intel_vdm_acked(port, type, vdo_count, vdm); - break; - } - default: - CPRINTS("C%d: Received unexpected VDM ACK for SVID %d", port, - svid); - } -} - -void dpm_vdm_naked(int port, enum tcpci_msg_type type, uint16_t svid, - uint8_t vdm_cmd) -{ - switch (svid) { - case USB_SID_DISPLAYPORT: - dp_vdm_naked(port, type, vdm_cmd); - break; - case USB_VID_INTEL: - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { - intel_vdm_naked(port, type, vdm_cmd); - break; - } - default: - CPRINTS("C%d: Received unexpected VDM NAK for SVID %d", port, - svid); - } -} - -/* - * Requests that the PE send one VDM, whichever is next in the mode entry - * sequence. This only happens if preconditions for mode entry are met. If - * CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY is enabled, this function waits for the - * AP to direct mode entry. - */ -static void dpm_attempt_mode_entry(int port) -{ - int vdo_count = 0; - uint32_t vdm[VDO_MAX_SIZE]; - enum tcpci_msg_type tx_type = TCPCI_MSG_SOP; - bool enter_mode_requested = - IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) ? false : true; - - if (pd_get_data_role(port) != PD_ROLE_DFP) { - if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4)) - DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4); - /* - * TODO(b/168030639): Notify the AP that the enter mode request - * failed. - */ - return; - } - -#ifdef HAS_TASK_CHIPSET - /* - * Do not try to enter mode while CPU is off. - * CPU transitions (e.g b/158634281) can occur during the discovery - * phase or during enter/exit negotiations, and the state - * of the modes can get out of sync, causing the attempt to - * enter the mode to fail prematurely. - */ - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - return; -#endif - /* - * If discovery has not occurred for modes, do not attempt to switch - * to alt mode. - */ - if (pd_get_svids_discovery(port, TCPCI_MSG_SOP) != PD_DISC_COMPLETE || - pd_get_modes_discovery(port, TCPCI_MSG_SOP) != PD_DISC_COMPLETE) - return; - - if (dp_entry_is_done(port) || - (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - tbt_entry_is_done(port)) || - (IS_ENABLED(CONFIG_USB_PD_USB4) && enter_usb_entry_is_done(port))) { - dpm_set_mode_entry_done(port); - return; - } - - /* Check if port, port partner and cable support USB4. */ - if (IS_ENABLED(CONFIG_USB_PD_USB4) && - board_is_tbt_usb4_port(port) && - enter_usb_port_partner_is_capable(port) && - enter_usb_cable_is_capable(port) && - dpm_mode_entry_requested(port, TYPEC_MODE_USB4)) { - /* - * For certain cables, enter Thunderbolt alt mode with the - * cable and USB4 mode with the port partner. - */ - if (tbt_cable_entry_required_for_usb4(port)) { - vdo_count = tbt_setup_next_vdm(port, - ARRAY_SIZE(vdm), vdm, &tx_type); - } else { - pd_dpm_request(port, DPM_REQUEST_ENTER_USB); - return; - } - } - - /* If not, check if they support Thunderbolt alt mode. */ - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - board_is_tbt_usb4_port(port) && - pd_is_mode_discovered_for_svid(port, TCPCI_MSG_SOP, - USB_VID_INTEL) && - dpm_mode_entry_requested(port, TYPEC_MODE_TBT)) { - enter_mode_requested = true; - vdo_count = tbt_setup_next_vdm(port, - ARRAY_SIZE(vdm), vdm, &tx_type); - } - - /* If not, check if they support DisplayPort alt mode. */ - if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE) && - pd_is_mode_discovered_for_svid(port, TCPCI_MSG_SOP, - USB_SID_DISPLAYPORT) && - dpm_mode_entry_requested(port, TYPEC_MODE_DP)) { - enter_mode_requested = true; - vdo_count = dp_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); - } - - /* - * If the PE didn't discover any supported (requested) alternate mode, - * just mark setup done and get out of here. - */ - if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) { - if (enter_mode_requested) { - /* - * TODO(b/168030639): Notify the AP that mode entry - * failed. - */ - CPRINTS("C%d: No supported alt mode discovered", port); - } - /* - * If the AP did not request mode entry, it may do so in the - * future, but the DPM is done trying for now. - */ - dpm_set_mode_entry_done(port); - return; - } - - if (vdo_count < 0) { - dpm_set_mode_entry_done(port); - CPRINTS("C%d: Couldn't construct alt mode VDM", port); - return; - } - - /* - * TODO(b/155890173): Provide a host command to request that the PE send - * an arbitrary VDM via this mechanism. - */ - if (!pd_setup_vdm_request(port, tx_type, vdm, vdo_count)) { - dpm_set_mode_entry_done(port); - return; - } - - pd_dpm_request(port, DPM_REQUEST_VDM); -} - -static void dpm_attempt_mode_exit(int port) -{ - uint32_t vdm = 0; - int vdo_count = 0; - enum tcpci_msg_type tx_type = TCPCI_MSG_SOP; - - if (IS_ENABLED(CONFIG_USB_PD_USB4) && - enter_usb_entry_is_done(port)) { - CPRINTS("C%d: USB4 teardown", port); - usb4_exit_mode_request(port); - } - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - tbt_is_active(port)) { - /* - * When the port is in USB4 mode and receives an exit request, - * it leaves USB4 SOP in active state. - * TODO(b/156749387): Support Data Reset for exiting USB4 SOP. - */ - CPRINTS("C%d: TBT teardown", port); - tbt_exit_mode_request(port); - vdo_count = tbt_setup_next_vdm(port, VDO_MAX_SIZE, &vdm, - &tx_type); - } else if (dp_is_active(port)) { - CPRINTS("C%d: DP teardown", port); - vdo_count = dp_setup_next_vdm(port, VDO_MAX_SIZE, &vdm); - } else { - /* Clear exit mode request */ - dpm_clear_mode_exit_request(port); - return; - } - - if (!pd_setup_vdm_request(port, tx_type, &vdm, vdo_count)) { - dpm_clear_mode_exit_request(port); - return; - } - - pd_dpm_request(port, DPM_REQUEST_VDM); -} - -static void dpm_send_attention_vdm(int port) -{ - /* Set up VDM ATTEN msg that was passed in previously */ - if (pd_setup_vdm_request(port, TCPCI_MSG_SOP, dpm[port].vdm_attention, - dpm[port].vdm_cnt) == true) - /* Trigger PE to start a VDM command run */ - pd_dpm_request(port, DPM_REQUEST_VDM); - - /* Clear flag after message is sent to PE layer */ - DPM_CLR_FLAG(port, DPM_FLAG_SEND_ATTENTION); -} - -void dpm_run(int port) -{ - if (pd_get_data_role(port) == PD_ROLE_DFP) { - /* Run DFP related DPM requests */ - if (DPM_CHK_FLAG(port, DPM_FLAG_EXIT_REQUEST)) - dpm_attempt_mode_exit(port); - else if (!DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) - dpm_attempt_mode_entry(port); - } else { - /* Run UFP related DPM requests */ - if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) - dpm_send_attention_vdm(port); - } -} - -/* - * Source-out policy variables and APIs - * - * Priority for the available 3.0 A ports is given in the following order: - * - sink partners which report requiring > 1.5 A in their Sink_Capabilities - */ - -/* - * Bitmasks of port numbers in each following category - * - * Note: request bitmasks should be accessed atomically as other ports may alter - * them - */ -static uint32_t max_current_claimed; -K_MUTEX_DEFINE(max_current_claimed_lock); - -/* Ports with PD sink needing > 1.5 A */ -static uint32_t sink_max_pdo_requested; -/* Ports with FRS source needing > 1.5 A */ -static uint32_t source_frs_max_requested; -/* Ports with non-PD sinks, so current requirements are unknown */ -static uint32_t non_pd_sink_max_requested; - -#define LOWEST_PORT(p) __builtin_ctz(p) /* Undefined behavior if p == 0 */ - -static int count_port_bits(uint32_t bitmask) -{ - int i, total = 0; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (bitmask & BIT(i)) - total++; - } - - return total; -} - -/* - * Centralized, mutex-controlled updates to the claimed 3.0 A ports - */ -static void balance_source_ports(void); -DECLARE_DEFERRED(balance_source_ports); - -static void balance_source_ports(void) -{ - uint32_t removed_ports, new_ports; - static bool deferred_waiting; - - if (task_get_current() == TASK_ID_HOOKS) - deferred_waiting = false; - - /* - * Ignore balance attempts while we're waiting for a downgraded port to - * finish the downgrade. - */ - if (deferred_waiting) - return; - - mutex_lock(&max_current_claimed_lock); - - /* Remove any ports which no longer require 3.0 A */ - removed_ports = max_current_claimed & ~(sink_max_pdo_requested | - source_frs_max_requested | - non_pd_sink_max_requested); - max_current_claimed &= ~removed_ports; - - /* Allocate 3.0 A to new PD sink ports that need it */ - new_ports = sink_max_pdo_requested & ~max_current_claimed; - while (new_ports) { - int new_max_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_max_port); - typec_select_src_current_limit_rp(new_max_port, - TYPEC_RP_3A0); - } else if (non_pd_sink_max_requested & max_current_claimed) { - /* Always downgrade non-PD ports first */ - int rem_non_pd = LOWEST_PORT(non_pd_sink_max_requested & - max_current_claimed); - typec_select_src_current_limit_rp(rem_non_pd, - typec_get_default_current_limit_rp(rem_non_pd)); - max_current_claimed &= ~BIT(rem_non_pd); - - /* Wait tSinkAdj before using current */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - PD_T_SINK_ADJ); - goto unlock; - } else if (source_frs_max_requested & max_current_claimed) { - /* Downgrade lowest FRS port from 3.0 A slot */ - int rem_frs = LOWEST_PORT(source_frs_max_requested & - max_current_claimed); - pd_dpm_request(rem_frs, DPM_REQUEST_FRS_DET_DISABLE); - max_current_claimed &= ~BIT(rem_frs); - - /* Give 20 ms for the PD task to process DPM flag */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - 20 * MSEC); - goto unlock; - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_max_port); - } - - /* Allocate 3.0 A to any new FRS ports that need it */ - new_ports = source_frs_max_requested & ~max_current_claimed; - while (new_ports) { - int new_frs_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_frs_port); - pd_dpm_request(new_frs_port, - DPM_REQUEST_FRS_DET_ENABLE); - } else if (non_pd_sink_max_requested & max_current_claimed) { - int rem_non_pd = LOWEST_PORT(non_pd_sink_max_requested & - max_current_claimed); - typec_select_src_current_limit_rp(rem_non_pd, - typec_get_default_current_limit_rp(rem_non_pd)); - max_current_claimed &= ~BIT(rem_non_pd); - - /* Wait tSinkAdj before using current */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - PD_T_SINK_ADJ); - goto unlock; - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_frs_port); - } - - /* Allocate 3.0 A to any non-PD ports which could need it */ - new_ports = non_pd_sink_max_requested & ~max_current_claimed; - while (new_ports) { - int new_max_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_max_port); - typec_select_src_current_limit_rp(new_max_port, - TYPEC_RP_3A0); - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_max_port); - } -unlock: - mutex_unlock(&max_current_claimed_lock); -} - -/* Process port's first Sink_Capabilities PDO for port current consideration */ -void dpm_evaluate_sink_fixed_pdo(int port, uint32_t vsafe5v_pdo) -{ - /* Verify partner supplied valid vSafe5V fixed object first */ - if ((vsafe5v_pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - - if (PDO_FIXED_VOLTAGE(vsafe5v_pdo) != 5000) - return; - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) { - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - /* Valid PDO to process, so evaluate whether >1.5A is needed */ - if (PDO_FIXED_CURRENT(vsafe5v_pdo) <= 1500) - return; - - atomic_or(&sink_max_pdo_requested, BIT(port)); - } else { - int frs_current = vsafe5v_pdo & PDO_FIXED_FRS_CURR_MASK; - - if (!IS_ENABLED(CONFIG_USB_PD_FRS)) - return; - - /* FRS is only supported in PD 3.0 and higher */ - if (pd_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) - return; - - if ((vsafe5v_pdo & PDO_FIXED_DUAL_ROLE) && frs_current) { - /* Always enable FRS when 3.0 A is not needed */ - if (frs_current == PDO_FIXED_FRS_CURR_DFLT_USB_POWER || - frs_current == PDO_FIXED_FRS_CURR_1A5_AT_5V) { - pd_dpm_request(port, - DPM_REQUEST_FRS_DET_ENABLE); - return; - } - - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - atomic_or(&source_frs_max_requested, BIT(port)); - } else { - return; - } - } - - balance_source_ports(); -} - -void dpm_add_non_pd_sink(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - atomic_or(&non_pd_sink_max_requested, BIT(port)); - - balance_source_ports(); -} - -void dpm_remove_sink(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - if (!(BIT(port) & sink_max_pdo_requested) && - !(BIT(port) & non_pd_sink_max_requested)) - return; - - atomic_clear_bits(&sink_max_pdo_requested, BIT(port)); - atomic_clear_bits(&non_pd_sink_max_requested, BIT(port)); - - /* Restore selected default Rp on the port */ - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - balance_source_ports(); -} - -void dpm_remove_source(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - if (!IS_ENABLED(CONFIG_USB_PD_FRS)) - return; - - if (!(BIT(port) & source_frs_max_requested)) - return; - - atomic_clear_bits(&source_frs_max_requested, BIT(port)); - - balance_source_ports(); -} - -/* - * Note: all ports receive the 1.5 A source offering until they are found to - * match a criteria on the 3.0 A priority list (ex. through sink capability - * probing), at which point they will be offered a new 3.0 A source capability. - */ -__overridable int dpm_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - /* Max PDO may not exist on boards which don't offer 3 A */ -#if CONFIG_USB_PD_3A_PORTS > 0 - if (max_current_claimed & BIT(port)) { - *src_pdo = pd_src_pdo_max; - return pd_src_pdo_max_cnt; - } -#endif - - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} - -int dpm_get_source_current(const int port) -{ - if (pd_get_power_role(port) == PD_ROLE_SINK) - return 0; - - if (max_current_claimed & BIT(port)) - return 3000; - else if (typec_get_default_current_limit_rp(port) == TYPEC_RP_1A5) - return 1500; - else - return 500; -} diff --git a/common/usbc/usb_pd_host.c b/common/usbc/usb_pd_host.c deleted file mode 100644 index 4d0fadeec3..0000000000 --- a/common/usbc/usb_pd_host.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright 2020 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. - * - * Host commands for TCPMv2 USB PD module - */ - -#include <string.h> - -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* Retrieve all discovery results for the given port and transmit type */ -static enum ec_status hc_typec_discovery(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_discovery *p = args->params; - struct ec_response_typec_discovery *r = args->response; - const struct pd_discovery *disc; - enum tcpci_msg_type type; - - /* Confirm the number of HC VDOs matches our stored VDOs */ - BUILD_ASSERT(sizeof(r->discovery_vdo) == sizeof(union disc_ident_ack)); - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->partner_type > TYPEC_PARTNER_SOP_PRIME) - return EC_RES_INVALID_PARAM; - - type = p->partner_type == TYPEC_PARTNER_SOP ? - TCPCI_MSG_SOP : TCPCI_MSG_SOP_PRIME; - - /* - * Clear out access mask so we can track if tasks have touched data - * since read started. - */ - pd_discovery_access_clear(p->port, type); - - disc = pd_get_am_discovery_and_notify_access(p->port, type); - - /* Initialize return size to that of discovery with no SVIDs */ - args->response_size = sizeof(*r); - - if (pd_get_identity_discovery(p->port, type) == PD_DISC_COMPLETE) { - r->identity_count = disc->identity_cnt; - memcpy(r->discovery_vdo, - pd_get_identity_response(p->port, type)->raw_value, - sizeof(r->discovery_vdo)); - } else { - r->identity_count = 0; - return EC_RES_SUCCESS; - } - - if (pd_get_modes_discovery(p->port, type) == PD_DISC_COMPLETE) { - int svid_i; - int max_resp_svids = (args->response_max - args->response_size)/ - sizeof(struct svid_mode_info); - - if (disc->svid_cnt > max_resp_svids) { - CPRINTS("Warn: SVIDS exceeded HC response"); - r->svid_count = max_resp_svids; - } else { - r->svid_count = disc->svid_cnt; - } - - for (svid_i = 0; svid_i < r->svid_count; svid_i++) { - r->svids[svid_i].svid = disc->svids[svid_i].svid; - r->svids[svid_i].mode_count = - disc->svids[svid_i].mode_cnt; - memcpy(r->svids[svid_i].mode_vdo, - disc->svids[svid_i].mode_vdo, - sizeof(r->svids[svid_i].mode_vdo)); - args->response_size += sizeof(struct svid_mode_info); - } - } else { - r->svid_count = 0; - } - - /* - * Verify that another task did not access this data during the duration - * of the copy. If the data was accessed, return BUSY so the AP will - * try retrieving again and get the updated data. - */ - if (!pd_discovery_access_validate(p->port, type)) { - CPRINTS("[C%d] %s returns EC_RES_BUSY!!\n", p->port, __func__); - return EC_RES_BUSY; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_DISCOVERY, - hc_typec_discovery, - EC_VER_MASK(0)); - -static enum ec_status hc_typec_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_control *p = args->params; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - switch (p->command) { - case TYPEC_CONTROL_COMMAND_EXIT_MODES: - pd_dpm_request(p->port, DPM_REQUEST_EXIT_MODES); - break; - case TYPEC_CONTROL_COMMAND_CLEAR_EVENTS: - pd_clear_events(p->port, p->clear_events_mask); - break; - case TYPEC_CONTROL_COMMAND_ENTER_MODE: - return pd_request_enter_mode(p->port, p->mode_to_enter); - default: - return EC_RES_INVALID_PARAM; - } - - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_CONTROL, hc_typec_control, EC_VER_MASK(0)); - -static enum ec_status hc_typec_status(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_status *p = args->params; - struct ec_response_typec_status *r = args->response; - const char *tc_state_name; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (args->response_max < sizeof(*r)) - return EC_RES_RESPONSE_TOO_BIG; - - args->response_size = sizeof(*r); - - r->pd_enabled = pd_comm_is_enabled(p->port); - r->dev_connected = pd_is_connected(p->port); - r->sop_connected = pd_capable(p->port); - - r->power_role = pd_get_power_role(p->port); - r->data_role = pd_get_data_role(p->port); - r->vconn_role = pd_get_vconn_state(p->port) ? PD_ROLE_VCONN_SRC : - PD_ROLE_VCONN_OFF; - r->polarity = pd_get_polarity(p->port); - r->cc_state = pd_get_task_cc_state(p->port); - r->dp_pin = get_dp_pin_mode(p->port); - r->mux_state = usb_mux_get(p->port); - - tc_state_name = pd_get_task_state_name(p->port); - strzcpy(r->tc_state, tc_state_name, sizeof(r->tc_state)); - - r->events = pd_get_events(p->port); - - r->sop_revision = r->sop_connected ? - PD_STATUS_REV_SET_MAJOR(pd_get_rev(p->port, TCPCI_MSG_SOP)) : 0; - r->sop_prime_revision = - pd_get_identity_discovery(p->port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_COMPLETE ? - PD_STATUS_REV_SET_MAJOR(pd_get_rev(p->port, - TCPCI_MSG_SOP_PRIME)) - : 0; - - r->source_cap_count = pd_get_src_cap_cnt(p->port); - memcpy(r->source_cap_pdos, pd_get_src_caps(p->port), - r->source_cap_count * sizeof(uint32_t)); - - r->sink_cap_count = pd_get_snk_cap_cnt(p->port); - memcpy(r->sink_cap_pdos, pd_get_snk_caps(p->port), - r->sink_cap_count * sizeof(uint32_t)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_STATUS, hc_typec_status, EC_VER_MASK(0)); diff --git a/common/usbc/usb_pd_timer.c b/common/usbc/usb_pd_timer.c deleted file mode 100644 index 67a574904f..0000000000 --- a/common/usbc/usb_pd_timer.c +++ /dev/null @@ -1,268 +0,0 @@ -/* 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 "assert.h" -#include "common.h" -#include "console.h" -#include "limits.h" -#include "system.h" -#include "usb_pd_timer.h" -#include "usb_tc_sm.h" - -#define MAX_PD_PORTS CONFIG_USB_PD_PORT_MAX_COUNT -#define MAX_PD_TIMERS PD_TIMER_COUNT -#define PD_TIMERS_ALL_MASK ((uint32_t)(((uint64_t)1 << PD_TIMER_COUNT) - 1)) - -#define MAX_EXPIRE (0x7FFFFFFF) -#define NO_TIMEOUT (-1) -#define EXPIRE_NOW (0) - -#define PD_SET_ACTIVE(p, m) atomic_or(&timer_active[p], (m)) -#define PD_CLR_ACTIVE(p, m) atomic_clear_bits(&timer_active[p], (m)) -#define PD_CHK_ACTIVE(p, m) (timer_active[p] & (m)) - -#define PD_SET_DISABLED(p, m) atomic_or(&timer_disabled[p], (m)) -#define PD_CLR_DISABLED(p, m) atomic_clear_bits(&timer_disabled[p], (m)) -#define PD_CHK_DISABLED(p, m) (timer_disabled[p] & (m)) - -static uint32_t timer_active[MAX_PD_PORTS]; -static uint32_t timer_disabled[MAX_PD_PORTS]; -static uint64_t timer_expires[MAX_PD_PORTS][MAX_PD_TIMERS]; - -/* - * CONFIG_CMD_PD_TIMER debug variables - */ -static int count[MAX_PD_PORTS]; -static int max_count[MAX_PD_PORTS]; - -__maybe_unused static __const_data const char * const pd_timer_names[] = { - [PE_TIMER_BIST_CONT_MODE] = "PE-BIST_CONT_MODE", - [PE_TIMER_CHUNKING_NOT_SUPPORTED] = "PE-CHUNKING_NOT_SUPPORTED", - [PE_TIMER_DISCOVER_IDENTITY] = "PE-DISCOVER_IDENTITY", - [PE_TIMER_NO_RESPONSE] = "PE-NO_RESPONSE", - [PE_TIMER_PR_SWAP_WAIT] = "PE-PR_SWAP_WAIT", - [PE_TIMER_PS_HARD_RESET] = "PE-PS_HARD_RESET", - [PE_TIMER_PS_SOURCE] = "PE-PS_SOURCE", - [PE_TIMER_PS_TRANSITION] = "PE-PS_TRANSITION", - [PE_TIMER_SENDER_RESPONSE] = "PE-SENDER_RESPONSE", - [PE_TIMER_SINK_REQUEST] = "PE-SINK_REQUEST", - [PE_TIMER_SOURCE_CAP] = "PE-SOURCE_CAP", - [PE_TIMER_SRC_TRANSITION] = "PE-SRC_TRANSITION", - [PE_TIMER_SWAP_SOURCE_START] = "PE-SWAP_SOURCE_START", - [PE_TIMER_TIMEOUT] = "PE-TIMEOUT", - [PE_TIMER_VCONN_ON] = "PE-VCONN_ON", - [PE_TIMER_VDM_RESPONSE] = "PE-VDM_RESPONSE", - [PE_TIMER_WAIT_AND_ADD_JITTER] = "PE-WAIT_AND_ADD_JITTER", - - [PR_TIMER_CHUNK_SENDER_REQUEST] = "PR-CHUNK_SENDER_REQUEST", - [PR_TIMER_CHUNK_SENDER_RESPONSE] = "PR-CHUNK_SENDER_RESPONSE", - [PR_TIMER_HARD_RESET_COMPLETE] = "PR-HARD_RESET_COMPLETE", - [PR_TIMER_SINK_TX] = "PR-SINK_TX", - [PR_TIMER_TCPC_TX_TIMEOUT] = "PR-TCPC_TX_TIMEOUT", - - [TC_TIMER_CC_DEBOUNCE] = "TC-CC_DEBOUNCE", - [TC_TIMER_LOW_POWER_EXIT_TIME] = "TC-LOW_POWER_EXIT_TIME", - [TC_TIMER_LOW_POWER_TIME] = "TC-LOW_POWER_TIME", - [TC_TIMER_NEXT_ROLE_SWAP] = "TC-NEXT_ROLE_SWAP", - [TC_TIMER_PD_DEBOUNCE] = "TC-PD_DEBOUNCE", - [TC_TIMER_TIMEOUT] = "TC-TIMEOUT", - [TC_TIMER_TRY_WAIT_DEBOUNCE] = "TC-TRY_WAIT_DEBOUNCE", - [TC_TIMER_VBUS_DEBOUNCE] = "TC-VBUS_DEBOUNCE", -}; - -/***************************************************************************** - * PD_TIMER private functions - * - * The view of timers to the outside world is enabled and disabled. Internally - * timers that are enabled are in the active and inactive states. An active - * timer has a valid timeout value that gets checked for expiration and can - * adjust the task wakeup time. An inactive timer is assumed to have expired - * already and will always return that it is still expired. This timer state - * will not adjust the task scheduling timeout value. - */ -static void pd_timer_inactive(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - if (PD_CHK_ACTIVE(port, mask)) { - PD_CLR_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port]--; - } - PD_CLR_DISABLED(port, mask); -} - -static bool pd_timer_is_active(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return PD_CHK_ACTIVE(port, mask); -} - -static bool pd_timer_is_inactive(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return !PD_CHK_ACTIVE(port, mask) && !PD_CHK_DISABLED(port, mask); -} - -/***************************************************************************** - * PD_TIMER public functions - */ -void pd_timer_init(int port) -{ - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port] = 0; - - PD_CLR_ACTIVE(port, PD_TIMERS_ALL_MASK); - PD_SET_DISABLED(port, PD_TIMERS_ALL_MASK); -} - -void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us) -{ - uint32_t mask = 1 << timer; - - if (!PD_CHK_ACTIVE(port, mask)) { - PD_SET_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) { - count[port]++; - if (count[port] > max_count[port]) - max_count[port] = count[port]; - } - } - PD_CLR_DISABLED(port, mask); - timer_expires[port][timer] = get_time().val + expires_us; -} - -void pd_timer_disable(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - if (PD_CHK_ACTIVE(port, mask)) { - PD_CLR_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port]--; - } - PD_SET_DISABLED(port, mask); -} - -void pd_timer_disable_range(int port, enum pd_timer_range range) -{ - int start, end; - enum pd_task_timer timer; - - switch (range) { - case PE_TIMER_RANGE: - start = PE_TIMER_START; - end = PE_TIMER_END; - break; - case PR_TIMER_RANGE: - start = PR_TIMER_START; - end = PR_TIMER_END; - break; - case TC_TIMER_RANGE: - start = TC_TIMER_START; - end = TC_TIMER_END; - break; - default: - return; - } - - for (timer = start; timer <= end; ++timer) - pd_timer_disable(port, timer); -} - -bool pd_timer_is_disabled(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return PD_CHK_DISABLED(port, mask); -} - -bool pd_timer_is_expired(int port, enum pd_task_timer timer) -{ - if (pd_timer_is_active(port, timer)) { - if (get_time().val >= timer_expires[port][timer]) { - pd_timer_inactive(port, timer); - return true; - } - return false; - } - return pd_timer_is_inactive(port, timer); -} - -void pd_timer_manage_expired(int port) -{ - int timer; - - if (timer_active[port]) - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) - if (pd_timer_is_active(port, timer) && - pd_timer_is_expired(port, timer)) - pd_timer_inactive(port, timer); -} - -int pd_timer_next_expiration(int port) -{ - int timer; - int ret_value = MAX_EXPIRE; - uint64_t now = get_time().val; - - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) { - /* Only use active timers for the next expired value */ - if (pd_timer_is_active(port, timer)) { - int delta; - uint64_t t_value = timer_expires[port][timer]; - - if (t_value <= now) { - ret_value = EXPIRE_NOW; - break; - } - - delta = t_value - now; - if (ret_value > delta) - ret_value = delta; - } - } - - if (ret_value == MAX_EXPIRE) - ret_value = NO_TIMEOUT; - - return ret_value; -} - -#ifdef CONFIG_CMD_PD_TIMER -void pd_timer_dump(int port) -{ - int timer; - uint64_t now = get_time().val; - - ccprints("Timers(%d): cur=%d max=%d", - port, count[port], max_count[port]); - - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) { - if (pd_timer_is_disabled(port, timer)) { - continue; - } else if (pd_timer_is_active(port, timer)) { - uint32_t delta = 0; - - if (now < timer_expires[port][timer]) - delta = timer_expires[port][timer] - now; - - ccprints("[%2d] Active: %s (%d%s)", - timer, pd_timer_names[timer], (uint32_t)delta, - tc_event_loop_is_paused(port) - ? "-PAUSED" - : ""); - } else { - ccprints("[%2d] Inactive: %s", - timer, pd_timer_names[timer]); - } - } -} -#endif /* CONFIG_CMD_PD_TIMER */ diff --git a/common/usbc/usb_pe_ctvpd_sm.c b/common/usbc/usb_pe_ctvpd_sm.c deleted file mode 100644 index 346a57a461..0000000000 --- a/common/usbc/usb_pe_ctvpd_sm.c +++ /dev/null @@ -1,237 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "console.h" -#include "task.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_pd_tcpm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" - -/* USB Policy Engine Charge-Through VCONN Powered Device module */ - -/* Policy Engine Flags */ -#define PE_FLAGS_MSG_RECEIVED BIT(0) - -/** - * This is the PE Port object that contains information needed to - * implement a VCONN and Charge-Through VCONN Powered Device. - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* port flags, see PE_FLAGS_* */ - uint32_t flags; -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all policy-engine-level states */ -enum usb_pe_state { - PE_REQUEST, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_states */ -static const struct usb_state pe_states[]; - -static void set_state_pe(const int port, enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -static void pe_init(int port) -{ - const struct sm_ctx cleared = {}; - - pe[port].flags = 0; - pe[port].ctx = cleared; - set_state_pe(port, PE_REQUEST); -} - -bool pe_in_frs_mode(int port) -{ - /* Will never be in FRS mode */ - return false; -} - -bool pe_in_local_ams(int port) -{ - /* We never start a local AMS */ - return false; -} - -void pe_run(int port, int evt, int en) -{ - static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (en) - run_state(port, &pe[port].ctx); - else - local_state[port] = SM_PAUSED; - break; - } -} - -void pe_message_received(int port) -{ - pe[port].flags |= PE_FLAGS_MSG_RECEIVED; - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/** - * NOTE: - * The Charge-Through Vconn Powered Device's Policy Engine is very - * simple and no implementation is needed for the following functions - * that might be called by the Protocol Layer. - */ - -void pe_hard_reset_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_hard_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) -{ - /* No implementation needed by this policy engine */ -} - -void pe_report_discard(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_soft_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_message_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -static void pe_request_run(const int port) -{ - uint32_t *payload = (uint32_t *)tx_emsg[port].buf; - uint32_t header = rx_emsg[port].header; - uint32_t vdo = *(uint32_t *)rx_emsg[port].buf; - - if (pe[port].flags & PE_FLAGS_MSG_RECEIVED) { - pe[port].flags &= ~PE_FLAGS_MSG_RECEIVED; - - /* - * Only support Structured VDM Discovery - * Identity message - */ - - if (PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF) - return; - - if (PD_HEADER_CNT(header) == 0) - return; - - if (!PD_VDO_SVDM(vdo)) - return; - - if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT) - return; - -#ifdef CONFIG_USB_CTVPD - /* - * We have a valid DISCOVER IDENTITY message. - * Attempt to reset support timer - */ - tc_reset_support_timer(port); -#endif - /* Prepare to send ACK */ - - /* VDM Header */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(1) | - VDO_CMDT(CMDT_RSP_ACK) | - CMD_DISCOVER_IDENT); - - /* ID Header VDO */ - payload[1] = VDO_IDH( - 0, /* Not a USB Host */ - 1, /* Capable of being enumerated as USB Device */ - IDH_PTYPE_VPD, - 0, /* Modal Operation Not Supported */ - USB_VID_GOOGLE); - - /* Cert State VDO */ - payload[2] = 0; - - /* Product VDO */ - payload[3] = VDO_PRODUCT( - CONFIG_USB_PID, - USB_BCD_DEVICE); - - /* VPD VDO */ - payload[4] = VDO_VPD( - VPD_HW_VERSION, - VPD_FW_VERSION, - VPD_MAX_VBUS_20V, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_CT_CURRENT - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_VBUS_IMP( - VPD_VBUS_IMPEDANCE) - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_GND_IMP( - VPD_GND_IMPEDANCE) - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_CTS_SUPPORTED - : VPD_CTS_NOT_SUPPORTED); - - /* 20 bytes, 5 data objects */ - tx_emsg[port].len = 20; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, - (PD_HEADER_REV(header) > PD_REV30) ? - PD_REV30 : PD_HEADER_REV(header)); - /* Send the ACK */ - prl_send_data_msg(port, TCPCI_MSG_SOP_PRIME, - PD_DATA_VENDOR_DEF); - } -} - -/* All policy-engine-level states. */ -static const struct usb_state pe_states[] = { - [PE_REQUEST] = { - .run = pe_request_run, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - }, -}; -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); -#endif diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c deleted file mode 100644 index 096f689b0a..0000000000 --- a/common/usbc/usb_pe_drp_sm.c +++ /dev/null @@ -1,7486 +0,0 @@ -/* 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. - */ - -#include "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "dps.h" -#include "driver/tcpm/tcpm.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "stdbool.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "util.h" -#include "usb_common.h" -#include "usb_dp_alt_mode.h" -#include "usb_mode.h" -#include "usb_pd_dpm.h" -#include "usb_pd_policy.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_tbt_alt_mode.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "usbc_ppc.h" - -/* - * USB Policy Engine Sink / Source module - * - * Based on Revision 3.0, Version 1.2 of - * the USB Power Delivery Specification. - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -#define CPRINTF_LX(x, format, args...) \ - do { \ - if (pe_debug_level >= x) \ - CPRINTF(format, ## args); \ - } while (0) -#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ## args) -#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ## args) -#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ## args) - -#define CPRINTS_LX(x, format, args...) \ - do { \ - if (pe_debug_level >= x) \ - CPRINTS(format, ## args); \ - } while (0) -#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ## args) -#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ## args) -#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ## args) - -#define PE_SET_FLAG(port, flag) atomic_or(&pe[port].flags, (flag)) -#define PE_CLR_FLAG(port, flag) atomic_clear_bits(&pe[port].flags, (flag)) -#define PE_CHK_FLAG(port, flag) (pe[port].flags & (flag)) - -/* - * These macros SET, CLEAR, and CHECK, a DPM (Device Policy Manager) - * Request. The Requests are listed in usb_pe_sm.h. - */ -#define PE_SET_DPM_REQUEST(port, req) atomic_or(&pe[port].dpm_request, (req)) -#define PE_CLR_DPM_REQUEST(port, req) \ - atomic_clear_bits(&pe[port].dpm_request, (req)) -#define PE_CHK_DPM_REQUEST(port, req) (pe[port].dpm_request & (req)) - -/* - * Policy Engine Layer Flags - * These are reproduced in test/usb_pe.h. If they change here, they must change - * there. - */ - -/* At least one successful PD communication packet received from port partner */ -#define PE_FLAGS_PD_CONNECTION BIT(0) -/* Accept message received from port partner */ -#define PE_FLAGS_ACCEPT BIT(1) -/* Power Supply Ready message received from port partner */ -#define PE_FLAGS_PS_READY BIT(2) -/* Protocol Error was determined based on error recovery current state */ -#define PE_FLAGS_PROTOCOL_ERROR BIT(3) -/* Set if we are in Modal Operation */ -#define PE_FLAGS_MODAL_OPERATION BIT(4) -/* A message we requested to be sent has been transmitted */ -#define PE_FLAGS_TX_COMPLETE BIT(5) -/* A message sent by a port partner has been received */ -#define PE_FLAGS_MSG_RECEIVED BIT(6) -/* A hard reset has been requested but has not been sent, not currently used */ -#define PE_FLAGS_HARD_RESET_PENDING BIT(7) -/* Port partner sent a Wait message. Wait before we resend our message */ -#define PE_FLAGS_WAIT BIT(8) -/* An explicit contract is in place with our port partner */ -#define PE_FLAGS_EXPLICIT_CONTRACT BIT(9) -/* Waiting for Sink Capabailities timed out. Used for retry error handling */ -#define PE_FLAGS_SNK_WAIT_CAP_TIMEOUT BIT(10) -/* Power Supply voltage/current transition timed out */ -#define PE_FLAGS_PS_TRANSITION_TIMEOUT BIT(11) -/* Flag to note current Atomic Message Sequence is interruptible */ -#define PE_FLAGS_INTERRUPTIBLE_AMS BIT(12) -/* Flag to note Power Supply reset has completed */ -#define PE_FLAGS_PS_RESET_COMPLETE BIT(13) -/* VCONN swap operation has completed */ -#define PE_FLAGS_VCONN_SWAP_COMPLETE BIT(14) -/* Flag to note no more setup VDMs (discovery, etc.) should be sent */ -#define PE_FLAGS_VDM_SETUP_DONE BIT(15) -/* Flag to note PR Swap just completed for Startup entry */ -#define PE_FLAGS_PR_SWAP_COMPLETE BIT(16) -/* Flag to note Port Discovery port partner replied with BUSY */ -#define PE_FLAGS_VDM_REQUEST_BUSY BIT(17) -/* Flag to note Port Discovery port partner replied with NAK */ -#define PE_FLAGS_VDM_REQUEST_NAKED BIT(18) -/* Flag to note FRS/PRS context in shared state machine path */ -#define PE_FLAGS_FAST_ROLE_SWAP_PATH BIT(19) -/* Flag to note if FRS listening is enabled */ -#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(20) -/* Flag to note TCPC passed on FRS signal from port partner */ -#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(21) -/* TODO: POLICY decision: Triggers a DR SWAP attempt from UFP to DFP */ -#define PE_FLAGS_DR_SWAP_TO_DFP BIT(22) -/* - * TODO: POLICY decision - * Flag to trigger a message resend after receiving a WAIT from port partner - */ -#define PE_FLAGS_WAITING_PR_SWAP BIT(23) -/* FLAG is set when an AMS is initiated locally. ie. AP requested a PR_SWAP */ -#define PE_FLAGS_LOCALLY_INITIATED_AMS BIT(24) -/* Flag to note the first message sent in PE_SRC_READY and PE_SNK_READY */ -#define PE_FLAGS_FIRST_MSG BIT(25) -/* Flag to continue a VDM request if it was interrupted */ -#define PE_FLAGS_VDM_REQUEST_CONTINUE BIT(26) -/* TODO: POLICY decision: Triggers a Vconn SWAP attempt to on */ -#define PE_FLAGS_VCONN_SWAP_TO_ON BIT(27) -/* FLAG to track that VDM request to port partner timed out */ -#define PE_FLAGS_VDM_REQUEST_TIMEOUT BIT(28) -/* FLAG to note message was discarded due to incoming message */ -#define PE_FLAGS_MSG_DISCARDED BIT(29) -/* FLAG to note that hard reset can't be performed due to battery low */ -#define PE_FLAGS_SNK_WAITING_BATT BIT(30) - -/* Message flags which should not persist on returning to ready state */ -#define PE_FLAGS_READY_CLR (PE_FLAGS_LOCALLY_INITIATED_AMS \ - | PE_FLAGS_MSG_DISCARDED \ - | PE_FLAGS_VDM_REQUEST_TIMEOUT \ - | PE_FLAGS_INTERRUPTIBLE_AMS) - -/* - * Combination to check whether a reply to a message was received. Our message - * should have sent (i.e. not been discarded) and a partner message is ready to - * process. - * - * When chunking is disabled (ex. for PD 2.0), these flags will set - * on the same run cycle. With chunking, received message will take an - * additional cycle to be flagged. - */ -#define PE_CHK_REPLY(port) (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED) && \ - !PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) - -/* 6.7.3 Hard Reset Counter */ -#define N_HARD_RESET_COUNT 2 - -/* 6.7.4 Capabilities Counter */ -#define N_CAPS_COUNT 25 - -/* 6.7.5 Discover Identity Counter */ -/* - * NOTE: The Protocol Layer tries to send a message 3 time before giving up, - * so a Discover Identity SOP' message will be sent 3*6 = 18 times (slightly - * less than spec maximum of 20). This counter applies only to cable plug - * discovery. - */ -#define N_DISCOVER_IDENTITY_COUNT 6 - -/* - * It is permitted to send SOP' Discover Identity messages before a PD contract - * is in place. However, this is only beneficial if the cable powers up quickly - * solely from VCONN. Limit the number of retries without a contract to - * ensure we attempt some cable discovery after a contract is in place. - */ -#define N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT 2 - -/* - * Once this limit of SOP' Discover Identity messages has been set, downgrade - * to PD 2.0 in case the cable is non-compliant about GoodCRC-ing higher - * revisions. This limit should be higher than the precontract limit. - */ -#define N_DISCOVER_IDENTITY_PD3_0_LIMIT 4 - -/* - * tDiscoverIdentity is only defined while an explicit contract is in place, so - * extend the interval between retries pre-contract. - */ -#define PE_T_DISCOVER_IDENTITY_NO_CONTRACT (200*MSEC) - -/* - * Only VCONN source can communicate with the cable plug. Hence, try VCONN swap - * 3 times before giving up. - * - * Note: This is not a part of power delivery specification - */ -#define N_VCONN_SWAP_COUNT 3 - -/* - * Counter to track how many times to attempt SRC to SNK PR swaps before giving - * up. - * - * Note: This is not a part of power delivery specification - */ -#define N_SNK_SRC_PR_SWAP_COUNT 5 - -/* - * ChromeOS policy: - * For PD2.0, We must be DFP before sending Discover Identity message - * to the port partner. Attempt to DR SWAP from UFP to DFP - * N_DR_SWAP_ATTEMPT_COUNT times before giving up on sending a - * Discover Identity message. - */ -#define N_DR_SWAP_ATTEMPT_COUNT 5 - -#define TIMER_DISABLED 0xffffffffffffffff /* Unreachable time in future */ - -/* - * The time that we allow the port partner to send any messages after an - * explicit contract is established. 200ms was chosen somewhat arbitrarily as - * it should be long enough for sources to decide to send a message if they were - * going to, but not so long that a "low power charger connected" notification - * would be shown in the chrome OS UI. Setting t0o large a delay can cause - * problems if the PD discovery time exceeds 1s (tAMETimeout) - */ -#define SRC_SNK_READY_HOLD_OFF_US (200 * MSEC) - -/* - * Function pointer to a Structured Vendor Defined Message (SVDM) response - * function defined in the board's usb_pd_policy.c file. - */ -typedef int (*svdm_rsp_func)(int port, uint32_t *payload); - -/* List of all Policy Engine level states */ -enum usb_pe_state { - /* Super States */ - PE_PRS_FRS_SHARED, - PE_VDM_SEND_REQUEST, - - /* Normal States */ - PE_SRC_STARTUP, - PE_SRC_DISCOVERY, - PE_SRC_SEND_CAPABILITIES, - PE_SRC_NEGOTIATE_CAPABILITY, - PE_SRC_TRANSITION_SUPPLY, - PE_SRC_READY, - PE_SRC_DISABLED, - PE_SRC_CAPABILITY_RESPONSE, - PE_SRC_HARD_RESET, - PE_SRC_HARD_RESET_RECEIVED, - PE_SRC_TRANSITION_TO_DEFAULT, - PE_SNK_STARTUP, - PE_SNK_DISCOVERY, - PE_SNK_WAIT_FOR_CAPABILITIES, - PE_SNK_EVALUATE_CAPABILITY, - PE_SNK_SELECT_CAPABILITY, - PE_SNK_READY, - PE_SNK_HARD_RESET, - PE_SNK_TRANSITION_TO_DEFAULT, - PE_SNK_GIVE_SINK_CAP, - PE_SNK_GET_SOURCE_CAP, - PE_SNK_TRANSITION_SINK, - PE_SEND_SOFT_RESET, - PE_SOFT_RESET, - PE_SEND_NOT_SUPPORTED, - PE_SRC_PING, - PE_DRS_EVALUATE_SWAP, - PE_DRS_CHANGE, - PE_DRS_SEND_SWAP, - PE_PRS_SRC_SNK_EVALUATE_SWAP, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF, - PE_PRS_SRC_SNK_ASSERT_RD, - PE_PRS_SRC_SNK_WAIT_SOURCE_ON, - PE_PRS_SRC_SNK_SEND_SWAP, - PE_PRS_SNK_SRC_EVALUATE_SWAP, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF, - PE_PRS_SNK_SRC_ASSERT_RP, - PE_PRS_SNK_SRC_SOURCE_ON, - PE_PRS_SNK_SRC_SEND_SWAP, - PE_VCS_EVALUATE_SWAP, - PE_VCS_SEND_SWAP, - PE_VCS_WAIT_FOR_VCONN_SWAP, - PE_VCS_TURN_ON_VCONN_SWAP, - PE_VCS_TURN_OFF_VCONN_SWAP, - PE_VCS_SEND_PS_RDY_SWAP, - PE_VCS_CBL_SEND_SOFT_RESET, - PE_VDM_IDENTITY_REQUEST_CBL, - PE_INIT_PORT_VDM_IDENTITY_REQUEST, - PE_INIT_VDM_SVIDS_REQUEST, - PE_INIT_VDM_MODES_REQUEST, - PE_VDM_REQUEST_DPM, - PE_VDM_RESPONSE, - PE_HANDLE_CUSTOM_VDM_REQUEST, - PE_WAIT_FOR_ERROR_RECOVERY, - PE_BIST_TX, - PE_DEU_SEND_ENTER_USB, - PE_DR_GET_SINK_CAP, - PE_DR_SNK_GIVE_SOURCE_CAP, - PE_DR_SRC_GET_SOURCE_CAP, - - /* PD3.0 only states below here*/ - PE_FRS_SNK_SRC_START_AMS, - PE_GIVE_BATTERY_CAP, - PE_GIVE_BATTERY_STATUS, - PE_SEND_ALERT, - PE_SRC_CHUNK_RECEIVED, - PE_SNK_CHUNK_RECEIVED, - PE_VCS_FORCE_VCONN, -}; - -/* - * The result of a previously sent DPM request; used by PE_VDM_SEND_REQUEST to - * indicate to child states when they need to handle a response. - */ -enum vdm_response_result { - /* The parent state is still waiting for a response. */ - VDM_RESULT_WAITING, - /* - * The parent state parsed a message, but there is nothing for the child - * to handle, e.g. BUSY. - */ - VDM_RESULT_NO_ACTION, - /* The parent state processed an ACK response. */ - VDM_RESULT_ACK, - /* - * The parent state processed a NAK-like response (NAK, Not Supported, - * or response timeout. - */ - VDM_RESULT_NAK, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_state */ -static const struct usb_state pe_states[]; - -/* - * We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME) - * and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or - * we are overriding and the level is not DISABLED. - * - * If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0 - * then the DEBUG LABELS will be removed from the build. - */ -#if defined(CONFIG_COMMON_RUNTIME) && \ - (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \ - (CONFIG_USB_PD_DEBUG_LEVEL > 0)) -#define USB_PD_DEBUG_LABELS -#endif - -/* List of human readable state names for console debugging */ -__maybe_unused static __const_data const char * const pe_state_names[] = { - /* Super States */ -#ifdef CONFIG_USB_PD_REV30 - [PE_PRS_FRS_SHARED] = "SS:PE_PRS_FRS_SHARED", -#endif - [PE_VDM_SEND_REQUEST] = "SS:PE_VDM_Send_Request", - - /* Normal States */ - [PE_SRC_STARTUP] = "PE_SRC_Startup", - [PE_SRC_DISCOVERY] = "PE_SRC_Discovery", - [PE_SRC_SEND_CAPABILITIES] = "PE_SRC_Send_Capabilities", - [PE_SRC_NEGOTIATE_CAPABILITY] = "PE_SRC_Negotiate_Capability", - [PE_SRC_TRANSITION_SUPPLY] = "PE_SRC_Transition_Supply", - [PE_SRC_READY] = "PE_SRC_Ready", - [PE_SRC_DISABLED] = "PE_SRC_Disabled", - [PE_SRC_CAPABILITY_RESPONSE] = "PE_SRC_Capability_Response", - [PE_SRC_HARD_RESET] = "PE_SRC_Hard_Reset", - [PE_SRC_HARD_RESET_RECEIVED] = "PE_SRC_Hard_Reset_Received", - [PE_SRC_TRANSITION_TO_DEFAULT] = "PE_SRC_Transition_to_default", - [PE_SNK_STARTUP] = "PE_SNK_Startup", - [PE_SNK_DISCOVERY] = "PE_SNK_Discovery", - [PE_SNK_WAIT_FOR_CAPABILITIES] = "PE_SNK_Wait_for_Capabilities", - [PE_SNK_EVALUATE_CAPABILITY] = "PE_SNK_Evaluate_Capability", - [PE_SNK_SELECT_CAPABILITY] = "PE_SNK_Select_Capability", - [PE_SNK_READY] = "PE_SNK_Ready", - [PE_SNK_HARD_RESET] = "PE_SNK_Hard_Reset", - [PE_SNK_TRANSITION_TO_DEFAULT] = "PE_SNK_Transition_to_default", - [PE_SNK_GIVE_SINK_CAP] = "PE_SNK_Give_Sink_Cap", - [PE_SNK_GET_SOURCE_CAP] = "PE_SNK_Get_Source_Cap", - [PE_SNK_TRANSITION_SINK] = "PE_SNK_Transition_Sink", - [PE_SEND_SOFT_RESET] = "PE_Send_Soft_Reset", - [PE_SOFT_RESET] = "PE_Soft_Reset", - [PE_SEND_NOT_SUPPORTED] = "PE_Send_Not_Supported", - [PE_SRC_PING] = "PE_SRC_Ping", - [PE_DRS_EVALUATE_SWAP] = "PE_DRS_Evaluate_Swap", - [PE_DRS_CHANGE] = "PE_DRS_Change", - [PE_DRS_SEND_SWAP] = "PE_DRS_Send_Swap", - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = "PE_PRS_SRC_SNK_Evaluate_Swap", - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = "PE_PRS_SRC_SNK_Transition_To_Off", - [PE_PRS_SRC_SNK_ASSERT_RD] = "PE_PRS_SRC_SNK_Assert_Rd", - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = "PE_PRS_SRC_SNK_Wait_Source_On", - [PE_PRS_SRC_SNK_SEND_SWAP] = "PE_PRS_SRC_SNK_Send_Swap", - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = "PE_PRS_SNK_SRC_Evaluate_Swap", - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = "PE_PRS_SNK_SRC_Transition_To_Off", - [PE_PRS_SNK_SRC_ASSERT_RP] = "PE_PRS_SNK_SRC_Assert_Rp", - [PE_PRS_SNK_SRC_SOURCE_ON] = "PE_PRS_SNK_SRC_Source_On", - [PE_PRS_SNK_SRC_SEND_SWAP] = "PE_PRS_SNK_SRC_Send_Swap", -#ifdef CONFIG_USBC_VCONN - [PE_VCS_EVALUATE_SWAP] = "PE_VCS_Evaluate_Swap", - [PE_VCS_SEND_SWAP] = "PE_VCS_Send_Swap", - [PE_VCS_WAIT_FOR_VCONN_SWAP] = "PE_VCS_Wait_For_Vconn_Swap", - [PE_VCS_TURN_ON_VCONN_SWAP] = "PE_VCS_Turn_On_Vconn_Swap", - [PE_VCS_TURN_OFF_VCONN_SWAP] = "PE_VCS_Turn_Off_Vconn_Swap", - [PE_VCS_SEND_PS_RDY_SWAP] = "PE_VCS_Send_Ps_Rdy_Swap", - [PE_VCS_CBL_SEND_SOFT_RESET] = "PE_VCS_CBL_Send_Soft_Reset", -#endif - [PE_VDM_IDENTITY_REQUEST_CBL] = "PE_VDM_Identity_Request_Cbl", - [PE_INIT_PORT_VDM_IDENTITY_REQUEST] = - "PE_INIT_PORT_VDM_Identity_Request", - [PE_INIT_VDM_SVIDS_REQUEST] = "PE_INIT_VDM_SVIDs_Request", - [PE_INIT_VDM_MODES_REQUEST] = "PE_INIT_VDM_Modes_Request", - [PE_VDM_REQUEST_DPM] = "PE_VDM_Request_DPM", - [PE_VDM_RESPONSE] = "PE_VDM_Response", - [PE_HANDLE_CUSTOM_VDM_REQUEST] = "PE_Handle_Custom_Vdm_Request", - [PE_WAIT_FOR_ERROR_RECOVERY] = "PE_Wait_For_Error_Recovery", - [PE_BIST_TX] = "PE_Bist_TX", - [PE_DEU_SEND_ENTER_USB] = "PE_DEU_Send_Enter_USB", - [PE_DR_GET_SINK_CAP] = "PE_DR_Get_Sink_Cap", - [PE_DR_SNK_GIVE_SOURCE_CAP] = "PE_DR_SNK_Give_Source_Cap", - [PE_DR_SRC_GET_SOURCE_CAP] = "PE_DR_SRC_Get_Source_Cap", - - /* PD3.0 only states below here*/ -#ifdef CONFIG_USB_PD_REV30 - [PE_FRS_SNK_SRC_START_AMS] = "PE_FRS_SNK_SRC_Start_Ams", -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [PE_GIVE_BATTERY_CAP] = "PE_Give_Battery_Cap", - [PE_GIVE_BATTERY_STATUS] = "PE_Give_Battery_Status", - [PE_SEND_ALERT] = "PE_Send_Alert", -#else - [PE_SRC_CHUNK_RECEIVED] = "PE_SRC_Chunk_Received", - [PE_SNK_CHUNK_RECEIVED] = "PE_SNK_Chunk_Received", -#endif -#ifdef CONFIG_USBC_VCONN - [PE_VCS_FORCE_VCONN] = "PE_VCS_Force_Vconn", -#endif -#endif /* CONFIG_USB_PD_REV30 */ -}; - -#ifndef CONFIG_USBC_VCONN -GEN_NOT_SUPPORTED(PE_VCS_EVALUATE_SWAP); -#define PE_VCS_EVALUATE_SWAP PE_VCS_EVALUATE_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_SEND_SWAP); -#define PE_VCS_SEND_SWAP PE_VCS_SEND_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_WAIT_FOR_VCONN_SWAP); -#define PE_VCS_WAIT_FOR_VCONN_SWAP PE_VCS_WAIT_FOR_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_TURN_ON_VCONN_SWAP); -#define PE_VCS_TURN_ON_VCONN_SWAP PE_VCS_TURN_ON_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_TURN_OFF_VCONN_SWAP); -#define PE_VCS_TURN_OFF_VCONN_SWAP PE_VCS_TURN_OFF_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_SEND_PS_RDY_SWAP); -#define PE_VCS_SEND_PS_RDY_SWAP PE_VCS_SEND_PS_RDY_SWAP_NOT_SUPPORTED -#endif /* CONFIG_USBC_VCONN */ - -#ifndef CONFIG_USB_PD_REV30 -GEN_NOT_SUPPORTED(PE_FRS_SNK_SRC_START_AMS); -#define PE_FRS_SNK_SRC_START_AMS PE_FRS_SNK_SRC_START_AMS_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_PRS_FRS_SHARED); -#define PE_PRS_FRS_SHARED PE_PRS_FRS_SHARED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED); -#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED); -#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_REV30 */ - -#if !defined(CONFIG_USBC_VCONN) || !defined(CONFIG_USB_PD_REV30) -GEN_NOT_SUPPORTED(PE_VCS_FORCE_VCONN); -#define PE_VCS_FORCE_VCONN PE_VCS_FORCE_VCONN_NOT_SUPPORTED -#endif - -#ifndef CONFIG_USB_PD_EXTENDED_MESSAGES -GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_CAP); -#define PE_GIVE_BATTERY_CAP PE_GIVE_BATTERY_CAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_STATUS); -#define PE_GIVE_BATTERY_STATUS PE_GIVE_BATTERY_STATUS_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SEND_ALERT); -#define PE_SEND_ALERT PE_SEND_ALERT_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED); -#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED); -#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Common message send checking - * - * PE_MSG_SEND_PENDING: A message has been requested to be sent. It has - * not been GoodCRCed or Discarded. - * PE_MSG_SEND_COMPLETED: The message that was requested has been sent. - * This will only be returned one time and any other - * request for message send status will just return - * PE_MSG_SENT. This message actually includes both - * The COMPLETED and the SENT bit for easier checking. - * NOTE: PE_MSG_SEND_COMPLETED will only be returned - * a single time, directly after TX_COMPLETE. - * PE_MSG_SENT: The message that was requested to be sent has - * successfully been transferred to the partner. - * PE_MSG_DISCARDED: The message that was requested to be sent was - * discarded. The partner did not receive it. - * NOTE: PE_MSG_DISCARDED will only be returned - * one time and it is up to the caller to process - * what ever is needed to handle the Discard. - * PE_MSG_DPM_DISCARDED: The message that was requested to be sent was - * discarded and an active DRP_REQUEST was active. - * The DRP_REQUEST that was current will be moved - * back to the drp_requests so it can be performed - * later if needed. - * NOTE: PE_MSG_DPM_DISCARDED will only be returned - * one time and it is up to the caller to process - * what ever is needed to handle the Discard. - */ -enum pe_msg_check { - PE_MSG_SEND_PENDING = BIT(0), - PE_MSG_SENT = BIT(1), - PE_MSG_DISCARDED = BIT(2), - - PE_MSG_SEND_COMPLETED = BIT(3) | PE_MSG_SENT, - PE_MSG_DPM_DISCARDED = BIT(4) | PE_MSG_DISCARDED, -}; -static void pe_sender_response_msg_entry(const int port); -static enum pe_msg_check pe_sender_response_msg_run(const int port); -static void pe_sender_response_msg_exit(const int port); - -/* Debug log level - higher number == more log */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level pe_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level pe_debug_level = DEBUG_LEVEL_1; -#endif - -/* - * Policy Engine State Machine Object - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* state machine flags */ - uint32_t flags; - /* Device Policy Manager Request */ - uint32_t dpm_request; - uint32_t dpm_curr_request; - /* last requested voltage PDO index */ - int requested_idx; - - /* - * Port events - PD_STATUS_EVENT_* values - * Set from PD task but may be cleared by host command - */ - uint32_t events; - - /* port address where soft resets are sent */ - enum tcpci_msg_type soft_reset_sop; - - /* Current limit / voltage based on the last request message */ - uint32_t curr_limit; - uint32_t supply_voltage; - - /* PD_VDO_INVALID is used when there is an invalid VDO */ - int32_t ama_vdo; - int32_t vpd_vdo; - /* Alternate mode discovery results */ - struct pd_discovery discovery[DISCOVERY_TYPE_COUNT]; - /* Active alternate modes */ - struct partner_active_modes partner_amodes[AMODE_TYPE_COUNT]; - - /* Partner type to send */ - enum tcpci_msg_type tx_type; - - /* VDM - used to send information to shared VDM Request state */ - uint32_t vdm_cnt; - uint32_t vdm_data[VDO_HDR_SIZE + VDO_MAX_SIZE]; - uint8_t vdm_ack_min_data_objects; - - /* Counters */ - - /* - * This counter is used to retry the Hard Reset whenever there is no - * response from the remote device. - */ - uint32_t hard_reset_counter; - - /* - * This counter is used to count the number of Source_Capabilities - * Messages which have been sent by a Source at power up or after a - * Hard Reset. - */ - uint32_t caps_counter; - - /* - * This counter maintains a count of Discover Identity Messages sent - * to a cable. If no GoodCRC messages are received after - * nDiscoverIdentityCount, the port shall not send any further - * SOP'/SOP'' messages. - */ - uint32_t discover_identity_counter; - /* - * For PD2.0, we need to be a DFP before sending a discovery identity - * message to our port partner. This counter keeps track of how - * many attempts to DR SWAP from UFP to DFP. - */ - uint32_t dr_swap_attempt_counter; - - /* - * This counter tracks how many PR Swap messages are sent when the - * partner responds with a Wait message. Only used during SRC to SNK - * PR swaps - */ - uint8_t src_snk_pr_swap_counter; - - /* - * This counter maintains a count of VCONN swap requests. If VCONN swap - * isn't successful after N_VCONN_SWAP_COUNT, the port calls - * dpm_vdm_naked(). - */ - uint8_t vconn_swap_counter; - - /* Last received source cap */ - uint32_t src_caps[PDO_MAX_OBJECTS]; - int src_cap_cnt; /* -1 on error retrieving source caps */ - - /* Last received sink cap */ - uint32_t snk_caps[PDO_MAX_OBJECTS]; - int snk_cap_cnt; - - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_image current_image; -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -test_export_static enum usb_pe_state get_state_pe(const int port); -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state); -static void pe_set_dpm_curr_request(const int port, const int request); -/* - * The spec. revision is used to index into this array. - * PD 1.0 (VDO 1.0) - return VDM_VER10 - * PD 2.0 (VDO 1.0) - return VDM_VER10 - * PD 3.0 (VDO 2.0) - return VDM_VER20 - */ -static const uint8_t vdo_ver[] = { - [PD_REV10] = VDM_VER10, - [PD_REV20] = VDM_VER10, - [PD_REV30] = VDM_VER20, -}; - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ - return prl_get_rev(port, type); -} - -int pd_get_vdo_ver(int port, enum tcpci_msg_type type) -{ - enum pd_rev_type rev = prl_get_rev(port, type); - - if (rev < PD_REV30) - return vdo_ver[rev]; - else - return VDM_VER20; -} - -static void pe_set_ready_state(int port) -{ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); -} - -static inline void send_data_msg(int port, enum tcpci_msg_type type, - enum pd_data_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_data_msg(port, type, msg); -} - -static __maybe_unused inline void send_ext_data_msg( - int port, enum tcpci_msg_type type, enum pd_ext_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_ext_data_msg(port, type, msg); -} - -static inline void send_ctrl_msg(int port, enum tcpci_msg_type type, - enum pd_ctrl_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_ctrl_msg(port, type, msg); -} - -static void set_cable_rev(int port) -{ - /* - * If port partner runs PD 2.0, cable communication must - * also be PD 2.0 - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) { - /* - * If the cable supports PD 3.0, but the port partner supports PD 2.0, - * redo the cable discover with PD 2.0 - */ - if (prl_get_rev(port, TCPCI_MSG_SOP_PRIME) == PD_REV30 && - pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_COMPLETE) { - pd_set_identity_discovery(port, TCPCI_MSG_SOP_PRIME, - PD_DISC_NEEDED); - } - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - } -} - -/* Compile-time insurance to ensure this code does not call into prl directly */ -#define prl_send_data_msg DO_NOT_USE -#define prl_send_ext_data_msg DO_NOT_USE -#define prl_send_ctrl_msg DO_NOT_USE - -static void pe_init(int port) -{ - pe[port].flags = 0; - pe[port].dpm_request = 0; - pe[port].dpm_curr_request = 0; - pd_timer_disable_range(port, PE_TIMER_RANGE); - pe[port].data_role = pd_get_data_role(port); - pe[port].tx_type = TCPCI_MSG_INVALID; - pe[port].events = 0; - - tc_pd_connection(port, 0); - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_STARTUP); - else - set_state_pe(port, PE_SNK_STARTUP); -} - -int pe_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -bool pe_in_frs_mode(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -bool pe_in_local_ams(int port) -{ - return !!PE_CHK_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); -} - -void pe_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - pe_debug_level = debug_level; -#endif -} - -void pe_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (!en) { - local_state[port] = SM_PAUSED; - /* - * While we are paused, exit all states and wait until - * initialized again. - */ - set_state(port, &pe[port].ctx, NULL); - break; - } - - /* - * 8.3.3.3.8 PE_SNK_Hard_Reset State - * The Policy Engine Shall transition to the PE_SNK_Hard_Reset - * state from any state when: - * - Hard Reset request from Device Policy Manager - * - * USB PD specification clearly states that we should go to - * PE_SNK_Hard_Reset from ANY state (including states in which - * port is source) when DPM requests that. This can lead to - * execute Hard Reset path for sink when actually our power - * role is source. In our implementation we will choose Hard - * Reset path depending on current power role. - */ - if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_HARD_RESET_SEND)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_HARD_RESET_SEND); - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SNK_HARD_RESET); - } - - /* - * Check for Fast Role Swap signal - * This is not a typical pattern for adding state changes. - * I added this here because FRS SIGNALED can happen at any - * state once we are listening for the signal and we want to - * make sure to handle it immediately. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED)) { - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); - set_state_pe(port, PE_FRS_SNK_SRC_START_AMS); - } - - /* Run state machine */ - run_state(port, &pe[port].ctx); - break; - } -} - -int pe_is_explicit_contract(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); -} - -void pe_message_received(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pe_hard_reset_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_CLR_FLAG(port, PE_FLAGS_HARD_RESET_PENDING); -} - -void pe_got_hard_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * Transition from any state to the PE_SRC_Hard_Reset_Received or - * PE_SNK_Transition_to_default state when: - * 1) Hard Reset Signaling is detected. - */ - pe[port].power_role = pd_get_power_role(port); - - /* Exit BIST Test mode, in case the TCPC entered it. */ - tcpc_set_bist_test_mode(port, false); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET_RECEIVED); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -#ifdef CONFIG_USB_PD_REV30 -/* - * pd_got_frs_signal - * - * Called by the handler that detects the FRS signal in order to - * switch PE states to complete the FRS that the hardware has - * started. - * - * If the PE is not running, generate an error recovery to turn off - * Vbus and get the port back into a known state. - */ -void pd_got_frs_signal(int port) -{ - if (pe_is_running(port)) - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); - else - pd_set_error_recovery(port); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_USB_PD_REV30 */ - -/* - * PE_Set_FRS_Enable - * - * This function should be called every time an explicit contract - * is disabled, to disable FRS. - * - * Enabling an explicit contract is not enough to enable FRS, it - * also requires a Sink Capability power requirement from a Source - * that supports FRS so we can determine if this is something we - * can handle. - */ -static void pe_set_frs_enable(int port, int enable) -{ - int current = PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - - /* This should only be called from the PD task */ - if (!IS_ENABLED(TEST_BUILD)) - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - if (!IS_ENABLED(CONFIG_USB_PD_FRS) || !IS_ENABLED(CONFIG_USB_PD_REV30)) - return; - - /* Request an FRS change, only if the state has changed */ - if (!!current == !!enable) - return; - - pd_set_frs_enable(port, enable); - if (enable) { - int curr_limit = *pd_get_snk_caps(port) - & PDO_FIXED_FRS_CURR_MASK; - - typec_select_src_current_limit_rp(port, - curr_limit == - PDO_FIXED_FRS_CURR_3A0_AT_5V ? - TYPEC_RP_3A0 : TYPEC_RP_1A5); - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - } else { - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - } -} - -void pe_set_explicit_contract(int port) -{ - PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - - /* Set Rp for collision avoidance */ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - typec_update_cc(port); -} - -void pe_invalidate_explicit_contract(int port) -{ - pe_set_frs_enable(port, 0); - - PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - - /* Set Rp for current limit if still attached */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && pd_is_connected(port)) - typec_update_cc(port); -} - -void pd_notify_event(int port, uint32_t event_mask) -{ - atomic_or(&pe[port].events, event_mask); - - /* Notify the host that new events are available to read */ - pd_send_host_event(PD_EVENT_TYPEC); -} - -void pd_clear_events(int port, uint32_t clear_mask) -{ - atomic_clear_bits(&pe[port].events, clear_mask); -} - -uint32_t pd_get_events(int port) -{ - return pe[port].events; -} - -void pe_set_snk_caps(int port, int cnt, uint32_t *snk_caps) -{ - pe[port].snk_cap_cnt = cnt; - - memcpy(pe[port].snk_caps, snk_caps, sizeof(uint32_t) * cnt); -} - -const uint32_t * const pd_get_snk_caps(int port) -{ - return pe[port].snk_caps; -} - -uint8_t pd_get_snk_cap_cnt(int port) -{ - return pe[port].snk_cap_cnt; -} - -uint32_t pd_get_requested_voltage(int port) -{ - return pe[port].supply_voltage; -} - -uint32_t pd_get_requested_current(int port) -{ - return pe[port].curr_limit; -} - -/* - * Determine if this port may communicate with the cable plug. - * - * In both PD 2.0 and 3.0 (2.5.4 SOP'/SOP'' Communication with Cable Plugs): - * - * When no Contract or an Implicit Contract is in place (e.g. after a Power Role - * Swap or Fast Role Swap) only the Source port that is supplying Vconn is - * allowed to send packets to a Cable Plug - * - * When in an explicit contract, PD 3.0 requires that a port be Vconn source to - * communicate with the cable. PD 2.0 requires that a port be DFP to - * communicate with the cable plug, with an implication that it must be Vconn - * source as well (6.3.11 VCONN_Swap Message). - */ -static bool pe_can_send_sop_prime(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) { - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) - return tc_is_vconn_src(port) && - pe[port].data_role == PD_ROLE_DFP; - else - return tc_is_vconn_src(port); - } else { - return tc_is_vconn_src(port) && - pe[port].power_role == PD_ROLE_SOURCE; - } - } else { - return false; - } -} - -/* - * Determine if this port may send the given VDM type - * - * For PD 2.0, "Only the DFP Shall be an Initrator of Structured VDMs except for - * the Attention Command that Shall only be initiated by the UFP" - * - * For PD 3.0, "Either port May be an Initiator of Structured VDMs except for - * the Enter Mode and Exit Mode Commands which shall only be initiated by the - * DFP" (6.4.4.2 Structured VDM) - * - * In both revisions, VDMs may only be initiated while in an explicit contract, - * with the only exception being for cable plug discovery. - */ -static bool pe_can_send_sop_vdm(int port, int vdm_cmd) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) { - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) { - if (pe[port].data_role == PD_ROLE_UFP && - vdm_cmd != CMD_ATTENTION) { - return false; - } - } else { - if (pe[port].data_role == PD_ROLE_UFP && - (vdm_cmd == CMD_ENTER_MODE || - vdm_cmd == CMD_EXIT_MODE)) { - return false; - } - } - return true; - } - - return false; -} - -static void pe_send_soft_reset(const int port, enum tcpci_msg_type type) -{ - pe[port].soft_reset_sop = type; - set_state_pe(port, PE_SEND_SOFT_RESET); -} - -void pe_report_discard(int port) -{ - /* - * Clear local AMS indicator as our AMS message was discarded, and flag - * the discard for the PE - */ - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_SET_FLAG(port, PE_FLAGS_MSG_DISCARDED); - - /* TODO(b/157228506): Ensure all states are checking discard */ -} - -/* - * Utility function to check for an outgoing message discard during states which - * send a message as a part of an AMS and wait for the transmit to complete. - * Note these states should not be power transitioning. - * - * In these states, discard due to an incoming message is a protocol error. - */ -static bool pe_check_outgoing_discard(int port) -{ - /* - * On outgoing discard, soft reset with SOP* of incoming message - * - * See Table 6-65 Response to an incoming Message (except VDM) in PD 3.0 - * Version 2.0 Specification. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - enum tcpci_msg_type sop = - PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED); - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - pe_send_soft_reset(port, sop); - return true; - } - - return false; -} - -void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * If there is a timeout error while waiting for a chunk of a chunked - * message, there is no requirement to trigger a soft reset. - */ - if (e == ERR_RCH_CHUNK_WAIT_TIMEOUT) - return; - - /* - * Generate Hard Reset if Protocol Error occurred - * while in PE_Send_Soft_Reset state. - */ - if (get_state_pe(port) == PE_SEND_SOFT_RESET) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - /* - * The following states require custom handling of protocol errors, - * because they either need special handling of the no GoodCRC case - * (cable identity request, send capabilities), occur before explicit - * contract (discovery), or happen during a power transition. - * - * TODO(b/150774779): TCPMv2: Improve pe_error documentation - */ - if ((get_state_pe(port) == PE_SRC_SEND_CAPABILITIES || - get_state_pe(port) == PE_SRC_TRANSITION_SUPPLY || - get_state_pe(port) == PE_PRS_SNK_SRC_EVALUATE_SWAP || - get_state_pe(port) == PE_PRS_SNK_SRC_SOURCE_ON || - get_state_pe(port) == PE_PRS_SRC_SNK_WAIT_SOURCE_ON || - get_state_pe(port) == PE_SRC_DISABLED || - get_state_pe(port) == PE_SRC_DISCOVERY || - get_state_pe(port) == PE_VCS_CBL_SEND_SOFT_RESET || - get_state_pe(port) == PE_VDM_IDENTITY_REQUEST_CBL) || - (pe_in_frs_mode(port) && - get_state_pe(port) == PE_PRS_SNK_SRC_SEND_SWAP) - ) { - PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - task_wake(PD_PORT_TO_TASK_ID(port)); - return; - } - - /* - * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State: - * - * The PE_Send_Soft_Reset state shall be entered from - * any state when - * * A Protocol Error is detected by Protocol Layer during a - * Non-Interruptible AMS or - * * A message has not been sent after retries or - * * When not in an explicit contract and - * * Protocol Errors occurred on SOP during an Interruptible AMS or - * * Protocol Errors occurred on SOP during any AMS where the first - * Message in the sequence has not yet been sent i.e. an unexpected - * Message is received instead of the expected GoodCRC Message - * response. - */ - /* All error types besides transmit errors are Protocol Errors. */ - if ((e != ERR_TCH_XMIT && - !PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS)) - || e == ERR_TCH_XMIT - || (!PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT) && - type == TCPCI_MSG_SOP)) { - pe_send_soft_reset(port, type); - } - /* - * Transition to PE_Snk_Ready or PE_Src_Ready by a Protocol - * Error during an Interruptible AMS. - */ - else { - pe_set_ready_state(port); - } -} - -void pe_got_soft_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * The PE_SRC_Soft_Reset state Shall be entered from any state when a - * Soft_Reset Message is received from the Protocol Layer. - */ - set_state_pe(port, PE_SOFT_RESET); -} - -__overridable bool pd_can_charge_from_device(int port, const int pdo_cnt, - const uint32_t *pdos) -{ - /* - * Don't attempt to charge from a device we have no SrcCaps from. Or, if - * drp_state is FORCE_SOURCE then don't attempt a PRS. - */ - if (pdo_cnt == 0 || pd_get_dual_role(port) == PD_DRP_FORCE_SOURCE) - return false; - - /* - * Treat device as a dedicated charger (meaning we should charge - * from it) if: - * - it does not support power swap, or - * - it is unconstrained power, or - * - it presents at least 27 W of available power - */ - - /* Unconstrained Power or NOT Dual Role Power we can charge from */ - if (pdos[0] & PDO_FIXED_UNCONSTRAINED || - (pdos[0] & PDO_FIXED_DUAL_ROLE) == 0) - return true; - - /* [virtual] allow_list */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - uint32_t max_ma, max_mv, max_pdo, max_mw, unused; - - /* - * Get max power that the partner offers (not necessarily what - * this board will request) - */ - pd_find_pdo_index(pdo_cnt, pdos, - PD_REV3_MAX_VOLTAGE, - &max_pdo); - pd_extract_pdo_power(max_pdo, &max_ma, &max_mv, &unused); - max_mw = max_ma * max_mv / 1000; - - if (max_mw >= PD_DRP_CHARGE_POWER_MIN) - return true; - } - return false; -} - -void pd_resume_check_pr_swap_needed(int port) -{ - /* - * Explicit contract, current power role of SNK, the device - * indicates it should not power us, and device isn't selected - * as the charging port (ex. through the GUI) then trigger a PR_Swap - */ - if (pe_is_explicit_contract(port) && - pd_get_power_role(port) == PD_ROLE_SINK && - !pd_can_charge_from_device(port, pd_get_src_cap_cnt(port), - pd_get_src_caps(port)) && - (!IS_ENABLED(CONFIG_CHARGE_MANAGER) || - charge_manager_get_active_charge_port() != port)) - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); -} - -void pd_dpm_request(int port, enum pd_dpm_request req) -{ - PE_SET_DPM_REQUEST(port, req); -} - -void pe_vconn_swap_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); -} - -void pe_ps_reset_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); -} - -void pe_message_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_TX_COMPLETE); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - /* Copy VDM Header */ - pe[port].vdm_data[0] = - VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? 1 : - (PD_VDO_CMD(cmd) <= CMD_ATTENTION), - VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)) | - cmd); - - /* - * Copy VDOs after the VDM Header. Note that the count refers to VDO - * count. - */ - memcpy((pe[port].vdm_data + 1), data, count * sizeof(uint32_t)); - - pe[port].vdm_cnt = count + 1; - - /* - * The PE transmit routine assumes that tx_type was set already. Note, - * that this function is likely called from outside the PD task. - * (b/180465870) - */ - pe[port].tx_type = TCPCI_MSG_SOP; - pd_dpm_request(port, DPM_REQUEST_VDM); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef TEST_BUILD -/* - * Allow unit tests to access this function to clear internal state data between - * runs - */ -void pe_clear_port_data(int port) -#else -static void pe_clear_port_data(int port) -#endif /* TEST_BUILD */ -{ - /* - * PD 3.0 Section 8.3.3.3.8 - * Note: The HardResetCounter is reset on a power cycle or Detach. - */ - pe[port].hard_reset_counter = 0; - - /* Reset port events */ - pd_clear_events(port, GENMASK(31, 0)); - - /* But then set disconnected event */ - pd_notify_event(port, PD_STATUS_EVENT_DISCONNECTED); - - /* Tell Policy Engine to invalidate the explicit contract */ - pe_invalidate_explicit_contract(port); - - /* - * Saved Source and Sink Capabilities are no longer valid on disconnect - */ - pd_set_src_caps(port, 0, NULL); - pe_set_snk_caps(port, 0, NULL); - - dpm_remove_sink(port); - dpm_remove_source(port); - - /* Exit BIST Test mode, in case the TCPC entered it. */ - tcpc_set_bist_test_mode(port, false); -} - -static void pe_handle_detach(void) -{ - const int port = TASK_ID_TO_PD_PORT(task_get_current()); - - pe_clear_port_data(port); -} -DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, pe_handle_detach, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC -static void pe_update_waiting_batt_flag(void) -{ - int i; - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (PE_CHK_FLAG(i, PE_FLAGS_SNK_WAITING_BATT)) { - /* - * Battery has gained sufficient charge to kick off PD - * negotiation and withstand a hard reset. Clear the - * flag and perform Hard Reset. - */ - PE_CLR_FLAG(i, PE_FLAGS_SNK_WAITING_BATT); - CPRINTS("C%d: Battery has enough charge (%d%%) " \ - "to withstand a hard reset", i, batt_soc); - pd_dpm_request(i, DPM_REQUEST_HARD_RESET_SEND); - } - } -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pe_update_waiting_batt_flag, - HOOK_PRIO_DEFAULT); -#endif - -/* - * Private functions - */ -static void pe_set_dpm_curr_request(const int port, - const int request) -{ - PE_CLR_DPM_REQUEST(port, request); - pe[port].dpm_curr_request = request; -} - -/* Set the TypeC state machine to a new state. */ -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_pe_state get_state_pe(const int port) -{ - return pe[port].ctx.current - &pe_states[0]; -} - -/* - * Handle common DPM requests to both source and sink. - * - * Note: it is assumed the calling state set PE_FLAGS_LOCALLY_INITIATED_AMS - * - * Returns true if state was set and calling run state should now return. - */ -static bool common_src_snk_dpm_requests(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SEND_ALERT)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SEND_ALERT); - set_state_pe(port, PE_SEND_ALERT); - return true; - } else if (IS_ENABLED(CONFIG_USBC_VCONN) && - PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, PE_VCS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_BIST_TX)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_BIST_TX); - set_state_pe(port, PE_BIST_TX); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SNK_STARTUP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SNK_STARTUP); - set_state_pe(port, PE_SNK_STARTUP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SRC_STARTUP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SRC_STARTUP); - set_state_pe(port, PE_SRC_STARTUP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOFT_RESET_SEND)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SOFT_RESET_SEND); - /* Currently only support sending soft reset to SOP */ - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_PORT_DISCOVERY)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_PORT_DISCOVERY); - if (!PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) { - /* - * Clear counters and reset timer to trigger a - * port discovery, and also clear any pending VDM send - * requests. - */ - pd_dfp_discovery_init(port); - /* - * TODO(b/189353401): Do not reinitialize modes when no - * longer required. - */ - pd_dfp_mode_init(port); - pe[port].dr_swap_attempt_counter = 0; - pe[port].discover_identity_counter = 0; - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - PD_T_DISCOVER_IDENTITY); - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_VDM); - } - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VDM)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_VDM); - /* Send previously set up SVDM. */ - set_state_pe(port, PE_VDM_REQUEST_DPM); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_ENTER_USB)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_ENTER_USB); - set_state_pe(port, PE_DEU_SEND_ENTER_USB); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_EXIT_MODES)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_EXIT_MODES); - dpm_set_mode_exit_request(port); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_GET_SNK_CAPS)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GET_SNK_CAPS); - set_state_pe(port, PE_DR_GET_SINK_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VCS_CBL_SEND_SOFT_RESET); - return true; - } - - return false; -} - -/* - * Handle source-specific DPM requests - * - * Returns true if state was set and calling run state should now return. - */ -static bool source_dpm_requests(int port) -{ - /* - * Ignore sink-specific request: - * DPM_REQUEST_NEW_POWER_LEVEL - * DPM_REQUEST_SOURCE_CAP - * DPM_REQUEST_FRS_DET_ENABLE - * DPM_REQURST_FRS_DET_DISABLE - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_NEW_POWER_LEVEL | - DPM_REQUEST_SOURCE_CAP | - DPM_REQUEST_FRS_DET_ENABLE | - DPM_REQUEST_FRS_DET_DISABLE); - - if (pe[port].dpm_request) { - uint32_t dpm_request = pe[port].dpm_request; - - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_PR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SRC_SNK_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_GOTO_MIN)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GOTO_MIN); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SRC_CAP_CHANGE)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SRC_CAP_CHANGE); - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_GET_SRC_CAPS)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GET_SRC_CAPS); - set_state_pe(port, PE_DR_SRC_GET_SOURCE_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SEND_PING)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SEND_PING); - set_state_pe(port, PE_SRC_PING); - return true; - } else if (common_src_snk_dpm_requests(port)) { - return true; - } - - CPRINTF("Unhandled DPM Request %x received\n", - dpm_request); - PE_CLR_DPM_REQUEST(port, dpm_request); - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - } - return false; -} - -/* - * Handle sink-specific DPM requests - * - * Returns true if state was set and calling run state should now return. - */ -static bool sink_dpm_requests(int port) -{ - /* - * Ignore source specific requests: - * DPM_REQUEST_GOTO_MIN - * DPM_REQUEST_SRC_CAP_CHANGE, - * DPM_REQUEST_SEND_PING - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN | - DPM_REQUEST_SRC_CAP_CHANGE | - DPM_REQUEST_SEND_PING); - - if (pe[port].dpm_request) { - uint32_t dpm_request = pe[port].dpm_request; - - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_PR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOURCE_CAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SOURCE_CAP); - set_state_pe(port, PE_SNK_GET_SOURCE_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_NEW_POWER_LEVEL)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_NEW_POWER_LEVEL); - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_FRS_DET_ENABLE)) { - pe_set_frs_enable(port, 1); - - /* Requires no state change, fall through to false */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_ENABLE); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_FRS_DET_DISABLE)) { - pe_set_frs_enable(port, 0); - /* Restore a default port current limit */ - typec_select_src_current_limit_rp(port, - CONFIG_USB_PD_PULLUP); - - /* Requires no state change, fall through to false */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_DISABLE); - } else if (common_src_snk_dpm_requests(port)) { - return true; - } else { - CPRINTF("Unhandled DPM Request %x received\n", - dpm_request); - PE_CLR_DPM_REQUEST(port, dpm_request); - } - - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - } - return false; -} - -/* Get the previous TypeC state. */ -static enum usb_pe_state get_last_state_pe(const int port) -{ - return pe[port].ctx.previous - &pe_states[0]; -} - -static void print_current_state(const int port) -{ - const char *mode = ""; - - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_in_frs_mode(port)) - mode = " FRS-MODE"; - - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS_L1("C%d: %s%s", port, - pe_state_names[get_state_pe(port)], mode); - else - CPRINTS("C%d: pe-st%d", port, get_state_pe(port)); -} - -static void send_source_cap(int port) -{ - const uint32_t *src_pdo; - const int src_pdo_cnt = dpm_get_source_pdo(&src_pdo, port); - - if (src_pdo_cnt == 0) { - /* No source capabilities defined, sink only */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } - - tx_emsg[port].len = src_pdo_cnt * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)src_pdo, tx_emsg[port].len); - - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SOURCE_CAP); -} - -/* - * Request desired charge voltage from source. - */ -static void pe_send_request_msg(int port) -{ - uint32_t vpd_vdo = 0; - uint32_t rdo; - uint32_t curr_limit; - uint32_t supply_voltage; - - /* - * If we are charging through a VPD, the requested voltage and current - * might need adjusting. - */ - if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) && - is_vpd_ct_supported(port)) { - union vpd_vdo vpd = pd_get_am_discovery(port, - TCPCI_MSG_SOP_PRIME)->identity.product_t1.vpd; - - /* The raw vpd_vdo is passed to pd_build_request */ - vpd_vdo = vpd.raw_value; - } - - /* Build and send request RDO */ - pd_build_request(vpd_vdo, &rdo, &curr_limit, - &supply_voltage, port); - - CPRINTF("C%d: Req [%d] %dmV %dmA", port, RDO_POS(rdo), - supply_voltage, curr_limit); - if (rdo & RDO_CAP_MISMATCH) - CPRINTF(" Mismatch"); - CPRINTF("\n"); - - pe[port].curr_limit = curr_limit; - pe[port].supply_voltage = supply_voltage; - - tx_emsg[port].len = 4; - - memcpy(tx_emsg[port].buf, (uint8_t *)&rdo, tx_emsg[port].len); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_REQUEST); -} - -static void pe_update_src_pdo_flags(int port, int pdo_cnt, uint32_t *pdos) -{ - /* - * Only parse PDO flags if type is fixed - * - * Note: From 6.4.1 Capabilities Message "The vSafe5V Fixed Supply - * Object Shall always be the first object." so hitting this condition - * would mean the partner is voilating spec. - */ - if ((pdos[0] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - if (pd_can_charge_from_device(port, pdo_cnt, pdos)) { - charge_manager_update_dualrole(port, CAP_DEDICATED); - } else { - charge_manager_update_dualrole(port, CAP_DUALROLE); - } - } -} - -/* - * Evaluate whether our PR role is in the middle of changing, meaning we our - * current PR role is not the one we expect to have very shortly. - */ -bool pe_is_pr_swapping(int port) -{ - enum usb_pe_state cur_state = get_state_pe(port); - - if (cur_state == PE_PRS_SRC_SNK_EVALUATE_SWAP || - cur_state == PE_PRS_SRC_SNK_TRANSITION_TO_OFF || - cur_state == PE_PRS_SNK_SRC_EVALUATE_SWAP || - cur_state == PE_PRS_SNK_SRC_TRANSITION_TO_OFF) - return true; - - return false; -} - -void pd_request_power_swap(int port) -{ - /* Ignore requests when the board does not wish to swap */ - if (!pd_check_power_swap(port)) - return; - - /* Ignore requests when our power role is transitioning */ - if (pe_is_pr_swapping(port)) - return; - - /* - * Always reset the SRC to SNK PR swap counter when a PR swap is - * requested by policy. - */ - pe[port].src_snk_pr_swap_counter = 0; - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); -} - -/* The function returns true if there is a PE state change, false otherwise */ -static bool port_try_vconn_swap(int port) -{ - if (pe[port].vconn_swap_counter < N_VCONN_SWAP_COUNT) { - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, get_last_state_pe(port)); - return true; - } - return false; -} - -/* - * Run discovery at our leisure from PE_SNK_Ready or PE_SRC_Ready, after - * attempting to get into the desired default policy of DFP/Vconn source - * - * Return indicates whether set_state was called, in which case the calling - * function should return as well. - */ -__maybe_unused static bool pe_attempt_port_discovery(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - /* - * DONE set once modal entry is successful, discovery completes, or - * discovery results in a NAK - */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) - return false; - - /* Apply Port Discovery DR Swap Policy */ - if (port_discovery_dr_swap_policy(port, pe[port].data_role, - PE_CHK_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP))) { - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } - - /* Apply Port Discovery VCONN Swap Policy */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - port_discovery_vconn_swap_policy(port, - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON))) { - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON); - set_state_pe(port, PE_VCS_SEND_SWAP); - return true; - } - - /* If mode entry was successful, disable the timer */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) { - pd_timer_disable(port, PE_TIMER_DISCOVER_IDENTITY); - return false; - } - - /* - * Run discovery functions when the timer indicating either cable - * discovery spacing or BUSY spacing runs out. - */ - if (pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) { - if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL); - return true; - } else if (pd_get_identity_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_IDENT)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, - PE_INIT_PORT_VDM_IDENTITY_REQUEST); - return true; - } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_SVID)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST); - return true; - } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_MODES)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, PE_INIT_VDM_MODES_REQUEST); - return true; - } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP_PRIME) - == PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST); - return true; - } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_INIT_VDM_MODES_REQUEST); - return true; - } - } - - return false; -} - -bool pd_setup_vdm_request(int port, enum tcpci_msg_type tx_type, - uint32_t *vdm, uint32_t vdo_cnt) -{ - if (vdo_cnt < VDO_HDR_SIZE || vdo_cnt > VDO_MAX_SIZE) - return false; - - pe[port].tx_type = tx_type; - memcpy(pe[port].vdm_data, vdm, vdo_cnt * sizeof(*vdm)); - pe[port].vdm_cnt = vdo_cnt; - - return true; -} - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ - pe[port].dev_id = dev_id; - memcpy(pe[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - pd_dev_dump_info(dev_id, rw_hash); -#endif - pe[port].current_image = current_image; - - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) { - int i; - - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); - } - - return 0; -} - -void pd_dev_get_rw_hash(int port, uint16_t *dev_id, uint8_t *rw_hash, - uint32_t *current_image) -{ - *dev_id = pe[port].dev_id; - *current_image = pe[port].current_image; - if (*dev_id) - memcpy(rw_hash, pe[port].dev_rw_hash, PD_RW_HASH_SIZE); -} - -/* - * This function must only be called from the PE_SNK_READY entry and - * PE_SRC_READY entry State. - * - * TODO(b:181339670) Rethink jitter timer restart if this is the first - * message but the partner gets a message in first, may not want to - * disable and restart it. - */ -static void pe_update_wait_and_add_jitter_timer(int port) -{ - /* - * In PD2.0 Mode - * - * For Source: - * Give the sink some time to send any messages - * before we may send messages of our own. Add - * some jitter of up to ~345ms, to prevent - * multiple collisions. This delay also allows - * the sink device to request power role swap - * and allow the the accept message to be sent - * prior to CMD_DISCOVER_IDENT being sent in the - * SRC_READY state. - * - * For Sink: - * Give the source some time to send any messages before - * we start our interrogation. Add some jitter of up to - * ~345ms to prevent multiple collisions. - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20 && - PE_CHK_FLAG(port, PE_FLAGS_FIRST_MSG) && - pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - pd_timer_enable(port, PE_TIMER_WAIT_AND_ADD_JITTER, - SRC_SNK_READY_HOLD_OFF_US + - (get_time().le.lo & 0xf) * 23 * MSEC); - } -} - -/** - * Common sender response message handling - * - * This is setup like a pseudo state machine parent state. It - * centralizes the SenderResponseTimer for the calling states, as - * well as checking message send status. - */ -/* - * pe_sender_response_msg_entry - * Initialization for handling sender response messages. - * - * @param port USB-C Port number - */ -static void pe_sender_response_msg_entry(const int port) -{ - /* Stop sender response timer */ - pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE); -} - -/* - * pe_sender_response_msg_run - * Check status of sender response messages. - * - * The normal progression of pe_sender_response_msg_entry is: - * PENDING -> (COMPLETED/SENT) -> SENT -> SENT ... - * or - * PENDING -> DISCARDED - * PENDING -> DPM_DISCARDED - * - * NOTE: it is not valid to call this function for a message after - * receiving either PE_MSG_DISCARDED or PE_MSG_DPM_DISCARDED until - * another message has been sent and pe_sender_response_msg_entry is called - * again. - * - * @param port USB-C Port number - * @return the current pe_msg_check - */ -static enum pe_msg_check pe_sender_response_msg_run(const int port) -{ - timestamp_t tx_success_ts; - uint32_t offset; - if (pd_timer_is_disabled(port, PE_TIMER_SENDER_RESPONSE)) { - /* Check for Discard */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - int dpm_request = pe[port].dpm_curr_request; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED); - /* Restore the DPM Request */ - if (dpm_request) { - PE_SET_DPM_REQUEST(port, dpm_request); - return PE_MSG_DPM_DISCARDED; - } - return PE_MSG_DISCARDED; - } - - /* Check for GoodCRC */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* TCPC TX success time stamp */ - tx_success_ts = prl_get_tcpc_tx_success_ts(port); - /* Calculate the delay from TX success to PE */ - offset = time_since32(tx_success_ts); - - /* - * Initialize and run the SenderResponseTimer by - * offsetting it with TX transmit success time. - * This would remove the effect of the latency from - * propagating the TX status. - */ - pd_timer_enable(port, PE_TIMER_SENDER_RESPONSE, - PD_T_SENDER_RESPONSE - offset); - return PE_MSG_SEND_COMPLETED; - } - return PE_MSG_SEND_PENDING; - } - return PE_MSG_SENT; -} - -/* - * pe_sender_response_msg_exit - * Exit cleanup for handling sender response messages. - * - * @param port USB-C Port number - */ -static void pe_sender_response_msg_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE); -} - -/** - * PE_SRC_Startup - */ -static void pe_src_startup_entry(int port) -{ - print_current_state(port); - - /* Reset CapsCounter */ - pe[port].caps_counter = 0; - - /* Reset the protocol layer */ - prl_reset_soft(port); - - /* Set initial data role */ - pe[port].data_role = pd_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SOURCE; - - /* Clear explicit contract. */ - pe_invalidate_explicit_contract(port); - - if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - /* - * Protocol layer reset clears the message IDs for all SOP - * types. Indicate that a SOP' soft reset is required before any - * other messages are sent to the cable. - * - * Note that other paths into this state are for the initial - * connection and for a hard reset. In both cases the cable - * should also automatically clear the message IDs so don't - * generate an SOP' soft reset for those cases. Sending - * unnecessary SOP' soft resets causes bad behavior with - * some devices. See b/179325862. - */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - - /* Start SwapSourceStartTimer */ - pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START, - PD_T_SWAP_SOURCE_START); - - /* - * Evaluate port's sink caps for preferred current, if - * already available - */ - if (pd_get_snk_cap_cnt(port) > 0) - dpm_evaluate_sink_fixed_pdo(port, - *pd_get_snk_caps(port)); - - /* - * Remove prior FRS claims to 3.0 A now that sink current has - * been claimed, to avoid issues with lower priority ports - * potentially receiving a 3.0 A claim between calls. - */ - dpm_remove_source(port); - } else { - /* - * SwapSourceStartTimer delay is not needed, so trigger now. - * We can't use set_state_pe here, since we need to ensure that - * the protocol layer is running again (done in run function). - */ - pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START, 0); - - /* - * Set DiscoverIdentityTimer to trigger when we enter - * src_discovery for the first time. After initial startup - * set, vdm_identity_request_cbl will handle the timer updates. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0); - - /* Clear port discovery/mode flags */ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); - pe[port].ama_vdo = PD_VDO_INVALID; - pe[port].vpd_vdo = PD_VDO_INVALID; - pe[port].discover_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; - - /* Reset VCONN swap counter */ - pe[port].vconn_swap_counter = 0; - - /* Request partner sink caps if a feature requires them */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) || - CONFIG_USB_PD_3A_PORTS > 0 || - IS_ENABLED(CONFIG_USB_PD_FRS)) - pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS); - } -} - -static void pe_src_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - if (pd_timer_is_expired(port, PE_TIMER_SWAP_SOURCE_START)) - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); -} - -static void pe_src_startup_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SWAP_SOURCE_START); -} - -/** - * PE_SRC_Discovery - */ -static void pe_src_discovery_entry(int port) -{ - print_current_state(port); - - /* - * Initialize and run the SourceCapabilityTimer in order - * to trigger sending a Source_Capabilities Message. - * - * The SourceCapabilityTimer Shall continue to run during - * identity discover and Shall Not be initialized on re-entry - * to PE_SRC_Discovery. - * - * Note: Cable identity is the only valid VDM to probe before a contract - * is in place. All other probing must happen from ready states. - */ - if (get_last_state_pe(port) != PE_VDM_IDENTITY_REQUEST_CBL) - pd_timer_enable(port, PE_TIMER_SOURCE_CAP, - PD_T_SEND_SOURCE_CAP); -} - -static void pe_src_discovery_run(int port) -{ - /* - * Transition to the PE_SRC_Send_Capabilities state when: - * 1) The SourceCapabilityTimer times out and - * CapsCounter ≤ nCapsCount. - * - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners are not presently PD Connected - * 2) And the SourceCapabilityTimer times out - * 3) And CapsCounter > nCapsCount. - * - * Transition to the PE_SRC_VDM_Identity_request state when: - * 1) DPM requests the identity of the cable plug and - * 2) DiscoverIdentityCounter < nDiscoverIdentityCount - */ - if (pd_timer_is_expired(port, PE_TIMER_SOURCE_CAP)) { - if (pe[port].caps_counter <= N_CAPS_COUNT) { - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return; - } else if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } - } - - /* - * Note: While the DiscoverIdentityTimer is only required in an explicit - * contract, we use it here to ensure we space any potential BUSY - * requests properly. - */ - if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED - && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY) - && pe_can_send_sop_prime(port) - && (pe[port].discover_identity_counter < - N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT)) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL); - return; - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected. - * 2) And the NoResponseTimer times out. - * 3) And the HardResetCounter > nHardResetCount. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION) && - pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE) && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } -} - -/** - * PE_SRC_Send_Capabilities - */ -static void pe_src_send_capabilities_entry(int port) -{ - print_current_state(port); - - /* Send PD Capabilities message */ - send_source_cap(port); - pe_sender_response_msg_entry(port); - - /* Increment CapsCounter */ - pe[port].caps_counter++; -} - -static void pe_src_send_capabilities_run(int port) -{ - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle Discarded message - * PE_SNK/SRC_READY if DPM_REQUEST - * PE_SEND_SOFT_RESET otherwise - */ - if (msg_check == PE_MSG_DPM_DISCARDED) { - set_state_pe(port, PE_SRC_READY); - return; - } else if (msg_check == PE_MSG_DISCARDED) { - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return; - } - - /* - * Handle message that was just sent - */ - if (msg_check == PE_MSG_SEND_COMPLETED) { - /* - * If a GoodCRC Message is received then the Policy Engine - * Shall: - * 1) Stop the NoResponseTimer. - * 2) Reset the HardResetCounter and CapsCounter to zero. - * 3) Initialize and run the SenderResponseTimer. - */ - /* Stop the NoResponseTimer */ - pd_timer_disable(port, PE_TIMER_NO_RESPONSE); - - /* Reset the HardResetCounter to zero */ - pe[port].hard_reset_counter = 0; - - /* Reset the CapsCounter to zero */ - pe[port].caps_counter = 0; - } - - /* - * Transition to the PE_SRC_Negotiate_Capability state when: - * 1) A Request Message is received from the Sink - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * Request Message Received? - */ - if (PD_HEADER_CNT(rx_emsg[port].header) > 0 && - PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_REQUEST) { - - /* - * Set to highest revision supported by both - * ports. - */ - prl_set_rev(port, TCPCI_MSG_SOP, - MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header))); - - set_cable_rev(port); - - /* We are PD connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); - - /* - * Handle the Sink Request in - * PE_SRC_Negotiate_Capability state - */ - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - return; - } - - /* - * We have a Protocol Error. - * PE_SEND_SOFT_RESET - */ - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - } - - /* - * Transition to the PE_SRC_Discovery state when: - * 1) The Protocol Layer indicates that the Message has not been sent - * and we are presently not Connected - * - * Send soft reset when: - * 1) The Protocol Layer indicates that the Message has not been sent - * and we are already Connected - * - * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State and section - * 8.3.3.2.3 PE_SRC_Send_Capabilities State. - * - * NOTE: The PE_FLAGS_PROTOCOL_ERROR is set if a GoodCRC Message - * is not received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) - set_state_pe(port, PE_SRC_DISCOVERY); - else - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return; - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - * - * Transition to the Error Recovery state when: - * 1) The Port Partners have previously been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - */ - if (pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE)) { - if (pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) - set_state_pe(port, PE_SRC_HARD_RESET); - else if (PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - else - set_state_pe(port, PE_SRC_DISABLED); - return; - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) The SenderResponseTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } -} - -static void pe_src_send_capabilities_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_SRC_Negotiate_Capability - */ -static void pe_src_negotiate_capability_entry(int port) -{ - uint32_t payload; - - print_current_state(port); - - /* Get message payload */ - payload = *(uint32_t *)(&rx_emsg[port].buf); - - /* - * Evaluate the Request from the Attached Sink - */ - - /* - * Transition to the PE_SRC_Capability_Response state when: - * 1) The Request cannot be met. - * 2) Or the Request can be met later from the Power Reserve - * - * Transition to the PE_SRC_Transition_Supply state when: - * 1) The Request can be met - * - */ - if (pd_check_requested_voltage(payload, port) != EC_SUCCESS) { - set_state_pe(port, PE_SRC_CAPABILITY_RESPONSE); - } else { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - pe[port].requested_idx = RDO_POS(payload); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - } -} - -/** - * PE_SRC_Transition_Supply - */ -static void pe_src_transition_supply_entry(int port) -{ - print_current_state(port); - - /* Send a GotoMin Message or otherwise an Accept Message */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } else { - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GOTO_MIN); - } -} - -static void pe_src_transition_supply_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) The power supply is ready. - * - * NOTE: This code block is executed twice: - * First Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_ACCEPT or PD_CTRL_GOTO_MIN messages - * being sent. - * - * Second Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_PS_RDY message being sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * NOTE: If a message was received, - * pe_src_ready state will handle it. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_PS_READY)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_READY); - - /* - * Set first message flag to trigger a wait and add - * jitter delay when operating in PD2.0 mode. Skip - * if we already have a contract. - */ - if (!pe_is_explicit_contract(port)) { - PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, - PE_TIMER_WAIT_AND_ADD_JITTER); - } - - /* NOTE: Second pass through this code block */ - /* Explicit Contract is now in place */ - pe_set_explicit_contract(port); - - /* - * Setup to get Device Policy Manager to request - * Source Capabilities, if needed, for possible - * PR_Swap. Get the number directly to avoid re-probing - * if the partner generated an error and left -1 for the - * count. - */ - if (pe[port].src_cap_cnt == 0) - pd_dpm_request(port, DPM_REQUEST_GET_SRC_CAPS); - - set_state_pe(port, PE_SRC_READY); - } else { - /* NOTE: First pass through this code block */ - /* Wait for tSrcTransition before changing supply. */ - pd_timer_enable(port, PE_TIMER_SRC_TRANSITION, - PD_T_SRC_TRANSITION); - } - - return; - } - - if (pd_timer_is_expired(port, PE_TIMER_SRC_TRANSITION)) { - pd_timer_disable(port, PE_TIMER_SRC_TRANSITION); - /* Transition power supply and send PS_RDY. */ - pd_transition_voltage(pe[port].requested_idx); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); - PE_SET_FLAG(port, PE_FLAGS_PS_READY); - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -static void pe_src_transition_supply_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SRC_TRANSITION); -} - -/* - * Transitions state after receiving a Not Supported extended message. Under - * appropriate conditions, transitions to a PE_{SRC,SNK}_Chunk_Received. - */ -static void extended_message_not_supported(int port, uint32_t *payload) -{ - uint16_t ext_header = GET_EXT_HEADER(*payload); - - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - !IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - PD_EXT_HEADER_CHUNKED(ext_header) && - PD_EXT_HEADER_DATA_SIZE(ext_header) > - PD_MAX_EXTENDED_MSG_CHUNK_LEN) { - set_state_pe(port, - pe[port].power_role == PD_ROLE_SOURCE ? - PE_SRC_CHUNK_RECEIVED : PE_SNK_CHUNK_RECEIVED); - return; - } - - set_state_pe(port, PE_SEND_NOT_SUPPORTED); -} - -/** - * PE_SRC_Ready - */ -static void pe_src_ready_entry(int port) -{ - print_current_state(port); - - /* Ensure any message send flags are cleaned up */ - PE_CLR_FLAG(port, PE_FLAGS_READY_CLR); - - /* Clear DPM Current Request */ - pe[port].dpm_curr_request = 0; - - /* - * Wait and add jitter if we are operating in PD2.0 mode and no messages - * have been sent since enter this state. - */ - pe_update_wait_and_add_jitter_timer(port); -} - -static void pe_src_ready_run(int port) -{ - /* - * Handle incoming messages before discovery and DPMs other than hard - * reset - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Extended Message Requests */ - if (ext > 0) { - switch (type) { -#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY) - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */ - default: - extended_message_not_supported(port, payload); - } - return; - } - /* Data Message Requests */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_REQUEST: - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - return; - case PD_DATA_SINK_CAP: - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(*payload)) { - set_state_pe(port, - PE_VDM_RESPONSE); - } else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - return; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST_TX); - return; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - /* Control Message Requests */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - break; - case PD_CTRL_NOT_SUPPORTED: - break; - case PD_CTRL_PING: - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - return; - case PD_CTRL_GOTO_MIN: - break; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SRC_SNK_EVALUATE_SWAP); - return; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, - PE_FLAGS_MODAL_OPERATION)) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - set_state_pe(port, PE_DRS_EVALUATE_SWAP); - return; - case PD_CTRL_VCONN_SWAP: - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_state_pe(port, - PE_VCS_EVALUATE_SWAP); - else - set_state_pe(port, - PE_SEND_NOT_SUPPORTED); - return; - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - case PD_CTRL_ACCEPT: - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - case PD_CTRL_PS_RDY: - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - /* - * Receiving an unknown or unsupported message - * shall be responded to with a not supported message. - */ - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - } - - /* - * Make sure the PRL layer isn't busy with receiving or transmitting - * chunked messages before attempting to transmit a new message. - */ - if (prl_is_busy(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE); - set_state_pe(port, PE_VDM_REQUEST_DPM); - return; - } - - if (PE_CHK_FLAG(port, PE_FLAGS_WAITING_PR_SWAP) && - pd_timer_is_expired(port, PE_TIMER_PR_SWAP_WAIT)) { - PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP); - PE_SET_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - } - - if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) || - pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - - PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - /* - * Handle Device Policy Manager Requests - */ - if (source_dpm_requests(port)) - return; - - /* - * Attempt discovery if possible, and return if state was - * changed for that discovery. - */ - if (pe_attempt_port_discovery(port)) - return; - - /* No DPM requests; attempt mode entry/exit if needed */ - dpm_run(port); - } -} - -/** - * PE_SRC_Disabled - */ -static void pe_src_disabled_entry(int port) -{ - print_current_state(port); - - if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) && - is_vpd_ct_supported(port)) { - /* - * Inform the Device Policy Manager that a Charge-Through VCONN - * Powered Device was detected. - */ - tc_ctvpd_detected(port); - } - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - dpm_add_non_pd_sink(port); - - /* - * Unresponsive to USB Power Delivery messaging, but not to Hard Reset - * Signaling. See pe_got_hard_reset - */ -} - -/** - * PE_SRC_Capability_Response - */ -static void pe_src_capability_response_entry(int port) -{ - print_current_state(port); - - /* NOTE: Wait messaging should be implemented. */ - - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); -} - -static void pe_src_capability_response_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) There is an Explicit Contract and - * 2) A Reject Message has been sent and the present Contract is still - * Valid or - * 3) A Wait Message has been sent. - * - * Transition to the PE_SRC_Hard_Reset state when: - * 1) There is an Explicit Contract and - * 2) The Reject Message has been sent and the present - * Contract is Invalid - * - * Transition to the PE_SRC_Wait_New_Capabilities state when: - * 1) There is no Explicit Contract and - * 2) A Reject Message has been sent or - * 3) A Wait Message has been sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change so the present contract will - * never be invalid. - */ - set_state_pe(port, PE_SRC_READY); - else - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change, so no need to resending them - * again. Transition to disabled state. - */ - set_state_pe(port, PE_SRC_DISABLED); - } -} - -/** - * PE_SRC_Hard_Reset - */ -static void pe_src_hard_reset_entry(int port) -{ - print_current_state(port); - - /* Generate Hard Reset Signal */ - prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* Start NoResponseTimer */ - pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE); - - /* Start PSHardResetTimer */ - pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET); - - /* Clear error flags */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_VDM_REQUEST_BUSY); -} - -static void pe_src_hard_reset_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET)) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -static void pe_src_hard_reset_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_HARD_RESET); -} - -/** - * PE_SRC_Hard_Reset_Received - */ -static void pe_src_hard_reset_received_entry(int port) -{ - print_current_state(port); - - /* Start NoResponseTimer */ - pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE); - - /* Start PSHardResetTimer */ - pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET); -} - -static void pe_src_hard_reset_received_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET)) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -static void pe_src_hard_reset_received_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_HARD_RESET); -} - -/** - * PE_SRC_Transition_To_Default - */ -static void pe_src_transition_to_default_entry(int port) -{ - print_current_state(port); - - /* Reset flags */ - pe[port].flags = 0; - - /* Reset DPM Request */ - pe[port].dpm_request = 0; - - /* - * Request Device Policy Manager to request power - * supply Hard Resets to vSafe5V via vSafe0V - * Reset local HW - * Request Device Policy Manager to set Port Data - * Role to DFP and turn off VCONN - */ - tc_hard_reset_request(port); -} - -static void pe_src_transition_to_default_run(int port) -{ - /* - * Transition to the PE_SRC_Startup state when: - * 1) The power supply has reached the default level. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -/** - * PE_SNK_Startup State - */ -static void pe_snk_startup_entry(int port) -{ - print_current_state(port); - - /* Reset the protocol layer */ - prl_reset_soft(port); - - /* Set initial data role */ - pe[port].data_role = pd_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SINK; - - /* Invalidate explicit contract */ - pe_invalidate_explicit_contract(port); - - if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - /* - * Protocol layer reset clears the message IDs for all SOP - * types. Indicate that a SOP' soft reset is required before any - * other messages are sent to the cable. - * - * Note that other paths into this state are for the initial - * connection and for a hard reset. In both cases the cable - * should also automatically clear the message IDs so don't - * generate an SOP' soft reset for those cases. Sending - * unnecessary SOP' soft resets causes bad behavior with - * some devices. See b/179325862. - */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - - /* - * Some port partners may violate spec and attempt to - * communicate with the cable after power role swaps, despite - * not being Vconn source. Disable our SOP' receiving here to - * avoid GoodCRC-ing any erroneous cable probes, and re-enable - * after our contract is in place. - */ - if (tc_is_vconn_src(port)) - tcpm_sop_prime_enable(port, false); - - dpm_remove_sink(port); - } else { - /* - * Set DiscoverIdentityTimer to trigger when we enter - * snk_ready for the first time. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0); - - /* Clear port discovery/mode flags */ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); - pe[port].discover_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; - - /* Reset VCONN swap counter */ - pe[port].vconn_swap_counter = 0; - /* - * TODO: POLICY decision: - * Mark that we'd like to try being Vconn source and DFP - */ - PE_SET_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON); - } - - /* - * Request sink caps for FRS, output power consideration, or reporting - * to the AP through host commands. - * - * On entry to the PE_SNK_Ready state if the Sink supports Fast Role - * Swap, then the Policy Engine Shall do the following: - * - Send a Get_Sink_Cap Message - */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) || - CONFIG_USB_PD_3A_PORTS > 0 || - IS_ENABLED(CONFIG_USB_PD_FRS)) - pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS); - -} - -static void pe_snk_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - /* - * Once the reset process completes, the Policy Engine Shall - * transition to the PE_SNK_Discovery state - */ - set_state_pe(port, PE_SNK_DISCOVERY); -} - -/** - * PE_SNK_Discovery State - */ -static void pe_snk_discovery_entry(int port) -{ - print_current_state(port); -} - -static void pe_snk_discovery_run(int port) -{ - /* - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) VBUS has been detected - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); -} - -/** - * PE_SNK_Wait_For_Capabilities State - */ -static void pe_snk_wait_for_capabilities_entry(int port) -{ - print_current_state(port); - - /* Initialize and start the SinkWaitCapTimer */ - pd_timer_enable(port, PE_TIMER_TIMEOUT, PD_T_SINK_WAIT_CAP); -} - -static void pe_snk_wait_for_capabilities_run(int port) -{ - uint8_t type; - uint8_t cnt; - uint8_t ext; - - /* - * Transition to the PE_SNK_Evaluate_Capability state when: - * 1) A Source_Capabilities Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt > 0) && (type == PD_DATA_SOURCE_CAP)) { - set_state_pe(port, PE_SNK_EVALUATE_CAPABILITY); - return; - } - } - - /* When the SinkWaitCapTimer times out, perform a Hard Reset. */ - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) { - PE_SET_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT); - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_snk_wait_for_capabilities_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/** - * PE_SNK_Evaluate_Capability State - */ -static void pe_snk_evaluate_capability_entry(int port) -{ - uint32_t *pdo = (uint32_t *)rx_emsg[port].buf; - uint32_t num = rx_emsg[port].len >> 2; - - print_current_state(port); - - /* Reset Hard Reset counter to zero */ - pe[port].hard_reset_counter = 0; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPCI_MSG_SOP, - MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header))); - - set_cable_rev(port); - - /* Parse source caps if they have changed */ - if (pe[port].src_cap_cnt != num || - memcmp(pdo, pe[port].src_caps, num << 2)) { - /* - * If port policy preference is to be a power role source, - * then request a power role swap. If we'd previously queued a - * PR swap but can now charge from this device, clear it. - */ - if (!pd_can_charge_from_device(port, num, pdo)) - pd_request_power_swap(port); - else - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - } - - pe_update_src_pdo_flags(port, num, pdo); - pd_set_src_caps(port, num, pdo); - - /* Evaluate the options based on supplied capabilities */ - pd_process_source_cap(port, pe[port].src_cap_cnt, pe[port].src_caps); - - /* Device Policy Response Received */ - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - -#ifdef HAS_TASK_DPS - /* Wake DPS task to evaluate the SrcCaps */ - task_wake(TASK_ID_DPS); -#endif -} - -/** - * PE_SNK_Select_Capability State - */ -static void pe_snk_select_capability_entry(int port) -{ - print_current_state(port); - - /* Send Request */ - pe_send_request_msg(port); - pe_sender_response_msg_entry(port); - - /* We are PD Connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); -} - -static void pe_snk_select_capability_run(int port) -{ - uint8_t type; - uint8_t cnt; - enum tcpci_msg_type sop; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check & PE_MSG_DISCARDED) { - /* - * The sent REQUEST message was discarded. This can be at - * the start of an AMS or in the middle. Handle what to - * do based on where we came from. - * 1) SE_SNK_EVALUATE_CAPABILITY: sends SoftReset - * 2) SE_SNK_READY: goes back to SNK Ready - */ - if (get_last_state_pe(port) == PE_SNK_EVALUATE_CAPABILITY) - pe_send_soft_reset(port, TCPCI_MSG_SOP); - else - set_state_pe(port, PE_SNK_READY); - return; - } - - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - /* - * Transition to the PE_SNK_Transition_Sink state when: - * 1) An Accept Message is received from the Source. - * - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) There is no Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Ready state when: - * 1) There is an Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A SenderResponseTimer timeout occurs. - */ - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Accept Message Received - */ - if (type == PD_CTRL_ACCEPT) { - /* explicit contract is now in place */ - pe_set_explicit_contract(port); - - set_state_pe(port, PE_SNK_TRANSITION_SINK); - - return; - } - /* - * Reject or Wait Message Received - */ - else if (type == PD_CTRL_REJECT || - type == PD_CTRL_WAIT) { - if (type == PD_CTRL_WAIT) - PE_SET_FLAG(port, PE_FLAGS_WAIT); - - pd_timer_disable(port, PE_TIMER_SINK_REQUEST); - - /* - * We had a previous explicit contract, so - * transition to PE_SNK_Ready - */ - if (PE_CHK_FLAG(port, - PE_FLAGS_EXPLICIT_CONTRACT)) - set_state_pe(port, PE_SNK_READY); - /* - * No previous explicit contract, so transition - * to PE_SNK_Wait_For_Capabilities - */ - else - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - return; - } - /* - * Unexpected Control Message Received - */ - else { - /* Send Soft Reset */ - pe_send_soft_reset(port, sop); - return; - } - } - /* - * Unexpected Data Message - */ - else { - /* Send Soft Reset */ - pe_send_soft_reset(port, sop); - return; - } - } - - /* SenderResponsetimer timeout */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SNK_HARD_RESET); -} - -void pe_snk_select_capability_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_SNK_Transition_Sink State - */ -static void pe_snk_transition_sink_entry(int port) -{ - print_current_state(port); - - /* Initialize and run PSTransitionTimer */ - pd_timer_enable(port, PE_TIMER_PS_TRANSITION, PD_T_PS_TRANSITION); -} - -static void pe_snk_transition_sink_run(int port) -{ - /* - * Transition to the PE_SNK_Ready state when: - * 1) A PS_RDY Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * PS_RDY message received - */ - if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) && - (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_CTRL_PS_RDY)) { - /* - * Set first message flag to trigger a wait and add - * jitter delay when operating in PD2.0 mode. - */ - PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - /* - * If we've successfully completed our new power - * contract, ensure SOP' communication is enabled before - * entering PE_SNK_READY. It may have been disabled - * during a power role swap to avoid interoperability - * issues with out-of-spec partners. - */ - if (tc_is_vconn_src(port)) - tcpm_sop_prime_enable(port, true); - - /* - * Evaluate port's sink caps for FRS current, if - * already available - */ - if (pd_get_snk_cap_cnt(port) > 0) - dpm_evaluate_sink_fixed_pdo(port, - *pd_get_snk_caps(port)); - - set_state_pe(port, PE_SNK_READY); - } else { - /* - * Protocol Error - */ - set_state_pe(port, PE_SNK_HARD_RESET); - } - return; - } - - /* - * Timeout will lead to a Hard Reset - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_TRANSITION) && - pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) { - PE_SET_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_snk_transition_sink_exit(int port) -{ - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, - pe[port].curr_limit, pe[port].supply_voltage); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, pe[port].curr_limit); - - pd_timer_disable(port, PE_TIMER_PS_TRANSITION); - - if (IS_ENABLED(CONFIG_USB_PD_DPS)) - if (charge_manager_get_active_charge_port() == port) - dps_update_stabilized_time(port); -} - - -/** - * PE_SNK_Ready State - */ -static void pe_snk_ready_entry(int port) -{ - print_current_state(port); - - /* Ensure any message send flags are cleaned up */ - PE_CLR_FLAG(port, PE_FLAGS_READY_CLR); - - /* Clear DPM Current Request */ - pe[port].dpm_curr_request = 0; - - /* - * On entry to the PE_SNK_Ready state as the result of a wait, - * then do the following: - * 1) Initialize and run the SinkRequestTimer - */ - if (PE_CHK_FLAG(port, PE_FLAGS_WAIT)) { - PE_CLR_FLAG(port, PE_FLAGS_WAIT); - pd_timer_enable(port, PE_TIMER_SINK_REQUEST, - PD_T_SINK_REQUEST); - } - - /* - * Wait and add jitter if we are operating in PD2.0 mode and no messages - * have been sent since enter this state. - */ - pe_update_wait_and_add_jitter_timer(port); -} - -static void pe_snk_ready_run(int port) -{ - /* - * Handle incoming messages before discovery and DPMs other than hard - * reset - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Extended Message Request */ - if (ext > 0) { - switch (type) { -#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY) - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */ - default: - extended_message_not_supported(port, payload); - } - return; - } - /* Data Messages */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_SOURCE_CAP: - set_state_pe(port, - PE_SNK_EVALUATE_CAPABILITY); - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(*payload)) - set_state_pe(port, - PE_VDM_RESPONSE); - else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - break; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST_TX); - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - return; - } - /* Control Messages */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - /* Do nothing */ - break; - case PD_CTRL_PING: - /* Do nothing */ - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_DR_SNK_GIVE_SOURCE_CAP); - return; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - return; - case PD_CTRL_GOTO_MIN: - set_state_pe(port, PE_SNK_TRANSITION_SINK); - return; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SNK_SRC_EVALUATE_SWAP); - return; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, - PE_DRS_EVALUATE_SWAP); - return; - case PD_CTRL_VCONN_SWAP: - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_state_pe(port, - PE_VCS_EVALUATE_SWAP); - else - set_state_pe(port, - PE_SEND_NOT_SUPPORTED); - return; - case PD_CTRL_NOT_SUPPORTED: - /* Do nothing */ - break; - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - case PD_CTRL_ACCEPT: - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - case PD_CTRL_PS_RDY: - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - /* - * Receiving an unknown or unsupported message - * shall be responded to with a not supported message. - */ - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - } - - /* - * Make sure the PRL layer isn't busy with receiving or transmitting - * chunked messages before attempting to transmit a new message. - */ - if (prl_is_busy(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE); - set_state_pe(port, PE_VDM_REQUEST_DPM); - return; - } - - if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) || - pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - if (pd_timer_is_expired(port, PE_TIMER_SINK_REQUEST)) { - pd_timer_disable(port, PE_TIMER_SINK_REQUEST); - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - return; - } - - /* - * Handle Device Policy Manager Requests - */ - if (sink_dpm_requests(port)) - return; - - /* - * Attempt discovery if possible, and return if state was - * changed for that discovery. - */ - if (pe_attempt_port_discovery(port)) - return; - - /* No DPM requests; attempt mode entry/exit if needed */ - dpm_run(port); - - } -} - -/** - * PE_SNK_Hard_Reset - */ -static void pe_snk_hard_reset_entry(int port) -{ -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - int batt_soc; -#endif - - print_current_state(port); - - /* - * Note: If the SinkWaitCapTimer times out and the HardResetCounter is - * greater than nHardResetCount the Sink Shall assume that the - * Source is non-responsive. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT) && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } - - /* - * If we're about to kill our active charge port and have no battery to - * supply power, disable the PE layer instead. If we have no battery, - * but we haven't determined our active charge port yet, also avoid - * performing the HardReset. It might be that this port was our active - * charge port. - * - * Note: On systems without batteries (ex. chromeboxes), it's preferable - * to brown out rather than leave the port only semi-functional for a - * customer. For systems which should have a battery, this condition is - * not expected to be encountered by a customer. - */ - if (IS_ENABLED(CONFIG_BATTERY) && (battery_is_present() == BP_NO) && - IS_ENABLED(CONFIG_CHARGE_MANAGER) && - ((port == charge_manager_get_active_charge_port() || - (charge_manager_get_active_charge_port() == CHARGE_PORT_NONE))) && - system_get_reset_flags() & EC_RESET_FLAG_SYSJUMP) { - CPRINTS("C%d: Disabling port to avoid brown out, " - "please reboot EC to enable port again", port); - set_state_pe(port, PE_SRC_DISABLED); - return; - - } - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - /* - * If the battery has not met a configured safe level for hard - * resets, set state to PE_SRC_Disabled as a hard - * reset could brown out the board. - * Note this may mean that high-power chargers will stay at - * 15W until a reset is sent, depending on boot timing. - * - * PE_FLAGS_SNK_WAITING_BATT flags will be cleared and - * PE state will be switched to PE_SNK_Startup when - * battery reaches CONFIG_USB_PD_RESET_MIN_BATT_SOC. - * See pe_update_waiting_batt_flag() for more details. - */ - batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) { - PE_SET_FLAG(port, PE_FLAGS_SNK_WAITING_BATT); - CPRINTS("C%d: Battery low %d%%! Stay in disabled state " \ - "until battery level reaches %d%%", port, batt_soc, - CONFIG_USB_PD_RESET_MIN_BATT_SOC); - set_state_pe(port, PE_SRC_DISABLED); - return; - } -#endif - - PE_CLR_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT | - PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_VDM_REQUEST_BUSY); - - /* Request the generation of Hard Reset Signaling by the PHY Layer */ - prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* - * Transition the Sink’s power supply to the new power level if - * PSTransistionTimer timeout occurred. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, pe[port].curr_limit, - pe[port].supply_voltage); - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - pe[port].curr_limit); - } -} - -static void pe_snk_hard_reset_run(int port) -{ - /* - * Transition to the PE_SNK_Transition_to_default state when: - * 1) The Hard Reset is complete. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_HARD_RESET_PENDING)) - return; - - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -/** - * PE_SNK_Transition_to_default - */ -static void pe_snk_transition_to_default_entry(int port) -{ - print_current_state(port); - - /* Reset flags */ - pe[port].flags = 0; - - /* Reset DPM Request */ - pe[port].dpm_request = 0; - - /* Inform the TC Layer of Hard Reset */ - tc_hard_reset_request(port); -} - -static void pe_snk_transition_to_default_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SNK_STARTUP); - } -} - -/** - * PE_SNK_Get_Source_Cap - */ -static void pe_snk_get_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get_Source_Cap Message */ - tx_emsg[port].len = 0; - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP); -} - -static void pe_snk_get_source_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_SNK_Send_Soft_Reset and PE_SRC_Send_Soft_Reset - */ -static void pe_send_soft_reset_entry(int port) -{ - print_current_state(port); - - /* Reset Protocol Layer (softly) */ - prl_reset_soft(port); - - pe_sender_response_msg_entry(port); - - /* - * Mark the temporary timer PE_TIMER_TIMEOUT as expired to limit - * to sending a single SoftReset message. - */ - pd_timer_enable(port, PE_TIMER_TIMEOUT, 0); -} - -static void pe_send_soft_reset_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - /* - * Protocol layer is running, so need to send a single SoftReset. - * Use temporary timer to act as a flag to keep this as a single - * message send. - */ - if (!pd_timer_is_disabled(port, PE_TIMER_TIMEOUT)) { - pd_timer_disable(port, PE_TIMER_TIMEOUT); - - /* - * TODO(b/150614211): Soft reset type should match - * unexpected incoming message type - */ - /* Send Soft Reset message */ - send_ctrl_msg(port, - pe[port].soft_reset_sop, PD_CTRL_SOFT_RESET); - - return; - } - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check == PE_MSG_DISCARDED) { - pe_set_ready_state(port); - return; - } - - /* - * Transition to the PE_SNK_Send_Capabilities or - * PE_SRC_Send_Capabilities state when: - * 1) An Accept Message has been received. - */ - if (msg_check == PE_MSG_SENT && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_ACCEPT)) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, - PE_SRC_SEND_CAPABILITIES); - return; - } - } - - /* - * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender - * Response Timer Timeout or Protocol Layer or Protocol Error - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } -} - -static void pe_send_soft_reset_exit(int port) -{ - pe_sender_response_msg_exit(port); - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/** - * PE_SNK_Soft_Reset and PE_SNK_Soft_Reset - */ -static void pe_soft_reset_entry(int port) -{ - print_current_state(port); - - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); -} - -static void pe_soft_reset_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -/** - * PE_SRC_Not_Supported and PE_SNK_Not_Supported - * - * 6.7.1 Soft Reset and Protocol Error (Revision 2.0, Version 1.3) - * An unrecognized or unsupported Message (except for a Structured VDM), - * received in the PE_SNK_Ready or PE_SRC_Ready states, Shall Not cause - * a Soft_Reset Message to be generated but instead a Reject Message - * Shall be generated. - */ -static void pe_send_not_supported_entry(int port) -{ - print_current_state(port); - - /* Request the Protocol Layer to send a Not_Supported Message. */ - if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20) - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED); - else - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); -} - -static void pe_send_not_supported_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - - } -} - -/** - * PE_SRC_Chunk_Received and PE_SNK_Chunk_Received - * - * 6.11.2.1.1 Architecture of Device Including Chunking Layer (Revision 3.0, - * Version 2.0): If a PD Device or Cable Marker has no requirement to handle any - * message requiring more than one Chunk of any Extended Message, it May omit - * the Chunking Layer. In this case it Shall implement the - * ChunkingNotSupportedTimer to ensure compatible operation with partners which - * support Chunking. - * - * See also: - * 6.6.18.1 ChunkingNotSupportedTimer - * 8.3.3.6 Not Supported Message State Diagrams - */ -__maybe_unused static void pe_chunk_received_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - assert(0); - - print_current_state(port); - pd_timer_enable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED, - PD_T_CHUNKING_NOT_SUPPORTED); -} - -__maybe_unused static void pe_chunk_received_run(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - assert(0); - - if (pd_timer_is_expired(port, PE_TIMER_CHUNKING_NOT_SUPPORTED)) - set_state_pe(port, PE_SEND_NOT_SUPPORTED); -} - -__maybe_unused static void pe_chunk_received_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED); -} - -/** - * PE_SRC_Ping - */ -static void pe_src_ping_entry(int port) -{ - print_current_state(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PING); -} - -static void pe_src_ping_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/** - * PE_Give_Battery_Cap - */ -static void pe_give_battery_cap_entry(int port) -{ - uint8_t *payload = rx_emsg[port].buf; - uint16_t *msg = (uint16_t *)tx_emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - /* Set VID */ - msg[BCDB_VID] = USB_VID_GOOGLE; - - /* Set PID */ - msg[BCDB_PID] = CONFIG_USB_PID; - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - * This value is the first byte after the headers. - */ - if (payload[0] != 0) { - /* Invalid battery reference */ - msg[BCDB_DESIGN_CAP] = 0; - msg[BCDB_FULL_CAP] = 0; - /* Set invalid battery bit in response bit 0, byte 8 */ - msg[BCDB_BATT_TYPE] = 1; - } else { - /* - * The Battery Design Capacity field shall return the - * Battery’s design capacity in tenths of Wh. If the - * Battery is Hot Swappable and is not present, the - * Battery Design Capacity field shall be set to 0. If - * the Battery is unable to report its Design Capacity, - * it shall return 0xFFFF - */ - msg[BCDB_DESIGN_CAP] = 0xffff; - - /* - * The Battery Last Full Charge Capacity field shall - * return the Battery’s last full charge capacity in - * tenths of Wh. If the Battery is Hot Swappable and - * is not present, the Battery Last Full Charge Capacity - * field shall be set to 0. If the Battery is unable to - * report its Design Capacity, the Battery Last Full - * Charge Capacity field shall be set to 0xFFFF. - */ - msg[BCDB_FULL_CAP] = 0xffff; - - - if (IS_ENABLED(HAS_TASK_HOSTCMD) && - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) { - int design_volt, design_cap, full_cap; - - design_volt = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DVLT); - design_cap = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DCAP); - full_cap = *(int *)host_get_memmap( - EC_MEMMAP_BATT_LFCC); - - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_DESIGN_CAP] = DIV_ROUND_NEAREST( - (design_cap * design_volt), - 100000); - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_FULL_CAP] = DIV_ROUND_NEAREST( - (design_cap * full_cap), - 100000); - } else { - uint32_t v; - uint32_t c; - - if (battery_design_voltage(&v) == 0) { - if (battery_design_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_DESIGN_CAP] = - DIV_ROUND_NEAREST( - (c * v), - 100000); - } - - if (battery_full_charge_capacity(&c) - == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_FULL_CAP] = - DIV_ROUND_NEAREST( - (c * v), - 100000); - } - } - - } - /* Valid battery selected */ - msg[BCDB_BATT_TYPE] = 0; - } - } else { - /* Battery not present indicated by 0's in the capacity */ - msg[BCDB_DESIGN_CAP] = 0; - msg[BCDB_FULL_CAP] = 0; - if (payload[0] != 0) - msg[BCDB_BATT_TYPE] = 1; - else - msg[BCDB_BATT_TYPE] = 0; - } - - /* Extended Battery Cap data is 9 bytes */ - tx_emsg[port].len = 9; - - send_ext_data_msg(port, TCPCI_MSG_SOP, PD_EXT_BATTERY_CAP); -} - -static void pe_give_battery_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - } -} - -/** - * PE_Give_Battery_Status - */ -static void pe_give_battery_status_entry(int port) -{ - uint8_t *payload = rx_emsg[port].buf; - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - * This value is the first byte after the headers. - */ - if (payload[0] != 0) { - /* Invalid battery reference */ - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - *msg |= BSDO_INVALID; - } else { - uint32_t v; - uint32_t c; - - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - - if (IS_ENABLED(HAS_TASK_HOSTCMD) && - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) { - v = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DVLT); - c = *(int *)host_get_memmap( - EC_MEMMAP_BATT_CAP); - - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } else if (battery_design_voltage(&v) == 0 && - battery_remaining_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } - - /* Battery is present */ - *msg |= BSDO_PRESENT; - - /* - * For drivers that are not smart battery compliant, - * battery_status() returns EC_ERROR_UNIMPLEMENTED and - * the battery is assumed to be idle. - */ - if (battery_status(&c) != 0) { - *msg |= BSDO_IDLE; /* assume idle */ - } else { - if (c & STATUS_FULLY_CHARGED) - /* Fully charged */ - *msg |= BSDO_IDLE; - else if (c & STATUS_DISCHARGING) - /* Discharging */ - *msg |= BSDO_DISCHARGING; - /* else battery is charging.*/ - } - } - } else { - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - if (payload[0] != 0) - *msg |= BSDO_INVALID; - } - - /* Battery Status data is 4 bytes */ - tx_emsg[port].len = 4; - - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_BATTERY_STATUS); -} - -static void pe_give_battery_status_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -/** - * PE_SRC_Send_Source_Alert and - * PE_SNK_Send_Sink_Alert - */ -static void pe_send_alert_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - uint32_t *len = &tx_emsg[port].len; - - print_current_state(port); - - if (pd_build_alert_msg(msg, len, pe[port].power_role) != EC_SUCCESS) - pe_set_ready_state(port); - - /* Request the Protocol Layer to send Alert Message. */ - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_ALERT); -} - -static void pe_send_alert_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - } -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/** - * PE_DRS_Evaluate_Swap - */ -static void pe_drs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* Get evaluation of Data Role Swap request from DPM */ - if (pd_check_data_swap(port, pe[port].data_role)) { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - /* - * PE_DRS_UFP_DFP_Evaluate_Swap and - * PE_DRS_DFP_UFP_Evaluate_Swap states embedded here. - */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } else { - /* - * PE_DRS_UFP_DFP_Reject_Swap and PE_DRS_DFP_UFP_Reject_Swap - * states embedded here. - */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } -} - -static void pe_drs_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Accept Message sent. Transtion to PE_DRS_Change */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - set_state_pe(port, PE_DRS_CHANGE); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - pe_set_ready_state(port); - } - } -} - -/** - * PE_DRS_Change - */ -static void pe_drs_change_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Change_to_DFP and PE_DRS_DFP_UFP_Change_to_UFP - * states embedded here. - */ - /* Request DPM to change port data role */ - pd_request_data_swap(port); -} - -static void pe_drs_change_run(int port) -{ - /* Wait until the data role is changed */ - if (pe[port].data_role == pd_get_data_role(port)) - return; - - /* Update the data role */ - pe[port].data_role = pd_get_data_role(port); - - if (pe[port].data_role == PD_ROLE_DFP) - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - - /* - * Port changed. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - pe_set_ready_state(port); -} - -/** - * PE_DRS_Send_Swap - */ -static void pe_drs_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Send_Swap and PE_DRS_DFP_UFP_Send_Swap - * states embedded here. - */ - /* Request the Protocol Layer to send a DR_Swap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_DR_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_drs_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_DRS_Change when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - set_state_pe(port, PE_DRS_CHANGE); - return; - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT) || - (type == PD_CTRL_NOT_SUPPORTED)) { - pe_set_ready_state(port); - return; - } - } - } - - /* - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_drs_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_PRS_SRC_SNK_Evaluate_Swap - */ -static void pe_prs_src_snk_evaluate_swap_entry(int port) -{ - print_current_state(port); - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SRC_SNK_Reject_PR_Swap state embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } else { - tc_request_power_swap(port); - /* PE_PRS_SRC_SNK_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_src_snk_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Clear any pending DPM power role swap request so we - * don't trigger a power role swap request back to src - * power role. - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - /* - * Power Role Swap OK, transition to - * PE_PRS_SRC_SNK_Transition_to_off - */ - set_state_pe(port, PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SRC_Ready */ - set_state_pe(port, PE_SRC_READY); - } - } -} - -/** - * PE_PRS_SRC_SNK_Transition_To_Off - */ -static void pe_prs_src_snk_transition_to_off_entry(int port) -{ - print_current_state(port); - - /* Contract is invalid */ - pe_invalidate_explicit_contract(port); - - /* Tell TypeC to power off the source */ - tc_src_power_off(port); - - pd_timer_enable(port, PE_TIMER_PS_SOURCE, - PD_POWER_SUPPLY_TURN_OFF_DELAY); -} - -static void pe_prs_src_snk_transition_to_off_run(int port) -{ - /* - * This is a non-interruptible AMS and power is transitioning - hard - * reset on interruption. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - tc_pr_swap_complete(port, 0); - set_state_pe(port, PE_SRC_HARD_RESET); - } - - /* Give time for supply to power off */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) && - pd_check_vbus_level(port, VBUS_SAFE0V)) - set_state_pe(port, PE_PRS_SRC_SNK_ASSERT_RD); -} - -static void pe_prs_src_snk_transition_to_off_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); -} - -/** - * PE_PRS_SRC_SNK_Assert_Rd - */ -static void pe_prs_src_snk_assert_rd_entry(int port) -{ - print_current_state(port); - - /* Tell TypeC to swap from Attached.SRC to Attached.SNK */ - tc_prs_src_snk_assert_rd(port); -} - -static void pe_prs_src_snk_assert_rd_run(int port) -{ - /* Wait until Rd is asserted */ - if (tc_is_attached_snk(port)) - set_state_pe(port, PE_PRS_SRC_SNK_WAIT_SOURCE_ON); -} - -/** - * PE_PRS_SRC_SNK_Wait_Source_On - */ -static void pe_prs_src_snk_wait_source_on_entry(int port) -{ - print_current_state(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); -} - -static void pe_prs_src_snk_wait_source_on_run(int port) -{ - if (pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) && - PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Update pe power role */ - pe[port].power_role = pd_get_power_role(port); - pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_ON); - } - - /* - * Transition to PE_SNK_Startup when: - * 1) A PS_RDY Message is received. - */ - if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - int type = PD_HEADER_TYPE(rx_emsg[port].header); - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int ext = PD_HEADER_EXT(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - set_state_pe(port, PE_SNK_STARTUP); - } else { - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - pe_send_soft_reset(port, sop); - } - return; - } - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOnTimer times out. - * 2) PS_RDY not sent after retries. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - return; - } -} - -static void pe_prs_src_snk_wait_source_on_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - tc_pr_swap_complete(port, - PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)); -} - -/** - * PE_PRS_SRC_SNK_Send_Swap - */ -static void pe_prs_src_snk_send_swap_entry(int port) -{ - print_current_state(port); - - /* Making an attempt to PR_Swap, clear we were possibly waiting */ - pd_timer_disable(port, PE_TIMER_PR_SWAP_WAIT); - - /* Request the Protocol Layer to send a PR_Swap Message. */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_prs_src_snk_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_PRS_SRC_SNK_Transition_To_Off when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - pe[port].src_snk_pr_swap_counter = 0; - tc_request_power_swap(port); - set_state_pe(port, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - } else if (type == PD_CTRL_REJECT) { - pe[port].src_snk_pr_swap_counter = 0; - set_state_pe(port, PE_SRC_READY); - } else if (type == PD_CTRL_WAIT) { - if (pe[port].src_snk_pr_swap_counter < - N_SNK_SRC_PR_SWAP_COUNT) { - PE_SET_FLAG(port, - PE_FLAGS_WAITING_PR_SWAP); - pd_timer_enable(port, - PE_TIMER_PR_SWAP_WAIT, - PD_T_PR_SWAP_WAIT); - } - pe[port].src_snk_pr_swap_counter++; - set_state_pe(port, PE_SRC_READY); - } - return; - } - } - - /* - * Transition to PE_SRC_Ready state when: - * 1) Or the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SRC_READY); -} - -static void pe_prs_src_snk_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_PRS_SNK_SRC_Evaluate_Swap - */ -static void pe_prs_snk_src_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* - * Cancel any pending PR swap request due to a received Wait since the - * partner just sent us a PR swap message. - */ - PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP); - pe[port].src_snk_pr_swap_counter = 0; - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SNK_SRC_Reject_Swap state embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } else { - tc_request_power_swap(port); - /* PE_PRS_SNK_SRC_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_snk_src_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Clear any pending DPM power role swap request so we - * don't trigger a power role swap request back to sink - * power role. - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - /* - * Accept message sent, transition to - * PE_PRS_SNK_SRC_Transition_to_off - */ - set_state_pe(port, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SNK_Ready */ - set_state_pe(port, PE_SNK_READY); - } - } - - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* - * Protocol Error occurs while PR swap, this may - * brown out if the port-parnter can't hold VBUS - * for tSrcTransition. Notify TC that we end the PR - * swap and start to watch VBUS. - * - * TODO(b:155181980): issue soft reset on protocol error. - */ - tc_pr_swap_complete(port, 0); - } -} - -/** - * PE_PRS_SNK_SRC_Transition_To_Off - * PE_FRS_SNK_SRC_Transition_To_Off - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_transition_to_off_entry(int port) -{ - print_current_state(port); - - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - !pe_in_frs_mode(port)) - tc_snk_power_off(port); - - pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_OFF); -} - -static void pe_prs_snk_src_transition_to_off_run(int port) -{ - int type; - int cnt; - int ext; - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOffTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE)) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - - /* - * Transition to PE_PRS_SNK_SRC_Assert_Rp when: - * 1) An PS_RDY Message is received. - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - /* - * FRS: We are always ready to drive vSafe5v, so just - * skip PE_FRS_SNK_SRC_Vbus_Applied and go direct to - * PE_FRS_SNK_SRC_Assert_Rp - */ - set_state_pe(port, PE_PRS_SNK_SRC_ASSERT_RP); - } - } -} - -static void pe_prs_snk_src_transition_to_off_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); -} - -/** - * PE_PRS_SNK_SRC_Assert_Rp - * PE_FRS_SNK_SRC_Assert_Rp - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_assert_rp_entry(int port) -{ - print_current_state(port); - - /* - * Tell TypeC to Power/Fast Role Swap (PRS/FRS) from - * Attached.SNK to Attached.SRC - */ - tc_prs_snk_src_assert_rp(port); -} - -static void pe_prs_snk_src_assert_rp_run(int port) -{ - /* Wait until TypeC is in the Attached.SRC state */ - if (tc_is_attached_src(port)) { - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - !pe_in_frs_mode(port)) { - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - } - set_state_pe(port, PE_PRS_SNK_SRC_SOURCE_ON); - } -} - -/** - * PE_PRS_SNK_SRC_Source_On - * PE_FRS_SNK_SRC_Source_On - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_source_on_entry(int port) -{ - print_current_state(port); - - /* - * VBUS was enabled when the TypeC state machine entered - * Attached.SRC state - */ - pd_timer_enable(port, PE_TIMER_PS_SOURCE, - PD_POWER_SUPPLY_TURN_ON_DELAY); -} - -static void pe_prs_snk_src_source_on_run(int port) -{ - /* Wait until power supply turns on */ - if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE)) { - if (!pd_timer_is_expired(port, PE_TIMER_PS_SOURCE)) - return; - - /* update pe power role */ - pe[port].power_role = pd_get_power_role(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); - /* reset timer so PD_CTRL_PS_RDY isn't sent again */ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - } - - /* - * Transition to ErrorRecovery state when: - * 1) On protocol error - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - } - - else if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Run swap source timer on entry to pe_src_startup */ - PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -static void pe_prs_snk_src_source_on_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - tc_pr_swap_complete(port, - PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)); -} - -/** - * PE_PRS_SNK_SRC_Send_Swap - * PE_FRS_SNK_SRC_Send_Swap - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PRS_SNK_SRC_SEND_SWAP - * Request the Protocol Layer to send a PR_Swap Message. - * - * FRS_SNK_SRC_SEND_SWAP - * Hardware should have turned off sink power and started - * bringing Vbus to vSafe5. - * Request the Protocol Layer to send a FR_Swap Message. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) { - send_ctrl_msg(port, - TCPCI_MSG_SOP, - pe_in_frs_mode(port) - ? PD_CTRL_FR_SWAP - : PD_CTRL_PR_SWAP); - } else { - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP); - } - pe_sender_response_msg_entry(port); -} - -static void pe_prs_snk_src_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check & PE_MSG_DISCARDED) { - if (pe_in_frs_mode(port)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SNK_READY); - return; - } - - /* - * Transition to PE_PRS_SNK_SRC_Transition_to_off when: - * 1) An Accept Message is received. - * - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - tc_request_power_swap(port); - set_state_pe(port, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - set_state_pe(port, - pe_in_frs_mode(port) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - else - set_state_pe(port, PE_SNK_READY); - } - return; - } - } - - /* - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) The SenderResponseTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - set_state_pe(port, - pe_in_frs_mode(port) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - else - set_state_pe(port, PE_SNK_READY); - return; - } - /* - * FRS Only: Transition to ErrorRecovery state when: - * 2) The FR_Swap Message is not sent after retries (a GoodCRC Message - * has not been received). A soft reset Shall Not be initiated in - * this case. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_in_frs_mode(port) && - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - } -} - -static void pe_prs_snk_src_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_FRS_SNK_SRC_Start_AMS - */ -__maybe_unused static void pe_frs_snk_src_start_ams_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - print_current_state(port); - - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - - /* Inform Protocol Layer this is start of AMS */ - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - /* Shared PRS/FRS code, indicate FRS path */ - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); -} - -/** - * PE_PRS_FRS_SHARED - */ -__maybe_unused static void pe_prs_frs_shared_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - /* - * Shared PRS/FRS code, assume PRS path - * - * This is the super state entry. It will be called before - * the first entry state to get into the PRS/FRS path. - * For FRS, PE_FRS_SNK_SRC_START_AMS entry will be called - * after this and that will set for the FRS path. - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -__maybe_unused static void pe_prs_frs_shared_exit(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - /* - * Shared PRS/FRS code, when not in shared path - * indicate PRS path - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -/** - * PE_BIST_TX - */ -static void pe_bist_tx_entry(int port) -{ - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - uint8_t mode = BIST_MODE(payload[0]); - int vbus_mv; - int ibus_ma; - - print_current_state(port); - - /* Get the current nominal VBUS value */ - if (pe[port].power_role == PD_ROLE_SOURCE) { - const uint32_t *src_pdo; - uint32_t unused; - - dpm_get_source_pdo(&src_pdo, port); - pd_extract_pdo_power(src_pdo[pe[port].requested_idx - 1], - &ibus_ma, &vbus_mv, &unused); - } else { - vbus_mv = pe[port].supply_voltage; - } - - /* If VBUS is not at vSafe5V, then don't enter BIST test mode */ - if (vbus_mv != PD_V_SAFE5V_NOM) { - pe_set_ready_state(port); - return; - } - - if (mode == BIST_CARRIER_MODE_2) { - /* - * PE_BIST_Carrier_Mode embedded here. - * See PD 3.0 section 6.4.3.1 BIST Carrier Mode 2: With a BIST - * Carrier Mode 2 BIST Data Object, the UUT Shall send out a - * continuous string of BMC-encoded alternating "1"s and “0”s. - * The UUT Shall exit the Continuous BIST Mode within - * tBISTContMode of this Continuous BIST Mode being enabled. - */ - send_ctrl_msg(port, TCPCI_MSG_TX_BIST_MODE_2, 0); - pd_timer_enable(port, PE_TIMER_BIST_CONT_MODE, - PD_T_BIST_CONT_MODE); - } else if (mode == BIST_TEST_DATA) { - /* - * See PD 3.0 section 6.4.3.2 BIST Test Data: - * With a BIST Test Data BIST Data Object, the UUT Shall return - * a GoodCRC Message and Shall enter a test mode in which it - * sends no further Messages except for GoodCRC Messages in - * response to received Messages.... The test Shall be ended by - * sending Hard Reset Signaling to reset the UUT. - */ - if (tcpc_set_bist_test_mode(port, true) != EC_SUCCESS) - CPRINTS("C%d: Failed to enter BIST Test Mode", port); - } else { - /* Ignore unsupported BIST messages. */ - pe_set_ready_state(port); - return; - } -} - -static void pe_bist_tx_run(int port) -{ - if (pd_timer_is_expired(port, PE_TIMER_BIST_CONT_MODE)) { - /* - * Entry point to disable BIST in TCPC if that's not already - * handled automatically by the TCPC. Unless this method is - * implemented in a TCPM driver, this function does nothing. - */ - tcpm_reset_bist_type_2(port); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); - } else { - /* - * We are in test data mode and no further Messages except for - * GoodCRC Messages in response to received Messages will - * be sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - } -} - -static void pe_bist_tx_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_BIST_CONT_MODE); -} - -/** - * Give_Sink_Cap Message - */ -static void pe_snk_give_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Sink_Capabilities Message */ - tx_emsg[port].len = pd_snk_pdo_cnt * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)pd_snk_pdo, tx_emsg[port].len); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SINK_CAP); -} - -static void pe_snk_give_sink_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - return; - } - - if (pe_check_outgoing_discard(port)) - return; -} - -/** - * Wait For Error Recovery - */ -static void pe_wait_for_error_recovery_entry(int port) -{ - print_current_state(port); - tc_start_error_recovery(port); -} - -static void pe_wait_for_error_recovery_run(int port) -{ - /* Stay here until error recovery is complete */ -} - -/** - * PE_Handle_Custom_Vdm_Request - */ -static void pe_handle_custom_vdm_request_entry(int port) -{ - /* Get the message */ - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - int rlen = 0; - uint32_t *rdata; - - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - rlen = pd_custom_vdm(port, cnt, payload, &rdata); - if (rlen > 0) { - tx_emsg[port].len = rlen * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)rdata, tx_emsg[port].len); - send_data_msg(port, sop, PD_DATA_VENDOR_DEF); - } else { - if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20) { - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } else { - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - pe_set_ready_state(port); - } - } -} - -static void pe_handle_custom_vdm_request_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * Message sent. Transition back to - * PE_SRC_Ready or PE_SINK_Ready - */ - pe_set_ready_state(port); - } -} - -static void pe_handle_custom_vdm_request_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -static enum vdm_response_result parse_vdm_response_common(int port) -{ - /* Retrieve the message information */ - uint32_t *payload; - int sop; - uint8_t type; - uint8_t cnt; - uint8_t ext; - - if (!PE_CHK_REPLY(port)) - return VDM_RESULT_WAITING; - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - payload = (uint32_t *)rx_emsg[port].buf; - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if (sop == pe[port].tx_type && type == PD_DATA_VENDOR_DEF && cnt >= 1 - && ext == 0) { - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK && - cnt >= pe[port].vdm_ack_min_data_objects) { - /* Handle ACKs in state-specific code. */ - return VDM_RESULT_ACK; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK) { - /* Handle NAKs in state-specific code. */ - return VDM_RESULT_NAK; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) { - /* - * Don't fill in the discovery field so we re-probe in - * tVDMBusy - */ - CPRINTS("C%d: Partner BUSY, request will be retried", - port); - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - PD_T_VDM_BUSY); - - return VDM_RESULT_NO_ACTION; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_INIT) { - /* - * Unexpected VDM REQ received. Let Src.Ready or - * Snk.Ready handle it. - */ - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - return VDM_RESULT_NO_ACTION; - } - - /* - * Partner gave us an incorrect size or command; mark discovery - * as failed. - */ - CPRINTS("C%d: Unexpected VDM response: 0x%04x 0x%04x", - port, rx_emsg[port].header, payload[0]); - return VDM_RESULT_NAK; - } else if (sop == pe[port].tx_type && ext == 0 && cnt == 0 && - type == PD_CTRL_NOT_SUPPORTED) { - /* - * A NAK would be more expected here, but Not Supported is still - * allowed with the same meaning. - */ - return VDM_RESULT_NAK; - } - - /* Unexpected Message Received. Src.Ready or Snk.Ready can handle it. */ - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - return VDM_RESULT_NO_ACTION; -} - -/** - * PE_VDM_SEND_REQUEST - * Shared parent to manage VDM timer and other shared parts of the VDM request - * process - */ -static void pe_vdm_send_request_entry(int port) -{ - if (pe[port].tx_type == TCPCI_MSG_INVALID) { - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS("C%d: %s: Tx type expected to be set, " - "returning", - port, pe_state_names[get_state_pe(port)]); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !tc_is_vconn_src(port) && port_discovery_vconn_swap_policy(port, - PE_FLAGS_VCONN_SWAP_TO_ON)) { - if (port_try_vconn_swap(port)) - return; - } - - /* All VDM sequences are Interruptible */ - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS | - PE_FLAGS_INTERRUPTIBLE_AMS); -} - -static void pe_vdm_send_request_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) && - pd_timer_is_disabled(port, PE_TIMER_VDM_RESPONSE)) { - /* Message was sent */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Start no response timer */ - /* TODO(b/155890173): Support DPM-supplied timeout */ - pd_timer_enable(port, PE_TIMER_VDM_RESPONSE, - PD_T_VDM_SNDR_RSP); - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - /* - * Go back to ready on first AMS message discard - * (ready states will clear the discard flag) - */ - pe_set_ready_state(port); - return; - } - - /* - * Check the VDM timer, child will be responsible for processing - * messages and reacting appropriately to unexpected messages. - */ - if (pd_timer_is_expired(port, PE_TIMER_VDM_RESPONSE)) { - CPRINTF("VDM %s Response Timeout\n", - pe[port].tx_type == TCPCI_MSG_SOP ? - "Port" : "Cable"); - /* - * Flag timeout so child state can mark appropriate discovery - * item as failed. - */ - PE_SET_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - - set_state_pe(port, get_last_state_pe(port)); - } -} - -static void pe_vdm_send_request_exit(int port) -{ - /* - * Clear TX complete in case child called set_state_pe() before parent - * could process transmission - */ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - /* Invalidate TX type so it must be set before next call */ - pe[port].tx_type = TCPCI_MSG_INVALID; - - pd_timer_disable(port, PE_TIMER_VDM_RESPONSE); -} - -/** - * PE_VDM_IDENTITY_REQUEST_CBL - * Combination of PE_INIT_PORT_VDM_Identity_Request State specific to the - * cable and PE_SRC_VDM_Identity_Request State. - * pe[port].tx_type must be set (to SOP') prior to entry. - */ -static void pe_vdm_identity_request_cbl_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - if (!pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_IDENT); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - pe[port].discover_identity_counter++; - - /* - * Valid DiscoverIdentity responses should have at least 4 objects - * (header, ID header, Cert Stat, Product VDO). - */ - pe[port].vdm_ack_min_data_objects = 4; -} - -static void pe_vdm_identity_request_cbl_run(int port) -{ - /* Retrieve the message information */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* - * The common code didn't parse a message. Handle protocol - * errors; otherwise, continue waiting. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - /* - * No Good CRC: See section 6.4.4.3.1 - Discover - * Identity. - * - * Discover Identity Command request sent to SOP' Shall - * Not cause a Soft Reset if a GoodCRC Message response - * is not returned since this can indicate a non-PD - * Capable cable. - */ - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, get_last_state_pe(port)); - } - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - if (get_last_state_pe(port) == PE_SRC_DISCOVERY && - (sop != pe[port].tx_type || - type != PD_DATA_VENDOR_DEF || - cnt == 0 || ext != 0)) { - /* - * Unexpected non-VDM received: Before an explicit - * contract, an unexpected message shall generate a soft - * reset using the SOP* of the incoming message. - */ - pe_send_soft_reset(port, sop); - return; - } - break; - case VDM_RESULT_ACK: - /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */ - dfp_consume_identity(port, sop, cnt, payload); - - /* - * Note: If port partner runs PD 2.0, we must use PD 2.0 to - * communicate with the cable plug when in an explicit contract. - * - * PD Spec Table 6-2: Revision Interoperability during an - * Explicit Contract - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20) - prl_set_rev(port, sop, - PD_HEADER_REV(rx_emsg[port].header)); - break; - case VDM_RESULT_NAK: - /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready or PE_SRC_Discovery) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_vdm_identity_request_cbl_exit(int port) -{ - /* - * When cable GoodCRCs but does not reply, down-rev to PD 2.0 and try - * again. - * - * PD 3.0 Rev 2.0 6.2.1.1.5 Specification Revision - * - * "When a Cable Plug does not respond to a Revision 3.0 Discover - * Identity REQ with a Discover Identity ACK or BUSY the Vconn Source - * May repeat steps 1-4 using a Revision 2.0 Discover Identity REQ in - * step 1 before establishing that there is no Cable Plug to - * communicate with" - */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - } - - /* - * 6.6.15 DiscoverIdentityTimer - * - * No more than nDiscoverIdentityCount Discover Identity Messages - * without a GoodCRC Message response Shall be sent. If no GoodCRC - * Message response is received after nDiscoverIdentityCount Discover - * Identity Command requests have been sent by a Port, the Port Shall - * Not send any further SOP’/SOP’’ Messages. - */ - if (pe[port].discover_identity_counter >= N_DISCOVER_IDENTITY_COUNT) - pd_set_identity_discovery(port, pe[port].tx_type, - PD_DISC_FAIL); - else if (pe[port].discover_identity_counter == - N_DISCOVER_IDENTITY_PD3_0_LIMIT) - /* - * Downgrade to PD 2.0 if the partner hasn't replied before - * all retries are exhausted in case the cable is - * non-compliant about GoodCRC-ing higher revisions - */ - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - - /* - * Set discover identity timer unless BUSY case already did so. - */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_NEEDED - && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) { - /* - * The tDiscoverIdentity timer is used during an explicit - * contract when discovering whether a cable is PD capable. - * - * Pre-contract, slow the rate Discover Identity commands are - * sent. This permits operation with captive cable devices that - * power the SOP' responder from VBUS instead of VCONN. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - pe_is_explicit_contract(port) - ? PD_T_DISCOVER_IDENTITY - : PE_T_DISCOVER_IDENTITY_NO_CONTRACT); - } - - /* Do not attempt further discovery if identity discovery failed. */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) { - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } -} - -/** - * PE_INIT_PORT_VDM_Identity_Request - * - * Specific to SOP requests, as cables require additions for the discover - * identity counter, must tolerate not receiving a GoodCRC, and need to set the - * cable revision based on response. - * pe[port].tx_type must be set (to SOP) prior to entry. - */ -static void pe_init_port_vdm_identity_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_IDENT); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid DiscoverIdentity responses should have at least 4 objects - * (header, ID header, Cert Stat, Product VDO). - */ - pe[port].vdm_ack_min_data_objects = 4; -} - -static void pe_init_port_vdm_identity_request_run(int port) -{ - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - - /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */ - dfp_consume_identity(port, sop, cnt, payload); - - break; - } - case VDM_RESULT_NAK: - /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_port_vdm_identity_request_exit(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - /* - * Mark failure to respond as discovery failure. - * - * For PD 2.0 partners (6.10.3 Applicability of Structured VDM - * Commands Note 3): - * - * If Structured VDMs are not supported, a Structured VDM - * Command received by a DFP or UFP Shall be Ignored. - */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - } - - /* Do not attempt further discovery if identity discovery failed. */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) { - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } -} - -/** - * PE_INIT_VDM_SVIDs_Request - * - * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry. - */ -static void pe_init_vdm_svids_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_SVID); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid Discover SVIDs ACKs should have at least 2 objects (VDM header - * and at least 1 SVID VDO). - */ - pe[port].vdm_ack_min_data_objects = 2; -} - -static void pe_init_vdm_svids_request_run(int port) -{ - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - - /* PE_INIT_VDM_SVIDs_ACKed embedded here */ - dfp_consume_svids(port, sop, cnt, payload); - break; - } - case VDM_RESULT_NAK: - /* PE_INIT_VDM_SVIDs_NAKed embedded here */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_vdm_svids_request_exit(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - /* - * Mark failure to respond as discovery failure. - * - * For PD 2.0 partners (6.10.3 Applicability of Structured VDM - * Commands Note 3): - * - * If Structured VDMs are not supported, a Structured VDM - * Command received by a DFP or UFP Shall be Ignored. - */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - } - - /* If SVID discovery failed, discovery is done at this point */ - if (pd_get_svids_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); -} - -/** - * PE_INIT_VDM_Modes_Request - * - * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry. - */ -static void pe_init_vdm_modes_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - const struct svid_mode_data *mode_data = - pd_get_next_mode(port, pe[port].tx_type); - uint16_t svid; - /* - * The caller should have checked that there was something to discover - * before entering this state. - */ - assert(mode_data); - assert(mode_data->discovery == PD_DISC_NEEDED); - svid = mode_data->svid; - - print_current_state(port); - - if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_modes_discovery(port, pe[port].tx_type, svid, - PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO((uint16_t) svid, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_MODES); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid Discover Modes responses should have at least 2 objects (VDM - * header and at least 1 mode VDO). - */ - pe[port].vdm_ack_min_data_objects = 2; -} - -static void pe_init_vdm_modes_request_run(int port) -{ - const struct svid_mode_data *mode_data; - uint16_t requested_svid; - - mode_data = pd_get_next_mode(port, pe[port].tx_type); - - assert(mode_data); - assert(mode_data->discovery == PD_DISC_NEEDED); - requested_svid = mode_data->svid; - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint16_t response_svid = (uint16_t) PD_VDO_VID(payload[0]); - - /* - * Accept ACK if the request and response SVIDs are equal; - * otherwise, treat this as a NAK of the request SVID. - * - * TODO(b:169242812): support valid mode checking in - * dfp_consume_modes. - */ - if (requested_svid == response_svid) { - /* PE_INIT_VDM_Modes_ACKed embedded here */ - dfp_consume_modes(port, sop, cnt, payload); - break; - } - } - /* Fall Through */ - case VDM_RESULT_NAK: - /* PE_INIT_VDM_Modes_NAKed embedded here */ - pd_set_modes_discovery(port, pe[port].tx_type, requested_svid, - PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_vdm_modes_request_exit(int port) -{ - if (pd_get_modes_discovery(port, pe[port].tx_type) != PD_DISC_NEEDED) - /* Mode discovery done, notify the AP */ - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - -} - -/** - * PE_VDM_REQUEST_DPM - * - * Makes a VDM request with contents and SOP* type previously set up by the DPM. - */ - -static void pe_vdm_request_dpm_entry(int port) -{ - print_current_state(port); - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - dpm_vdm_naked(port, pe[port].tx_type, - PD_VDO_VID(pe[port].vdm_data[0]), - PD_VDO_CMD(pe[port].vdm_data[0])); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - /* Copy Vendor Data Objects (VDOs) into message buffer */ - if (pe[port].vdm_cnt > 0) { - /* Copy data after header */ - memcpy(&tx_emsg[port].buf, - (uint8_t *)pe[port].vdm_data, - pe[port].vdm_cnt * 4); - /* Update len with the number of VDO bytes */ - tx_emsg[port].len = pe[port].vdm_cnt * 4; - } - - /* - * Clear the VDM nak'ed flag so that each request is - * treated separately (NAKs are handled by the - * DPM layer). Otherwise previous NAKs received will - * cause the state to exit early. - */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED); - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * In general, valid VDM ACKs must have a VDM header. Other than that, - * ACKs must be validated based on the command and SVID. - */ - pe[port].vdm_ack_min_data_objects = 1; -} - -static void pe_vdm_request_dpm_run(int port) -{ - uint32_t vdm_hdr; - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* - * USB-PD 3.0 Rev 1.1 - 6.4.4.2.5 - * Structured VDM command consists of a command request and a - * command response (ACK, NAK, or BUSY). An exception is made - * for the Attention command which shall have no response. - * - * Since Attention commands do not have an expected reply, - * the SVDM command is complete once the Attention command - * transmit is complete. - */ - vdm_hdr = pe[port].vdm_data[0]; - if(PD_VDO_SVDM(vdm_hdr) && - (PD_VDO_CMD(vdm_hdr) == CMD_ATTENTION)) { - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - break; - } - } - /* - * If common code didn't parse a message, and the VDM - * just sent was not an Attention message, then continue - * waiting. - */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. This includes Attention commands which have no - * expected SVDM response. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint16_t svid = PD_VDO_VID(payload[0]); - uint8_t vdm_cmd = PD_VDO_CMD(payload[0]); - - /* - * PE initiator VDM-ACKed state for requested VDM, like - * PE_INIT_VDM_FOO_ACKed, embedded here. - */ - dpm_vdm_acked(port, sop, cnt, payload); - - if (sop == TCPCI_MSG_SOP && svid == USB_SID_DISPLAYPORT && - vdm_cmd == CMD_DP_CONFIG) { - PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE); - } - break; - } - case VDM_RESULT_NAK: - /* - * PE initiator VDM-NAKed state for requested VDM, like - * PE_INIT_VDM_FOO_NAKed, embedded here. - */ - PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE); - - /* - * Because Not Supported messages or response timeouts are - * treated as NAKs, there may not be a NAK message to parse. - * Extract the needed information from the sent VDM. - */ - dpm_vdm_naked(port, pe[port].tx_type, - PD_VDO_VID(pe[port].vdm_data[0]), - PD_VDO_CMD(pe[port].vdm_data[0])); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_vdm_request_dpm_exit(int port) -{ - /* - * Force Tx type to be reset before reentering a VDM state, unless the - * current VDM request will be resumed. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) - pe[port].tx_type = TCPCI_MSG_INVALID; -} - -/** - * PE_VDM_Response - */ -static void pe_vdm_response_entry(int port) -{ - int vdo_len = 0; - uint32_t *rx_payload; - uint32_t *tx_payload; - uint8_t vdo_cmd; - svdm_rsp_func func = NULL; - - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - /* Get the message */ - rx_payload = (uint32_t *)rx_emsg[port].buf; - - /* Extract VDM command from the VDM header */ - vdo_cmd = PD_VDO_CMD(rx_payload[0]); - /* This must be a command request to proceed further */ - if (PD_VDO_CMDT(rx_payload[0]) != CMDT_INIT) { - CPRINTF("ERR:CMDT:%d:%d\n", PD_VDO_CMDT(rx_payload[0]), - vdo_cmd); - - pe_set_ready_state(port); - return; - } - - tx_payload = (uint32_t *)tx_emsg[port].buf; - /* - * Designed in TCPMv1, svdm_response functions use same - * buffer to take received data and overwrite with response - * data. To work with this interface, here copy rx data to - * tx buffer and pass tx_payload to func. - * TODO(b/166455363): change the interface to pass both rx - * and tx buffer. - * - * The SVDM header is dependent on both VDM command request being - * replied to and the result of response function. The SVDM command - * message is copied into tx_payload. tx_payload[0] is the VDM header - * for the response message. The SVDM response function takes the role - * of the DPM layer and will indicate the response type (ACK/NAK/BUSY) - * by its return value (vdo_len) - * vdo_len > 0 --> ACK - * vdo_len == 0 --> NAK - * vdo_len < 0 --> BUSY - */ - memcpy(tx_payload, rx_payload, PD_HEADER_CNT(rx_emsg[port].header) * 4); - /* - * Clear fields in SVDM response message that will be set based on the - * result of the svdm response function. - */ - tx_payload[0] &= ~VDO_CMDT_MASK; - tx_payload[0] &= ~VDO_SVDM_VERS(0x3); - - /* Add SVDM structured version being used */ - tx_payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - - /* Use VDM command to select the response handler function */ - switch (vdo_cmd) { - case CMD_DISCOVER_IDENT: - func = svdm_rsp.identity; - break; - case CMD_DISCOVER_SVID: - func = svdm_rsp.svids; - break; - case CMD_DISCOVER_MODES: - func = svdm_rsp.modes; - break; - case CMD_ENTER_MODE: - func = svdm_rsp.enter_mode; - break; - case CMD_DP_STATUS: - if (svdm_rsp.amode) - func = svdm_rsp.amode->status; - break; - case CMD_DP_CONFIG: - if (svdm_rsp.amode) - func = svdm_rsp.amode->config; - break; - case CMD_EXIT_MODE: - func = svdm_rsp.exit_mode; - break; -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_ATTENTION: - /* - * attention is only SVDM with no response - * (just goodCRC) return zero here. - */ - dfp_consume_attention(port, rx_payload); - pe_set_ready_state(port); - return; -#endif - default: - CPRINTF("VDO ERR:CMD:%d\n", vdo_cmd); - } - - /* - * If the port partner is PD_REV20 and our data role is DFP, we must - * reply to any SVDM command with a NAK. If the SVDM was an Attention - * command, it does not have a response, and exits the function above. - */ - if (func && (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20 || - pe[port].data_role == PD_ROLE_UFP)) { - /* - * Execute SVDM response function selected above and set the - * correct response type in the VDM header. - */ - vdo_len = func(port, tx_payload); - if (vdo_len > 0) { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_ACK); - /* - * If command response is an ACK and if the command was - * either enter/exit mode, then update the PE modal flag - * accordingly. - */ - if (vdo_cmd == CMD_ENTER_MODE) - PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION); - if (vdo_cmd == CMD_EXIT_MODE) - PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION); - } else if (!vdo_len) { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK); - vdo_len = 1; - } else { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_BUSY); - vdo_len = 1; - } - } else { - /* - * Received at VDM command which is not supported. PD 2.0 may - * NAK or ignore the message (see TD.PD.VNDI.E1. VDM Identity - * steps), but PD 3.0 must send Not_Supported (PD 3.0 Ver 2.0 + - * ECNs 2020-12-10 Table 6-64 Response to an incoming - * VDM or TD.PD.VNDI3.E3 VDM Identity steps) - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30) { - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK); - vdo_len = 1; - } - - /* Send response message. Note len is in bytes, not VDO objects */ - tx_emsg[port].len = (vdo_len * sizeof(uint32_t)); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_VENDOR_DEF); -} - -static void pe_vdm_response_run(int port) -{ - /* - * This state waits for a VDM response message to be sent. Return to the - * ready state once the message has been sent, a protocol error was - * detected, or if the VDM response msg was discarded based on being - * interrupted by another rx message. Since VDM sequences are AMS - * interruptible, there is no need to soft reset regardless of exit - * reason. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR) || - PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_MSG_DISCARDED); - - pe_set_ready_state(port); - } -} - -static void pe_vdm_response_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -/** - * PE_DEU_SEND_ENTER_USB - */ -static void pe_enter_usb_entry(int port) -{ - uint32_t usb4_payload; - - print_current_state(port); - - if (!IS_ENABLED(CONFIG_USB_PD_USB4)) { - pe_set_ready_state(port); - return; - } - - /* Port is already in USB4 mode, do not send enter USB message again */ - if (enter_usb_entry_is_done(port)) { - pe_set_ready_state(port); - return; - } - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !tc_is_vconn_src(port)) { - if (port_try_vconn_swap(port)) - return; - } - - pe[port].tx_type = TCPCI_MSG_SOP; - usb4_payload = enter_usb_setup_next_msg(port, &pe[port].tx_type); - - if (!usb4_payload) { - enter_usb_failed(port); - pe_set_ready_state(port); - return; - } - - tx_emsg[port].len = sizeof(usb4_payload); - - memcpy(tx_emsg[port].buf, &usb4_payload, tx_emsg[port].len); - send_data_msg(port, pe[port].tx_type, PD_DATA_ENTER_USB); - pe_sender_response_msg_entry(port); -} - -static void pe_enter_usb_run(int port) -{ - enum pe_msg_check msg_check; - - if (!IS_ENABLED(CONFIG_USB_PD_USB4)) { - pe_set_ready_state(port); - return; - } - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle Discarded message, return to PE_SNK/SRC_READY - */ - if (msg_check & PE_MSG_DISCARDED) { - pe_set_ready_state(port); - return; - } else if (msg_check == PE_MSG_SEND_PENDING) { - /* Wait until message is sent */ - return; - } - - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - pe_set_ready_state(port); - enter_usb_failed(port); - return; - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int type = PD_HEADER_TYPE(rx_emsg[port].header); - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Only look at control messages */ - if (cnt == 0) { - /* Accept message received */ - if (type == PD_CTRL_ACCEPT) { - enter_usb_accepted(port, sop); - } else if (type == PD_CTRL_REJECT) { - enter_usb_rejected(port, sop); - } else { - /* - * Unexpected control message received. - * Send Soft Reset. - */ - pe_send_soft_reset(port, sop); - return; - } - } else { - /* Unexpected data message received. Send Soft reset */ - pe_send_soft_reset(port, sop); - return; - } - pe_set_ready_state(port); - } -} - -static void pe_enter_usb_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -#ifdef CONFIG_USBC_VCONN -/* - * PE_VCS_Evaluate_Swap - */ -static void pe_vcs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* - * Request the DPM for an evaluation of the VCONN Swap request. - * Note: Ports that are presently the VCONN Source must always - * accept a VCONN - */ - - /* - * Transition to the PE_VCS_Accept_Swap state when: - * 1) The Device Policy Manager indicates that a VCONN Swap is ok. - * - * Transition to the PE_VCS_Reject_Swap state when: - * 1) Port is not presently the VCONN Source and - * 2) The DPM indicates that a VCONN Swap is not ok or - * 3) The DPM indicates that a VCONN Swap cannot be done at this time. - */ - - /* DPM rejects a VCONN Swap and port is not a VCONN source*/ - if (!tc_check_vconn_swap(port) && tc_is_vconn_src(port) < 1) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } - /* Port is not ready to perform a VCONN swap */ - else if (tc_is_vconn_src(port) < 0) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_WAIT); - } - /* Port is ready to perform a VCONN swap */ - else { - /* NOTE: PE_VCS_Accept_Swap State embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_vcs_evaluate_swap_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - /* Accept Message sent and Presently VCONN Source */ - if (tc_is_vconn_src(port)) - set_state_pe(port, PE_VCS_WAIT_FOR_VCONN_SWAP); - /* Accept Message sent and Not presently VCONN Source */ - else - set_state_pe(port, PE_VCS_TURN_ON_VCONN_SWAP); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SINK_Ready - */ - pe_set_ready_state(port); - } - return; - } - - if (pe_check_outgoing_discard(port)) - return; -} - -/* - * PE_VCS_Send_Swap - */ -static void pe_vcs_send_swap_entry(int port) -{ - print_current_state(port); - - /* Send a VCONN_Swap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_VCONN_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_vcs_send_swap_run(int port) -{ - uint8_t type; - uint8_t cnt; - enum tcpci_msg_type sop; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Increment once message has successfully sent */ - pe[port].vconn_swap_counter++; - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Transition to the PE_VCS_Wait_For_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is presently the VCONN Source. - * - * Transition to the PE_VCS_Turn_On_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is not presently the VCONN Source. - */ - if (type == PD_CTRL_ACCEPT) { - if (tc_is_vconn_src(port)) { - set_state_pe(port, - PE_VCS_WAIT_FOR_VCONN_SWAP); - } else { - set_state_pe(port, - PE_VCS_TURN_ON_VCONN_SWAP); - } - return; - } - /* - * Transition back to either the PE_SRC_Ready or - * PE_SNK_Ready state when: - * 2) Reject message is received or - * 3) Wait message Received. - */ - if (type == PD_CTRL_REJECT || type == PD_CTRL_WAIT) { - pe_set_ready_state(port); - return; - } - - /* - * The Policy Engine May transition to the - * PE_VCS_Force_Vconn state when: - * - A Not_Supported Message is received and - * - The Port is not presently the VCONN Source - */ - if (type == PD_CTRL_NOT_SUPPORTED) { - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - !tc_is_vconn_src(port)) - set_state_pe(port, PE_VCS_FORCE_VCONN); - else - pe_set_ready_state(port); - return; - } - } - /* - * Unexpected Message Received, send soft reset with SOP* of - * incoming message. - */ - pe_send_soft_reset(port, sop); - return; - } - - /* - * Transition back to either the PE_SRC_Ready or - * PE_SNK_Ready state when: - * 1) SenderResponseTimer Timeout - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_vcs_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/* - * PE_VCS_Wait_for_VCONN_Swap - */ -static void pe_vcs_wait_for_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Start the VCONNOnTimer */ - pd_timer_enable(port, PE_TIMER_VCONN_ON, PD_T_VCONN_SOURCE_ON); - - /* - * The USB PD 3.0 spec indicates that the initial VCONN source - * shall cease sourcing VCONN within tVCONNSourceOff (25ms) - * after receiving the PS_RDY message. However, some partners - * begin sending SOP' messages only 1 ms after sending PS_RDY - * during VCONN swap. - * - * Preemptively disable receipt of SOP' and SOP'' messages while - * we wait for PS_RDY so we don't attempt to process messages - * directed at the cable. - * - * We continue to source VCONN while we wait as required by the - * spec. - */ - tcpm_sop_prime_enable(port, false); -} - -static void pe_vcs_wait_for_vconn_swap_run(int port) -{ - /* - * Transition to the PE_VCS_Turn_Off_VCONN state when: - * 1) A PS_RDY Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - /* - * PS_RDY message received - * - * Note: intentionally leave the receive flag set to indicate - * our route on exit when PS_RDY is received. - */ - if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) && - (PD_HEADER_EXT(rx_emsg[port].header) == 0) && - (PD_HEADER_TYPE(rx_emsg[port].header) == PD_CTRL_PS_RDY)) { - set_state_pe(port, PE_VCS_TURN_OFF_VCONN_SWAP); - return; - } else { - /* - * Unexpected message received - reset with the SOP* of - * the incoming message. - */ - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - } - } - - /* - * Transition to either the PE_SRC_Hard_Reset or - * PE_SNK_Hard_Reset state when: - * 1) The VCONNOnTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_VCONN_ON)) { - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_vcs_wait_for_vconn_swap_exit(int port) -{ - /* - * If we exited without getting PS_RDY, re-enable SOP' messaging since - * we are still the Vconn source. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - else - tcpm_sop_prime_enable(port, true); - - pd_timer_disable(port, PE_TIMER_VCONN_ON); -} - -/* - * PE_VCS_Turn_On_VCONN_Swap - */ -static void pe_vcs_turn_on_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn on VCONN */ - pd_request_vconn_swap_on(port); -} - -static void pe_vcs_turn_on_vconn_swap_run(int port) -{ - - /* - * Transition to the PE_VCS_Send_Ps_Rdy state when: - * 1) The Port’s VCONN is on. - */ - if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pd_timer_enable(port, PE_TIMER_TIMEOUT, - CONFIG_USBC_VCONN_SWAP_DELAY_US); - } - - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) - set_state_pe(port, PE_VCS_SEND_PS_RDY_SWAP); -} - -static void pe_vcs_turn_on_vconn_swap_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/* - * PE_VCS_Turn_Off_VCONN_Swap - */ -static void pe_vcs_turn_off_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn off VCONN */ - pd_request_vconn_swap_off(port); -} - -static void pe_vcs_turn_off_vconn_swap_run(int port) -{ - /* Wait for VCONN to turn off */ - if (PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - - /* - * A VCONN Swap Shall reset the DiscoverIdentityCounter - * to zero - */ - pe[port].discover_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - pe_set_ready_state(port); - return; - } -} - -/* - * PE_VCS_Send_PS_Rdy_Swap - */ -static void pe_vcs_send_ps_rdy_swap_entry(int port) -{ - print_current_state(port); - - /* Check for any interruptions to this non-interruptible AMS */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - enum tcpci_msg_type sop = - PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Soft reset with the SOP* of the incoming message */ - pe_send_soft_reset(port, sop); - return; - } - - /* Send a PS_RDY Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); -} - -static void pe_vcs_send_ps_rdy_swap_run(int port) -{ - /* - * After a VCONN Swap the VCONN Source needs to reset - * the Cable Plug’s Protocol Layer in order to ensure - * MessageID synchronization. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* - * A VCONN Swap Shall reset the - * DiscoverIdentityCounter to zero - */ - pe[port].discover_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - /* A SOP' soft reset is required after VCONN swap */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - pe_set_ready_state(port); - } - - if (pe_check_outgoing_discard(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* PS_RDY didn't send, soft reset */ - pe_send_soft_reset(port, TCPCI_MSG_SOP); - } -} - -/* - * PE_VCS_Force_Vconn - */ -__maybe_unused static void pe_vcs_force_vconn_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn on VCONN */ - pd_request_vconn_swap_on(port); -} - -__maybe_unused static void pe_vcs_force_vconn_run(int port) -{ - /* - * The Policy Engine Shall transition back to either the PE_SRC_Ready - * or PE_SNK_Ready state when: - * 1) The Port’s VCONN is on. - * - * Note we'll wait CONFIG_USBC_VCONN_SWAP_DELAY_US, as defined by the - * board, to ensure Vconn is on. - */ - if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pd_timer_enable(port, PE_TIMER_TIMEOUT, - CONFIG_USBC_VCONN_SWAP_DELAY_US); - } - - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) { - /* - * Note: A cable soft reset shouldn't be necessary as a - * Not_Supported reply means the partner doesn't support - * sourcing Vconn and did not communicate with the cable. - */ - pe_set_ready_state(port); - return; - } -} - -__maybe_unused static void pe_vcs_force_vconn_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/* - * PE_VCS_CBL_SEND_SOFT_RESET - * Note - Entry is only when directed by the DPM. Protocol errors are handled - * by the PE_SEND_SOFT_RESET state. - */ -static void pe_vcs_cbl_send_soft_reset_entry(int port) -{ - print_current_state(port); - - if (!pe_can_send_sop_prime(port)) { - /* - * If we're not VCONN source, return the appropriate state. - * A VCONN swap re-triggers sending SOP' soft reset - */ - if (pe_is_explicit_contract(port)) { - /* Return to PE_{SRC,SNK}_Ready state */ - pe_set_ready_state(port); - } else { - /* - * Not in Explicit Contract, so we must be a SRC, - * return to PE_Src_Send_Capabilities. - */ - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } - return; - } - - send_ctrl_msg(port, TCPCI_MSG_SOP_PRIME, PD_CTRL_SOFT_RESET); - pe_sender_response_msg_entry(port); -} - -static void pe_vcs_cbl_send_soft_reset_run(int port) -{ - bool cable_soft_reset_complete = false; - enum pe_msg_check msg_check; - - msg_check = pe_sender_response_msg_run(port); - - /* Got ACCEPT or REJECT from Cable Plug */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - cable_soft_reset_complete = true; - - /* - * Note: If port partner runs PD 2.0, we must use PD 2.0 to - * communicate with the cable plug when in an explicit contract. - * - * PD Spec Table 6-2: Revision Interoperability during an - * Explicit Contract - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20) - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, - PD_HEADER_REV(rx_emsg[port].header)); - } - - /* No GoodCRC received, cable is not present */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* - * TODO(b/171823328): TCPMv2: Implement cable reset - * Cable reset will only be done here if we know for certain - * a cable is present (we've received the SOP' DiscId response). - */ - cable_soft_reset_complete = true; - } - - if (cable_soft_reset_complete || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) || - (msg_check & PE_MSG_DISCARDED)) { - if (pe_is_explicit_contract(port)) { - /* Return to PE_{SRC,SNK}_Ready state */ - pe_set_ready_state(port); - } else { - /* - * Not in Explicit Contract, so we must be a SRC, - * return to PE_Src_Send_Capabilities. - */ - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } - } -} - -static void pe_vcs_cbl_send_soft_reset_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -#endif /* CONFIG_USBC_VCONN */ - -/* - * PE_DR_SNK_Get_Sink_Cap and PE_SRC_Get_Sink_Cap State (shared) - */ -static void pe_dr_get_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get Sink Cap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SINK_CAP); - pe_sender_response_msg_entry(port); -} - -static void pe_dr_get_sink_cap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - enum tcpci_msg_type sop; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_[SRC,SNK]_Ready when: - * 1) A Sink_Capabilities Message is received - * 2) Or SenderResponseTimer times out - * 3) Or a Reject Message is received. - * - * Transition to PE_SEND_SOFT_RESET state when: - * 1) An unexpected message is received - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - if (ext == 0 && sop == TCPCI_MSG_SOP) { - if ((cnt > 0) && (type == PD_DATA_SINK_CAP)) { - uint32_t *payload = - (uint32_t *)rx_emsg[port].buf; - uint8_t cap_cnt = rx_emsg[port].len / - sizeof(uint32_t); - - pe_set_snk_caps(port, cap_cnt, payload); - - dpm_evaluate_sink_fixed_pdo(port, payload[0]); - pe_set_ready_state(port); - return; - } else if (cnt == 0 && (type == PD_CTRL_REJECT || - type == PD_CTRL_NOT_SUPPORTED)) { - pe_set_ready_state(port); - return; - } - /* Unexpected messages fall through to soft reset */ - } - - pe_send_soft_reset(port, sop); - return; - } - - /* - * Transition to PE_[SRC,SNK]_Ready state when: - * 1) SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_dr_get_sink_cap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/* - * PE_DR_SNK_Give_Source_Cap - */ -static void pe_dr_snk_give_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send source capabilities. */ - send_source_cap(port); -} - -static void pe_dr_snk_give_source_cap_run(int port) -{ - /* - * Transition back to PE_SNK_Ready when the Source_Capabilities message - * has been successfully sent. - * - * Get Source Capabilities AMS is uninterruptible, but in case the - * partner violates the spec then send a soft reset rather than get - * stuck here. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SNK_READY); - } else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - pe_send_soft_reset(port, TCPCI_MSG_SOP); - } -} - -/* - * PE_DR_SRC_Get_Source_Cap - */ -static void pe_dr_src_get_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get_Source_Cap Message */ - tx_emsg[port].len = 0; - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP); - pe_sender_response_msg_entry(port); -} - -static void pe_dr_src_get_source_cap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_SRC_Ready when: - * 1) A Source Capabilities Message is received. - * 2) A Reject Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if (ext == 0) { - if ((cnt > 0) && (type == PD_DATA_SOURCE_CAP)) { - uint32_t *payload = - (uint32_t *)rx_emsg[port].buf; - - pd_set_src_caps(port, cnt, payload); - - /* - * If we'd prefer to charge from this partner, - * then propose a PR swap. - */ - if (pd_can_charge_from_device(port, cnt, - payload)) - pd_request_power_swap(port); - - /* - * Report dual role power capability to the - * charge manager if present - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER) && - pd_get_partner_dual_role_power(port)) - charge_manager_update_dualrole(port, - CAP_DUALROLE); - - set_state_pe(port, PE_SRC_READY); - } else if ((cnt == 0) && (type == PD_CTRL_REJECT || - type == PD_CTRL_NOT_SUPPORTED)) { - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SRC_READY); - } else { - /* - * On protocol error, consider source cap - * retrieval a failure - */ - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SEND_SOFT_RESET); - } - return; - } else { - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - } - - /* - * Transition to PE_SRC_Ready state when: - * 1) the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SRC_READY); -} - -static void pe_dr_src_get_source_cap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -const uint32_t * const pd_get_src_caps(int port) -{ - return pe[port].src_caps; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ - int i; - - pe[port].src_cap_cnt = cnt; - - for (i = 0; i < cnt; i++) - pe[port].src_caps[i] = *src_caps++; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - if (pe[port].src_cap_cnt > 0) - return pe[port].src_cap_cnt; - - return 0; -} - -/* Track access to the PD discovery structures during HC execution */ -uint32_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT]; - -void pd_dfp_discovery_init(int port) -{ - atomic_or(&task_access[port][TCPCI_MSG_SOP], BIT(task_get_current())); - atomic_or(&task_access[port][TCPCI_MSG_SOP_PRIME], - BIT(task_get_current())); - - memset(pe[port].discovery, 0, sizeof(pe[port].discovery)); - -} - -void pd_dfp_mode_init(int port) -{ - /* - * Clear the VDM Setup Done and Modal Operation flags so we will - * have a fresh discovery - */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_SETUP_DONE | - PE_FLAGS_MODAL_OPERATION); - - memset(pe[port].partner_amodes, 0, sizeof(pe[port].partner_amodes)); - - /* Reset the DPM and DP modules to enable alternate mode entry. */ - dpm_init(port); - dp_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) - tbt_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - enter_usb_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_UFP_DP)) - pd_ufp_set_dp_opos(port, 0); -} - -__maybe_unused void pd_discovery_access_clear(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - atomic_clear_bits(&task_access[port][type], 0xFFFFFFFF); -} - -__maybe_unused bool pd_discovery_access_validate(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - return !(task_access[port][type] & ~BIT(task_get_current())); -} - -__maybe_unused struct pd_discovery *pd_get_am_discovery_and_notify_access( - int port, enum tcpci_msg_type type) -{ - atomic_or(&task_access[port][type], BIT(task_get_current())); - return (struct pd_discovery *)pd_get_am_discovery(port, type); -} - -__maybe_unused const struct pd_discovery *pd_get_am_discovery(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - ASSERT(type < DISCOVERY_TYPE_COUNT); - - return &pe[port].discovery[type]; -} - -__maybe_unused struct partner_active_modes *pd_get_partner_active_modes( - int port, enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - ASSERT(type < AMODE_TYPE_COUNT); - return &pe[port].partner_amodes[type]; -} - -__maybe_unused void pd_set_dfp_enter_mode_flag(int port, bool set) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - if (set) - PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION); - else - PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION); -} - -const char *pe_get_current_state(int port) -{ - if (pe_is_running(port) && IS_ENABLED(USB_PD_DEBUG_LABELS)) - return pe_state_names[get_state_pe(port)]; - else - return ""; -} - -uint32_t pe_get_flags(int port) -{ - return pe[port].flags; -} - -static __const_data const struct usb_state pe_states[] = { - /* Super States */ -#ifdef CONFIG_USB_PD_REV30 - [PE_PRS_FRS_SHARED] = { - .entry = pe_prs_frs_shared_entry, - .exit = pe_prs_frs_shared_exit, - }, -#endif - [PE_VDM_SEND_REQUEST] = { - .entry = pe_vdm_send_request_entry, - .run = pe_vdm_send_request_run, - .exit = pe_vdm_send_request_exit, - }, - - /* Normal States */ - [PE_SRC_STARTUP] = { - .entry = pe_src_startup_entry, - .run = pe_src_startup_run, - .exit = pe_src_startup_exit, - }, - [PE_SRC_DISCOVERY] = { - .entry = pe_src_discovery_entry, - .run = pe_src_discovery_run, - }, - [PE_SRC_SEND_CAPABILITIES] = { - .entry = pe_src_send_capabilities_entry, - .run = pe_src_send_capabilities_run, - .exit = pe_src_send_capabilities_exit, - }, - [PE_SRC_NEGOTIATE_CAPABILITY] = { - .entry = pe_src_negotiate_capability_entry, - }, - [PE_SRC_TRANSITION_SUPPLY] = { - .entry = pe_src_transition_supply_entry, - .run = pe_src_transition_supply_run, - .exit = pe_src_transition_supply_exit, - }, - [PE_SRC_READY] = { - .entry = pe_src_ready_entry, - .run = pe_src_ready_run, - }, - [PE_SRC_DISABLED] = { - .entry = pe_src_disabled_entry, - }, - [PE_SRC_CAPABILITY_RESPONSE] = { - .entry = pe_src_capability_response_entry, - .run = pe_src_capability_response_run, - }, - [PE_SRC_HARD_RESET] = { - .entry = pe_src_hard_reset_entry, - .run = pe_src_hard_reset_run, - .exit = pe_src_hard_reset_exit, - }, - [PE_SRC_HARD_RESET_RECEIVED] = { - .entry = pe_src_hard_reset_received_entry, - .run = pe_src_hard_reset_received_run, - .exit = pe_src_hard_reset_received_exit, - }, - [PE_SRC_TRANSITION_TO_DEFAULT] = { - .entry = pe_src_transition_to_default_entry, - .run = pe_src_transition_to_default_run, - }, - [PE_SNK_STARTUP] = { - .entry = pe_snk_startup_entry, - .run = pe_snk_startup_run, - }, - [PE_SNK_DISCOVERY] = { - .entry = pe_snk_discovery_entry, - .run = pe_snk_discovery_run, - }, - [PE_SNK_WAIT_FOR_CAPABILITIES] = { - .entry = pe_snk_wait_for_capabilities_entry, - .run = pe_snk_wait_for_capabilities_run, - .exit = pe_snk_wait_for_capabilities_exit, - }, - [PE_SNK_EVALUATE_CAPABILITY] = { - .entry = pe_snk_evaluate_capability_entry, - }, - [PE_SNK_SELECT_CAPABILITY] = { - .entry = pe_snk_select_capability_entry, - .run = pe_snk_select_capability_run, - .exit = pe_snk_select_capability_exit, - }, - [PE_SNK_READY] = { - .entry = pe_snk_ready_entry, - .run = pe_snk_ready_run, - }, - [PE_SNK_HARD_RESET] = { - .entry = pe_snk_hard_reset_entry, - .run = pe_snk_hard_reset_run, - }, - [PE_SNK_TRANSITION_TO_DEFAULT] = { - .entry = pe_snk_transition_to_default_entry, - .run = pe_snk_transition_to_default_run, - }, - [PE_SNK_GIVE_SINK_CAP] = { - .entry = pe_snk_give_sink_cap_entry, - .run = pe_snk_give_sink_cap_run, - }, - [PE_SNK_GET_SOURCE_CAP] = { - .entry = pe_snk_get_source_cap_entry, - .run = pe_snk_get_source_cap_run, - }, - [PE_SNK_TRANSITION_SINK] = { - .entry = pe_snk_transition_sink_entry, - .run = pe_snk_transition_sink_run, - .exit = pe_snk_transition_sink_exit, - }, - [PE_SEND_SOFT_RESET] = { - .entry = pe_send_soft_reset_entry, - .run = pe_send_soft_reset_run, - .exit = pe_send_soft_reset_exit, - }, - [PE_SOFT_RESET] = { - .entry = pe_soft_reset_entry, - .run = pe_soft_reset_run, - }, - [PE_SEND_NOT_SUPPORTED] = { - .entry = pe_send_not_supported_entry, - .run = pe_send_not_supported_run, - }, - [PE_SRC_PING] = { - .entry = pe_src_ping_entry, - .run = pe_src_ping_run, - }, - [PE_DRS_EVALUATE_SWAP] = { - .entry = pe_drs_evaluate_swap_entry, - .run = pe_drs_evaluate_swap_run, - }, - [PE_DRS_CHANGE] = { - .entry = pe_drs_change_entry, - .run = pe_drs_change_run, - }, - [PE_DRS_SEND_SWAP] = { - .entry = pe_drs_send_swap_entry, - .run = pe_drs_send_swap_run, - .exit = pe_drs_send_swap_exit, - }, - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = { - .entry = pe_prs_src_snk_evaluate_swap_entry, - .run = pe_prs_src_snk_evaluate_swap_run, - }, - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = { - .entry = pe_prs_src_snk_transition_to_off_entry, - .run = pe_prs_src_snk_transition_to_off_run, - .exit = pe_prs_src_snk_transition_to_off_exit, - }, - [PE_PRS_SRC_SNK_ASSERT_RD] = { - .entry = pe_prs_src_snk_assert_rd_entry, - .run = pe_prs_src_snk_assert_rd_run, - }, - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = { - .entry = pe_prs_src_snk_wait_source_on_entry, - .run = pe_prs_src_snk_wait_source_on_run, - .exit = pe_prs_src_snk_wait_source_on_exit, - }, - [PE_PRS_SRC_SNK_SEND_SWAP] = { - .entry = pe_prs_src_snk_send_swap_entry, - .run = pe_prs_src_snk_send_swap_run, - .exit = pe_prs_src_snk_send_swap_exit, - }, - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = { - .entry = pe_prs_snk_src_evaluate_swap_entry, - .run = pe_prs_snk_src_evaluate_swap_run, - }, - /* - * Some of the Power Role Swap actions are shared with the very - * similar actions of Fast Role Swap. - */ - /* State actions are shared with PE_FRS_SNK_SRC_TRANSITION_TO_OFF */ - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = { - .entry = pe_prs_snk_src_transition_to_off_entry, - .run = pe_prs_snk_src_transition_to_off_run, - .exit = pe_prs_snk_src_transition_to_off_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_ASSERT_RP */ - [PE_PRS_SNK_SRC_ASSERT_RP] = { - .entry = pe_prs_snk_src_assert_rp_entry, - .run = pe_prs_snk_src_assert_rp_run, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_SOURCE_ON */ - [PE_PRS_SNK_SRC_SOURCE_ON] = { - .entry = pe_prs_snk_src_source_on_entry, - .run = pe_prs_snk_src_source_on_run, - .exit = pe_prs_snk_src_source_on_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_SEND_SWAP */ - [PE_PRS_SNK_SRC_SEND_SWAP] = { - .entry = pe_prs_snk_src_send_swap_entry, - .run = pe_prs_snk_src_send_swap_run, - .exit = pe_prs_snk_src_send_swap_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, -#ifdef CONFIG_USBC_VCONN - [PE_VCS_EVALUATE_SWAP] = { - .entry = pe_vcs_evaluate_swap_entry, - .run = pe_vcs_evaluate_swap_run, - }, - [PE_VCS_SEND_SWAP] = { - .entry = pe_vcs_send_swap_entry, - .run = pe_vcs_send_swap_run, - .exit = pe_vcs_send_swap_exit, - }, - [PE_VCS_WAIT_FOR_VCONN_SWAP] = { - .entry = pe_vcs_wait_for_vconn_swap_entry, - .run = pe_vcs_wait_for_vconn_swap_run, - .exit = pe_vcs_wait_for_vconn_swap_exit, - }, - [PE_VCS_TURN_ON_VCONN_SWAP] = { - .entry = pe_vcs_turn_on_vconn_swap_entry, - .run = pe_vcs_turn_on_vconn_swap_run, - .exit = pe_vcs_turn_on_vconn_swap_exit, - }, - [PE_VCS_TURN_OFF_VCONN_SWAP] = { - .entry = pe_vcs_turn_off_vconn_swap_entry, - .run = pe_vcs_turn_off_vconn_swap_run, - }, - [PE_VCS_SEND_PS_RDY_SWAP] = { - .entry = pe_vcs_send_ps_rdy_swap_entry, - .run = pe_vcs_send_ps_rdy_swap_run, - }, - [PE_VCS_CBL_SEND_SOFT_RESET] = { - .entry = pe_vcs_cbl_send_soft_reset_entry, - .run = pe_vcs_cbl_send_soft_reset_run, - .exit = pe_vcs_cbl_send_soft_reset_exit, - }, -#endif /* CONFIG_USBC_VCONN */ - [PE_VDM_IDENTITY_REQUEST_CBL] = { - .entry = pe_vdm_identity_request_cbl_entry, - .run = pe_vdm_identity_request_cbl_run, - .exit = pe_vdm_identity_request_cbl_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_PORT_VDM_IDENTITY_REQUEST] = { - .entry = pe_init_port_vdm_identity_request_entry, - .run = pe_init_port_vdm_identity_request_run, - .exit = pe_init_port_vdm_identity_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_VDM_SVIDS_REQUEST] = { - .entry = pe_init_vdm_svids_request_entry, - .run = pe_init_vdm_svids_request_run, - .exit = pe_init_vdm_svids_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_VDM_MODES_REQUEST] = { - .entry = pe_init_vdm_modes_request_entry, - .run = pe_init_vdm_modes_request_run, - .exit = pe_init_vdm_modes_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_VDM_REQUEST_DPM] = { - .entry = pe_vdm_request_dpm_entry, - .run = pe_vdm_request_dpm_run, - .exit = pe_vdm_request_dpm_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_VDM_RESPONSE] = { - .entry = pe_vdm_response_entry, - .run = pe_vdm_response_run, - .exit = pe_vdm_response_exit, - }, - [PE_HANDLE_CUSTOM_VDM_REQUEST] = { - .entry = pe_handle_custom_vdm_request_entry, - .run = pe_handle_custom_vdm_request_run, - .exit = pe_handle_custom_vdm_request_exit, - }, - [PE_DEU_SEND_ENTER_USB] = { - .entry = pe_enter_usb_entry, - .run = pe_enter_usb_run, - .exit = pe_enter_usb_exit, - }, - [PE_WAIT_FOR_ERROR_RECOVERY] = { - .entry = pe_wait_for_error_recovery_entry, - .run = pe_wait_for_error_recovery_run, - }, - [PE_BIST_TX] = { - .entry = pe_bist_tx_entry, - .run = pe_bist_tx_run, - .exit = pe_bist_tx_exit, - }, - [PE_DR_GET_SINK_CAP] = { - .entry = pe_dr_get_sink_cap_entry, - .run = pe_dr_get_sink_cap_run, - .exit = pe_dr_get_sink_cap_exit, - }, - [PE_DR_SNK_GIVE_SOURCE_CAP] = { - .entry = pe_dr_snk_give_source_cap_entry, - .run = pe_dr_snk_give_source_cap_run, - }, - [PE_DR_SRC_GET_SOURCE_CAP] = { - .entry = pe_dr_src_get_source_cap_entry, - .run = pe_dr_src_get_source_cap_run, - .exit = pe_dr_src_get_source_cap_exit, - }, -#ifdef CONFIG_USB_PD_REV30 - [PE_FRS_SNK_SRC_START_AMS] = { - .entry = pe_frs_snk_src_start_ams_entry, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [PE_GIVE_BATTERY_CAP] = { - .entry = pe_give_battery_cap_entry, - .run = pe_give_battery_cap_run, - }, - [PE_GIVE_BATTERY_STATUS] = { - .entry = pe_give_battery_status_entry, - .run = pe_give_battery_status_run, - }, - [PE_SEND_ALERT] = { - .entry = pe_send_alert_entry, - .run = pe_send_alert_run, - }, -#else - [PE_SRC_CHUNK_RECEIVED] = { - .entry = pe_chunk_received_entry, - .run = pe_chunk_received_run, - .exit = pe_chunk_received_exit, - }, - [PE_SNK_CHUNK_RECEIVED] = { - .entry = pe_chunk_received_entry, - .run = pe_chunk_received_run, - .exit = pe_chunk_received_exit, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -#ifdef CONFIG_USBC_VCONN - [PE_VCS_FORCE_VCONN] = { - .entry = pe_vcs_force_vconn_entry, - .run = pe_vcs_force_vconn_run, - .exit = pe_vcs_force_vconn_exit, - }, -#endif /* CONFIG_USBC_VCONN */ -#endif /* CONFIG_USB_PD_REV30 */ -}; - -#ifdef TEST_BUILD -/* TODO(b/173791979): Unit tests shouldn't need to access internal states */ -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - .names = pe_state_names, - .names_size = ARRAY_SIZE(pe_state_names), - }, -}; -BUILD_ASSERT(ARRAY_SIZE(pe_states) == ARRAY_SIZE(pe_state_names)); -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); - -void pe_set_flag(int port, int flag) -{ - PE_SET_FLAG(port, flag); -} -void pe_clr_flag(int port, int flag) -{ - PE_CLR_FLAG(port, flag); -} -int pe_chk_flag(int port, int flag) -{ - return PE_CHK_FLAG(port, flag); -} -int pe_get_all_flags(int port) -{ - return pe[port].flags; -} -void pe_set_all_flags(int port, int flags) -{ - pe[port].flags = flags; -} -void pe_clr_dpm_requests(int port) -{ - pe[port].dpm_request = 0; -} -#endif diff --git a/common/usbc/usb_prl_sm.c b/common/usbc/usb_prl_sm.c deleted file mode 100644 index a58a579775..0000000000 --- a/common/usbc/usb_prl_sm.c +++ /dev/null @@ -1,2471 +0,0 @@ -/* 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. - */ - -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "vpd_api.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* - * Define DEBUG_PRINT_FLAG_NAMES to print flag names when set and cleared. - */ -#undef DEBUG_PRINT_FLAG_NAMES - -#ifdef DEBUG_PRINT_FLAG_NAMES -__maybe_unused static void print_flag(const char *group, - int set_or_clear, - int flag); -#define SET_FLAG(group, flags, flag) \ - do { \ - print_flag(group, 1, flag); \ - atomic_or(flags, (flag)); \ - } while (0) -#define CLR_FLAG(group, flags, flag) \ - do { \ - int before = *flags; \ - atomic_clear_bits(flags, (flag)); \ - if (*flags != before) \ - print_flag(group, 0, flag); \ - } while (0) -#else -#define SET_FLAG(group, flags, flag) atomic_or(flags, (flag)) -#define CLR_FLAG(group, flags, flag) atomic_clear_bits(flags, (flag)) -#endif - - -#define RCH_SET_FLAG(port, flag) SET_FLAG("RCH", &rch[port].flags, (flag)) -#define RCH_CLR_FLAG(port, flag) CLR_FLAG("RCH", &rch[port].flags, (flag)) -#define RCH_CHK_FLAG(port, flag) (rch[port].flags & (flag)) - -#define TCH_SET_FLAG(port, flag) SET_FLAG("TCH", &tch[port].flags, (flag)) -#define TCH_CLR_FLAG(port, flag) CLR_FLAG("TCH", &tch[port].flags, (flag)) -#define TCH_CHK_FLAG(port, flag) (tch[port].flags & (flag)) - -#define PRL_TX_SET_FLAG(port, flag) \ - SET_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) -#define PRL_TX_CLR_FLAG(port, flag) \ - CLR_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) -#define PRL_TX_CHK_FLAG(port, flag) (prl_tx[port].flags & (flag)) - -#define PRL_HR_SET_FLAG(port, flag) \ - SET_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) -#define PRL_HR_CLR_FLAG(port, flag) \ - CLR_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) -#define PRL_HR_CHK_FLAG(port, flag) (prl_hr[port].flags & (flag)) - -#define PDMSG_SET_FLAG(port, flag) SET_FLAG("PDMSG", &pdmsg[port].flags, (flag)) -#define PDMSG_CLR_FLAG(port, flag) CLR_FLAG("PDMSG", &pdmsg[port].flags, (flag)) -#define PDMSG_CHK_FLAG(port, flag) (pdmsg[port].flags & (flag)) - -/* Protocol Layer Flags */ -/* - * NOTE: - * These flags are used in multiple state machines and could have - * different meanings in each state machine. - */ -/* Flag to note message transmission completed */ -#define PRL_FLAGS_TX_COMPLETE BIT(0) -/* Flag to note that PRL requested to set SINK_NG CC state */ -#define PRL_FLAGS_SINK_NG BIT(1) -/* Flag to note PRL waited for SINK_OK CC state before transmitting */ -#define PRL_FLAGS_WAIT_SINK_OK BIT(2) -/* Flag to note transmission error occurred */ -#define PRL_FLAGS_TX_ERROR BIT(3) -/* Flag to note PE triggered a hard reset */ -#define PRL_FLAGS_PE_HARD_RESET BIT(4) -/* Flag to note hard reset has completed */ -#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5) -/* Flag to note port partner sent a hard reset */ -#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6) -/* - * Flag to note a message transmission has been requested. It is only cleared - * when we send the message to the TCPC layer. - */ -#define PRL_FLAGS_MSG_XMIT BIT(7) -/* Flag to note a message was received */ -#define PRL_FLAGS_MSG_RECEIVED BIT(8) -/* Flag to note aborting current TX message, not currently set */ -#define PRL_FLAGS_ABORT BIT(9) -/* Flag to note current TX message uses chunking */ -#define PRL_FLAGS_CHUNKING BIT(10) - -struct bit_name { - int value; - const char *name; -}; - -static __const_data struct bit_name flag_bit_names[] = { - { PRL_FLAGS_TX_COMPLETE, "PRL_FLAGS_TX_COMPLETE" }, - { PRL_FLAGS_SINK_NG, "PRL_FLAGS_SINK_NG" }, - { PRL_FLAGS_WAIT_SINK_OK, "PRL_FLAGS_WAIT_SINK_OK" }, - { PRL_FLAGS_TX_ERROR, "PRL_FLAGS_TX_ERROR" }, - { PRL_FLAGS_PE_HARD_RESET, "PRL_FLAGS_PE_HARD_RESET" }, - { PRL_FLAGS_HARD_RESET_COMPLETE, "PRL_FLAGS_HARD_RESET_COMPLETE" }, - { PRL_FLAGS_PORT_PARTNER_HARD_RESET, - "PRL_FLAGS_PORT_PARTNER_HARD_RESET" }, - { PRL_FLAGS_MSG_XMIT, "PRL_FLAGS_MSG_XMIT" }, - { PRL_FLAGS_MSG_RECEIVED, "PRL_FLAGS_MSG_RECEIVED" }, - { PRL_FLAGS_ABORT, "PRL_FLAGS_ABORT" }, - { PRL_FLAGS_CHUNKING, "PRL_FLAGS_CHUNKING" }, -}; - -__maybe_unused static void print_bits(const char *group, - const char *desc, - int value, - struct bit_name *names, - int names_size) -{ - int i; - - CPRINTF("%s %s 0x%x : ", group, desc, value); - for (i = 0; i < names_size; i++) { - if (value & names[i].value) - CPRINTF("%s | ", names[i].name); - value &= ~names[i].value; - } - if (value != 0) - CPRINTF("0x%x", value); - CPRINTF("\n"); -} - -__maybe_unused static void print_flag(const char *group, - int set_or_clear, - int flag) -{ - print_bits(group, set_or_clear ? "Set" : "Clr", flag, flag_bit_names, - ARRAY_SIZE(flag_bit_names)); -} - -/* PD counter definitions */ -#define PD_MESSAGE_ID_COUNT 7 - -/* Size of PDMSG Chunk Buffer */ -#define CHK_BUF_SIZE 7 -#define CHK_BUF_SIZE_BYTES 28 - -/* - * Debug log level - higher number == more log - * Level 0: disabled - * Level 1: not currently used - * Level 2: plus non-ping messages - * Level 3: plus ping packet and PRL states - * - * Note that higher log level causes timing changes and thus may affect - * performance. - */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level prl_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level prl_debug_level = DEBUG_LEVEL_1; -#endif - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Protocol Transmit States (Section 6.11.2.2) */ -enum usb_prl_tx_state { - PRL_TX_PHY_LAYER_RESET, - PRL_TX_WAIT_FOR_MESSAGE_REQUEST, - PRL_TX_LAYER_RESET_FOR_TRANSMIT, - PRL_TX_WAIT_FOR_PHY_RESPONSE, - PRL_TX_SRC_SOURCE_TX, - PRL_TX_SNK_START_AMS, - PRL_TX_SRC_PENDING, - PRL_TX_SNK_PENDING, - PRL_TX_DISCARD_MESSAGE, -}; - -/* Protocol Hard Reset States (Section 6.11.2.4) */ -enum usb_prl_hr_state { - PRL_HR_WAIT_FOR_REQUEST, - PRL_HR_RESET_LAYER, - PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE, - PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE, -}; - -/* Chunked Rx states (Section 6.11.2.1.2) */ -enum usb_rch_state { - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER, - RCH_PASS_UP_MESSAGE, - RCH_PROCESSING_EXTENDED_MESSAGE, - RCH_REQUESTING_CHUNK, - RCH_WAITING_CHUNK, - RCH_REPORT_ERROR, -}; - -/* Chunked Tx states (Section 6.11.2.1.3) */ -enum usb_tch_state { - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE, - TCH_CONSTRUCT_CHUNKED_MESSAGE, - TCH_SENDING_CHUNKED_MESSAGE, - TCH_WAIT_CHUNK_REQUEST, - TCH_MESSAGE_RECEIVED, - TCH_MESSAGE_SENT, - TCH_REPORT_ERROR, -}; - -static const char * const prl_tx_state_names[] = { - [PRL_TX_PHY_LAYER_RESET] = "PRL_TX_PHY_LAYER_RESET", - [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = "PRL_TX_WAIT_FOR_MESSAGE_REQUEST", - [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = "PRL_TX_LAYER_RESET_FOR_TRANSMIT", - [PRL_TX_WAIT_FOR_PHY_RESPONSE] = "PRL_TX_WAIT_FOR_PHY_RESPONSE", - [PRL_TX_SRC_SOURCE_TX] = "PRL_TX_SRC_SOURCE_TX", - [PRL_TX_SNK_START_AMS] = "PRL_TX_SNK_START_AMS", - [PRL_TX_SRC_PENDING] = "PRL_TX_SRC_PENDING", - [PRL_TX_SNK_PENDING] = "PRL_TX_SNK_PENDING", - [PRL_TX_DISCARD_MESSAGE] = "PRL_TX_DISCARD_MESSAGE", -}; - -static const char * const prl_hr_state_names[] = { - [PRL_HR_WAIT_FOR_REQUEST] = "PRL_HR_WAIT_FOR_REQUEST", - [PRL_HR_RESET_LAYER] = "PRL_HR_RESET_LAYER", - [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] - = "PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE", - [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] - = "PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE", -}; - -__maybe_unused static const char * const rch_state_names[] = { - [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] - = "RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER", - [RCH_PASS_UP_MESSAGE] = "RCH_PASS_UP_MESSAGE", - [RCH_PROCESSING_EXTENDED_MESSAGE] = "RCH_PROCESSING_EXTENDED_MESSAGE", - [RCH_REQUESTING_CHUNK] = "RCH_REQUESTING_CHUNK", - [RCH_WAITING_CHUNK] = "RCH_WAITING_CHUNK", - [RCH_REPORT_ERROR] = "RCH_REPORT_ERROR", -}; - -__maybe_unused static const char * const tch_state_names[] = { - [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] - = "TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE", - [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] - = "TCH_WAIT_FOR_TRANSMISSION_COMPLETE", - [TCH_CONSTRUCT_CHUNKED_MESSAGE] = "TCH_CONSTRUCT_CHUNKED_MESSAGE", - [TCH_SENDING_CHUNKED_MESSAGE] = "TCH_SENDING_CHUNKED_MESSAGE", - [TCH_WAIT_CHUNK_REQUEST] = "TCH_WAIT_CHUNK_REQUEST", - [TCH_MESSAGE_RECEIVED] = "TCH_MESSAGE_RECEIVED", - [TCH_MESSAGE_SENT] = "TCH_MESSAGE_SENT", - [TCH_REPORT_ERROR] = "TCH_REPORT_ERROR", -}; - -/* Forward declare full list of states. Index by above enums. */ -static const struct usb_state prl_tx_states[]; -static const struct usb_state prl_hr_states[]; - -__maybe_unused static const struct usb_state rch_states[]; -__maybe_unused static const struct usb_state tch_states[]; - -/* Chunked Rx State Machine Object */ -static struct rx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* PRL_FLAGS */ - uint32_t flags; - /* error to report when moving to rch_report_error state */ - enum pe_error error; -} rch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunked Tx State Machine Object */ -static struct tx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* error to report when moving to tch_report_error state */ - enum pe_error error; -} tch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Reception State Machine Object */ -static struct protocol_layer_rx { - /* received message type */ - enum tcpci_msg_type sop; - /* message ids for all valid port partners */ - int msg_id[NUM_SOP_STAR_TYPES]; -} prl_rx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Transmission State Machine Object */ -static struct protocol_layer_tx { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* last message type we transmitted */ - enum tcpci_msg_type last_xmit_type; - /* message id counters for all 6 port partners */ - uint32_t msg_id_counter[NUM_SOP_STAR_TYPES]; - /* transmit status */ - int xmit_status; -} prl_tx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Hard Reset State Machine Object */ -static struct protocol_hard_reset { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; -} prl_hr[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunking Message Object */ -static struct pd_message { - /* message status flags */ - uint32_t flags; - /* SOP* */ - enum tcpci_msg_type xmit_type; - /* type of message */ - uint8_t msg_type; - /* PD revision */ - enum pd_rev_type rev[NUM_SOP_STAR_TYPES]; - /* Number of 32-bit objects in chk_buf */ - uint16_t data_objs; - /* temp chunk buffer */ - uint32_t tx_chk_buf[CHK_BUF_SIZE]; - uint32_t rx_chk_buf[CHK_BUF_SIZE]; - uint32_t chunk_number_expected; - uint32_t num_bytes_received; -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - /* extended message */ - uint8_t ext; - uint32_t chunk_number_to_send; - uint32_t send_offset; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -} pdmsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -struct extended_msg rx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; -struct extended_msg tx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_construct_message(int port); -static void prl_rx_wait_for_phy_message(const int port, int evt); -static void prl_copy_msg_to_buffer(int port); - -#ifndef CONFIG_USB_PD_REV30 -GEN_NOT_SUPPORTED(PRL_TX_SRC_SOURCE_TX); -#define PRL_TX_SRC_SOURCE_TX PRL_TX_SRC_SOURCE_TX_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PRL_TX_SNK_START_AMS); -#define PRL_TX_SNK_START_AMS PRL_TX_SNK_START_AMS_NOT_SUPPORTED - -GEN_NOT_SUPPORTED(RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -#define RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER \ - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_PASS_UP_MESSAGE); -#define RCH_PASS_UP_MESSAGE RCH_PASS_UP_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_PROCESSING_EXTENDED_MESSAGE); -#define RCH_PROCESSING_EXTENDED_MESSAGE \ - RCH_PROCESSING_EXTENDED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_REQUESTING_CHUNK); -#define RCH_REQUESTING_CHUNK RCH_REQUESTING_CHUNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_WAITING_CHUNK); -#define RCH_WAITING_CHUNK RCH_WAITING_CHUNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_REPORT_ERROR); -#define RCH_REPORT_ERROR RCH_REPORT_ERROR_NOT_SUPPORTED - -GEN_NOT_SUPPORTED(TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -#define TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE \ - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_WAIT_FOR_TRANSMISSION_COMPLETE); -#define TCH_WAIT_FOR_TRANSMISSION_COMPLETE \ - TCH_WAIT_FOR_TRANSMISSION_COMPLETE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_CONSTRUCT_CHUNKED_MESSAGE); -#define TCH_CONSTRUCT_CHUNKED_MESSAGE \ - TCH_CONSTRUCT_CHUNKED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_SENDING_CHUNKED_MESSAGE); -#define TCH_SENDING_CHUNKED_MESSAGE TCH_SENDING_CHUNKED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_WAIT_CHUNK_REQUEST); -#define TCH_WAIT_CHUNK_REQUEST TCH_WAIT_CHUNK_REQUEST_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_MESSAGE_RECEIVED); -#define TCH_MESSAGE_RECEIVED TCH_MESSAGE_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_MESSAGE_SENT); -#define TCH_MESSAGE_SENT TCH_MESSAGE_SENT_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_REPORT_ERROR); -#define TCH_REPORT_ERROR TCH_REPORT_ERROR_NOT_SUPPORTED -#endif /* !CONFIG_USB_PD_REV30 */ - -/* To store the time stamp when TCPC sets TX Complete Success */ -static timestamp_t tcpc_tx_success_ts[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Set the protocol transmit statemachine to a new state. */ -static void set_state_prl_tx(const int port, - const enum usb_prl_tx_state new_state) -{ - set_state(port, &prl_tx[port].ctx, &prl_tx_states[new_state]); -} - -/* Get the protocol transmit statemachine's current state. */ -test_export_static enum usb_prl_tx_state prl_tx_get_state(const int port) -{ - return prl_tx[port].ctx.current - &prl_tx_states[0]; -} - -/* Print the protocol transmit statemachine's current state. */ -static void print_current_prl_tx_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - prl_tx_state_names[prl_tx_get_state(port)]); -} - -/* Set the hard reset statemachine to a new state. */ -static void set_state_prl_hr(const int port, - const enum usb_prl_hr_state new_state) -{ - set_state(port, &prl_hr[port].ctx, &prl_hr_states[new_state]); -} - -/* Get the hard reset statemachine's current state. */ -enum usb_prl_hr_state prl_hr_get_state(const int port) -{ - return prl_hr[port].ctx.current - &prl_hr_states[0]; -} - -/* Print the hard reset statemachine's current state. */ -static void print_current_prl_hr_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - prl_hr_state_names[prl_hr_get_state(port)]); -} - -/* Set the chunked Rx statemachine to a new state. */ -static void set_state_rch(const int port, const enum usb_rch_state new_state) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - set_state(port, &rch[port].ctx, &rch_states[new_state]); -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* Get the chunked Rx statemachine's current state. */ -test_export_static enum usb_rch_state rch_get_state(const int port) -{ - return rch[port].ctx.current - &rch_states[0]; -} - -/* Print the chunked Rx statemachine's current state. */ -static void print_current_rch_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - rch_state_names[rch_get_state(port)]); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/* Set the chunked Tx statemachine to a new state. */ -static void set_state_tch(const int port, const enum usb_tch_state new_state) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - set_state(port, &tch[port].ctx, &tch_states[new_state]); -} - -/* Get the chunked Tx statemachine's current state. */ -test_export_static enum usb_tch_state tch_get_state(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - return tch[port].ctx.current - &tch_states[0]; - else - return 0; -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* Print the chunked Tx statemachine's current state. */ -static void print_current_tch_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - tch_state_names[tch_get_state(port)]); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - -timestamp_t prl_get_tcpc_tx_success_ts(int port) -{ - return tcpc_tx_success_ts[port]; -} - -/* Sets the time stamp when TCPC reports TX success. */ -static void set_tcpc_tx_success_ts(int port) -{ - tcpc_tx_success_ts[port] = get_time(); -} - -void pd_transmit_complete(int port, int status) -{ - if (status == TCPC_TX_COMPLETE_SUCCESS) - set_tcpc_tx_success_ts(port); - prl_tx[port].xmit_status = status; -} - -void pd_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PORT_PARTNER_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PE_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -int prl_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -static void prl_init(int port) -{ - int i; - const struct sm_ctx cleared = {}; - - /* - * flags without PRL_FLAGS_SINK_NG present means we are initially - * in SinkTxOK state - */ - prl_tx[port].flags = 0; - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - typec_select_src_collision_rp(port, SINK_TX_OK); - prl_tx[port].last_xmit_type = TCPCI_MSG_SOP; - prl_tx[port].xmit_status = TCPC_TX_UNSET; - - if (IS_ENABLED(CONFIG_USB_PD_REV30)) { - tch[port].flags = 0; - rch[port].flags = 0; - } - - pdmsg[port].flags = 0; - - prl_hr[port].flags = 0; - - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - pd_timer_disable_range(port, PR_TIMER_RANGE); - - /* Clear state machines and set initial states */ - prl_tx[port].ctx = cleared; - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - rch[port].ctx = cleared; - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - tch[port].ctx = cleared; - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } - - prl_hr[port].ctx = cleared; - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -bool prl_is_busy(int port) -{ -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - return rch_get_state(port) != - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER || - tch_get_state(port) != - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE; -#else - return false; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -} - -void prl_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - prl_debug_level = debug_level; -#endif -} - -void prl_hard_reset_complete(int port) -{ - PRL_HR_SET_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_send_ctrl_msg(int port, - enum tcpci_msg_type type, - enum pd_ctrl_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].data_objs = 0; - tx_emsg[port].len = 0; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - pdmsg[port].ext = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#else - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_send_data_msg(int port, - enum tcpci_msg_type type, - enum pd_data_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - pdmsg[port].ext = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#else - prl_copy_msg_to_buffer(port); - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -void prl_send_ext_data_msg(int port, - enum tcpci_msg_type type, - enum pd_ext_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].ext = 1; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -void prl_set_default_pd_revision(int port) -{ - /* - * Initialize to highest revision supported. If the port or cable - * partner doesn't support this revision, the Protocol Engine will - * lower this value to the revision supported by the partner. - */ - pdmsg[port].rev[TCPCI_MSG_SOP] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_PRIME_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME_PRIME] = PD_REVISION; -} - -void prl_reset_soft(int port) -{ - /* Do not change negotiated PD Revision Specification level */ - local_state[port] = SM_INIT; - - /* Ensure we process the reset quickly */ - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - prl_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (!en) { - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_CTVPD) || - IS_ENABLED(CONFIG_USB_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - local_state[port] = SM_PAUSED; - break; - } - - /* Run Protocol Layer Hard Reset state machine */ - run_state(port, &prl_hr[port].ctx); - - /* - * If the Hard Reset state machine is active, then there is no - * need to execute any other PRL state machines. When the hard - * reset is complete, all PRL state machines will have been - * reset. - */ - if (prl_hr_get_state(port) == PRL_HR_WAIT_FOR_REQUEST) { - - /* Run Protocol Layer Message Reception */ - prl_rx_wait_for_phy_message(port, evt); - - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* - * Run RX Chunked state machine after prl_rx. - * This is what informs the PE of incoming - * message. Its input is prl_rx - */ - run_state(port, &rch[port].ctx); - - /* - * Run TX Chunked state machine before prl_tx - * in case we need to split an extended message - * and prl_tx can send it for us - */ - run_state(port, &tch[port].ctx); - } - - /* Run Protocol Layer Message Tx state machine */ - run_state(port, &prl_tx[port].ctx); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - /* - * Run TX Chunked state machine again after - * prl_tx so we can handle passing TX_COMPLETE - * (or failure) up to PE in a single iteration. - */ - run_state(port, &tch[port].ctx); - } - break; - } -} - -void prl_set_rev(int port, enum tcpci_msg_type type, - enum pd_rev_type rev) -{ - /* We only store revisions for SOP* types. */ - ASSERT(type < NUM_SOP_STAR_TYPES); - - pdmsg[port].rev[type] = rev; -} - -enum pd_rev_type prl_get_rev(int port, enum tcpci_msg_type type) -{ - /* We only store revisions for SOP* types. */ - ASSERT(type < NUM_SOP_STAR_TYPES); - - return pdmsg[port].rev[type]; -} - -static void prl_copy_msg_to_buffer(int port) -{ - /* - * Control Messages will have a length of 0 and - * no need to spend time with the tx_chk_buf - * for this path - */ - if (tx_emsg[port].len == 0) { - pdmsg[port].data_objs = 0; - return; - } - - /* - * Make sure the Policy Engine isn't sending - * more than CHK_BUF_SIZE_BYTES. If so, - * truncate len. This will surely send a - * malformed packet resulting in the port - * partner soft\hard resetting us. - */ - if (tx_emsg[port].len > CHK_BUF_SIZE_BYTES) - tx_emsg[port].len = CHK_BUF_SIZE_BYTES; - - /* Copy message to chunked buffer */ - memset((uint8_t *)pdmsg[port].tx_chk_buf, 0, CHK_BUF_SIZE_BYTES); - memcpy((uint8_t *)pdmsg[port].tx_chk_buf, (uint8_t *)tx_emsg[port].buf, - tx_emsg[port].len); - /* - * Pad length to 4-byte boundary and - * convert to number of 32-bit objects. - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = (tx_emsg[port].len + 3) >> 2; -} - -static __maybe_unused int pdmsg_xmit_type_is_rev30(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - return ((pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) - && (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30)); - else - return 0; -} - -/* Returns true if the SOP port partner operates at PD rev3.0 */ -static bool is_sop_rev30(const int port) -{ - return IS_ENABLED(CONFIG_USB_PD_REV30) && - prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30; -} - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_phy_layer_reset_entry(const int port) -{ - print_current_prl_tx_state(port); - - if (IS_ENABLED(CONFIG_USB_CTVPD) - || IS_ENABLED(CONFIG_USB_VPD)) { - vpd_rx_enable(pd_is_connected(port)); - } else { - /* Note: can't clear PHY messages due to TCPC architecture */ - /* Enable communications*/ - tcpm_set_rx_enable(port, pd_is_connected(port)); - } - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); -} - -static void prl_tx_wait_for_message_request_entry(const int port) -{ - /* No phy layer response is pending */ - prl_tx[port].xmit_status = TCPC_TX_UNSET; - print_current_prl_tx_state(port); -} - -static void prl_tx_wait_for_message_request_run(const int port) -{ - /* Clear any AMS flags and state if we are no longer in an AMS */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && !pe_in_local_ams(port)) { - /* Note PRL_Tx_Src_Sink_Tx is embedded here. */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG)) { - typec_select_src_collision_rp(port, SINK_TX_OK); - typec_update_cc(port); - } - PRL_TX_CLR_FLAG(port, - PRL_FLAGS_SINK_NG | PRL_FLAGS_WAIT_SINK_OK); - } - - /* - * Check if we are starting an AMS and need to wait and/or set the CC - * lines appropriately. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && is_sop_rev30(port) && - pe_in_local_ams(port)) { - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG | - PRL_FLAGS_WAIT_SINK_OK)) { - /* - * If we are already in an AMS then allow the - * multi-message AMS to continue, even if we - * swap power roles. - * - * Fall Through using the current AMS - */ - } else { - /* - * Start of SRC AMS notification received from - * Policy Engine - */ - if (pd_get_power_role(port) == PD_ROLE_SOURCE) { - PRL_TX_SET_FLAG(port, PRL_FLAGS_SINK_NG); - set_state_prl_tx(port, PRL_TX_SRC_SOURCE_TX); - } else { - PRL_TX_SET_FLAG(port, PRL_FLAGS_WAIT_SINK_OK); - set_state_prl_tx(port, PRL_TX_SNK_START_AMS); - } - return; - } - } - - /* Handle non Rev 3.0 or subsequent messages in AMS sequence */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Soft Reset Message Message pending - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (tx_emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) - */ - else { - /* NOTE: PRL_TX_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -static void increment_msgid_counter(int port) -{ - /* If the last message wasn't an SOP* message, no need to increment */ - if (prl_tx[port].last_xmit_type >= NUM_SOP_STAR_TYPES) - return; - - prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] = - (prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] + 1) & - PD_MESSAGE_ID_COUNT; -} - -/* - * PrlTxDiscard - */ -static void prl_tx_discard_message_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* - * Discard queued message - * Note: We differ from spec here, which allows us to not discard on - * incoming SOP' or SOP''. However this would get the TCH out of sync. - * - * prl_tx will be set to this state following message reception in - * prl_rx. So this path will be entered following each rx message. If - * this state is entered, and there is either a message from the PE - * pending, or if a message was passed to the phy and there is either no - * response yet, or it was discarded in the phy layer, then a tx message - * discard event has been detected. - */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT) || - prl_tx[port].xmit_status == TCPC_TX_WAIT || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - increment_msgid_counter(port); - pe_report_discard(port); - } - - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); -} - -#ifdef CONFIG_USB_PD_REV30 -/* - * PrlTxSrcSourceTx - */ -static void prl_tx_src_source_tx_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* Set Rp = SinkTxNG */ - typec_select_src_collision_rp(port, SINK_TX_NG); - typec_update_cc(port); -} - -static void prl_tx_src_source_tx_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* - * Don't clear pending XMIT flag here. Wait until we send so - * we can detect if we dropped this message or not. - */ - set_state_prl_tx(port, PRL_TX_SRC_PENDING); - } -} - -/* - * PrlTxSnkStartAms - */ -static void prl_tx_snk_start_ams_entry(const int port) -{ - print_current_prl_tx_state(port); -} - -static void prl_tx_snk_start_ams_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* - * Don't clear pending XMIT flag here. Wait until we send so - * we can detect if we dropped this message or not. - */ - set_state_prl_tx(port, PRL_TX_SNK_PENDING); - } -} -#endif /* CONFIG_USB_PD_REV30 */ - -/* - * PrlTxLayerResetForTransmit - */ -static void prl_tx_layer_reset_for_transmit_entry(const int port) -{ - print_current_prl_tx_state(port); - - if (pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) { - /* - * This state is only used during soft resets. Reset only the - * matching message type. - * - * From section 6.3.13 Soft Reset Message in the USB PD 3.0 - * v2.0 spec, Soft_Reset Message Shall be targeted at a - * specific entity depending on the type of SOP* Packet used. - */ - prl_tx[port].msg_id_counter[pdmsg[port].xmit_type] = 0; - - /* - * From section 6.11.2.3.2, the MessageID should be cleared - * from the PRL_Rx_Layer_Reset_for_Receive state. However, we - * don't implement a full state machine for PRL RX states so - * clear the MessageID here. - */ - prl_rx[port].msg_id[pdmsg[port].xmit_type] = -1; - } -} - -static void prl_tx_layer_reset_for_transmit_run(const int port) -{ - /* NOTE: PRL_Tx_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); -} - -static uint32_t get_sop_star_header(const int port) -{ - const int is_sop_packet = pdmsg[port].xmit_type == TCPCI_MSG_SOP; - int ext; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - ext = pdmsg[port].ext; -#else - ext = 0; -#endif - - /* SOP vs SOP'/SOP" headers are different. Replace fields as needed */ - return PD_HEADER( - pdmsg[port].msg_type, - is_sop_packet ? - pd_get_power_role(port) : tc_get_cable_plug(port), - is_sop_packet ? - pd_get_data_role(port) : 0, - prl_tx[port].msg_id_counter[pdmsg[port].xmit_type], - pdmsg[port].data_objs, - pdmsg[port].rev[pdmsg[port].xmit_type], - ext); -} - -static void prl_tx_construct_message(const int port) -{ - /* The header is unused for hard reset, etc. */ - const uint32_t header = pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES ? - get_sop_star_header(port) : 0; - - /* Save SOP* so the correct msg_id_counter can be incremented */ - prl_tx[port].last_xmit_type = pdmsg[port].xmit_type; - - /* Indicate that a tx message is being passed to the phy layer */ - prl_tx[port].xmit_status = TCPC_TX_WAIT; - /* - * PRL_FLAGS_TX_COMPLETE could be set if this function is called before - * the Policy Engine is informed of the previous transmission. Clear the - * flag so that this message can be sent. - */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* - * Pass message to PHY Layer. It handles retries in hardware as the EC - * cannot handle the required timing ~ 1ms (tReceive + tRetry). - * - * Note if we ever start sending large, extendend messages, then we - * should not retry those messages. We do not support that and probably - * never will (since we support chunking). - */ - tcpm_transmit(port, pdmsg[port].xmit_type, header, - pdmsg[port].tx_chk_buf); -} - -/* - * PrlTxWaitForPhyResponse - */ -static void prl_tx_wait_for_phy_response_entry(const int port) -{ - print_current_prl_tx_state(port); - - pd_timer_enable(port, PR_TIMER_TCPC_TX_TIMEOUT, PD_T_TCPC_TX_TIMEOUT); -} - -static void prl_tx_wait_for_phy_response_run(const int port) -{ - /* Wait until TX is complete */ - - /* - * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED - * when a GoodCRC containing an incorrect MessageID is received. - * This condition satisfies the PRL_Tx_Match_MessageID state - * requirement. - */ - - if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) { - /* NOTE: PRL_TX_Message_Sent State embedded here. */ - /* Increment messageId counter */ - increment_msgid_counter(port); - - /* Inform Policy Engine Message was sent */ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_COMPLETE); - else - pe_message_sent(port); - - /* - * This event reduces the time of informing the policy engine of - * the transmission by one state machine cycle - */ - task_wake(PD_PORT_TO_TASK_ID(port)); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } else if (pd_timer_is_expired(port, PR_TIMER_TCPC_TX_TIMEOUT) || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED) { - /* - * NOTE: PRL_Tx_Transmission_Error State embedded - * here. - */ - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* - * State tch_wait_for_transmission_complete will - * inform policy engine of error - */ - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_ERROR); - } else { - /* Report Error To Policy Engine */ - pe_report_error(port, ERR_TCH_XMIT, - prl_tx[port].last_xmit_type); - } - - /* Increment message id counter */ - increment_msgid_counter(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } -} - -static void prl_tx_wait_for_phy_response_exit(const int port) -{ - pd_timer_disable(port, PR_TIMER_TCPC_TX_TIMEOUT); -} - -/* Source Protocol Layer Message Transmission */ -/* - * PrlTxSrcPending - */ -static void prl_tx_src_pending_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* Start SinkTxTimer */ - pd_timer_enable(port, PR_TIMER_SINK_TX, PD_T_SINK_TX); -} - -static void prl_tx_src_pending_run(const int port) -{ - if (pd_timer_is_expired(port, PR_TIMER_SINK_TX)) { - /* - * We clear the pending XMIT flag here right before we send so - * we can detect if we discarded this message or not - */ - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - /* - * Soft Reset Message pending & - * SinkTxTimer timeout - */ - if ((tx_emsg[port].len == 0) && - (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* Message pending (except Soft Reset) & - * SinkTxTimer timeout - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -static void prl_tx_src_pending_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_SINK_TX); -} - -/* - * PrlTxSnkPending - */ -static void prl_tx_snk_pending_entry(const int port) -{ - print_current_prl_tx_state(port); -} - -static void prl_tx_snk_pending_run(const int port) -{ - bool start_tx = false; - - /* - * Wait unit the SRC applies SINK_TX_OK so we can transmit. In FRS mode, - * don't wait for SINK_TX_OK since either the source (and Rp) could be - * gone or the TCPC CC_STATUS update time could be too long to meet - * tFRSwapInit. - */ - if (pe_in_frs_mode(port)) { - /* shortcut to save some i2c_xfer calls on the FRS path. */ - start_tx = true; - } else { - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - start_tx = (cc1 == TYPEC_CC_VOLT_RP_3_0 || - cc2 == TYPEC_CC_VOLT_RP_3_0); - } - if (start_tx) { - /* - * We clear the pending XMIT flag here right before we send so - * we can detect if we discarded this message or not - */ - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - /* - * Soft Reset Message Message pending & - * Rp = SinkTxOk - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (tx_emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) & - * Rp = SinkTxOk - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - return; - } -} - -/* Hard Reset Operation */ -void prl_hr_send_msg_to_phy(const int port) -{ - /* Header is not used for hard reset */ - const uint32_t header = 0; - - pdmsg[port].xmit_type = TCPCI_MSG_TX_HARD_RESET; - - /* - * These flags could be set if this function is called before the - * Policy Engine is informed of the previous transmission. Clear the - * flags so that this message can be sent. - */ - prl_tx[port].xmit_status = TCPC_TX_UNSET; - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* Pass message to PHY Layer */ - tcpm_transmit(port, pdmsg[port].xmit_type, header, - pdmsg[port].tx_chk_buf); -} - -static void prl_hr_wait_for_request_entry(const int port) -{ - print_current_prl_hr_state(port); - - prl_hr[port].flags = 0; -} - -static void prl_hr_wait_for_request_run(const int port) -{ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET | - PRL_FLAGS_PORT_PARTNER_HARD_RESET)) - set_state_prl_hr(port, PRL_HR_RESET_LAYER); -} - -/* - * PrlHrResetLayer - */ -static void prl_hr_reset_layer_entry(const int port) -{ - int i; - - print_current_prl_hr_state(port); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - tch[port].flags = 0; - rch[port].flags = 0; - } - - pdmsg[port].flags = 0; - - /* Hard reset resets messageIDCounters for all TX types */ - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_CTVPD) || - IS_ENABLED(CONFIG_USB_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - /* - * PD r3.0 v2.0, ss6.2.1.1.5: - * After a physical or logical (USB Type-C Error Recovery) Attach, a - * Port discovers the common Specification Revision level between itself - * and its Port Partner and/or the Cable Plug(s), and uses this - * Specification Revision level until a Detach, Hard Reset or Error - * Recovery happens. - * - * This covers the Hard Reset case. - */ - prl_set_default_pd_revision(port); - - /* Inform the AP of Hard Reset */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) - pd_notify_event(port, PD_STATUS_EVENT_HARD_RESET); - - /* - * Protocol Layer message transmission transitions to - * PRL_Tx_Wait_For_Message_Request state. - */ - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - - return; -} - -static void prl_hr_reset_layer_run(const int port) -{ - /* - * Protocol Layer reset Complete & - * Hard Reset was initiated by Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET)) { - /* - * Request PHY to perform a Hard Reset. Note - * PRL_HR_Request_Reset state is embedded here. - */ - prl_hr_send_msg_to_phy(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); - } - /* - * Protocol Layer reset complete & - * Hard Reset was initiated by Port Partner - */ - else { - /* Inform Policy Engine of the Hard Reset */ - pe_got_hard_reset(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - } -} - -/* - * PrlHrWaitForPhyHardResetComplete - */ -static void prl_hr_wait_for_phy_hard_reset_complete_entry(const int port) -{ - print_current_prl_hr_state(port); - - /* Start HardResetCompleteTimer */ - pd_timer_enable(port, PR_TIMER_HARD_RESET_COMPLETE, - PD_T_PS_HARD_RESET); -} - -static void prl_hr_wait_for_phy_hard_reset_complete_run(const int port) -{ - /* - * Wait for hard reset from PHY - * or timeout - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE) || - pd_timer_is_expired(port, PR_TIMER_HARD_RESET_COMPLETE)) { - /* PRL_HR_PHY_Hard_Reset_Requested */ - - /* Inform Policy Engine Hard Reset was sent */ - pe_hard_reset_sent(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - - return; - } -} - -static void prl_hr_wait_for_phy_hard_reset_complete_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_HARD_RESET_COMPLETE); -} - -/* - * PrlHrWaitForPeHardResetComplete - */ -static void prl_hr_wait_for_pe_hard_reset_complete_entry(const int port) -{ - print_current_prl_hr_state(port); -} - -static void prl_hr_wait_for_pe_hard_reset_complete_run(const int port) -{ - /* - * Wait for Hard Reset complete indication from Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE)) - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -static void prl_hr_wait_for_pe_hard_reset_complete_exit(const int port) -{ - /* Exit from Hard Reset */ - - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } -} - -static void copy_chunk_to_ext(int port) -{ - /* Calculate number of bytes */ - pdmsg[port].num_bytes_received = - (PD_HEADER_CNT(rx_emsg[port].header) * 4); - - /* Copy chunk into extended message */ - memcpy((uint8_t *)rx_emsg[port].buf, (uint8_t *)pdmsg[port].rx_chk_buf, - pdmsg[port].num_bytes_received); - - /* Set extended message length */ - rx_emsg[port].len = pdmsg[port].num_bytes_received; -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* - * Chunked Rx State Machine - */ -/* - * RchWaitForMessageFromProtocolLayer - */ -static void rch_wait_for_message_from_protocol_layer_entry(const int port) -{ - print_current_rch_state(port); - - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - rch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void rch_wait_for_message_from_protocol_layer_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - /* - * Are we communicating with a PD3.0 device and is - * this an extended message? - */ - if (pdmsg_xmit_type_is_rev30(port) - && PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exhdr = - GET_EXT_HEADER(*pdmsg[port].rx_chk_buf); - uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr); - - /* - * Received Extended Message & - * (Chunking = 1 & Chunked = 1) - */ - if ((RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) && - chunked) { - /* - * RCH_Processing_Extended_Message first chunk - * entry processing embedded here - * - * This is the first chunk: - * Set Chunk_number_expected = 0 and - * Num_Bytes_Received = 0 - */ - pdmsg[port].chunk_number_expected = 0; - pdmsg[port].num_bytes_received = 0; - pdmsg[port].msg_type = - PD_HEADER_TYPE(rx_emsg[port].header); - - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - /* - * (Received Extended Message & - * (Chunking = 0 & Chunked = 0)) - */ - else if (!RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING) && - !chunked) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Chunked != Chunking - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - } - /* - * Received Non-Extended Message - */ - else if (!PD_HEADER_EXT(rx_emsg[port].header)) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Received an Extended Message while communicating at a - * revision lower than PD3.0 - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - } -} - -/* - * RchPassUpMessage - */ -static void rch_pass_up_message_entry(const int port) -{ - print_current_rch_state(port); - - /* Pass Message to Policy Engine */ - pe_message_received(port); - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * RchProcessingExtendedMessage - */ -static void rch_processing_extended_message_entry(const int port) -{ - print_current_rch_state(port); -} - -static void rch_processing_extended_message_run(const int port) -{ - uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); - uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr); - uint32_t byte_num; - - /* - * Abort Flag Set - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - /* - * If expected Chunk Number: - * Append data to Extended_Message_Buffer - * Increment Chunk_number_Expected - * Adjust Num Bytes Received - */ - else if (chunk_num == pdmsg[port].chunk_number_expected) { - byte_num = data_size - pdmsg[port].num_bytes_received; - - if (byte_num >= PD_MAX_EXTENDED_MSG_CHUNK_LEN) - byte_num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; - - /* Make sure extended message buffer does not overflow */ - if (pdmsg[port].num_bytes_received + - byte_num > EXTENDED_BUFFER_SIZE) { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - return; - } - - /* Append data */ - /* Add 2 to chk_buf to skip over extended message header */ - memcpy(((uint8_t *)rx_emsg[port].buf + - pdmsg[port].num_bytes_received), - (uint8_t *)pdmsg[port].rx_chk_buf + 2, - byte_num); - /* increment chunk number expected */ - pdmsg[port].chunk_number_expected++; - /* adjust num bytes received */ - pdmsg[port].num_bytes_received += byte_num; - - /* Was that the last chunk? */ - if (pdmsg[port].num_bytes_received >= data_size) { - rx_emsg[port].len = pdmsg[port].num_bytes_received; - /* Pass Message to Policy Engine */ - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Message not Complete - */ - else - set_state_rch(port, RCH_REQUESTING_CHUNK); - } - /* - * Unexpected Chunk Number - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -/* - * RchRequestingChunk - */ -static void rch_requesting_chunk_entry(const int port) -{ - print_current_rch_state(port); - - /* - * Send Chunk Request to Protocol Layer - * with chunk number = Chunk_Number_Expected - */ - pdmsg[port].tx_chk_buf[0] = PD_EXT_HEADER( - pdmsg[port].chunk_number_expected, - 1, /* Request Chunk */ - 0 /* Data Size */ - ); - - pdmsg[port].data_objs = 1; - pdmsg[port].ext = 1; - pdmsg[port].xmit_type = prl_rx[port].sop; - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX); -} - -static void rch_requesting_chunk_run(const int port) -{ - /* - * Message Transmitted received from Protocol Layer - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_rch(port, RCH_WAITING_CHUNK); - } else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - /* Transmission Error from Protocol Layer detetected */ - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } else if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - /* - * It is possible to have both message received and the chunk - * request transmit complete before a full PRL SM run. But, the - * PRL_RX state machine runs prior to RCH, but before PRL_TX, so - * PRL_FLAGS_MSG_RECEIVED can be set without - * PRL_FLAGS_TX_COMPLETE set at this point (though it will be - * set as soon as PRL_TX is executed next. - */ - set_state_rch(port, RCH_WAITING_CHUNK); - } -} - -/* - * RchWaitingChunk - */ -static void rch_waiting_chunk_entry(const int port) -{ - print_current_rch_state(port); - - /* - * Start ChunkSenderResponseTimer - */ - pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_RESPONSE, - PD_T_CHUNK_SENDER_RESPONSE); -} - -static void rch_waiting_chunk_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - /* - * Because of the 5 msec tick time, it is possible to have both - * msg_received and tx_complete flags set for a given PRL sm - * run. Since prl_rx runs prior to the tx state machines, clear - * the tx_complete flag as the next chunk has already been - * received. - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* - * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error - * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED - * will be cleared in rch_report_error state. - */ - - if (PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exhdr = - GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - /* - * Other Message Received from Protocol Layer - */ - if (PD_EXT_HEADER_REQ_CHUNK(exhdr) || - !PD_EXT_HEADER_CHUNKED(exhdr)) { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - /* - * Chunk response Received from Protocol Layer - */ - else { - /* - * No error was detected, so clear - * PRL_FLAGS_MSG_RECEIVED flag. - */ - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - } - } - /* - * ChunkSenderResponseTimer Timeout - */ - else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_RESPONSE)) { - rch[port].error = ERR_RCH_CHUNK_WAIT_TIMEOUT; - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -static void rch_waiting_chunk_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_RESPONSE); -} - -/* - * RchReportError - */ -static void rch_report_error_entry(const int port) -{ - print_current_rch_state(port); - - /* - * If the state was entered because a message was received, - * this message is passed to the Policy Engine. - */ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - /* Pass Message to Policy Engine */ - pe_message_received(port); - /* Report error */ - pe_report_error(port, ERR_RCH_MSG_REC, prl_rx[port].sop); - } else { - pe_report_error(port, rch[port].error, prl_rx[port].sop); - } -} - -static void rch_report_error_run(const int port) -{ - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * Chunked Tx State Machine - */ - -/* - * TchWaitForMessageRequestFromPe - */ -static void tch_wait_for_message_request_from_pe_entry(const int port) -{ - print_current_tch_state(port); - - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - tch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void tch_wait_for_message_request_from_pe_run(const int port) -{ - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer - * & Abort Supported - * - * Discard the Message - */ - if (rch_get_state(port) != - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } else { - /* - * Extended Message Request & Chunking - */ - if (pdmsg_xmit_type_is_rev30(port) - && pdmsg[port].ext - && TCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) { - /* - * NOTE: TCH_Prepare_To_Send_Chunked_Message - * embedded here. - */ - pdmsg[port].send_offset = 0; - pdmsg[port].chunk_number_to_send = 0; - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } else - /* - * Non-Extended Message Request - */ - { - /* NOTE: TCH_Pass_Down_Message embedded here */ - prl_copy_msg_to_buffer(port); - - /* Pass Message to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - set_state_tch(port, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE); - } - } - } -} - -/* - * TchWaitForTransmissionComplete - */ -static void tch_wait_for_transmission_complete_entry(const int port) -{ - print_current_tch_state(port); -} - -static void tch_wait_for_transmission_complete_run(const int port) -{ - /* - * Inform Policy Engine that Message was sent. - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_tch(port, TCH_MESSAGE_SENT); - return; - } - /* - * Inform Policy Engine of Tx Error - */ - else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_ERROR); - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - return; - } - /* - * A message was received while TCH is waiting for the phy to complete - * sending a tx message. - * - * Because of our prl_sm architecture and I2C access delays for TCPCs, - * it's possible to have a message received and the prl_tx state not be - * in its default waiting state. To avoid a false protocol error, only - * jump to TCH_MESSAGE_RECEIVED if the phy layer has not indicated that - * the tx message was sent successfully. - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED) && - prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } -} - -/* - * TchConstructChunkedMessage - */ -static void tch_construct_chunked_message_entry(const int port) -{ - uint16_t *ext_hdr; - uint8_t *data; - uint16_t num; - - print_current_tch_state(port); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - /* Prepare to copy chunk into chk_buf */ - - ext_hdr = (uint16_t *)pdmsg[port].tx_chk_buf; - data = ((uint8_t *)pdmsg[port].tx_chk_buf + 2); - num = tx_emsg[port].len - pdmsg[port].send_offset; - - if (num > PD_MAX_EXTENDED_MSG_CHUNK_LEN) - num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; - - /* Set the chunks extended header */ - *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send, - 0, /* Chunk Request */ - tx_emsg[port].len); - - /* Copy the message chunk into chk_buf */ - memset(data, 0, 28); - memcpy(data, tx_emsg[port].buf + pdmsg[port].send_offset, num); - pdmsg[port].send_offset += num; - - /* - * Add in 2 bytes for extended header - * pad out to 4-byte boundary - * convert to number of 4-byte words - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = (num + 2 + 3) >> 2; - - /* Pass message chunk to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -} - -static void tch_construct_chunked_message_run(const int port) -{ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - else - set_state_tch(port, TCH_SENDING_CHUNKED_MESSAGE); -} - -/* - * TchSendingChunkedMessage - */ -static void tch_sending_chunked_message_entry(const int port) -{ - print_current_tch_state(port); -} - -static void tch_sending_chunked_message_run(const int port) -{ - /* - * Transmission Error - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } - /* - * Message Transmitted from Protocol Layer & - * Last Chunk - */ - else if (tx_emsg[port].len == pdmsg[port].send_offset) - set_state_tch(port, TCH_MESSAGE_SENT); - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * Message Transmitted from Protocol Layer & - * Not Last Chunk - */ - else - set_state_tch(port, TCH_WAIT_CHUNK_REQUEST); -} - -/* - * TchWaitChunkRequest - */ -static void tch_wait_chunk_request_entry(const int port) -{ - print_current_tch_state(port); - - /* Increment Chunk Number to Send */ - pdmsg[port].chunk_number_to_send++; - /* Start Chunk Sender Request Timer */ - pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_REQUEST, - PD_T_CHUNK_SENDER_REQUEST); -} - -static void tch_wait_chunk_request_run(const int port) -{ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - if (PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exthdr; - - exthdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) { - /* - * Chunk Request Received & - * Chunk Number = Chunk Number to Send - */ - if (PD_EXT_HEADER_CHUNK_NUM(exthdr) == - pdmsg[port].chunk_number_to_send) { - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } - /* - * Chunk Request Received & - * Chunk Number != Chunk Number to Send - */ - else { - tch[port].error = ERR_TCH_CHUNKED; - set_state_tch(port, TCH_REPORT_ERROR); - } - return; - } - } - - /* - * Other message received - */ - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * ChunkSenderRequestTimer timeout - */ - else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_REQUEST)) - set_state_tch(port, TCH_MESSAGE_SENT); -} - -static void tch_wait_chunk_request_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_REQUEST); -} - -/* - * TchMessageReceived - */ -static void tch_message_received_entry(const int port) -{ - print_current_tch_state(port); - - /* Pass message to chunked Rx */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - /* Clear extended message objects */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - pe_report_discard(port); - } - pdmsg[port].data_objs = 0; -} - -static void tch_message_received_run(const int port) -{ - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchMessageSent - */ -static void tch_message_sent_entry(const int port) -{ - print_current_tch_state(port); - - /* Tell PE message was sent */ - pe_message_sent(port); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - * MUST be checked after notifying PE - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - - - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchReportError - */ -static void tch_report_error_entry(const int port) -{ - print_current_tch_state(port); - - /* Report Error To Policy Engine */ - pe_report_error(port, tch[port].error, prl_tx[port].last_xmit_type); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - * MUST be checked after notifying PE - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/* - * Protocol Layer Message Reception State Machine - */ -static void prl_rx_wait_for_phy_message(const int port, int evt) -{ - uint32_t header; - uint8_t type; - uint8_t cnt; - int8_t msid; - - /* - * If PD3, wait for the RX chunk SM to copy the pdmsg into the extended - * buffer before overwriting pdmsg. - */ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) - return; - - /* If we don't have any message, just stop processing now. */ - if (!tcpm_has_pending_message(port) || - tcpm_dequeue_message(port, pdmsg[port].rx_chk_buf, &header)) - return; - - rx_emsg[port].header = header; - type = PD_HEADER_TYPE(header); - cnt = PD_HEADER_CNT(header); - msid = PD_HEADER_ID(header); - prl_rx[port].sop = PD_HEADER_GET_SOP(header); - - /* Make sure an incorrect count doesn't overflow the chunk buffer */ - if (cnt > CHK_BUF_SIZE) - cnt = CHK_BUF_SIZE; - - /* dump received packet content (only dump ping at debug level MAX) */ - if ((prl_debug_level >= DEBUG_LEVEL_2 && type != PD_CTRL_PING) || - prl_debug_level >= DEBUG_LEVEL_3) { - int p; - - ccprintf("C%d: RECV %04x/%d ", port, header, cnt); - for (p = 0; p < cnt; p++) - ccprintf("[%d]%08x ", p, pdmsg[port].rx_chk_buf[p]); - ccprintf("\n"); - } - - /* - * Ignore messages sent to the cable from our - * port partner if we aren't Vconn powered device. - */ - if (!IS_ENABLED(CONFIG_USB_CTVPD) && - !IS_ENABLED(CONFIG_USB_VPD) && - PD_HEADER_GET_SOP(header) != TCPCI_MSG_SOP && - PD_HEADER_PROLE(header) == PD_PLUG_FROM_DFP_UFP) - return; - - /* Handle incoming soft reset as special case */ - if (cnt == 0 && type == PD_CTRL_SOFT_RESET) { - /* Clear MessageIdCounter */ - prl_tx[port].msg_id_counter[prl_rx[port].sop] = 0; - /* Clear stored MessageID value */ - prl_rx[port].msg_id[prl_rx[port].sop] = -1; - - /* Soft Reset occurred */ - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - set_state_rch(port, - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } - - /* - * Inform Policy Engine of Soft Reset. Note perform this after - * performing the protocol layer reset, otherwise we will lose - * the PE's outgoing ACCEPT message to the soft reset. - */ - pe_got_soft_reset(port); - - return; - } - - /* - * Ignore if this is a duplicate message. Stop processing. - */ - if (prl_rx[port].msg_id[prl_rx[port].sop] == msid) - return; - - /* - * Discard any pending tx message if this is - * not a ping message (length must be checked to verify this is a - * control message, rather than data) - */ - if ((cnt > 0) || (type != PD_CTRL_PING)) { - /* - * Note: Spec dictates that we always go into - * PRL_Tx_Discard_Message upon receivng a message. However, due - * to our TCPC architecture we may be receiving a transmit - * complete at the same time as a response so only do this if a - * message is pending. - */ - if (prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS || - PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) - set_state_prl_tx(port, PRL_TX_DISCARD_MESSAGE); - } - - /* Store Message Id */ - prl_rx[port].msg_id[prl_rx[port].sop] = msid; - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* RTR Chunked Message Router States. */ - /* - * Received Ping from Protocol Layer - */ - if (cnt == 0 && type == PD_CTRL_PING) { - /* NOTE: RTR_PING State embedded here. */ - rx_emsg[port].len = 0; - pe_message_received(port); - return; - } - /* - * Message (not Ping) Received from - * Protocol Layer & Doing Tx Chunks - * - * Also, handle the case where a message has been - * queued for sending but a message is received before - * tch_wait_for_message_request_from_pe has been run - */ - else if (tch_get_state(port) != - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE || - TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* NOTE: RTR_TX_CHUNKS State embedded here. */ - /* - * Send Message to Tx Chunk - * Chunk State Machine - */ - TCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - /* - * Message (not Ping) Received from - * Protocol Layer & Not Doing Tx Chunks - */ - else { - /* NOTE: RTR_RX_CHUNKS State embedded here. */ - /* - * Send Message to Rx - * Chunk State Machine - */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - } else { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - /* Send message to Policy Engine */ - pe_message_received(port); - } - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* All necessary Protocol Transmit States (Section 6.11.2.2) */ -static __const_data const struct usb_state prl_tx_states[] = { - [PRL_TX_PHY_LAYER_RESET] = { - .entry = prl_tx_phy_layer_reset_entry, - }, - [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = { - .entry = prl_tx_wait_for_message_request_entry, - .run = prl_tx_wait_for_message_request_run, - }, - [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = { - .entry = prl_tx_layer_reset_for_transmit_entry, - .run = prl_tx_layer_reset_for_transmit_run, - }, - [PRL_TX_WAIT_FOR_PHY_RESPONSE] = { - .entry = prl_tx_wait_for_phy_response_entry, - .run = prl_tx_wait_for_phy_response_run, - .exit = prl_tx_wait_for_phy_response_exit, - }, -#ifdef CONFIG_USB_PD_REV30 - [PRL_TX_SRC_SOURCE_TX] = { - .entry = prl_tx_src_source_tx_entry, - .run = prl_tx_src_source_tx_run, - }, - [PRL_TX_SNK_START_AMS] = { - .entry = prl_tx_snk_start_ams_entry, - .run = prl_tx_snk_start_ams_run, - }, -#endif /* CONFIG_USB_PD_REV30 */ - [PRL_TX_SRC_PENDING] = { - .entry = prl_tx_src_pending_entry, - .run = prl_tx_src_pending_run, - .exit = prl_tx_src_pending_exit, - }, - [PRL_TX_SNK_PENDING] = { - .entry = prl_tx_snk_pending_entry, - .run = prl_tx_snk_pending_run, - }, - [PRL_TX_DISCARD_MESSAGE] = { - .entry = prl_tx_discard_message_entry, - }, -}; - -/* All necessary Protocol Hard Reset States (Section 6.11.2.4) */ -static __const_data const struct usb_state prl_hr_states[] = { - [PRL_HR_WAIT_FOR_REQUEST] = { - .entry = prl_hr_wait_for_request_entry, - .run = prl_hr_wait_for_request_run, - }, - [PRL_HR_RESET_LAYER] = { - .entry = prl_hr_reset_layer_entry, - .run = prl_hr_reset_layer_run, - }, - [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = { - .entry = prl_hr_wait_for_phy_hard_reset_complete_entry, - .run = prl_hr_wait_for_phy_hard_reset_complete_run, - .exit = prl_hr_wait_for_phy_hard_reset_complete_exit, - }, - [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = { - .entry = prl_hr_wait_for_pe_hard_reset_complete_entry, - .run = prl_hr_wait_for_pe_hard_reset_complete_run, - .exit = prl_hr_wait_for_pe_hard_reset_complete_exit, - }, -}; - -/* All necessary Chunked Rx states (Section 6.11.2.1.2) */ -__maybe_unused static const struct usb_state rch_states[] = { -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = { - .entry = rch_wait_for_message_from_protocol_layer_entry, - .run = rch_wait_for_message_from_protocol_layer_run, - }, - [RCH_PASS_UP_MESSAGE] = { - .entry = rch_pass_up_message_entry, - }, - [RCH_PROCESSING_EXTENDED_MESSAGE] = { - .entry = rch_processing_extended_message_entry, - .run = rch_processing_extended_message_run, - }, - [RCH_REQUESTING_CHUNK] = { - .entry = rch_requesting_chunk_entry, - .run = rch_requesting_chunk_run, - }, - [RCH_WAITING_CHUNK] = { - .entry = rch_waiting_chunk_entry, - .run = rch_waiting_chunk_run, - .exit = rch_waiting_chunk_exit, - }, - [RCH_REPORT_ERROR] = { - .entry = rch_report_error_entry, - .run = rch_report_error_run, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; - -/* All necessary Chunked Tx states (Section 6.11.2.1.3) */ -__maybe_unused static const struct usb_state tch_states[] = { -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = { - .entry = tch_wait_for_message_request_from_pe_entry, - .run = tch_wait_for_message_request_from_pe_run, - }, - [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = { - .entry = tch_wait_for_transmission_complete_entry, - .run = tch_wait_for_transmission_complete_run, - }, - [TCH_CONSTRUCT_CHUNKED_MESSAGE] = { - .entry = tch_construct_chunked_message_entry, - .run = tch_construct_chunked_message_run, - }, - [TCH_SENDING_CHUNKED_MESSAGE] = { - .entry = tch_sending_chunked_message_entry, - .run = tch_sending_chunked_message_run, - }, - [TCH_WAIT_CHUNK_REQUEST] = { - .entry = tch_wait_chunk_request_entry, - .run = tch_wait_chunk_request_run, - .exit = tch_wait_chunk_request_exit, - }, - [TCH_MESSAGE_RECEIVED] = { - .entry = tch_message_received_entry, - .run = tch_message_received_run, - }, - [TCH_MESSAGE_SENT] = { - .entry = tch_message_sent_entry, - }, - [TCH_REPORT_ERROR] = { - .entry = tch_report_error_entry, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; - -#ifdef TEST_BUILD - -const struct test_sm_data test_prl_sm_data[] = { - { - .base = prl_tx_states, - .size = ARRAY_SIZE(prl_tx_states), - .names = prl_tx_state_names, - .names_size = ARRAY_SIZE(prl_tx_state_names), - }, - { - .base = prl_hr_states, - .size = ARRAY_SIZE(prl_hr_states), - .names = prl_hr_state_names, - .names_size = ARRAY_SIZE(prl_hr_state_names), - }, -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - { - .base = rch_states, - .size = ARRAY_SIZE(rch_states), - .names = rch_state_names, - .names_size = ARRAY_SIZE(rch_state_names), - }, - { - .base = tch_states, - .size = ARRAY_SIZE(tch_states), - .names = tch_state_names, - .names_size = ARRAY_SIZE(tch_state_names), - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; -BUILD_ASSERT(ARRAY_SIZE(prl_tx_states) == ARRAY_SIZE(prl_tx_state_names)); -BUILD_ASSERT(ARRAY_SIZE(prl_hr_states) == ARRAY_SIZE(prl_hr_state_names)); -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -BUILD_ASSERT(ARRAY_SIZE(rch_states) == ARRAY_SIZE(rch_state_names)); -BUILD_ASSERT(ARRAY_SIZE(tch_states) == ARRAY_SIZE(tch_state_names)); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -const int test_prl_sm_data_size = ARRAY_SIZE(test_prl_sm_data); -#endif diff --git a/common/usbc/usb_retimer_fw_update.c b/common/usbc/usb_retimer_fw_update.c deleted file mode 100644 index 1ff198c78f..0000000000 --- a/common/usbc/usb_retimer_fw_update.c +++ /dev/null @@ -1,189 +0,0 @@ -/* 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 <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "hooks.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_tc_sm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -/* - * Retimer firmware update is initiated by AP. - * The operations requested by AP are: - * 0 - USB_RETIMER_FW_UPDATE_QUERY_PORT - * 1 - USB_RETIMER_FW_UPDATE_SUSPEND_PD - * 2 - USB_RETIMER_FW_UPDATE_RESUME_PD - * 3 - USB_RETIMER_FW_UPDATE_GET_MUX - * 4 - USB_RETIMER_FW_UPDATE_SET_USB - * 5 - USB_RETIMER_FW_UPDATE_SET_SAFE - * 6 - USB_RETIMER_FW_UPDATE_SET_TBT - * 7 - USB_RETIMER_FW_UPDATE_DISCONNECT - * - * Operation 0 is processed immediately. - * Operations 1 to 7 are deferred and processed inside tc_run(). - * Operations 1/2/3 can be processed any time; while 4/5/6/7 have - * to be processed when PD task is suspended. - * Two TC flags are created for this situation. - * If Op 1/2/3 is received, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN - * is set, PD task will be waken up and process it. - * If 4/5/6/7 is received, TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN is - * set, PD task should be in suspended mode and process it. - * - */ - -#define SUSPEND 1 -#define RESUME 0 - -/* Track current port AP requested to update retimer firmware */ -static int cur_port; -static int last_op; /* Operation received from AP via ACPI_WRITE */ -/* Operation result returned to ACPI_READ */ -static int last_result; - -int usb_retimer_fw_update_get_result(void) -{ - int result = 0; - - switch (last_op) { - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - if (last_result == USB_RETIMER_FW_UPDATE_ERR) { - result = last_result; - break; - } - /* fall through */ - case USB_RETIMER_FW_UPDATE_RESUME_PD: - result = pd_is_port_enabled(cur_port); - break; - case USB_RETIMER_FW_UPDATE_QUERY_PORT: - result = usb_mux_retimer_fw_update_port_info(); - break; - case USB_RETIMER_FW_UPDATE_GET_MUX: - case USB_RETIMER_FW_UPDATE_SET_USB: - case USB_RETIMER_FW_UPDATE_SET_SAFE: - case USB_RETIMER_FW_UPDATE_SET_TBT: - case USB_RETIMER_FW_UPDATE_DISCONNECT: - result = last_result; - break; - default: - break; - } - - return result; -} - -static void deferred_pd_suspend(void) -{ - pd_set_suspend(cur_port, SUSPEND); -} -DECLARE_DEFERRED(deferred_pd_suspend); - -static inline mux_state_t retimer_fw_update_usb_mux_get(int port) -{ - return usb_mux_get(port) & USB_RETIMER_FW_UPDATE_MUX_MASK; -} - -void usb_retimer_fw_update_process_op_cb(int port) -{ - switch (last_op) { - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - last_result = 0; - /* - * Do not perform retimer firmware update process - * if battery is not present, or battery level is low. - */ - if (!pd_firmware_upgrade_check_power_readiness(port)) { - last_result = USB_RETIMER_FW_UPDATE_ERR; - break; - } - - /* - * If the port has entered low power mode, the PD task - * is paused and will not complete processing of - * pd_set_suspend(). Move pd_set_suspend() into a deferred - * call so that it runs from the HOOKS task and can generate - * a wake event to the PD task and enter suspended mode. - */ - hook_call_deferred(&deferred_pd_suspend_data, 0); - break; - case USB_RETIMER_FW_UPDATE_RESUME_PD: - pd_set_suspend(port, RESUME); - break; - case USB_RETIMER_FW_UPDATE_GET_MUX: - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_USB: - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, - USB_SWITCH_CONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_SAFE: - usb_mux_set_safe_mode(port); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_TBT: - usb_mux_set(port, USB_PD_MUX_TBT_COMPAT_ENABLED, - USB_SWITCH_CONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_DISCONNECT: - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - default: - break; - } -} - -void usb_retimer_fw_update_process_op(int port, int op) -{ - ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); - - /* - * TODO(b/179220036): check not overlapping requests; - * not change cur_port if retimer scan is in progress - */ - last_op = op; - - switch (op) { - case USB_RETIMER_FW_UPDATE_QUERY_PORT: - break; - /* Operations can't be processed in ISR, defer to later */ - case USB_RETIMER_FW_UPDATE_GET_MUX: - last_result = USB_RETIMER_FW_UPDATE_INVALID_MUX; - tc_usb_firmware_fw_update_run(port); - break; - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - case USB_RETIMER_FW_UPDATE_RESUME_PD: - cur_port = port; - tc_usb_firmware_fw_update_run(port); - break; - case USB_RETIMER_FW_UPDATE_SET_USB: - case USB_RETIMER_FW_UPDATE_SET_SAFE: - case USB_RETIMER_FW_UPDATE_SET_TBT: - case USB_RETIMER_FW_UPDATE_DISCONNECT: - if (pd_is_port_enabled(port)) { - last_result = USB_RETIMER_FW_UPDATE_ERR; - } else { - last_result = USB_RETIMER_FW_UPDATE_INVALID_MUX; - tc_usb_firmware_fw_update_limited_run(port); - } - break; - default: - break; - } -} diff --git a/common/usbc/usb_sm.c b/common/usbc/usb_sm.c deleted file mode 100644 index 04b7193c0f..0000000000 --- a/common/usbc/usb_sm.c +++ /dev/null @@ -1,202 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "console.h" -#include "stdbool.h" -#include "task.h" -#include "usb_pd.h" -#include "usb_sm.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Private structure (to this file) used to track state machine context */ -struct internal_ctx { - usb_state_ptr last_entered; - uint32_t running : 1; - uint32_t enter : 1; - uint32_t exit : 1; -}; -BUILD_ASSERT(sizeof(struct internal_ctx) == - member_size(struct sm_ctx, internal)); - -/* Gets the first shared parent state between a and b (inclusive) */ -static usb_state_ptr shared_parent_state(usb_state_ptr a, usb_state_ptr b) -{ - const usb_state_ptr orig_b = b; - - /* There are no common ancestors */ - if (b == NULL) - return NULL; - - /* This assumes that both A and B are NULL terminated without cycles */ - while (a != NULL) { - /* We found a match return */ - if (a == b) - return a; - - /* - * Otherwise, increment b down the list for comparison until we - * run out, then increment a and start over on b for comparison - */ - if (b->parent == NULL) { - a = a->parent; - b = orig_b; - } else { - b = b->parent; - } - } - - return NULL; -} - -/* - * Call all entry functions of parents before children. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_entry_functions(const int port, - struct internal_ctx *const internal, - const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - call_entry_functions(port, internal, stop, current->parent); - - /* - * If the previous entry function called set_state, then don't enter - * remaining states. - */ - if (!internal->enter) - return; - - /* Track the latest state that was entered, so we can exit properly. */ - internal->last_entered = current; - if (current->entry) - current->entry(port); -} - -/* - * Call all exit functions of children before parents. Note set_state is ignored - * during an exit function. - */ -static void call_exit_functions(const int port, const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - if (current->exit) - current->exit(port); - - call_exit_functions(port, stop, current->parent); -} - -void set_state(const int port, struct sm_ctx *const ctx, - const usb_state_ptr new_state) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - usb_state_ptr last_state; - usb_state_ptr shared_parent; - - /* - * It does not make sense to call set_state in an exit phase of a state - * since we are already in a transition; we would always ignore the - * intended state to transition into. - */ - if (internal->exit) { - CPRINTF("C%d: Ignoring set state to 0x%pP within 0x%pP", - port, new_state, ctx->current); - return; - } - - /* - * Determine the last state that was entered. Normally it is current, - * but we could have called set_state within an entry phase, so we - * shouldn't exit any states that weren't fully entered. - */ - last_state = internal->enter ? internal->last_entered : ctx->current; - - /* We don't exit and re-enter shared parent states */ - shared_parent = shared_parent_state(last_state, new_state); - - /* - * Exit all of the non-common states from the last state. - */ - internal->exit = true; - call_exit_functions(port, shared_parent, last_state); - internal->exit = false; - - ctx->previous = ctx->current; - ctx->current = new_state; - - /* - * Enter all new non-common states. last_entered will contain the last - * state that successfully entered before another set_state was called. - */ - internal->last_entered = NULL; - internal->enter = true; - call_entry_functions(port, internal, shared_parent, ctx->current); - /* - * Setting enter to false ensures that all pending entry calls will be - * skipped (in the case of a parent state calling set_state, which means - * we should not enter any child states) - */ - internal->enter = false; - - /* - * If we set_state while we are running a child state, then stop running - * any remaining parent states. - */ - internal->running = false; - - /* - * Since we are changing states, we want to ensure that we process the - * next state's run method as soon as we can to ensure that we don't - * delay important processing until the next task interval. - */ - if (IS_ENABLED(HAS_TASK_PD_C0)) - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* - * Call all run functions of children before parents. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_run_functions(const int port, - const struct internal_ctx *const internal, - const usb_state_ptr current) -{ - if (!current) - return; - - /* If set_state is called during run, don't call remain functions. */ - if (!internal->running) - return; - - if (current->run) - current->run(port); - - call_run_functions(port, internal, current->parent); -} - -void run_state(const int port, struct sm_ctx *const ctx) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - - internal->running = true; - call_run_functions(port, internal, ctx->current); - internal->running = false; -} diff --git a/common/usbc/usb_tc_ctvpd_sm.c b/common/usbc/usb_tc_ctvpd_sm.c deleted file mode 100644 index a2babe754a..0000000000 --- a/common/usbc/usb_tc_ctvpd_sm.c +++ /dev/null @@ -1,1716 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "vpd_api.h" - -/* USB Type-C CTVPD module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -#define SUPPORT_TIMER_RESET_INIT 0 -#define SUPPORT_TIMER_RESET_REQUEST 1 -#define SUPPORT_TIMER_RESET_COMPLETE 2 - -/** - * This is the Type-C Port object that contains information needed to - * implement a Charge Through VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* - * Time a charge-through port shall wait before it can determine it - * is attached - */ - uint64_t cc_debounce; - /* Time a host port shall wait before it can determine it is attached */ - uint64_t host_cc_debounce; - /* Time a Sink port shall wait before it can determine it is detached - * due to the potential for USB PD signaling on CC as described in - * the state definitions. - */ - uint64_t pd_debounce; - /* Maintains state of billboard device */ - int billboard_presented; - /* - * Time a port shall wait before it can determine it is - * re-attached during the try-wait process. - */ - uint64_t try_wait_debounce; - /* charge-through support timer */ - uint64_t support_timer; - /* reset the charge-through support timer */ - uint8_t support_timer_reset; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; - /* The cc state */ - enum pd_cc_states cc_state; - uint64_t next_role_swap; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_ERROR_RECOVERY, - TC_TRY_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_TRY_WAIT_SRC, - TC_ATTACHED_SRC, - TC_CT_TRY_SNK, - TC_CT_ATTACH_WAIT_UNSUPPORTED, - TC_CT_ATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_VPD, - TC_CT_DISABLED_VPD, - TC_CT_ATTACHED_VPD, - TC_CT_ATTACH_WAIT_VPD, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD_CT_RD, - TC_HOST_OPEN_CT_OPEN, - TC_HOST_RP3_CT_RD, - TC_HOST_RP3_CT_RPU, - TC_HOST_RPU_CT_RD, -}; - -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - - -/* List of human readable state names for console debugging */ -__maybe_unused const char * const tc_state_names[] = { -#ifdef CONFIG_COMMON_RUNTIME - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_TRY_SNK] = "Try.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_TRY_WAIT_SRC] = "TryWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_CT_TRY_SNK] = "CTTry.SNK", - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = "CTAttachWait.Unsupported", - [TC_CT_ATTACHED_UNSUPPORTED] = "CTAttached.Unsupported", - [TC_CT_UNATTACHED_UNSUPPORTED] = "CTUnattached.Unsupported", - [TC_CT_UNATTACHED_VPD] = "CTUnattached.VPD", - [TC_CT_DISABLED_VPD] = "CTDisabled.VPD", - [TC_CT_ATTACHED_VPD] = "CTAttached.VPD", - [TC_CT_ATTACH_WAIT_VPD] = "CTAttachWait.VPD", -#endif -}; - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* Public TypeC functions */ - -enum pd_power_role pd_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role pd_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP matches SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_reset_support_timer(int port) -{ - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST; -} - -/* - * TCPC CC/Rp management - * - * Stub for linking purposes. - * This is not supported for ctvpd, we are never the SOP partner. - */ -void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ -} -int typec_update_cc(int port) -{ - return EC_SUCCESS; -} - -void tc_state_init(int port) -{ - int res = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: init %s", port, res ? "failed" : "ready"); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].billboard_presented = 0; - tc[port].flags = 0; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* Internal Functions */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -int pd_is_connected(int port) -{ - return (get_state_tc(port) == TC_ATTACHED_SNK) || - (get_state_tc(port) == TC_ATTACHED_SRC) || - (get_state_tc(port) == TC_CT_ATTACHED_UNSUPPORTED) || - (get_state_tc(port) == TC_CT_ATTACHED_VPD); -} - -bool pd_is_disconnected(int port) -{ - return !pd_is_connected(port); -} - -/** - * Disabled - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tcpm_init(port) != 0) { - CPRINTS("C%d: restart failed!", port); - return; - } - } - - CPRINTS("C%d: resumed!", port); -} - -void pd_set_suspend(int port, int suspend) -{ - /* - * This shouldn't happen. If it does, we need to send an event to the - * PD task to put the SM into the disabled state. It is not safe to - * directly set_state here since this may be in another task. - */ - assert(false); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - /* Use cc_debounce state variable for error recovery timeout */ - tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY; -} - -static void tc_error_recovery_run(const int port) -{ - if (get_time().val > tc[port].cc_debounce) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Unattached.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_snk_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) - print_current_state(port); - - tc[port].flags &= ~TC_FLAGS_VCONN_ON; - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - int new_cc_state; - int cc1; - int cc2; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) { - set_state_tc(port, TC_ATTACH_WAIT_SNK); - return; - } - - /* Check Charge-Through CCs for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce Charge-Through CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - } - - /* If we are here, Host CC must be open */ - - /* Wait for Charge-Through CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SRC when the state of the Host-side port’s CC pin is - * SNK.Open for tDRP − dcSRC.DRP ∙ tDRP and both of the following - * is detected on the Charge-Through port. - * 1) SNK.Rp state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce - * 2) VBUS is detected - */ - if (vpd_is_ct_vbus_present() && - tc[port].cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].host_cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].host_cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - /* - * This state can only be entered from states AttachWait.SNK - * and Try.SNK. So the Host port is isolated from the - * Charge-Through port. We only need to High-Z the - * Charge-Through ports CC1 and CC2 pins. - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Start Charge-Through support timer */ - tc[port].support_timer_reset = SUPPORT_TIMER_RESET_INIT; - tc[port].support_timer = get_time().val + PD_T_AME; -} - -static void tc_attached_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * Reset the Charge-Through Support Timer when it first - * receives any USB PD Structured VDM Command it supports, - * which is the Discover Identity command. And this is only - * done one time. - */ - if (tc[port].support_timer_reset == SUPPORT_TIMER_RESET_REQUEST) { - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_COMPLETE; - tc[port].support_timer = get_time().val + PD_T_AME; - } - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD if VCONN is present and the state of - * its Host-side port’s CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - /* Check the Support Timer */ - if (get_time().val > tc[port].support_timer && - !tc[port].billboard_presented) { - /* - * Present USB Billboard Device Class interface - * indicating that Charge-Through is not supported - */ - tc[port].billboard_presented = 1; - vpd_present_billboard(BB_SNK); - } -} - -static void tc_attached_snk_exit(const int port) -{ - tc[port].billboard_presented = 0; - vpd_present_billboard(BB_NONE); -} - -/** - * Super State HOST_RA_CT_RD - */ -static void tc_host_rard_ct_rd_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Super State HOST_OPEN_CT_OPEN - */ -static void tc_host_open_ct_open_entry(const int port) -{ - /* Remove the terminations from Host */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* Remove the terminations from Charge-Through */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Isolate the Host-side port from the Charge-Through port */ - vpd_vbus_pass_en(0); - - /* Remove Charge-Through side port CCs */ - vpd_ct_cc_sel(CT_OPEN); - - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -/** - * Unattached.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_src_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_unattached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SRC when host-side VBUS is - * vSafe0V and SRC.Rd state is detected on the Host-side - * port’s CC pin. - */ - if (!vpd_is_host_vbus_present() && host_cc == TYPEC_CC_VOLT_RD) { - set_state_tc(port, TC_ATTACH_WAIT_SRC); - return; - } - - /* - * Transition to Unattached.SNK within tDRPTransition or - * if Charge-Through VBUS is removed. - */ - if (!vpd_is_ct_vbus_present() || - get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK when the SRC.Open state is detected on the - * Host-side port’s CC or if Charge-Through VBUS falls below - * vSinkDisconnect. The Charge-Through VCONN-Powered USB Device - * shall detect the SRC.Open state within tSRCDisconnect, but - * should detect it as quickly as possible. - */ - if (host_new_cc_state == PD_CC_NONE || !vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Try.SNK when the host-side VBUS is at vSafe0V and the SRC.Rd - * state is on the Host-side port’s CC pin for at least tCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_TRY_SNK); - return; - } -} - -/** - * Attached.SRC - */ -static void tc_attached_src_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - /* Connect Charge-Through VBUS to Host VBUS */ - vpd_vbus_pass_en(1); - - /* - * Get power from VBUS. No need to test because - * the Host VBUS is connected to the Charge-Through - * VBUS - */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); -} - -static void tc_attached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK when VBUS falls below vSinkDisconnect or the - * Host-side port’s CC pin is SRC.Open. The Charge-Through - * VCONNPowered USB Device shall detect the SRC.Open state within - * tSRCDisconnect, but should detect it as quickly as possible. - */ - if (!vpd_is_ct_vbus_present() || host_cc == TYPEC_CC_VOLT_OPEN) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Super State HOST_RPU_CT_RD - */ -static void tc_host_rpu_ct_rd_entry(const int port) -{ - /* Place RpUSB on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Try.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Using next_role_swap timer as try_src timer */ - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * The Charge-Through VCONN-Powered USB Device shall then transition to - * Attached.SNK when the SNK.Rp state is detected on the Host-side - * port’s CC pin for at least tTryCCDebounce and VBUS or VCONN is - * detected on Host-side port. - * - * Alternatively, the Charge-Through VCONN-Powered USB Device shall - * transition to TryWait.SRC if Host-side SNK.Rp state is not detected - * for tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_host_vbus_present() || vpd_is_vconn_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_TRY_WAIT_SRC); -} - -/** - * TryWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = - get_time().val + PD_T_TRY_CC_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].host_cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Attached.SRC when host-side VBUS is at vSafe0V and the - * SRC.Rd state is detected on the Host-side port’s CC pin for - * at least tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - } - - if (get_time().val > tc[port].next_role_swap) { - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tDRPTry if the Host-side port’s CC - * pin is not in the SRC.Rd state. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } -} - -/** - * CTTry.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_ct_try_snk_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the CT CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - tc[port].try_wait_debounce = get_time().val + PD_T_TRY_WAIT; - - return; - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * The Charge-Through VCONN-Powered USB Device shall then - * transition to CTAttached.VPD when the SNK.Rp state is - * detected on the Charge-Through port’s CC pins for at - * least tTryCCDebounce and VBUS is detected on - * Charge-Through port. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].try_wait_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTAttached.Unsupported if SNK.Rp state is not detected - * for tDRPTryWait. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, - TC_CT_ATTACHED_UNSUPPORTED); - return; - } - } -} - -static void tc_ct_try_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttachWait.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attach_wait_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_unsupported_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_at_least_one_rd(cc1, cc2)) - new_cc_state = PD_CC_UFP_ATTACHED; - else if (cc_is_audio_acc(cc1, cc2)) - new_cc_state = PD_CC_UFP_AUDIO_ACC; - else /* (cc1 == TYPEC_CC_VOLT_OPEN or cc2 == TYPEC_CC_VOLT_OPEN */ - new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when the state of either the Charge-Through - * Port’s CC1 or CC2 pin is SRC.Open for at least tCCDebounce. - * - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTTry.SNK if the state of at least one of the Charge-Through - * port’s CC pins is SRC.Rd, or if the state of both the CC1 and CC2 - * pins is SRC.Ra. for at least tCCDebounce. - */ - if (new_cc_state == PD_CC_NONE) - set_state_tc(port, TC_CT_UNATTACHED_VPD); - else /* PD_CC_UFP_ATTACHED or PD_CC_UFP_AUDIO_ACC */ - set_state_tc(port, TC_CT_TRY_SNK); -} - -static void tc_ct_attach_wait_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attached_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Present Billboard device */ - vpd_present_billboard(BB_SNK); -} - -static void tc_ct_attached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when SRC.Open state is detected on both the - * Charge-Through port’s CC pins or the SRC.Open state is detected - * on one CC pin and SRC.Ra is detected on the other CC pin. - */ - if ((cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) || - (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_RA) || - (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_OPEN)) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_attached_unsupported_exit(const int port) -{ - vpd_present_billboard(BB_NONE); -} - -/** - * CTUnattached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_unattached_unsupported_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_VPD) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_ct_unattached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.Unsupported when a Sink connection is detected on - * the Charge-Through port, as indicated by the SRC.Rd state on at - * least one of the Charge-Through port’s CC pins or SRC.Ra state - * on both the CC1 and CC2 pins. - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) { - set_state_tc(port, - TC_CT_ATTACH_WAIT_UNSUPPORTED); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD within tDRPTransition after dcSRC.DRP ∙ tDRP. - */ - if (get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_unattached_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTUnattached.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_unattached_vpd_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_UNSUPPORTED) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_unattached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.VPD when a Source connection is detected on the - * Charge-Through port, as indicated by the SNK.Rp state on - * exactly one of the Charge-Through port’s CC pins. - */ - if (new_cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_CT_ATTACH_WAIT_VPD); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DRP_SRC; - return; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.Unsupported within tDRPTransition after the state - * of both the Charge-Through port’s CC1 and CC2 pins is SNK.Open - * for tDRP-dcSRC.DRP ∙ tDRP, or if directed. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_UNSUPPORTED); - return; - } -} - -static void tc_ct_unattached_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTDisabled.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_ct_disabled_vpd_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE; -} - -static void tc_ct_disabled_vpd_run(const int port) -{ - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tVPDDisable. - */ - if (get_time().val > tc[port].next_role_swap) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * CTAttached.VPD - */ -static void tc_ct_attached_vpd_entry(const int port) -{ - int cc1; - int cc2; - print_current_state(port); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); - - /* - * Detect which of the Charge-Through port’s CC1 or CC2 - * pins is connected through the cable - */ - vpd_ct_get_cc(&cc1, &cc2); - tc[port].ct_cc = cc_is_rp(cc2) ? CT_CC2 : CT_CC1; - - /* - * 1. Remove or reduce any additional capacitance on the - * Host-side CC port - */ - vpd_mcu_cc_en(0); - - /* - * 2. Disable the Rp termination advertising 3.0 A on the - * host port’s CC pin - */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 3. Passively multiplex the detected Charge-Through port’s - * CC pin through to the host port’s CC - */ - vpd_ct_cc_sel(tc[port].ct_cc); - - /* - * 4. Disable the Rd on the Charge-Through port’s CC1 and CC2 - * pins - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 5. Connect the Charge-Through port’s VBUS through to the - * host port’s VBUS - */ - vpd_vbus_pass_en(1); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - if ((tc[port].ct_cc ? cc2 : cc1) == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_DFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - if (get_time().val < tc[port].pd_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when VBUS falls below vSinkDisconnect and the - * state of the passed-through CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].cc_state == PD_CC_NONE && !vpd_is_ct_vbus_present()) - set_state_tc(port, TC_CT_UNATTACHED_VPD); -} - -/** - * CTAttachWait.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_attach_wait_vpd_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - tc[port].pd_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].pd_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD when the state of both the Charge-Through - * port’s CC1 and CC2 pins are SNK.Open for at least - * tPDDebounce. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttached.VPD after the state of only one of the - * Charge-Through port’s CC1 or CC2 pins is SNK.Rp for at - * least tCCDebounce and VBUS on the Charge-Through port is - * detected. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } -} - -static void tc_ct_attach_wait_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * Super State HOST_RP3_CT_RD - */ -static void tc_host_rp3_ct_rd_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Connect Charge-Through Rd */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/** - * Super State HOST_RP3_CT_RPU - */ -static void tc_host_rp3_ct_rpu_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Place RPUSB on Charge-Through CC */ - vpd_ct_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/* All necessary Type-C states */ - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ------------------------------------------------------| - * | | - * | | TC_HOST_RARD_CT_RD -----------| | TC_HOST_OPEN_CT_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | | TC_ERROR_RECOVERY | | - * | | TC_TRY_SNK | |-------------------------------| | - * | |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RD ------------| | TC_HOST_RPU_CT_RD ------------| | - * | | | | | | - * | | TC_CT_TRY_SNK | | TC_UNATTACHED_SRC | | - * | | TC_CT_UNATTACHED_VPD | | TC_ATTACH_WAIT_SRC | | - * | | TC_CT_ATTACH_WAIT_VPD | | TC_TRY_WAIT_SR | | - * | |-------------------------------| |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RPU -----------| | - * | | | | - * | | TC_CT_ATTACH_WAIT_UNSUPPORTED | | - * | | TC_CT_ATTACHED_UNSUPPORTED | | - * | | TC_CT_UNATTACHED_UNSUPPORTED | | - * | |-------------------------------| | - * |----------------------------------------------------------------------| - * - * TC_ATTACHED_SNK - * TC_ATTACHED_SRC - * TC_CT_ATTACHED_VPD - * - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD_CT_RD] = { - .entry = tc_host_rard_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN_CT_OPEN] = { - .entry = tc_host_open_ct_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RD] = { - .entry = tc_host_rp3_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RPU] = { - .entry = tc_host_rp3_ct_rpu_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RPU_CT_RD] = { - .entry = tc_host_rpu_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_TRY_SNK] = { - .entry = tc_try_snk_entry, - .run = tc_try_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_TRY_WAIT_SRC] = { - .entry = tc_try_wait_src_entry, - .run = tc_try_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - }, - [TC_CT_TRY_SNK] = { - .entry = tc_ct_try_snk_entry, - .run = tc_ct_try_snk_run, - .exit = tc_ct_try_snk_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = { - .entry = tc_ct_attach_wait_unsupported_entry, - .run = tc_ct_attach_wait_unsupported_run, - .exit = tc_ct_attach_wait_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_ATTACHED_UNSUPPORTED] = { - .entry = tc_ct_attached_unsupported_entry, - .run = tc_ct_attached_unsupported_run, - .exit = tc_ct_attached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_UNSUPPORTED] = { - .entry = tc_ct_unattached_unsupported_entry, - .run = tc_ct_unattached_unsupported_run, - .exit = tc_ct_unattached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_VPD] = { - .entry = tc_ct_unattached_vpd_entry, - .run = tc_ct_unattached_vpd_run, - .exit = tc_ct_unattached_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_DISABLED_VPD] = { - .entry = tc_ct_disabled_vpd_entry, - .run = tc_ct_disabled_vpd_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_CT_ATTACHED_VPD] = { - .entry = tc_ct_attached_vpd_entry, - .run = tc_ct_attached_vpd_run, - }, - [TC_CT_ATTACH_WAIT_VPD] = { - .entry = tc_ct_attach_wait_vpd_entry, - .run = tc_ct_attach_wait_vpd_run, - .exit = tc_ct_attach_wait_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c deleted file mode 100644 index 182ea686ec..0000000000 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ /dev/null @@ -1,4160 +0,0 @@ -/* 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. - */ - -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ocp.h" -#include "usbc_ppc.h" -#include "vboot.h" - -/* - * USB Type-C DRP with Accessory and Try.SRC module - * See Figure 4-16 in Release 1.4 of USB Type-C Spec. - */ -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -#define CPRINTF_LX(x, format, args...) \ - do { \ - if (tc_debug_level >= x) \ - CPRINTF(format, ## args); \ - } while (0) -#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ## args) -#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ## args) -#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ## args) - -#define CPRINTS_LX(x, format, args...) \ - do { \ - if (tc_debug_level >= x) \ - CPRINTS(format, ## args); \ - } while (0) -#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ## args) -#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ## args) -#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ## args) - -/* - * Define DEBUG_PRINT_FLAG_AND_EVENT_NAMES to print flag names when set and - * cleared, and event names when handled by tc_event_check(). - */ -#undef DEBUG_PRINT_FLAG_AND_EVENT_NAMES - -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES -void print_flag(int port, int set_or_clear, int flag); -#define TC_SET_FLAG(port, flag) \ - do { \ - print_flag(port, 1, flag); \ - atomic_or(&tc[port].flags, (flag)); \ - } while (0) -#define TC_CLR_FLAG(port, flag) \ - do { \ - print_flag(port, 0, flag); \ - atomic_clear_bits(&tc[port].flags, (flag)); \ - } while (0) -#else -#define TC_SET_FLAG(port, flag) atomic_or(&tc[port].flags, (flag)) -#define TC_CLR_FLAG(port, flag) atomic_clear_bits(&tc[port].flags, (flag)) -#endif -#define TC_CHK_FLAG(port, flag) (tc[port].flags & (flag)) - -/* Type-C Layer Flags */ -/* Flag to note we are sourcing VCONN */ -#define TC_FLAGS_VCONN_ON BIT(0) -/* Flag to note port partner has Rp/Rp or Rd/Rd */ -#define TC_FLAGS_TS_DTS_PARTNER BIT(1) -/* Flag to note VBus input has never been low */ -#define TC_FLAGS_VBUS_NEVER_LOW BIT(2) -/* Flag to note Low Power Mode transition is currently happening */ -#define TC_FLAGS_LPM_TRANSITION BIT(3) -/* Flag to note Low Power Mode is currently on */ -#define TC_FLAGS_LPM_ENGAGED BIT(4) -/* Flag to note CVTPD has been detected */ -#define TC_FLAGS_CTVPD_DETECTED BIT(5) -/* Flag to note request to swap to VCONN on */ -#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(6) -/* Flag to note request to swap to VCONN off */ -#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(7) -/* Flag to note request to swap VCONN is being rejected */ -#define TC_FLAGS_REJECT_VCONN_SWAP BIT(8) -/* Flag to note request to power role swap */ -#define TC_FLAGS_REQUEST_PR_SWAP BIT(9) -/* Flag to note request to data role swap */ -#define TC_FLAGS_REQUEST_DR_SWAP BIT(10) -/* Flag to note request to power off sink */ -#define TC_FLAGS_POWER_OFF_SNK BIT(11) -/* Flag to note port partner is Power Delivery capable */ -#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(12) -/* Flag to note hard reset has been requested */ -#define TC_FLAGS_HARD_RESET_REQUESTED BIT(13) -/* Flag to note we are currently performing PR Swap */ -#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(14) -/* Flag to note we should check for connection */ -#define TC_FLAGS_CHECK_CONNECTION BIT(15) -/* Flag to note request from pd_set_suspend to enter TC_DISABLED state */ -#define TC_FLAGS_REQUEST_SUSPEND BIT(16) -/* Flag to note we are in TC_DISABLED state */ -#define TC_FLAGS_SUSPENDED BIT(17) -/* Flag to indicate the port current limit has changed */ -#define TC_FLAGS_UPDATE_CURRENT BIT(18) -/* Flag to indicate USB mux should be updated */ -#define TC_FLAGS_UPDATE_USB_MUX BIT(19) -/* Flag for retimer firmware update */ -#define TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN BIT(20) -#define TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN BIT(21) -/* Flag for asynchronous call to request Error Recovery */ -#define TC_FLAGS_REQUEST_ERROR_RECOVERY BIT(22) - -/* For checking flag_bit_names[] array */ -#define TC_FLAGS_COUNT 23 - -/* On disconnect, clear most of the flags. */ -#define CLR_FLAGS_ON_DISCONNECT(port) TC_CLR_FLAG(port, \ - ~(TC_FLAGS_LPM_ENGAGED | TC_FLAGS_REQUEST_SUSPEND | TC_FLAGS_SUSPENDED)) - -/* - * 10 ms is enough time for any TCPC transaction to complete - * - * This value must be below ~39.7 ms to put ANX7447 into LPM due to bug in - * silicon (see b/77544959 and b/149761477 for more details). - */ -#define PD_LPM_DEBOUNCE_US (10 * MSEC) - -/* - * This delay is not part of the USB Type-C specification or the USB port - * controller specification. Some TCPCs require extra time before the CC_STATUS - * register is updated when exiting low power mode. - * - * This delay can be possibly shortened or removed by checking VBUS state - * before trying to re-enter LPM. - * - * TODO(b/162347811): TCPMv2: Wait for debounce on Vbus and CC lines - */ -#define PD_LPM_EXIT_DEBOUNCE_US CONFIG_USB_PD_TCPC_LPM_EXIT_DEBOUNCE - -/* - * The TypeC state machine uses this bit to disable/enable PD - * This bit corresponds to bit-0 of pd_disabled_mask - */ -#define PD_DISABLED_NO_CONNECTION BIT(0) -/* - * Console and Host commands use this bit to override the - * PD_DISABLED_NO_CONNECTION bit that was set by the TypeC - * state machine. - * This bit corresponds to bit-1 of pd_disabled_mask - */ -#define PD_DISABLED_BY_POLICY BIT(1) - -/* Unreachable time in future */ -#define TIMER_DISABLED 0xffffffffffffffff - -enum ps_reset_sequence { - PS_STATE0, - PS_STATE1, - PS_STATE2, -}; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Super States */ - TC_CC_OPEN, - TC_CC_RD, - TC_CC_RP, - /* Normal States */ - TC_DISABLED, - TC_ERROR_RECOVERY, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_ATTACHED_SRC, - TC_TRY_SRC, - TC_TRY_WAIT_SNK, - TC_DRP_AUTO_TOGGLE, - TC_LOW_POWER_MODE, - TC_CT_UNATTACHED_SNK, - TC_CT_ATTACHED_SNK, - - TC_STATE_COUNT, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -/* - * Remove all of the states that aren't support at link time. This allows - * IS_ENABLED to work. - */ -#ifndef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -GEN_NOT_SUPPORTED(TC_DRP_AUTO_TOGGLE); -#define TC_DRP_AUTO_TOGGLE TC_DRP_AUTO_TOGGLE_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifndef CONFIG_USB_PD_TCPC_LOW_POWER -GEN_NOT_SUPPORTED(TC_LOW_POWER_MODE); -#define TC_LOW_POWER_MODE TC_LOW_POWER_MODE_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -#ifndef CONFIG_USB_PE_SM -GEN_NOT_SUPPORTED(TC_CT_UNATTACHED_SNK); -#define TC_CT_UNATTACHED_SNK TC_CT_UNATTACHED_SNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TC_CT_ATTACHED_SNK); -#define TC_CT_ATTACHED_SNK TC_CT_ATTACHED_SNK_NOT_SUPPORTED -#endif /* CONFIG_USB_PE_SM */ - -/* - * If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then - * _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work. - */ -#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT -extern int _GPIO_CCD_MODE_ODL; -#else -#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL -#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */ - -/* - * We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME) - * and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or - * we are overriding and the level is not DISABLED. - * - * If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0 - * then the DEBUG LABELS will be removed from the build. - */ -#if defined(CONFIG_COMMON_RUNTIME) && \ - (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \ - (CONFIG_USB_PD_DEBUG_LEVEL > 0)) -#define USB_PD_DEBUG_LABELS -#endif - -/* - * Helper Macro to determine if the machine is in state - * TC_ATTACHED_SRC - */ -#define IS_ATTACHED_SRC(port) (get_state_tc(port) == TC_ATTACHED_SRC) - -/* - * Helper Macro to determine if the machine is in state - * TC_ATTACHED_SNK - */ -#define IS_ATTACHED_SNK(port) (get_state_tc(port) == TC_ATTACHED_SNK) - - -/* List of human readable state names for console debugging */ -__maybe_unused static __const_data const char * const tc_state_names[] = { -#ifdef USB_PD_DEBUG_LABELS - [TC_DISABLED] = "Disabled", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_TRY_SRC] = "Try.SRC", - [TC_TRY_WAIT_SNK] = "TryWait.SNK", -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = "DRPAutoToggle", -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = "LowPowerMode", -#endif -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = "CTUnattached.SNK", - [TC_CT_ATTACHED_SNK] = "CTAttached.SNK", -#endif - /* Super States */ - [TC_CC_OPEN] = "SS:CC_OPEN", - [TC_CC_RD] = "SS:CC_RD", - [TC_CC_RP] = "SS:CC_RP", - - [TC_STATE_COUNT] = "", -#endif -}; - -/* Debug log level - higher number == more log */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level tc_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level tc_debug_level = DEBUG_LEVEL_1; -#endif - -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES -struct bit_name { - int value; - const char *name; -}; - -static struct bit_name flag_bit_names[] = { - { TC_FLAGS_VCONN_ON, "VCONN_ON" }, - { TC_FLAGS_TS_DTS_PARTNER, "TS_DTS_PARTNER" }, - { TC_FLAGS_VBUS_NEVER_LOW, "VBUS_NEVER_LOW" }, - { TC_FLAGS_LPM_TRANSITION, "LPM_TRANSITION" }, - { TC_FLAGS_LPM_ENGAGED, "LPM_ENGAGED" }, - { TC_FLAGS_CTVPD_DETECTED, "CTVPD_DETECTED" }, - { TC_FLAGS_REQUEST_VC_SWAP_ON, "REQUEST_VC_SWAP_ON" }, - { TC_FLAGS_REQUEST_VC_SWAP_OFF, "REQUEST_VC_SWAP_OFF" }, - { TC_FLAGS_REJECT_VCONN_SWAP, "REJECT_VCONN_SWAP" }, - { TC_FLAGS_REQUEST_PR_SWAP, "REQUEST_PR_SWAP" }, - { TC_FLAGS_REQUEST_DR_SWAP, "REQUEST_DR_SWAP" }, - { TC_FLAGS_POWER_OFF_SNK, "POWER_OFF_SNK" }, - { TC_FLAGS_PARTNER_PD_CAPABLE, "PARTNER_PD_CAPABLE" }, - { TC_FLAGS_HARD_RESET_REQUESTED, "HARD_RESET_REQUESTED" }, - { TC_FLAGS_PR_SWAP_IN_PROGRESS, "PR_SWAP_IN_PROGRESS" }, - { TC_FLAGS_CHECK_CONNECTION, "CHECK_CONNECTION" }, - { TC_FLAGS_REQUEST_SUSPEND, "REQUEST_SUSPEND" }, - { TC_FLAGS_SUSPENDED, "SUSPENDED" }, - { TC_FLAGS_UPDATE_CURRENT, "UPDATE_CURRENT" }, - { TC_FLAGS_UPDATE_USB_MUX, "UPDATE_USB_MUX" }, - { TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN, - "USB_RETIMER_FW_UPDATE_RUN" }, - { TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN, - "USB_RETIMER_FW_UPDATE_LTD_RUN" }, - { TC_FLAGS_REQUEST_ERROR_RECOVERY, "REQUEST_ERROR_RECOCVERY"}, -}; -BUILD_ASSERT(ARRAY_SIZE(flag_bit_names) == TC_FLAGS_COUNT); - -static struct bit_name event_bit_names[] = { - { TASK_EVENT_SYSJUMP_READY, "SYSJUMP_READY" }, - { TASK_EVENT_IPC_READY, "IPC_READY" }, - { TASK_EVENT_PD_AWAKE, "PD_AWAKE" }, - { TASK_EVENT_PECI_DONE, "PECI_DONE" }, - { TASK_EVENT_I2C_IDLE, "I2C_IDLE" }, -#ifdef TASK_EVENT_PS2_DONE - { TASK_EVENT_PS2_DONE, "PS2_DONE" }, -#endif - { TASK_EVENT_DMA_TC, "DMA_TC" }, - { TASK_EVENT_ADC_DONE, "ADC_DONE" }, - { TASK_EVENT_RESET_DONE, "RESET_DONE" }, - { TASK_EVENT_WAKE, "WAKE" }, - { TASK_EVENT_MUTEX, "MUTEX" }, - { TASK_EVENT_TIMER, "TIMER" }, - { PD_EVENT_TX, "TX" }, - { PD_EVENT_CC, "CC" }, - { PD_EVENT_TCPC_RESET, "TCPC_RESET" }, - { PD_EVENT_UPDATE_DUAL_ROLE, "UPDATE_DUAL_ROLE" }, - { PD_EVENT_DEVICE_ACCESSED, "DEVICE_ACCESSED" }, - { PD_EVENT_POWER_STATE_CHANGE, "POWER_STATE_CHANGE" }, - { PD_EVENT_SEND_HARD_RESET, "SEND_HARD_RESET" }, - { PD_EVENT_SYSJUMP, "SYSJUMP" }, -}; - -static void print_bits(int port, const char *desc, int value, - struct bit_name *names, int names_size) -{ - int i; - - CPRINTF("C%d: %s 0x%x : ", port, desc, value); - for (i = 0; i < names_size; i++) { - if (value & names[i].value) - CPRINTF("%s | ", names[i].name); - value &= ~names[i].value; - } - if (value != 0) - CPRINTF("0x%x", value); - CPRINTF("\n"); -} - -void print_flag(int port, int set_or_clear, int flag) -{ - print_bits(port, set_or_clear ? "Set" : "Clr", flag, flag_bit_names, - ARRAY_SIZE(flag_bit_names)); -} -#endif /* DEBUG_PRINT_FLAG_AND_EVENT_NAMES */ - -#ifndef CONFIG_USB_PD_TRY_SRC -extern int TC_TRY_SRC_UNDEFINED; -extern int TC_TRY_WAIT_SNK_UNDEFINED; -#define TC_TRY_SRC TC_TRY_SRC_UNDEFINED -#define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED -#endif - -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* - * Higher-level power deliver state machines are enabled if false, - * else they're disabled if bits PD_DISABLED_NO_CONNECTION or - * PD_DISABLED_BY_POLICY are set. - */ - uint32_t pd_disabled_mask; - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#ifdef CONFIG_USB_PE_SM - /* Power supply reset sequence during a hard reset */ - enum ps_reset_sequence ps_reset_state; -#endif - /* Port polarity */ - enum tcpc_cc_polarity polarity; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* The cc state */ - enum pd_cc_states cc_state; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; - /* Voltage on CC pin */ - enum tcpc_cc_voltage_status cc_voltage; - /* Type-C current */ - typec_current_t typec_curr; - /* Type-C current change */ - typec_current_t typec_curr_change; - - /* Selected TCPC CC/Rp values */ - enum tcpc_cc_pull select_cc_pull; - enum tcpc_rp_value select_current_limit_rp; - enum tcpc_rp_value select_collision_rp; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Port dual-role state */ -static volatile __maybe_unused -enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = - CONFIG_USB_PD_INITIAL_DRP_STATE}; - -static void set_vconn(int port, int enable); - -/* Forward declare common, private functions */ -static __maybe_unused int reset_device_and_notify(int port); -static __maybe_unused void check_drp_connection(const int port); -static void sink_power_sub_states(int port); -static void set_ccd_mode(int port, bool enable); - -__maybe_unused static void handle_new_power_state(int port); - -static void pd_update_dual_role_config(int port); - -/* Forward declare common, private functions */ -static void set_state_tc(const int port, const enum usb_tc_state new_state); -test_export_static enum usb_tc_state get_state_tc(const int port); - -/* Enable variable for Try.SRC states */ -static uint32_t pd_try_src; -static volatile enum try_src_override_t pd_try_src_override; -static void pd_update_try_source(void); - -static void sink_stop_drawing_current(int port); - -__maybe_unused static bool is_try_src_enabled(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - return ((pd_try_src_override == TRY_SRC_OVERRIDE_ON) || - (pd_try_src_override == TRY_SRC_NO_OVERRIDE && pd_try_src)); -} - -/* - * Public Functions - * - * NOTE: Functions prefixed with pd_ are defined in usb_pd.h - * Functions prefixed with tc_ are defined int usb_tc_sm.h - */ - -#ifndef CONFIG_USB_PRL_SM - -/* - * These pd_ functions are implemented in common/usb_prl_sm.c - */ - -void pd_transmit_complete(int port, int status) -{ - /* DO NOTHING */ -} - -void pd_execute_hard_reset(int port) -{ - /* DO NOTHING */ -} - -__overridable void pd_set_vbus_discharge(int port, int enable) -{ - /* DO NOTHING */ -} - -#endif /* !CONFIG_USB_PRL_SM */ - -#ifndef CONFIG_USB_PE_SM - -/* - * These pd_ functions are implemented in the PE layer - */ -const uint32_t * const pd_get_src_caps(int port) -{ - return NULL; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - return 0; -} - -const uint32_t * const pd_get_snk_caps(int port) -{ - return NULL; -} - -uint8_t pd_get_snk_cap_cnt(int port) -{ - return 0; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ -} - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ - return PD_REV30; -} - -#endif /* !CONFIG_USB_PR_SM */ - -#ifndef HAS_TASK_CHIPSET -__overridable enum pd_dual_role_states board_tc_get_initial_drp_mode(int port) -{ - /* - * DRP state is typically adjusted as the chipset state is changed. For - * projects which don't include an AP this function can be used for to - * specify what the starting DRP state should be. - */ - return PD_DRP_FORCE_SINK; -} -#endif - -void pd_update_contract(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (IS_ATTACHED_SRC(port)) - pd_dpm_request(port, DPM_REQUEST_SRC_CAP_CHANGE); - } -} - -void pd_request_source_voltage(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - if (IS_ATTACHED_SNK(port)) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - else - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_external_voltage_limit(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_new_power_request(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - } -} - -void tc_request_power_swap(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* - * Must be in Attached.SRC or Attached.SNK - */ - if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - - /* Let tc_pr_swap_complete start the Vbus debounce */ - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - } - - /* - * TCPCI Rev2 V1.1 4.4.5.4.4 - * Disconnect Detection by the Sink TCPC during a Connection - * - * Upon reception of or prior to transmitting a PR_Swap - * message, the TCPM acting as a Sink shall disable the Sink - * disconnect detection to retain PD message delivery when - * Power Role Swap happens. Disable AutoDischargeDisconnect. - */ - if (IS_ATTACHED_SNK(port)) - tcpm_enable_auto_discharge_disconnect(port, 0); - } -} - -/* Flag to indicate PD comm is disabled on init */ -static int pd_disabled_on_init; - -static void pd_update_pd_comm(void) -{ - int i; - - /* - * Some batteries take much longer time to report its SOC. - * The init function disabled PD comm on startup. Need this - * hook to enable PD comm when the battery level is enough. - */ - if (pd_disabled_on_init && pd_is_battery_capable()) { - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) - pd_comm_enable(i, 1); - pd_disabled_on_init = 0; - } -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_pd_comm, HOOK_PRIO_DEFAULT); - -static bool pd_comm_allowed_by_policy(void) -{ - if (system_is_in_rw()) - return true; - - if (vboot_allow_usb_pd()) - return true; - - /* - * If enable PD in RO on a non-EFS2 device, a hard reset will be issued - * when sysjump to RW that makes the device brownout on the dead-battery - * case. Disable PD for this special case as a workaround. - */ - if (!system_is_locked()) { - if (IS_ENABLED(CONFIG_VBOOT_EFS2)) - return true; - - if (pd_is_battery_capable()) - return true; - - pd_disabled_on_init = 1; - } - - return false; -} - -static void tc_policy_pd_enable(int port, int en) -{ - if (en) - atomic_clear_bits(&tc[port].pd_disabled_mask, - PD_DISABLED_BY_POLICY); - else - atomic_or(&tc[port].pd_disabled_mask, PD_DISABLED_BY_POLICY); - - CPRINTS("C%d: PD comm policy %sabled", port, en ? "en" : "dis"); -} - -static void tc_enable_pd(int port, int en) -{ - if (en) - atomic_clear_bits(&tc[port].pd_disabled_mask, - PD_DISABLED_NO_CONNECTION); - else - atomic_or(&tc[port].pd_disabled_mask, - PD_DISABLED_NO_CONNECTION); -} - -__maybe_unused static void tc_enable_try_src(int en) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - if (en) - atomic_or(&pd_try_src, 1); - else - atomic_clear_bits(&pd_try_src, 1); -} - -/* - * Exit all modes due to a detach event - * Note: this skips the ExitMode VDM steps in the PE because it is assumed the - * partner is not present to receive them, and the PE will no longer be running. - */ -static void tc_set_modes_exit(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0); - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, 0, 0); - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME_PRIME, 0, 0); - } -} - -static void tc_detached(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - hook_notify(HOOK_USB_PD_DISCONNECT); - tc_pd_connection(port, 0); - tcpm_debug_accessory(port, 0); - set_ccd_mode(port, 0); - tc_set_modes_exit(port); - if (IS_ENABLED(CONFIG_USB_PRL_SM)) - prl_set_default_pd_revision(port); - - /* Clear any mux connection on detach */ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); -} - -static inline void pd_set_dual_role_and_event(int port, - enum pd_dual_role_states state, uint32_t event) -{ - drp_state[port] = state; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - pd_update_try_source(); - - if (event != 0) - task_set_event(PD_PORT_TO_TASK_ID(port), event); -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_and_event(port, state, PD_EVENT_UPDATE_DUAL_ROLE); -} - -int pd_comm_is_enabled(int port) -{ - return tc_get_pd_enabled(port); -} - -void pd_request_data_swap(int port) -{ - /* - * Must be in Attached.SRC, Attached.SNK, DebugAccessory.SNK, - * or UnorientedDebugAccessory.SRC when this function - * is called - */ - if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -/* Return true if partner port is known to be PD capable. */ -bool pd_capable(int port) -{ - return !!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -/* - * Return true if we transition through Unattached.SNK, but we're still waiting - * to receive source caps from the partner. This indicates that the PD - * capabilities are not yet known. - */ -bool pd_waiting_on_partner_src_caps(int port) -{ - return !pd_get_src_cap_cnt(port); -} - -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint32_t *hash) -{ - int j; - - ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id)); - for (j = 0; j < PD_RW_HASH_SIZE / 4; j++) - ccprintf(" %08x ", hash[j]); - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -const char *tc_get_current_state(int port) -{ - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - return tc_state_names[get_state_tc(port)]; - else - return ""; -} - -uint32_t tc_get_flags(int port) -{ - return tc[port].flags; -} - -int tc_is_attached_src(int port) -{ - return IS_ATTACHED_SRC(port); -} - -int tc_is_attached_snk(int port) -{ - return IS_ATTACHED_SNK(port); -} - -void tc_pd_connection(int port, int en) -{ - if (en) { - bool new_pd_capable = false; - - if (!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE)) - new_pd_capable = true; - - TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - /* If a PD device is attached then disable deep sleep */ - if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) && - !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) { - disable_sleep(SLEEP_MASK_USB_PD); - } - - /* - * Update the mux state, only when the PD capable flag - * transitions from 0 to 1. This ensures that PD charger - * devices, without data capability are not marked as having - * USB. - */ - if (new_pd_capable) - set_usb_mux_with_current_data_role(port); - } else { - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - /* If a PD device isn't attached then enable deep sleep */ - if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) && - !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) { - int i; - - /* If all ports are not connected, allow the sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd_capable(i)) - break; - } - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - } - } -} - -void tc_ctvpd_detected(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED); -} - -void pd_try_vconn_src(int port) -{ - set_vconn(port, 1); -} - -int tc_check_vconn_swap(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP)) - return 0; - - return pd_check_vconn_swap(port); - } else - return 0; -} - -void tc_pr_swap_complete(int port, bool success) -{ - if (IS_ATTACHED_SNK(port)) { - /* - * Give the ADCs in the TCPC or PPC time to react following - * a PS_RDY message received during a SRC to SNK swap. - * Note: This is empirically determined, not strictly - * part of the USB PD spec. - * Note: Swap in progress should not be cleared until the - * debounce is completed. - */ - pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, PD_T_DEBOUNCE); - } else { - /* PR Swap is no longer in progress */ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - - /* - * AutoDischargeDisconnect was turned off near the SNK->SRC - * PR-Swap message. If the swap was a success, Vbus should be - * valid, so re-enable AutoDischargeDisconnect - */ - if (success) - tcpm_enable_auto_discharge_disconnect(port, 1); - } -} - -void tc_prs_src_snk_assert_rd(int port) -{ - /* - * Must be in Attached.SRC or UnorientedDebugAccessory.SRC - * when this function is called - */ - if (IS_ATTACHED_SRC(port)) { - /* - * Transition to Attached.SNK to - * DebugAccessory.SNK assert Rd - */ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void tc_prs_snk_src_assert_rp(int port) -{ - /* - * Must be in Attached.SNK or DebugAccessory.SNK - * when this function is called - */ - if (IS_ATTACHED_SNK(port)) { - /* - * Transition to Attached.SRC or - * UnorientedDebugAccessory.SRC to assert Rp - */ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -/* - * Hard Reset is being requested. This should not allow a TC connection - * to go to an unattached state until the connection is recovered from - * the hard reset. It is possible for a Hard Reset to cause a timeout - * in trying to recover and an additional Hard Reset would be issued. - * During this entire process it is important that the TC is not allowed - * to go to an unattached state. - * - * Type-C Spec Rev 2.0 section 4.5.2.2.5.2 - * Exiting from Attached.SNK State - * A port that is not a V CONN-Powered USB Device and is not in the - * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD - * FR_Swap shall transition to Unattached.SNK - */ -void tc_hard_reset_request(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_try_src_override(enum try_src_override_t ov) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) { - switch (ov) { - case TRY_SRC_OVERRIDE_OFF: /* 0 */ - pd_try_src_override = TRY_SRC_OVERRIDE_OFF; - break; - case TRY_SRC_OVERRIDE_ON: /* 1 */ - pd_try_src_override = TRY_SRC_OVERRIDE_ON; - break; - default: - pd_try_src_override = TRY_SRC_NO_OVERRIDE; - } - } -} - -enum try_src_override_t tc_get_try_src_override(void) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - return pd_try_src_override; -} - -void tc_snk_power_off(int port) -{ - if (IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK); - sink_stop_drawing_current(port); - } -} - -int tc_src_power_on(int port) -{ - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (IS_ENABLED(CONFIG_USBC_OCP) && usbc_ocp_is_port_latched_off(port)) - return EC_ERROR_ACCESS_DENIED; - - if (IS_ATTACHED_SRC(port)) - return pd_set_power_supply_ready(port); - - return 0; -} - -void tc_src_power_off(int port) -{ - /* Remove VBUS */ - pd_power_supply_reset(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -} - -/* Set what role the partner is right now, for the PPC and OCP module */ -static void tc_set_partner_role(int port, enum ppc_device_role role) -{ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_dev_is_connected(port, role); - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - usbc_ocp_snk_is_connected(port, role == PPC_DEV_SNK); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - if (role == PPC_DEV_DISCONNECTED) - usbc_ocp_clear_event_counter(port); - } -} - -/* - * Depending on the load on the processor and the tasks running - * it can take a while for the task associated with this port - * to run. So build in 1ms delays, for up to 300ms, to wait for - * the suspend to actually happen. - */ -#define SUSPEND_SLEEP_DELAY 1 -#define SUSPEND_SLEEP_RETRIES 300 - -void pd_set_suspend(int port, int suspend) -{ - if (pd_is_port_enabled(port) == !suspend) - return; - - /* Track if we are suspended or not */ - if (suspend) { - int wait = 0; - - TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - - /* - * Avoid deadlock when running from task - * which we are going to suspend - */ - if (PD_PORT_TO_TASK_ID(port) == task_get_current()) - return; - - task_wake(PD_PORT_TO_TASK_ID(port)); - - /* Sleep this task if we are not suspended */ - while (pd_is_port_enabled(port)) { - if (++wait > SUSPEND_SLEEP_RETRIES) { - CPRINTS("C%d: NOT SUSPENDED after %dms", - port, wait * SUSPEND_SLEEP_DELAY); - return; - } - msleep(SUSPEND_SLEEP_DELAY); - } - } else { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_error_recovery(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY); -} - -int pd_is_port_enabled(int port) -{ - /* - * Checking get_state_tc(port) from another task isn't safe since it - * can return TC_DISABLED before tc_cc_open_entry and tc_disabled_entry - * are complete. So check TC_FLAGS_SUSPENDED instead. - */ - return !TC_CHK_FLAG(port, TC_FLAGS_SUSPENDED); -} - -int pd_fetch_acc_log_entry(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0); - - return EC_RES_SUCCESS; -} - -enum tcpc_cc_polarity pd_get_polarity(int port) -{ - return tc[port].polarity; -} - -enum pd_data_role pd_get_data_role(int port) -{ - return tc[port].data_role; -} - -enum pd_power_role pd_get_power_role(int port) -{ - return tc[port].power_role; -} - -enum pd_cc_states pd_get_task_cc_state(int port) -{ - return tc[port].cc_state; -} - -uint8_t pd_get_task_state(int port) -{ - return get_state_tc(port); -} - -bool pd_get_vconn_state(int port) -{ - return !!TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); -} - -const char *pd_get_task_state_name(int port) -{ - return tc_get_current_state(port); -} - -void pd_vbus_low(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_VBUS_NEVER_LOW); -} - -int pd_is_connected(int port) -{ - return (IS_ATTACHED_SRC(port) || - (IS_ENABLED(CONFIG_USB_PE_SM) && - ((get_state_tc(port) == TC_CT_UNATTACHED_SNK) || - (get_state_tc(port) == TC_CT_ATTACHED_SNK))) || - IS_ATTACHED_SNK(port)); -} - -bool pd_is_disconnected(int port) -{ - return !pd_is_connected(port); -} - -/* - * PD functions which query our fixed PDO flags. Both the source and sink - * capabilities can present these values, and they should match between the two - * for compliant partners. - */ -static bool pd_check_fixed_flag(int port, uint32_t flag) -{ - uint32_t fixed_pdo; - - if (pd_get_src_cap_cnt(port) != 0) - fixed_pdo = *pd_get_src_caps(port); - else if (pd_get_snk_cap_cnt(port) != 0) - fixed_pdo = *pd_get_snk_caps(port); - else - return false; - - /* - * Error check that first PDO is fixed, as 6.4.1 Capabilities requires - * in the Power Delivery Specification. - * "The vSafe5V Fixed Supply Object Shall always be the first object" - */ - if ((fixed_pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return false; - - return fixed_pdo & flag; -} - -bool pd_get_partner_data_swap_capable(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_DATA_SWAP); -} - -bool pd_get_partner_usb_comm_capable(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_COMM_CAP); -} - -bool pd_get_partner_dual_role_power(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_DUAL_ROLE); -} - -bool pd_get_partner_unconstr_power(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_UNCONSTRAINED); -} - -static void bc12_role_change_handler(int port, enum pd_data_role prev_data_role, - enum pd_data_role data_role) -{ - int event = 0; - int task_id = USB_CHG_PORT_TO_TASK_ID(port); - bool role_changed = (data_role != prev_data_role); - - if (!IS_ENABLED(CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER)) - return; - - /* Get the data role of our device */ - switch (data_role) { - case PD_ROLE_UFP: - /* Only trigger BC12 detection on a role change */ - if (role_changed) - event = USB_CHG_EVENT_DR_UFP; - break; - case PD_ROLE_DFP: - /* Only trigger BC12 host mode on a role change */ - if (role_changed) - event = USB_CHG_EVENT_DR_DFP; - break; - case PD_ROLE_DISCONNECTED: - event = USB_CHG_EVENT_CC_OPEN; - break; - default: - return; - } - - if (event) - task_set_event(task_id, event); -} - -/* - * TCPC CC/Rp management - */ -static void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ - tc[port].select_cc_pull = pull; -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ - tc[port].select_current_limit_rp = rp; - if (IS_ATTACHED_SRC(port)) - TC_SET_FLAG(port, TC_FLAGS_UPDATE_CURRENT); -} -__overridable int typec_get_default_current_limit_rp(int port) -{ - return CONFIG_USB_PD_PULLUP; -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ - tc[port].select_collision_rp = rp; -} -static enum tcpc_rp_value typec_get_active_select_rp(int port) -{ - /* Explicit contract will use the collision Rp */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_is_explicit_contract(port)) - return tc[port].select_collision_rp; - return tc[port].select_current_limit_rp; -} -int typec_update_cc(int port) -{ - int rv; - enum tcpc_cc_pull pull = tc[port].select_cc_pull; - enum tcpc_rp_value rp = typec_get_active_select_rp(port); - - rv = tcpm_select_rp_value(port, rp); - if (rv) - return rv; - - return tcpm_set_cc(port, pull); -} - -#ifdef CONFIG_USB_PE_SM -/* - * This function performs a source hard reset. It should be called - * repeatedly until a true value is returned, signaling that the - * source hard reset is complete. A false value is returned otherwise. - */ -static bool tc_perform_src_hard_reset(int port) -{ - switch (tc[port].ps_reset_state) { - case PS_STATE0: - /* Remove VBUS */ - tc_src_power_off(port); - - /* Turn off VCONN */ - set_vconn(port, 0); - - /* Set role to DFP */ - tc_set_data_role(port, PD_ROLE_DFP); - - tc[port].ps_reset_state = PS_STATE1; - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SRC_RECOVER); - return false; - case PS_STATE1: - /* Enable VBUS */ - tc_src_power_on(port); - - /* Update the Rp Value */ - typec_update_cc(port); - - /* Turn off VCONN */ - set_vconn(port, 1); - - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_POWER_SUPPLY_TURN_ON_DELAY); - return false; - case PS_STATE2: - /* Tell Policy Engine Hard Reset is complete */ - pe_ps_reset_complete(port); - - tc[port].ps_reset_state = PS_STATE0; - return true; - } - - /* - * This return is added to appease the compiler. It should - * never be reached because the switch handles all possible - * cases of the enum ps_reset_sequence type. - */ - return true; -} - -/* - * Wait for recovery after a hard reset. Call repeatedly until true is - * returned, signaling that the hard reset is complete. - */ -static bool tc_perform_snk_hard_reset(int port) -{ - switch (tc[port].ps_reset_state) { - case PS_STATE0: - /* Hard reset sets us back to default data role */ - tc_set_data_role(port, PD_ROLE_UFP); - - /* - * When VCONN is supported, the Hard Reset Shall cause - * the Port with the Rd resistor asserted to turn off - * VCONN. - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* Wait up to tVSafe0V for Vbus to disappear */ - tc[port].ps_reset_state = PS_STATE1; - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SAFE_0V); - return false; - case PS_STATE1: - if (pd_check_vbus_level(port, VBUS_SAFE0V)) { - /* - * Partner dropped Vbus, reduce our current consumption - * and await its return. - */ - sink_stop_drawing_current(port); - - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* Move on to waiting for the return of Vbus */ - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON); - } - - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - /* - * No Vbus drop likely indicates a non-PD port partner, - * move to the next stage anyway. - */ - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON); - } - return false; - case PS_STATE2: - /* - * Look for the voltage to be above disconnect. Since we didn't - * drop our draw on non-PD partners, they may have dipped below - * vSafe5V but still be in a valid connected voltage. - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) { - /* - * Inform policy engine that power supply - * reset is complete - */ - tc[port].ps_reset_state = PS_STATE0; - pe_ps_reset_complete(port); - - /* - * Now that VBUS is back, let's notify charge manager - * regarding the source's current capabilities. - * sink_power_sub_states() reacts to changes in CC - * terminations, however during a HardReset, the - * terminations of a non-PD port partner will not - * change. Therefore, set the debounce time to right - * now, such that we'll actually reset the correct input - * current limit. - */ - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, 0); - sink_power_sub_states(port); - - /* Power is back, Enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - return true; - } - /* - * If Vbus isn't back after wait + tSrcTurnOn, go unattached - */ - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc[port].ps_reset_state = PS_STATE0; - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - } - - return false; -} -#endif /* CONFIG_USB_PE_SM */ - -void tc_start_error_recovery(int port) -{ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * The port should transition to the ErrorRecovery state - * from any other state when directed. - */ - set_state_tc(port, TC_ERROR_RECOVERY); -} - -static void restart_tc_sm(int port, enum usb_tc_state start_state) -{ - int res; - - /* Clear flags before we transitions states */ - tc[port].flags = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: TCPC init %s", port, res ? "failed" : "ready"); - - /* - * Update the Rp Value. We don't need to update CC lines though as that - * happens in below set_state transition. - */ - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : start_state); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - /* Initialize USB mux to its default state */ - usb_mux_init(port); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* - * Wait to initialize the PPC after tcpc, which sets - * the correct Rd values; otherwise the TCPC might - * not be pulling the CC lines down when the PPC connects the - * CC lines from the USB connector to the TCPC cause the source - * to drop Vbus causing a brown out. - */ - ppc_init(port); - } - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - /* - * Only initialize PD supplier current limit to 0. - * Defer initializing type-C supplier current limit - * to Unattached.SNK or Attached.SNK. - */ - pd_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); - } - - /* - * PD r3.0 v2.0, ss6.2.1.1.5: - * After a physical or logical (USB Type-C Error Recovery) Attach, a - * Port discovers the common Specification Revision level between itself - * and its Port Partner and/or the Cable Plug(s), and uses this - * Specification Revision level until a Detach, Hard Reset or Error - * Recovery happens. - * - * This covers the Error Recovery case, because TC_ERROR_RECOVERY - * reinitializes the TC state machine. This also covers the implicit - * case when PD is suspended and resumed or when the state machine is - * first initialized. - */ - if (IS_ENABLED(CONFIG_USB_PRL_SM)) - prl_set_default_pd_revision(port); - -#ifdef CONFIG_USB_PE_SM - tc_enable_pd(port, 0); - tc[port].ps_reset_state = PS_STATE0; -#endif -} - -void tc_state_init(int port) -{ - enum usb_tc_state first_state; - - /* For test builds, replicate static initialization */ - if (IS_ENABLED(TEST_BUILD)) { - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; ++i) { - memset(&tc[i], 0, sizeof(tc[i])); - drp_state[i] = CONFIG_USB_PD_INITIAL_DRP_STATE; - } - } - - /* If port is not available, there is nothing to initialize */ - if (port >= board_get_usb_pd_port_count()) { - tc_enable_pd(port, 0); - TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - return; - } - - - /* Allow system to set try src enable */ - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - tc_try_src_override(TRY_SRC_NO_OVERRIDE); - - /* - * Set initial PD communication policy. - */ - tc_policy_pd_enable(port, pd_comm_allowed_by_policy()); - -#ifdef HAS_TASK_CHIPSET - /* Set dual-role state based on chipset power state */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - pd_set_dual_role_and_event(port, PD_DRP_FORCE_SINK, 0); - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - pd_set_dual_role_and_event(port, pd_get_drp_state_in_suspend(), 0); - else /* CHIPSET_STATE_ON */ - pd_set_dual_role_and_event(port, PD_DRP_TOGGLE_ON, 0); -#else - pd_set_dual_role_and_event(port, board_tc_get_initial_drp_mode(port), 0); -#endif - - /* - * We are going to apply CC open (start with ErrorRecovery state) - * unless there is something which forbids us to do that (one of - * conditions below is true) - */ - first_state = TC_ERROR_RECOVERY; - - /* - * If we just lost power, don't apply CC open. Otherwise we would boot - * loop, and if this is a fresh power on, then we know there isn't any - * stale PD state as well. - */ - if (system_get_reset_flags() & - (EC_RESET_FLAG_BROWNOUT | EC_RESET_FLAG_POWER_ON)) { - first_state = TC_UNATTACHED_SNK; - } - - /* - * If this is non-EFS2 device, battery is not present and EC RO doesn't - * keep power-on reset flag after reset caused by H1, then don't apply - * CC open because it will cause brown out. - * - * Please note that we are checking if CONFIG_BOARD_RESET_AFTER_POWER_ON - * is defined now, but actually we need to know if it was enabled in - * EC RO! It was assumed that if CONFIG_BOARD_RESET_AFTER_POWER_ON is - * defined now it was defined in EC RO too. - */ - if (!IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON) && - !IS_ENABLED(CONFIG_VBOOT_EFS2) && IS_ENABLED(CONFIG_BATTERY) && - (battery_is_present() == BP_NO)) { - first_state = TC_UNATTACHED_SNK; - } - - if (first_state == TC_UNATTACHED_SNK) { - /* Turn off any previous sourcing */ - tc_src_power_off(port); - set_vconn(port, 0); - } - -#ifdef CONFIG_USB_PD_TCPC_BOARD_INIT - /* Board specific TCPC init */ - board_tcpc_init(); -#endif - - /* - * Start with ErrorRecovery state if we can to put us in - * a clean state from any previous boots. - */ - restart_tc_sm(port, first_state); -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* - * Messages sent by this state machine are always from a DFP/UFP, - * i.e. the chromebook. - */ - return PD_PLUG_FROM_DFP_UFP; -} - -void pd_comm_enable(int port, int en) -{ - tc_policy_pd_enable(port, en); -} - -uint8_t tc_get_polarity(int port) -{ - return tc[port].polarity; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return !tc[port].pd_disabled_mask; -} - -bool pd_alt_mode_capable(int port) -{ - return IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port); -} - -void tc_set_power_role(int port, enum pd_power_role role) -{ - tc[port].power_role = role; -} - -/* - * Private Functions - */ - -/* Set GPIO_CCD_MODE_ODL gpio */ -static void set_ccd_mode(const int port, const bool enable) -{ - if (IS_ENABLED(CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT) && - port == CONFIG_CCD_USBC_PORT_NUMBER) { - if (enable) - CPRINTS("Asserting GPIO_CCD_MODE_ODL"); - gpio_set_level(_GPIO_CCD_MODE_ODL, !enable); - } -} - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - /* Default to returning TC_STATE_COUNT if no state has been set */ - if (tc[port].ctx.current == NULL) - return TC_STATE_COUNT; - else - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -static void print_current_state(const int port) -{ - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS_L1("C%d: %s", port, tc_state_names[get_state_tc(port)]); - else - CPRINTS("C%d: tc-st%d", port, get_state_tc(port)); -} - -static void handle_device_access(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - get_state_tc(port) == TC_LOW_POWER_MODE) { - tc_start_event_loop(port); - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, - PD_LPM_DEBOUNCE_US); - } -} - -void tc_event_check(int port, int evt) -{ -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES - if (evt != TASK_EVENT_TIMER) - print_bits(port, "Event", evt, event_bit_names, - ARRAY_SIZE(event_bit_names)); -#endif - - if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) - TC_SET_FLAG(port, TC_FLAGS_CHECK_CONNECTION); - - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); - - if (evt & PD_EVENT_TCPC_RESET) - reset_device_and_notify(port); - - if (evt & PD_EVENT_RX_HARD_RESET) - pd_execute_hard_reset(port); - - if (evt & PD_EVENT_SEND_HARD_RESET) { - /* Pass Hard Reset request to PE layer if available */ - if (IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port)) - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } - -#ifdef CONFIG_POWER_COMMON - if (IS_ENABLED(CONFIG_POWER_COMMON)) { - if (evt & PD_EVENT_POWER_STATE_CHANGE) - handle_new_power_state(port); - } -#endif /* CONFIG_POWER_COMMON */ - - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - int i; - - /* - * Notify all ports of sysjump - */ - if (evt & PD_EVENT_SYSJUMP) { - for (i = 0; i < - CONFIG_USB_PD_PORT_MAX_COUNT; i++) - dpm_set_mode_exit_request(i); - notify_sysjump_ready(); - } - } - - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -} - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -void tc_set_data_role(int port, enum pd_data_role role) -{ - enum pd_data_role prev_data_role; - - prev_data_role = tc[port].data_role; - tc[port].data_role = role; - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - set_usb_mux_with_current_data_role(port); - - /* - * Run any board-specific code for role swap (e.g. setting OTG signals - * to SoC). - */ - pd_execute_data_swap(port, role); - - /* - * For BC1.2 detection that is triggered on data role change events - * instead of VBUS changes, need to set an event to wake up the USB_CHG - * task and indicate the current data role. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - /* Notify TCPC of role update */ - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - -static void sink_stop_drawing_current(int port) -{ - pd_set_input_current_limit(port, 0, 0); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, CHARGE_CEIL_NONE); - } -} - -static void pd_update_try_source(void) -{ -#ifdef CONFIG_USB_PD_TRY_SRC - tc_enable_try_src(pd_is_try_source_capable()); -#endif -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT); - -static void set_vconn(int port, int enable) -{ - if (enable) - TC_SET_FLAG(port, TC_FLAGS_VCONN_ON); - else - TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON); - - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (IS_ENABLED(CONFIG_USBC_OCP) && - enable && usbc_ocp_is_port_latched_off(port)) - return; - - /* - * Disable PPC Vconn first then TCPC in case the voltage feeds back - * to TCPC and damages. - */ - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && !enable) - ppc_set_vconn(port, 0); - - /* - * Some TCPCs/PPC combinations can trigger OVP if the TCPC doesn't - * source VCONN. This happens if the TCPC will trip OVP with 5V, and the - * PPC doesn't isolate the TCPC from VCONN when sourcing. But, some PPCs - * which do isolate the TCPC can't handle 5V on its host-side CC pins, - * so the TCPC shouldn't source VCONN in those cases. - * - * In the first case, both TCPC and PPC will potentially source Vconn, - * but that should be okay since Vconn has "make before break" - * electrical requirements when swapping anyway. - * - * See b/72961003 and b/180973460 - */ - tcpm_set_vconn(port, enable); - - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && enable) - ppc_set_vconn(port, 1); -} - -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - if (tc[port].power_role == PD_ROLE_SOURCE && - (drp_state[port] == PD_DRP_FORCE_SINK || - (drp_state[port] == PD_DRP_TOGGLE_OFF && - get_state_tc(port) == TC_UNATTACHED_SRC))) { - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is toggle off and we are - * in the source disconnected state). - */ - set_state_tc(port, TC_UNATTACHED_SNK); - } else if (tc[port].power_role == PD_ROLE_SINK && - drp_state[port] == PD_DRP_FORCE_SOURCE) { - /* - * Change to source if port is currently a sink and the - * new DRP state is force source. - */ - set_state_tc(port, TC_UNATTACHED_SRC); - } -} - -__maybe_unused static void handle_new_power_state(int port) -{ - if (!IS_ENABLED(CONFIG_POWER_COMMON)) - assert(0); - - if (IS_ENABLED(CONFIG_POWER_COMMON) && - IS_ENABLED(CONFIG_USB_PE_SM)) { - if (chipset_in_or_transitioning_to_state( - CHIPSET_STATE_ANY_OFF)) { - /* - * The SoC will negotiate alternate mode again when it - * boots up - */ - dpm_set_mode_exit_request(port); - } - } - - /* - * If the sink port was sourcing Vconn, and can no longer, request a - * hard reset on this port to restore Vconn to the source. - */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (tc_is_vconn_src(port) && tc_is_attached_snk(port) && - !pd_check_vconn_swap(port)) - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } - - /* - * TC_FLAGS_UPDATE_USB_MUX is set on chipset startup and shutdown. - * Set the USB mux according to the new power state. If the chipset - * is transitioning to OFF, this disconnects USB and DP mux. - * - * Transitions to and from suspend states do not change the USB mux - * or the alternate mode configuration. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_USB_MUX)) { - TC_CLR_FLAG(port, TC_FLAGS_UPDATE_USB_MUX); - set_usb_mux_with_current_data_role(port); - } -} - -#ifdef CONFIG_USBC_VCONN_SWAP -void pd_request_vconn_swap_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_request_vconn_swap_on(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_request_vconn_swap(int port) -{ - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); -} -#endif - -int tc_is_vconn_src(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - return TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); - else - return 0; -} - -static __maybe_unused int reset_device_and_notify(int port) -{ - int rv; - int task, waiting_tasks; - - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION); - rv = tcpm_init(port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION); - TC_CLR_FLAG(port, TC_FLAGS_LPM_ENGAGED); - tc_start_event_loop(port); - - CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready"); - - /* - * Before getting the other tasks that are waiting, clear the reset - * event from this PD task to prevent multiple reset/init events - * occurring. - * - * The double reset event happens when the higher priority PD interrupt - * task gets an interrupt during the above tcpm_init function. When that - * occurs, the higher priority task waits correctly for us to finish - * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which - * would result in a second, unnecessary init. - */ - atomic_clear_bits(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_clear(&tc[port].tasks_waiting_on_reset); - - /* Wake up all waiting tasks. */ - while (waiting_tasks) { - task = __fls(waiting_tasks); - waiting_tasks &= ~BIT(task); - task_set_event(task, TASK_EVENT_PD_AWAKE); - } - - return rv; -} - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -void pd_wait_exit_low_power(int port) -{ - if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) - return; - - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_TRANSITION)) - reset_device_and_notify(port); - } else { - /* Otherwise, we need to wait for the TCPC reset to complete */ - atomic_or(&tc[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset event while - * it is already processing the reset event. If that occurs, - * then we will reset the TCPC multiple times, which is - * undesirable but most likely benign. Empirically, this doesn't - * happen much, but it if starts occurring, we can add a guard - * to prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TCPC_RESET); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } -} - -/* - * This can be called from any task. If we are in the PD task, we can handle - * immediately. Otherwise, we need to notify the PD task via event. - */ -void pd_device_accessed(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) - handle_device_access(port); - else - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_DEVICE_ACCESSED); -} - -/* - * TODO(b/137493121): Move this function to a separate file that's shared - * between the this and the original stack. - */ -void pd_prevent_low_power_mode(int port, int prevent) -{ - const int current_task_mask = (1 << task_get_current()); - - if (prevent) - atomic_or(&tc[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear_bits(&tc[port].tasks_preventing_lpm, - current_task_mask); -} -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -static void sink_power_sub_states(int port) -{ - enum tcpc_cc_voltage_status cc1, cc2, cc; - enum tcpc_cc_voltage_status new_cc_voltage; - - tcpm_get_cc(port, &cc1, &cc2); - - cc = polarity_rm_dts(tc[port].polarity) ? cc2 : cc1; - - if (cc == TYPEC_CC_VOLT_RP_DEF) - new_cc_voltage = TYPEC_CC_VOLT_RP_DEF; - else if (cc == TYPEC_CC_VOLT_RP_1_5) - new_cc_voltage = TYPEC_CC_VOLT_RP_1_5; - else if (cc == TYPEC_CC_VOLT_RP_3_0) - new_cc_voltage = TYPEC_CC_VOLT_RP_3_0; - else - new_cc_voltage = TYPEC_CC_VOLT_OPEN; - - /* Debounce the cc state */ - if (new_cc_voltage != tc[port].cc_voltage) { - tc[port].cc_voltage = new_cc_voltage; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, - PD_T_RP_VALUE_CHANGE); - return; - } - - if (!pd_timer_is_disabled(port, TC_TIMER_CC_DEBOUNCE)) { - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = usb_get_typec_current_limit( - tc[port].polarity, cc1, cc2); - - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - charge_manager_update_dualrole(port, CAP_DEDICATED); - } - } -} - - -/* - * TYPE-C State Implementations - */ - -/** - * Disabled - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set VBUS and VCONN off - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); - /* - * We have completed tc_cc_open_entry (our super state), so set flag - * to indicate to pd_is_port_enabled that we are now suspended. - */ - TC_SET_FLAG(port, TC_FLAGS_SUSPENDED); -} - -static void tc_disabled_run(const int port) -{ - /* If pd_set_suspend clears the request, go to TC_UNATTACHED_SNK/SRC. */ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) { - set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ? - TC_UNATTACHED_SRC : TC_UNATTACHED_SNK); - } else { - if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) { - if (TC_CHK_FLAG(port, - TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN)) { - TC_CLR_FLAG(port, - TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN); - usb_retimer_fw_update_process_op_cb(port); - } - } - tc_pause_event_loop(port); - } -} - -static void tc_disabled_exit(const int port) -{ - int rv; - - tc_start_event_loop(port); - TC_CLR_FLAG(port, TC_FLAGS_SUSPENDED); - - rv = tcpm_init(port); - CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready"); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set's VBUS and VCONN off - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_ERROR_RECOVERY); - - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY); -} - -static void tc_error_recovery_run(const int port) -{ - enum usb_tc_state start_state; - - if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - /* - * If we transitioned to error recovery as the first state and we - * didn't brown out, we don't need to reinitialized the tc statemachine - * because we just did that. So transition to the state directly. - */ - if (tc[port].ctx.previous == NULL) { - set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ? - TC_UNATTACHED_SRC : TC_UNATTACHED_SNK); - return; - } - - /* - * If try src support is active (e.g. in S0). Then try to become the - * SRC, otherwise we should try to be the sink. - */ - start_state = TC_UNATTACHED_SNK; - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - if (is_try_src_enabled(port) || - drp_state[port] == PD_DRP_FORCE_SOURCE) - start_state = TC_UNATTACHED_SRC; - - restart_tc_sm(port, start_state); -} - -static void tc_error_recovery_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -/** - * Unattached.SNK - */ -static void tc_unattached_snk_entry(const int port) -{ - enum pd_data_role prev_data_role; - - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) { - tc_detached(port); - print_current_state(port); - } - - /* - * We are in an unattached state and considering to be a SNK - * searching for a SRC partner. We set the CC pull value to - * to indicate our intent to be SNK in hopes a partner SRC - * will is there to attach to. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - * - * Restore default current limit Rp in case we swap to source - * - * Run any debug detaches needed before setting CC, as some TCPCs may - * require we set CC Open before changing power roles with a debug - * accessory. - */ - tcpm_debug_detach(port); - typec_select_pull(port, TYPEC_CC_RD); - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - typec_update_cc(port); - - - prev_data_role = tc[port].data_role; - tc[port].data_role = PD_ROLE_DISCONNECTED; - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - - /* - * Indicate that the port is disconnected so the board - * can restore state from any previous data swap. - */ - pd_execute_data_swap(port, PD_ROLE_DISCONNECTED); - pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SNK); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_FLAGS_ON_DISCONNECT(port); - tc_enable_pd(port, 0); - } -} - -static void tc_unattached_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * TODO(b/137498392): Add wait before sampling the CC - * status after role changes - */ - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - tc_set_data_role(port, PD_ROLE_UFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * The port shall transition to AttachWait.SNK when a Source - * connection is detected, as indicated by the SNK.Rp state - * on at least one of its CC pins. - * - * A DRP shall transition to Unattached.SRC within tDRPTransition - * after the state of both CC pins is SNK.Open for - * tDRP − dcSRC.DRP ∙ tDRP. - */ - if (cc_is_rp(cc1) || cc_is_rp(cc2)) { - /* Connection Detected */ - set_state_tc(port, TC_ATTACH_WAIT_SNK); - return; - } - - /* - * Debounce the CC open status. Some TCPC needs time to get the CC - * status valid. Before that, CC open is reported by default. Wait - * to make sure the CC is really open. Reuse the role toggle timer. - */ - if (!pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP)) - return; - - /* - * Initialize type-C supplier current limits to 0. The charge - * manage is now seeded if it was not. - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - typec_set_input_current_limit(port, 0, 0); - - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && - drp_state[port] == PD_DRP_TOGGLE_ON && - tcpm_auto_toggle_supported(port)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - } else if (drp_state[port] == PD_DRP_TOGGLE_ON) { - /* DRP Toggle. The timer was checked above. */ - set_state_tc(port, TC_UNATTACHED_SRC); - } else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - (drp_state[port] == PD_DRP_FORCE_SINK || - drp_state[port] == PD_DRP_TOGGLE_OFF)) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -} - -static void tc_unattached_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP); -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_rp(cc1) && cc_is_rp(cc2) && board_is_dts_port(port)) - new_cc_state = PD_CC_DFP_DEBUG_ACC; - else if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE); - tc[port].cc_state = new_cc_state; - return; - } - - /* - * A DRP shall transition to Unattached.SRC when the state of both - * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce, however - * when DRP state prevents switch to SRC the next state should be - * Unattached.SNK. - */ - if (new_cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) { - /* We are detached */ - if (drp_state[port] == PD_DRP_TOGGLE_OFF - || drp_state[port] == PD_DRP_FREEZE - || drp_state[port] == PD_DRP_FORCE_SINK) - set_state_tc(port, TC_UNATTACHED_SNK); - else - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } - - /* Wait for CC debounce */ - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - /* - * The port shall transition to Attached.SNK after the state of only - * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and - * VBUS is detected. - * - * A DRP that strongly prefers the Source role may optionally - * transition to Try.SRC instead of Attached.SNK when the state of only - * one CC pin has been SNK.Rp for at least tCCDebounce and VBUS is - * detected. - * - * If the port supports Debug Accessory Mode, the port shall transition - * to DebugAccessory.SNK if the state of both the CC1 and CC2 pins is - * SNK.Rp for at least tCCDebounce and VBUS is detected. - */ - if (pd_is_vbus_present(port)) { - if (new_cc_state == PD_CC_DFP_ATTACHED) { - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) && - is_try_src_enabled(port)) - set_state_tc(port, TC_TRY_SRC); - else - set_state_tc(port, TC_ATTACHED_SNK); - } else { - /* new_cc_state is PD_CC_DFP_DEBUG_ACC */ - CPRINTS("C%d: Debug accessory detected", port); - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_ATTACHED_SNK); - } - - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - hook_call_deferred(&pd_usb_billboard_deferred_data, - PD_T_AME); - } - } -} - -static void tc_attach_wait_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE); -} - -/** - * Attached.SNK, shared with Debug Accessory.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* - * Known state of attach is SNK. We need to apply this pull value - * to make it set in hardware at the correct time but set the common - * pull here. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - - /* Inform the PPC and OCP module that a source is connected */ - tc_set_partner_role(port, PPC_DEV_SRC); - - if (IS_ENABLED(CONFIG_USB_PE_SM) && - TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Flipping power role - Disable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* Apply Rd */ - typec_update_cc(port); - - /* Change role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, - tc[port].data_role); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. Do not re-enable - * AutoDischargeDisconnect until the swap is completed - * and tc_pr_swap_complete is called. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - tc_set_data_role(port, PD_ROLE_UFP); - - hook_notify(HOOK_USB_PD_CONNECT); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = - usb_get_typec_current_limit(tc[port].polarity, - cc1, cc2); - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - /* - * Start new connections as dedicated until source caps - * are received, at which point the PE will update the - * flag. - */ - charge_manager_update_dualrole(port, CAP_DEDICATED); - } - - /* Apply Rd */ - typec_update_cc(port); - - /* - * Attached.SNK - enable AutoDischargeDisconnect - * Do this after applying Rd to CC lines to avoid - * TCPC_REG_FAULT_STATUS_AUTO_DISCHARGE_FAIL (b/171567398) - */ - tcpm_enable_auto_discharge_disconnect(port, 1); - } - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - - /* Enable PD */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - tc_enable_pd(port, 1); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - tcpm_debug_accessory(port, 1); - set_ccd_mode(port, 1); - } -} - -/* - * Check whether Vbus has been removed on this port, accounting for some Vbus - * debounce if FRS is enabled. - * - * Returns true if a new state was set and the calling run should exit. - */ -static bool tc_snk_check_vbus_removed(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_FRS)) { - /* - * Debounce Vbus presence when FRS is enabled. Note that we may - * lose Vbus before the FRS signal comes in to let us know - * we're PR swapping, but we must still transition to unattached - * within tSinkDisconnect. - * - * We may safely re-use the Vbus debounce timer here - * since a PR swap would no longer be in progress when Vbus - * removal is checked. - */ - if (pd_check_vbus_level(port, VBUS_REMOVED)) { - if (pd_timer_is_disabled(port, - TC_TIMER_VBUS_DEBOUNCE)) { - pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, - PD_T_FRS_VBUS_DEBOUNCE); - } else if (pd_timer_is_expired(port, - TC_TIMER_VBUS_DEBOUNCE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - } else { - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - } - } else if (pd_check_vbus_level(port, VBUS_REMOVED)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - - return false; -} - -static void tc_attached_snk_run(const int port) -{ -#ifdef CONFIG_USB_PE_SM - /* - * Perform Hard Reset - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - /* - * Wait to clear the hard reset request until Vbus has returned - * to default (or, if it didn't return, we transition to - * unattached) - */ - if (tc_perform_snk_hard_reset(port)) - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - - return; - } - - /* - * From 4.5.2.2.5.2 Exiting from Attached.SNK State: - * - * "A port that is not a Vconn-Powered USB Device and is not in the - * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD - * FR_Swap shall transition to Unattached.SNK within tSinkDisconnect - * when Vbus falls below vSinkDisconnect for Vbus operating at or - * below 5 V or below vSinkDisconnectPD when negotiated by USB PD - * to operate above 5 V." - * - * TODO(b/149530538): Use vSinkDisconnectPD when above 5V - */ - - /* - * Debounce Vbus before we drop that we are doing a PR_Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - pd_timer_is_expired(port, TC_TIMER_VBUS_DEBOUNCE)) { - /* PR Swap is no longer in progress */ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - - /* - * AutoDischargeDisconnect was turned off when we - * hit Safe0V on SRC->SNK PR-Swap. We now are done - * with the swap and should have Vbus, so re-enable - * AutoDischargeDisconnect. - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) - tcpm_enable_auto_discharge_disconnect(port, 1); - } - - /* - * The sink will be powered off during a power role swap but we don't - * want to trigger a disconnect. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Detach detection - */ - if (tc_snk_check_vbus_removed(port)) - return; - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* - * PD swap commands - */ - if (tc_get_pd_enabled(port) && prl_is_running(port)) { - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* - * We may want to verify partner is applying Rd before - * we swap. However, some TCPCs (such as TUSB422) will - * not report the correct CC status before VBUS falls to - * vSafe0V, so this will be problematic in the FRS case. - */ - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - - /* - * Data Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_UFP ? - PD_ROLE_DFP : PD_ROLE_UFP); - } - - /* - * VCONN Swap - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - - set_vconn(port, 1); - /* - * Inform policy engine that vconn swap is - * complete - */ - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, - TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - - set_vconn(port, 0); - /* - * Inform policy engine that vconn swap is - * complete - */ - pe_vconn_swap_complete(port); - } - } - - if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - /* - * If the port supports Charge-Through VCONN-Powered USB - * devices, and an explicit PD contract has failed to be - * negotiated, the port shall query the identity of the - * cable via USB PD on SOP’ - */ - if (!pe_is_explicit_contract(port) && - TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - /* - * A port that via SOP’ has detected an - * attached Charge-Through VCONN-Powered USB - * device shall transition to Unattached.SRC - * if an explicit PD contract has failed to - * be negotiated. - */ - /* CTVPD detected */ - set_state_tc(port, TC_UNATTACHED_SRC); - } - } - } - -#else /* CONFIG_USB_PE_SM */ - - /* Detach detection */ - if (tc_snk_check_vbus_removed(port)) - return; - - /* Run Sink Power Sub-State */ - sink_power_sub_states(port); -#endif /* CONFIG_USB_PE_SM */ -} - -static void tc_attached_snk_exit(const int port) -{ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* - * If supplying VCONN, the port shall cease to supply - * it within tVCONNOFF of exiting Attached.SNK if not - * PR swapping. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* - * Attached.SNK exit - disable AutoDischargeDisconnect - * NOTE: This should not happen if we are suspending. It will - * happen in tc_cc_open_entry if that is the path we are - * taking. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) - tcpm_enable_auto_discharge_disconnect(port, 0); - } - - /* Clear flags after checking Vconn status */ - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - tcpm_debug_detach(port); - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); -} - -/** - * Unattached.SRC - */ -static void tc_unattached_src_entry(const int port) -{ - enum pd_data_role prev_data_role; - - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) { - tc_detached(port); - print_current_state(port); - } - - /* - * We are in an unattached state and considering to be a SRC - * searching for a SNK partner. We set the CC pull value to - * to indicate our intent to be SRC in hopes a partner SNK - * will is there to attach to. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - * - * Restore default current limit Rp. - * - * Run any debug detaches needed before setting CC, as some TCPCs may - * require we set CC Open before changing power roles with a debug - * accessory. - */ - tcpm_debug_detach(port); - typec_select_pull(port, TYPEC_CC_RP); - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - typec_update_cc(port); - - prev_data_role = tc[port].data_role; - tc[port].data_role = PD_ROLE_DISCONNECTED; - - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_FLAGS_ON_DISCONNECT(port); - tc_enable_pd(port, 0); - } - - pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SRC); -} - -static void tc_unattached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - tc_set_data_role(port, PD_ROLE_DFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (usbc_ocp_is_port_latched_off(port)) - return; - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * Transition to AttachWait.SRC when: - * 1) The SRC.Rd state is detected on either CC1 or CC2 pin or - * 2) The SRC.Ra state is detected on both the CC1 and CC2 pins. - * - * A DRP shall transition to Unattached.SNK within tDRPTransition - * after dcSRC.DRP ∙ tDRP - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) - set_state_tc(port, TC_ATTACH_WAIT_SRC); - else if (pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP) && - drp_state[port] != PD_DRP_FORCE_SOURCE && - drp_state[port] != PD_DRP_FREEZE) - set_state_tc(port, TC_UNATTACHED_SNK); - /* - * Attempt TCPC auto DRP toggle - */ - else if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && - drp_state[port] == PD_DRP_TOGGLE_ON && - tcpm_auto_toggle_supported(port) && cc_is_open(cc1, cc2)) - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - (drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_TOGGLE_OFF)) - set_state_tc(port, TC_LOW_POWER_MODE); -} - -static void tc_unattached_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP); -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_snk_dbg_acc(cc1, cc2) && board_is_dts_port(port)) { - /* - * Debug accessory. - * A debug accessory in a non-DTS port will be - * recognized by at_least_one_rd as UFP attached. - */ - new_cc_state = PD_CC_UFP_DEBUG_ACC; - } else if (cc_is_at_least_one_rd(cc1, cc2)) { - /* UFP attached */ - new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc_is_audio_acc(cc1, cc2)) { - /* AUDIO Accessory not supported. Just ignore */ - new_cc_state = PD_CC_UFP_AUDIO_ACC; - } else { - /* No UFP */ - if (drp_state[port] == PD_DRP_FORCE_SOURCE) - set_state_tc(port, TC_UNATTACHED_SRC); - else - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - tc[port].cc_state = new_cc_state; - return; - } - - /* Wait for CC debounce */ - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - /* - * The port shall transition to Attached.SRC when VBUS is at vSafe0V - * and the SRC.Rd state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce. - * - * If the port supports Debug Accessory Mode, it shall transition to - * UnorientedDebugAccessory.SRC when VBUS is at vSafe0V and the SRC.Rd - * state is detected on both the CC1 and CC2 pins for at least - * tCCDebounce. - */ - if (pd_check_vbus_level(port, VBUS_SAFE0V)) { - if (new_cc_state == PD_CC_UFP_ATTACHED) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } else if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { - CPRINTS("C%d: Debug accessory detected", port); - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - } -} - -static void tc_attach_wait_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); -} - -/** - * Attached.SRC, shared with UnorientedDebugAccessory.SRC - */ -static void tc_attached_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - pd_timer_disable(port, TC_TIMER_TIMEOUT); - - /* - * Known state of attach is SRC. We need to apply this pull value - * to make it set in hardware at the correct time but set the common - * pull here. - * - * Both CC1 and CC2 pins shall be independently terminated to - * pulled up through Rp. - * - * Set selected current limit in the hardware. - */ - typec_select_pull(port, TYPEC_CC_RP); - typec_set_source_current_limit(port, tc[port].select_current_limit_rp); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Change role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, - tc[port].power_role, - tc[port].data_role); - - /* Enable VBUS */ - tc_src_power_on(port); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and - * its data role / usb mux connections. Do not - * re-enable AutoDischargeDisconnect until the swap is - * completed and tc_pr_swap_complete is called. - */ - } else { - /* - * Set up CC's, Vconn, and ADD before Vbus, as per - * Figure 4-24. DRP Initialization and Connection - * Detection in TCPCI r2 v1.2 specification. - */ - - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_src_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - /* Attached.SRC - enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - * - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (tc_src_power_on(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, - USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, - tc[port].polarity); - } - - tc_enable_pd(port, 0); - pd_timer_enable(port, TC_TIMER_TIMEOUT, - MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_T_VCONN_STABLE)); - } - } else { - /* - * Set up CC's, Vconn, and ADD before Vbus, as per - * Figure 4-24. DRP Initialization and Connection - * Detection in TCPCI r2 v1.2 specification. - */ - - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_src_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - /* Attached.SRC - enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - * - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (tc_src_power_on(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - } - - /* Inform PPC and OCP module that a sink is connected. */ - tc_set_partner_role(port, PPC_DEV_SNK); - - /* Initialize type-C supplier to seed the charge manger */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - typec_set_input_current_limit(port, 0, 0); - - /* - * Only notify if we're not performing a power role swap. During a - * power role swap, the port partner is not disconnecting/connecting. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - hook_notify(HOOK_USB_PD_CONNECT); - } - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - tcpm_debug_accessory(port, 1); - set_ccd_mode(port, 1); - } - - /* - * Some TCPCs require time to correctly return CC status after - * changing the ROLE_CONTROL register. Due to that, we have to ignore - * CC_NONE state until PD_T_SRC_DISCONNECT delay has elapsed. - * From the "Universal Serial Bus Type-C Cable and Connector - * Specification" Release 2.0 paragraph 4.5.2.2.9.2: - * The Source shall detect the SRC.Open state within tSRCDisconnect, - * but should detect it as quickly as possible - */ - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_SRC_DISCONNECT); -} - -static void tc_attached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (polarity_rm_dts(tc[port].polarity)) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - tc[port].cc_state = PD_CC_NONE; - else - tc[port].cc_state = PD_CC_UFP_ATTACHED; - - /* - * When the SRC.Open state is detected on the monitored CC pin, a DRP - * shall transition to Unattached.SNK unless it strongly prefers the - * Source role. In that case, it shall transition to TryWait.SNK. - * This transition to TryWait.SNK is needed so that two devices that - * both prefer the Source role do not loop endlessly between Source - * and Sink. In other words, a DRP that would enter Try.SRC from - * AttachWait.SNK shall enter TryWait.SNK for a Sink detach from - * Attached.SRC. - */ - if (tc[port].cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) { - bool tryWait; - enum usb_tc_state new_tc_state = TC_UNATTACHED_SNK; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - tryWait = is_try_src_enabled(port) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - - if (drp_state[port] == PD_DRP_FORCE_SOURCE) - new_tc_state = TC_UNATTACHED_SRC; - else if(IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - new_tc_state = tryWait ? - TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK; - - set_state_tc(port, new_tc_state); - return; - } - -#ifdef CONFIG_USB_PE_SM - /* - * Enable PD communications after power supply has fully - * turned on - */ - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc_enable_pd(port, 1); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - } - - if (!tc_get_pd_enabled(port)) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - /* Ignoring Hard Resets while the power supply is resetting.*/ - if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT) && - !pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - if (tc_perform_src_hard_reset(port)) - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - - return; - } - - /* - * PD swap commands - */ - if (tc_get_pd_enabled(port) && prl_is_running(port)) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* Clear TC_FLAGS_REQUEST_PR_SWAP on exit */ - return set_state_tc(port, TC_ATTACHED_SNK); - } - - /* - * Data Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_DFP ? - PD_ROLE_UFP : PD_ROLE_DFP); - } - - /* - * Vconn Swap Request - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - /* - * VCONN Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - set_vconn(port, 1); - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, - TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - set_vconn(port, 0); - pe_vconn_swap_complete(port); - } - } - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - */ - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - * - * If it detects that it is connected to a VCONN-Powered USB - * Device, the port may remove VBUS and discharge it to - * vSafe0V, while continuing to remain in this state with VCONN - * applied. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER) && - TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - - set_state_tc(port, TC_CT_UNATTACHED_SNK); - } - } -#endif - - if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_CURRENT)) { - TC_CLR_FLAG(port, TC_FLAGS_UPDATE_CURRENT); - typec_set_source_current_limit(port, - tc[port].select_current_limit_rp); - pd_update_contract(port); - - /* Update Rp if no contract is present */ - if (!IS_ENABLED(CONFIG_USB_PE_SM) || - !pe_is_explicit_contract(port)) - typec_update_cc(port); - } -} - -static void tc_attached_src_exit(const int port) -{ - /* - * A port shall cease to supply VBUS within tVBUSOFF of exiting - * Attached.SRC. - */ - tc_src_power_off(port); - - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* Attached.SRC exit - disable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* - * Disable VCONN if not power role swapping and - * a CTVPD was not detected - */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) && - !TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) - set_vconn(port, 0); - } - - /* Clear CTVPD detected after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_CTVPD_DETECTED); - - /* Clear PR swap flag after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - tcpm_debug_detach(port); - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -static __maybe_unused void check_drp_connection(const int port) -{ - enum pd_drp_next_states next_state; - enum tcpc_cc_voltage_status cc1, cc2; - - TC_CLR_FLAG(port, TC_FLAGS_CHECK_CONNECTION); - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - tc[port].drp_sink_time = get_time().val; - - /* Get the next toggle state */ - next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time, - tc[port].power_role, drp_state[port], cc1, cc2, - tcpm_auto_toggle_supported(port)); - - if (next_state == DRP_TC_DEFAULT) - next_state = (PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE) - ? DRP_TC_UNATTACHED_SRC - : DRP_TC_UNATTACHED_SNK; - - switch (next_state) { - case DRP_TC_UNATTACHED_SNK: - set_state_tc(port, TC_UNATTACHED_SNK); - break; - case DRP_TC_ATTACHED_WAIT_SNK: - set_state_tc(port, TC_ATTACH_WAIT_SNK); - break; - case DRP_TC_UNATTACHED_SRC: - set_state_tc(port, TC_UNATTACHED_SRC); - break; - case DRP_TC_ATTACHED_WAIT_SRC: - set_state_tc(port, TC_ATTACH_WAIT_SRC); - break; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - case DRP_TC_DRP_AUTO_TOGGLE: - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - break; -#endif - - default: - CPRINTS("C%d: Error: DRP next state %d", port, next_state); - break; - } -} - -/** - * DrpAutoToggle - */ -__maybe_unused static void tc_drp_auto_toggle_entry(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) - assert(0); - - print_current_state(port); - - /* - * We need to ensure that we are waiting in the previous Rd or Rp state - * for the minimum of DRP SNK or SRC so the first toggle cause by - * transition into auto toggle doesn't violate spec timing. - */ - pd_timer_enable(port, TC_TIMER_TIMEOUT, - MAX(PD_T_DRP_SNK, PD_T_DRP_SRC)); -} - -__maybe_unused static void tc_drp_auto_toggle_run(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) - assert(0); - - /* - * A timer is running, but if a connection comes in while waiting - * then allow that to take higher priority. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION)) - check_drp_connection(port); - - else if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) { - if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - pd_timer_disable(port, TC_TIMER_TIMEOUT); - tcpm_enable_drp_toggle(port); - - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - set_state_tc(port, TC_LOW_POWER_MODE); - } - } -} - -__maybe_unused static void tc_drp_auto_toggle_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -__maybe_unused static void tc_low_power_mode_entry(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - assert(0); - - print_current_state(port); - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, PD_LPM_DEBOUNCE_US); -} - -__maybe_unused static void tc_low_power_mode_run(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - assert(0); - - if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION)) { - tc_start_event_loop(port); - if (pd_timer_is_disabled(port, TC_TIMER_LOW_POWER_EXIT_TIME)) { - pd_timer_enable(port, TC_TIMER_LOW_POWER_EXIT_TIME, - PD_LPM_EXIT_DEBOUNCE_US); - } else if (pd_timer_is_expired(port, - TC_TIMER_LOW_POWER_EXIT_TIME)) { - CPRINTS("C%d: Exit Low Power Mode", port); - check_drp_connection(port); - } - return; - } - - if (tc[port].tasks_preventing_lpm) - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, - PD_LPM_DEBOUNCE_US); - - if (pd_timer_is_expired(port, TC_TIMER_LOW_POWER_TIME)) { - CPRINTS("C%d: TCPC Enter Low Power Mode", port); - TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED); - TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION); - tcpm_enter_low_power_mode(port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION); - tc_pause_event_loop(port); - - pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME); - } -} - -__maybe_unused static void tc_low_power_mode_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_LOW_POWER_TIME); - pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME); -} - -/** - * Try.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -#ifdef CONFIG_USB_PD_TRY_SRC -static void tc_try_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; - pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_DRP_TRY); - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_TRY_TIMEOUT); - - /* - * We are a SNK but would prefer to be a SRC. Set the pull to - * indicate we want to be a SRC and looking for a SNK. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - */ - typec_select_pull(port, TYPEC_CC_RP); - - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - /* Apply Rp */ - typec_update_cc(port); -} - -static void tc_try_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if ((cc1 == TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) || - (cc1 != TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD)) - new_cc_state = PD_CC_UFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - } - - /* - * The port shall transition to Attached.SRC when the SRC.Rd state is - * detected on exactly one of the CC1 or CC2 pins for at least - * tTryCCDebounce. - */ - if (new_cc_state == PD_CC_UFP_ATTACHED && - pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - set_state_tc(port, TC_ATTACHED_SRC); - - /* - * The port shall transition to TryWait.SNK after tDRPTry and the - * SRC.Rd state has not been detected and VBUS is within vSafe0V, - * or after tTryTimeout and the SRC.Rd state has not been detected. - */ - if (new_cc_state == PD_CC_NONE) { - if ((pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) && - pd_check_vbus_level(port, VBUS_SAFE0V)) || - pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - set_state_tc(port, TC_TRY_WAIT_SNK); - } - } -} - -static void tc_try_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE); -} - -/** - * TryWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_try_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc_enable_pd(port, 0); - tc[port].cc_state = PD_CC_UNSET; - pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_CC_DEBOUNCE); - - /* - * We were a SNK, tried to be a SRC and it didn't work out. Try to - * go back to being a SNK. Set the pull to indicate we want to be - * a SNK and looking for a SRC. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - - /* Apply Rd */ - typec_update_cc(port); -} - -static void tc_try_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE); - } - - /* - * The port shall transition to Unattached.SNK when the state of both - * of the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (new_cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The port shall transition to Attached.SNK after tCCDebounce if or - * when VBUS is detected. - */ - if (pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) && - pd_is_vbus_present(port)) - set_state_tc(port, TC_ATTACHED_SNK); -} - -static void tc_try_wait_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE); -} -#endif - -/* - * CTUnattached.SNK - */ -__maybe_unused static void tc_ct_unattached_snk_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - print_current_state(port); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - typec_update_cc(port); - - tc[port].cc_state = PD_CC_UNSET; - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); - - /* - * The policy engine is in the disabled state. Disable PD and - * re-enable it - */ - tc_enable_pd(port, 0); - - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_POWER_SUPPLY_TURN_ON_DELAY); -} - -__maybe_unused static void tc_ct_unattached_snk_run(int port) -{ - enum tcpc_cc_voltage_status cc1; - enum tcpc_cc_voltage_status cc2; - enum pd_cc_states new_cc_state; - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) { - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc_enable_pd(port, 1); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - } else { - return; - } - } - - /* Wait until Protocol Layer is ready */ - if (!prl_is_running(port)) - return; - - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_VPDDETACH); - } - - /* - * The port shall transition to Unattached.SNK if the state of - * the CC pin is SNK.Open for tVPDDetach after VBUS is vSafe0V. - */ - else if (pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) { - if (new_cc_state == PD_CC_NONE && - pd_check_vbus_level(port, VBUS_SAFE0V)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } - - /* - * The port shall transition to CTAttached.SNK when VBUS is detected. - */ - if (pd_is_vbus_present(port)) - set_state_tc(port, TC_CT_ATTACHED_SNK); -} - -__maybe_unused static void tc_ct_unattached_snk_exit(int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -/** - * CTAttached.SNK - */ -__maybe_unused static void tc_ct_attached_snk_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - print_current_state(port); - - /* The port shall reject a VCONN swap request. */ - TC_SET_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -__maybe_unused static void tc_ct_attached_snk_run(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* - * A port that is not in the process of a USB PD Hard Reset shall - * transition to CTUnattached.SNK within tSinkDisconnect when VBUS - * falls below vSinkDisconnect - */ - if (pd_check_vbus_level(port, VBUS_REMOVED)) { - set_state_tc(port, TC_CT_UNATTACHED_SNK); - return; - } - - /* - * The port shall operate in one of the Sink Power Sub-States - * and remain within the Sink Power Sub-States, until either VBUS is - * removed or a USB PD contract is established with the source. - */ - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); -} - -__maybe_unused static void tc_ct_attached_snk_exit(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - /* Stop drawing power */ - sink_stop_drawing_current(port); - - TC_CLR_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -/** - * Super State CC_RD - */ -static void tc_cc_rd_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - - -/** - * Super State CC_RP - */ -static void tc_cc_rp_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - -/** - * Super State CC_OPEN - */ -static void tc_cc_open_entry(const int port) -{ - /* Ensure we are not sourcing Vbus */ - tc_src_power_off(port); - - /* Disable VCONN */ - set_vconn(port, 0); - - /* - * Ensure we disable discharging before setting CC lines to open. - * If we were sourcing above, then we already drained Vbus. If partner - * is sourcing Vbus they will drain Vbus if they are PD-capable. This - * should only be done if a battery is present as a batteryless - * device will brown out when AutoDischargeDisconnect is disabled and - * we do not want this to happen until the set_cc open/open to make - * sure the TCPC has managed its internal states for disconnecting - * the only source of power it has. - */ - if (battery_is_present()) - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* - * We may brown out after applying CC open, so flush console first. - * Console flush can take a long time, so if we aren't in danger of - * browning out, don't do it so we can meet certain compliance timing - * requirements. - */ - CPRINTS("C%d: Applying CC Open!", port); - if (!battery_is_present()) - cflush(); - - /* Remove terminations from CC */ - typec_select_pull(port, TYPEC_CC_OPEN); - typec_update_cc(port); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - tc_detached(port); -} - -void tc_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - tc_debug_level = debug_level; -#endif -} - -void tc_usb_firmware_fw_update_limited_run(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_usb_firmware_fw_update_run(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_run(const int port) -{ - /* - * If pd_set_suspend set TC_FLAGS_REQUEST_SUSPEND, go directly to - * TC_DISABLED. - */ - if (get_state_tc(port) != TC_DISABLED - && TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) { - /* Invalidate a contract, if there is one */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_invalidate_explicit_contract(port); - - set_state_tc(port, TC_DISABLED); - } - - /* If error recovery has been requested, transition now */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY)) { - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_invalidate_explicit_contract(port); - set_state_tc(port, TC_ERROR_RECOVERY); - } - - if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) { - if (TC_CHK_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN)) { - TC_CLR_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN); - usb_retimer_fw_update_process_op_cb(port); - } - } - - run_state(port, &tc[port].ctx); -} - -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - if(IS_ENABLED(CONFIG_USB_PE_SM)) - pd_resume_check_pr_swap_needed(i); - - pd_set_dual_role_and_event(i, - PD_DRP_TOGGLE_ON, - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_and_event(i, - pd_get_drp_state_in_suspend(), - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_reset(void) -{ - int i; - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - enum tcpci_msg_type tx; - - /* Do not notify the AP of irrelevant past Hard Resets. */ - pd_clear_events(i, PD_STATUS_EVENT_HARD_RESET); - - /* - * Re-set events for SOP and SOP' discovery complete so the - * kernel knows to consume discovery information for them. - */ - for (tx = TCPCI_MSG_SOP; tx <= TCPCI_MSG_SOP_PRIME; tx++) { - if (pd_get_identity_discovery(i, tx) != PD_DISC_NEEDED - && pd_get_svids_discovery(i, tx) != PD_DISC_NEEDED - && pd_get_modes_discovery(i, tx) != PD_DISC_NEEDED) - pd_notify_event(i, tx == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } - - /* Exit mode so AP can enter mode again after reset */ - if (IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY)) - dpm_set_mode_exit_request(i); - } -} -DECLARE_HOOK(HOOK_CHIPSET_RESET, pd_chipset_reset, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX); - pd_set_dual_role_and_event(i, - pd_get_drp_state_in_suspend(), - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - /* - * Request port discovery to restore any - * alt modes. - * TODO(b/158042116): Do not start port discovery if there - * is an existing connection. - */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pd_dpm_request(i, DPM_REQUEST_PORT_DISCOVERY); - } - - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX); - pd_set_dual_role_and_event(i, - PD_DRP_FORCE_SINK, - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - -static void pd_set_power_change(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE); - } -} -DECLARE_DEFERRED(pd_set_power_change); - -static void pd_chipset_hard_off(void) -{ - /* - * Wait 1 second to check our Vconn sourcing status, as the power rails - * which were supporting it may take some time to change after entering - * G3. - */ - hook_call_deferred(&pd_set_power_change_data, 1 * SECOND); -} -DECLARE_HOOK(HOOK_CHIPSET_HARD_OFF, pd_chipset_hard_off, HOOK_PRIO_DEFAULT); - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * |TC_CC_RD --------------| |TC_CC_RP ------------------------| - * | | | | - * | TC_UNATTACHED_SNK | | TC_UNATTACHED_SRC | - * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC | - * | TC_TRY_WAIT_SNK | | TC_TRY_SRC | - * |-----------------------| |---------------------------------| - * - * |TC_CC_OPEN -----------| - * | | - * | TC_DISABLED | - * | TC_ERROR_RECOVERY | - * |----------------------| - * - * TC_ATTACHED_SNK TC_ATTACHED_SRC TC_DRP_AUTO_TOGGLE TC_LOW_POWER_MODE - * - */ -static __const_data const struct usb_state tc_states[] = { - /* Super States */ - [TC_CC_OPEN] = { - .entry = tc_cc_open_entry, - }, - [TC_CC_RD] = { - .entry = tc_cc_rd_entry, - }, - [TC_CC_RP] = { - .entry = tc_cc_rp_entry, - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .exit = tc_error_recovery_exit, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .exit = tc_unattached_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .exit = tc_attach_wait_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .exit = tc_unattached_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .exit = tc_attach_wait_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - .exit = tc_attached_src_exit, - }, -#ifdef CONFIG_USB_PD_TRY_SRC - [TC_TRY_SRC] = { - .entry = tc_try_src_entry, - .run = tc_try_src_run, - .exit = tc_try_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_TRY_WAIT_SNK] = { - .entry = tc_try_wait_snk_entry, - .run = tc_try_wait_snk_run, - .exit = tc_try_wait_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, -#endif /* CONFIG_USB_PD_TRY_SRC */ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = { - .entry = tc_drp_auto_toggle_entry, - .run = tc_drp_auto_toggle_run, - .exit = tc_drp_auto_toggle_exit, - }, -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = { - .entry = tc_low_power_mode_entry, - .run = tc_low_power_mode_run, - .exit = tc_low_power_mode_exit, - }, -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = { - .entry = tc_ct_unattached_snk_entry, - .run = tc_ct_unattached_snk_run, - .exit = tc_ct_unattached_snk_exit, - }, - [TC_CT_ATTACHED_SNK] = { - .entry = tc_ct_attached_snk_entry, - .run = tc_ct_attached_snk_run, - .exit = tc_ct_attached_snk_exit, - }, -#endif -}; - -#if defined(TEST_BUILD) && defined(USB_PD_DEBUG_LABELS) -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_vpd_sm.c b/common/usbc/usb_tc_vpd_sm.c deleted file mode 100644 index f230d15003..0000000000 --- a/common/usbc/usb_tc_vpd_sm.c +++ /dev/null @@ -1,430 +0,0 @@ -/* 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. - */ - -#include "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "usb_sm.h" -#include "vpd_api.h" - -/* USB Type-C VCONN Powered Device module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -/** - * This is the Type-C Port object that contains information needed to - * implement a VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* Time a port shall wait before it can determine it is attached */ - uint64_t cc_debounce; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD, - TC_HOST_OPEN, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -/* List of human readable state names for console debugging */ -__maybe_unused static const char * const tc_state_names[] = { -#ifdef CONFIG_COMMON_RUNTIME - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", -#endif -}; - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* - * TCPC CC/Rp management - * - * Stub for linking purposes. - * This is not supported for vpd, it uses a different mechanism to update - * cc values. - */ -void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ -} -int typec_update_cc(int port) -{ - return EC_SUCCESS; -} - -/* Public TypeC functions */ - -void tc_state_init(int port) -{ - int res = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: init %s", port, res ? "failed" : "ready"); - - /* Disable TCPC RX until connection is established */ - tcpm_set_rx_enable(port, 0); - - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].flags = 0; -} - -enum pd_power_role pd_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role pd_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP match SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity yet */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -/* - * Private Functions - */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -/** - * Disabled - * - * Super State Entries: - * Enable mcu communication - * Remove the terminations from Host CC - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -void pd_set_suspend(int port, int suspend) -{ - /* - * This shouldn't happen. If it does, we need to send an event to the - * PD task to put the SM into the disabled state. It is not safe to - * directly set_state here since this may be in another task. - */ - assert(false); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tcpm_init(port) != 0) { - CPRINTS("C%d: restart failed!", port); - return; - } - } - - CPRINTS("C%d: resumed!", port); -} - -/** - * Unattached.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_unattached_snk_entry(const int port) -{ - print_current_state(port); -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) - set_state_tc(port, TC_ATTACH_WAIT_SNK); -} - -/** - * AttachedWait.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - /* Forces an initial debounce in run function */ - tc[port].host_cc_state = -1; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); -} - -static void tc_attached_snk_run(const int port) -{ - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - } -} - -static void tc_attached_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; - tc[port].flags &= ~TC_FLAGS_VCONN_ON; -} - -/** - * Super State HOST_RARD - */ -static void tc_host_rard_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); -} - -/** - * Super State HOST_OPEN - */ -static void tc_host_open_entry(const int port) -{ - /* Remove the terminations from Host CC */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ----------------------------------------| - * | | - * | | TC_HOST_RARD -----------| | TC_HOST_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | |-----------------------| | - * | |-------------------------| | - * |--------------------------------------------------------| - * - * TC_ATTACHED_SNK - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD] = { - .entry = tc_host_rard_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN] = { - .entry = tc_host_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usbc_pd_policy.c b/common/usbc/usbc_pd_policy.c deleted file mode 100644 index 6a06d4014f..0000000000 --- a/common/usbc/usbc_pd_policy.c +++ /dev/null @@ -1,48 +0,0 @@ -/* 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 "console.h" -#include "ec_commands.h" -#include "usb_pe_sm.h" -#include "usb_tc_sm.h" - -/* - * TODO(b:159715784): Implement a more robust solution - * to managing PD Policies. - */ - -/* - * Default Port Discovery DR Swap Policy. - * - * 1) If dr_swap_to_dfp_flag == true and port data role is UFP, - * transition to pe_drs_send_swap - */ -__overridable bool port_discovery_dr_swap_policy(int port, - enum pd_data_role dr, bool dr_swap_flag) -{ - if (dr_swap_flag && dr == PD_ROLE_UFP) - return true; - - /* Do not perform a DR swap */ - return false; -} - -/* - * Default Port Discovery VCONN Swap Policy. - * - * 1) If vconn_swap_to_on_flag == true, and vconn is currently off, - * 2) Sourcing VCONN is possible - * then transition to pe_vcs_send_swap - */ -__overridable bool port_discovery_vconn_swap_policy(int port, - bool vconn_swap_flag) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN) && vconn_swap_flag && - !tc_is_vconn_src(port) && tc_check_vconn_swap(port)) - return true; - - /* Do not perform a VCONN swap */ - return false; -} diff --git a/common/usbc/usbc_task.c b/common/usbc/usbc_task.c deleted file mode 100644 index 09be36cd5e..0000000000 --- a/common/usbc/usbc_task.c +++ /dev/null @@ -1,182 +0,0 @@ -/* 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. - */ - -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_timer.h" -#include "usb_prl_sm.h" -#include "tcpm/tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ppc.h" - -#define USBC_EVENT_TIMEOUT (5 * MSEC) -#define USBC_MIN_EVENT_TIMEOUT (1 * MSEC) - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* - * If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then - * _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work. - */ -#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT -extern int _GPIO_CCD_MODE_ODL; -#else -#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL -#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */ - -static uint8_t paused[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void tc_pause_event_loop(int port) -{ - paused[port] = 1; -} - -bool tc_event_loop_is_paused(int port) -{ - return paused[port]; -} - -void tc_start_event_loop(int port) -{ - /* - * Only generate TASK_EVENT_WAKE event if state - * machine is transitioning to un-paused - */ - if (paused[port]) { - paused[port] = 0; - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE); - } -} - -static void pd_task_init(int port) -{ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_state_init(port); - paused[port] = 0; - - /* - * Since most boards configure the TCPC interrupt as edge - * and it is possible that the interrupt line was asserted between init - * and calling set_state, we need to process any pending interrupts now. - * Otherwise future interrupts will never fire because another edge - * never happens. Note this needs to happen after set_state() is called. - */ - if (IS_ENABLED(CONFIG_HAS_TASK_PD_INT)) - schedule_deferred_pd_interrupt(port); - - /* - * GPIO_CCD_MODE_ODL must be initialized with GPIO_ODR_HIGH - * when CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is enabled - */ - if (IS_ENABLED(CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT)) - ASSERT(gpio_get_default_flags(_GPIO_CCD_MODE_ODL) & - GPIO_ODR_HIGH); -} - -static int pd_task_timeout(int port) -{ - int timeout; - - if (paused[port]) - timeout = -1; - else { - timeout = pd_timer_next_expiration(port); - if (timeout < 0 || timeout > USBC_EVENT_TIMEOUT) - timeout = USBC_EVENT_TIMEOUT; - else if (timeout < USBC_MIN_EVENT_TIMEOUT) - timeout = USBC_MIN_EVENT_TIMEOUT; - } - return timeout; -} - -static bool pd_task_loop(int port) -{ - /* wait for next event/packet or timeout expiration */ - const uint32_t evt = task_wait_event(pd_task_timeout(port)); - - /* Manage expired PD Timers on timeouts */ - if (evt & TASK_EVENT_TIMER) - pd_timer_manage_expired(port); - - /* - * Re-use TASK_EVENT_RESET_DONE in tests to restart the USB task - * if this code is running in a unit test. - */ - if (IS_ENABLED(TEST_BUILD) && (evt & TASK_EVENT_RESET_DONE)) - return false; - - /* handle events that affect the state machine as a whole */ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_event_check(port, evt); - - /* - * run port controller task to check CC and/or read incoming - * messages - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC)) - tcpc_run(port, evt); - - /* Run policy engine state machine */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_run(port, evt, tc_get_pd_enabled(port)); - - /* Run protocol state machine */ - if (IS_ENABLED(CONFIG_USB_PRL_SM) || IS_ENABLED(CONFIG_TEST_USB_PE_SM)) - prl_run(port, evt, tc_get_pd_enabled(port)); - - /* Run TypeC state machine */ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_run(port); - - return true; -} - -void pd_task(void *u) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); - - /* - * If port does not exist, return - */ - if (port >= board_get_usb_pd_port_count()) - return; - - while (1) { - pd_timer_init(port); - pd_task_init(port); - - /* As long as pd_task_loop returns true, keep running the loop. - * pd_task_loop returns false when the code needs to re-init - * the task, so once the code breaks out of the inner while - * loop, the re-init code at the top of the outer while loop - * will run. - */ - while (pd_task_loop(port)) - continue; - } -} diff --git a/common/usbc_intr_task.c b/common/usbc_intr_task.c deleted file mode 100644 index 0532645a35..0000000000 --- a/common/usbc_intr_task.c +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright 2020 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. - */ - -/* High-priority interrupt tasks implementations */ - -#include <stdint.h> - -#include "assert.h" -#include "common.h" -#include "compile_time_macros.h" -#include "console.h" -#include "ec_commands.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "timer.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) - -/* Events for pd_interrupt_handler_task */ -#define PD_PROCESS_INTERRUPT BIT(0) - -/* - * Theoretically, we may need to support up to 480 USB-PD packets per second for - * intensive operations such as FW update over PD. This value has tested well - * preventing watchdog resets with a single bad port partner plugged in. - */ -#define ALERT_STORM_MAX_COUNT 480 -#define ALERT_STORM_INTERVAL SECOND - -static uint8_t pd_int_task_id[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void schedule_deferred_pd_interrupt(const int port) -{ - /* - * Don't set event to idle task if task id is 0. This happens when - * not all the port have pd int task, the pd_int_task_id of port - * that doesn't have pd int task is 0. - */ - if (pd_int_task_id[port] != 0) - task_set_event(pd_int_task_id[port], PD_PROCESS_INTERRUPT); -} - -static struct { - int count; - timestamp_t time; -} storm_tracker[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static void service_one_port(int port) -{ - timestamp_t now; - - tcpc_alert(port); - - now = get_time(); - if (timestamp_expired(storm_tracker[port].time, - &now)) { - /* Reset timer into future */ - storm_tracker[port].time.val = now.val + ALERT_STORM_INTERVAL; - - /* - * Start at 1 since we are processing an interrupt right - * now - */ - storm_tracker[port].count = 1; - } else if (++storm_tracker[port].count > ALERT_STORM_MAX_COUNT) { - CPRINTS("C%d: Interrupt storm detected." - " Disabling port temporarily", - port); - - pd_set_suspend(port, 1); - pd_deferred_resume(port); - } -} - -__overridable void board_process_pd_alert(int port) -{ -} - -/* - * Main task entry point that handles PD interrupts for a single port. These - * interrupts usually come from a TCPC, but may also come from PD-related chips - * sharing the TCPC interrupt line. - * - * @param p The PD port number for which to handle interrupts (pointer is - * reinterpreted as an integer directly). - */ -void pd_interrupt_handler_task(void *p) -{ - const int port = (int) ((intptr_t) p); - const int port_mask = (PD_STATUS_TCPC_ALERT_0 << port); - - ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); - - /* - * If port does not exist, return - */ - if (port >= board_get_usb_pd_port_count()) - return; - - pd_int_task_id[port] = task_get_current(); - - while (1) { - const int evt = task_wait_event(-1); - - if ((evt & PD_PROCESS_INTERRUPT) == 0) - continue; - /* - * While the interrupt signal is asserted; we have more - * work to do. This effectively makes the interrupt a - * level-interrupt instead of an edge-interrupt without - * having to enable/disable a real level-interrupt in - * multiple locations. - * - * Also, if the port is disabled do not process - * interrupts. Upon existing suspend, we schedule a - * PD_PROCESS_INTERRUPT to check if we missed anything. - */ - while ((tcpc_get_alert_status() & port_mask) && - pd_is_port_enabled(port)) { - - service_one_port(port); - } - - board_process_pd_alert(port); - } -} - -/* - * This code assumes port alert masks are adjacent to each other. - */ -BUILD_ASSERT(PD_STATUS_TCPC_ALERT_3 == (PD_STATUS_TCPC_ALERT_0 << 3)); - -/* - * Shared TCPC interrupt handler. The function argument in ec.tasklist - * is the mask of ports to handle. For example: - * - * BIT(USBC_PORT_C2) | BIT(USBC_PORT_C0) - * - * Note that this bitmask is 0-based while PD_STATUS_TCPC_ALERT_<port> - * is not. - */ - -void pd_shared_alert_task(void *p) -{ - const int sources_mask = (int) ((intptr_t) p); - int want_alerts = 0; - int port; - int port_mask; - - CPRINTS("%s: port mask 0x%02x", __func__, sources_mask); - - for (port = 0; port < CONFIG_USB_PD_PORT_MAX_COUNT; ++port) { - if ((sources_mask & BIT(port)) == 0) - continue; - if (!board_is_usb_pd_port_present(port)) - continue; - - port_mask = PD_STATUS_TCPC_ALERT_0 << port; - want_alerts |= port_mask; - pd_int_task_id[port] = task_get_current(); - } - - if (want_alerts == 0) { - /* - * None of the configured alert sources are available. - */ - return; - } - - while (1) { - const int evt = task_wait_event(-1); - int have_alerts; - - if ((evt & PD_PROCESS_INTERRUPT) == 0) - continue; - - /* - * While the interrupt signal is asserted; we have more - * work to do. This effectively makes the interrupt a - * level-interrupt instead of an edge-interrupt without - * having to enable/disable a real level-interrupt in - * multiple locations. - * - * Also, if the port is disabled do not process - * interrupts. Upon existing suspend, we schedule a - * PD_PROCESS_INTERRUPT to check if we missed anything. - */ - do { - have_alerts = tcpc_get_alert_status(); - have_alerts &= want_alerts; - - for (port = 0; port < CONFIG_USB_PD_PORT_MAX_COUNT; - ++port) { - port_mask = PD_STATUS_TCPC_ALERT_0 << port; - if ((have_alerts & port_mask) == 0) { - /* skip quiet port */ - continue; - } - if (!pd_is_port_enabled(port)) { - /* filter out disabled port */ - have_alerts &= ~port_mask; - continue; - } - service_one_port(port); - } - } while (have_alerts != 0); - } -} diff --git a/common/usbc_ocp.c b/common/usbc_ocp.c deleted file mode 100644 index e20cf9f1f8..0000000000 --- a/common/usbc_ocp.c +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright 2020 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. - */ - -/* USB-C Overcurrent Protection Common Code */ - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "timer.h" -#include "usb_pd.h" -#include "usbc_ocp.h" -#include "util.h" - -#ifndef TEST_BUILD -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(args...) -#define CPRINTS(args...) -#endif - -/* - * Number of times a port may overcurrent before we latch off the port until a - * physical disconnect is detected. - */ -#define OCP_CNT_THRESH 3 - -/* - * Number of seconds until a latched-off port is re-enabled for sourcing after - * detecting a physical disconnect. - */ -#define OCP_COOLDOWN_DELAY_US (2 * SECOND) - -/* - * A per-port table that indicates how many VBUS overcurrent events have - * occurred. This table is cleared after detecting a physical disconnect of the - * sink. - */ -static uint8_t oc_event_cnt_tbl[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* A flag for ports with sink device connected. */ -static uint32_t snk_connected_ports; - -static void clear_oc_tbl(void) -{ - int port; - - for (port = 0; port < board_get_usb_pd_port_count(); port++) - /* - * Only clear the table if the port partner is no longer - * attached after debouncing. - */ - if ((!(BIT(port) & snk_connected_ports)) && - oc_event_cnt_tbl[port]) { - oc_event_cnt_tbl[port] = 0; - CPRINTS("C%d: OC events cleared", port); - } -} -DECLARE_DEFERRED(clear_oc_tbl); - -int usbc_ocp_add_event(int port) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - oc_event_cnt_tbl[port]++; - - /* The port overcurrented, so don't clear it's OC events. */ - atomic_clear_bits(&snk_connected_ports, 1 << port); - - if (oc_event_cnt_tbl[port] >= OCP_CNT_THRESH) - CPRINTS("C%d: OC event limit reached! " - "Source path disabled until physical disconnect.", - port); - return EC_SUCCESS; -} - - -int usbc_ocp_clear_event_counter(int port) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - /* - * If we are clearing our event table in quick succession, we may be in - * an overcurrent loop where we are also detecting a disconnect on the - * CC pins. Therefore, let's not clear it just yet and the let the - * limit be reached. This way, we won't send the hard reset and - * actually detect the physical disconnect. - */ - if (oc_event_cnt_tbl[port]) { - hook_call_deferred(&clear_oc_tbl_data, - OCP_COOLDOWN_DELAY_US); - } - return EC_SUCCESS; -} - -int usbc_ocp_is_port_latched_off(int port) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return 0; - } - - return oc_event_cnt_tbl[port] >= OCP_CNT_THRESH; -} - -void usbc_ocp_snk_is_connected(int port, bool connected) -{ - if ((port < 0) || (port >= board_get_usb_pd_port_count())) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return; - } - - if (connected) - atomic_or(&snk_connected_ports, 1 << port); - else - atomic_clear_bits(&snk_connected_ports, 1 << port); -} - -__overridable void board_overcurrent_event(int port, int is_overcurrented) -{ - /* Do nothing by default. Boards with overcurrent GPIOs may override */ -} diff --git a/common/usbc_ppc.c b/common/usbc_ppc.c deleted file mode 100644 index 0281ceaf64..0000000000 --- a/common/usbc_ppc.c +++ /dev/null @@ -1,307 +0,0 @@ -/* Copyright 2017 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. - */ - -/* USB-C Power Path Controller Common Code */ - -#include "atomic.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "timer.h" -#include "usb_pd.h" -#include "usbc_ppc.h" -#include "util.h" - -#ifndef TEST_BUILD -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(args...) -#define CPRINTS(args...) -#endif - -int ppc_prints(const char *string, int port) -{ -#ifndef TEST_BUILD - return CPRINTS("ppc p%d %s", port, string); -#else - return 0; -#endif -} - -int ppc_err_prints(const char *string, int port, int error) -{ -#ifndef TEST_BUILD - return CPRINTS("ppc p%d %s (%d)", port, string, error); -#else - return 0; -#endif -} - -/* Simple wrappers to dispatch to the drivers. */ - -int ppc_init(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->init) { - rv = ppc->drv->init(port); - if (rv) - ppc_err_prints("init failed!", port, rv); - else - ppc_prints("init'd.", port); - } - - return rv; -} - -int ppc_is_sourcing_vbus(int port) -{ - int rv = 0; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return 0; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->is_sourcing_vbus) - rv = ppc->drv->is_sourcing_vbus(port); - - return rv; -} - -#ifdef CONFIG_USBC_PPC_POLARITY -int ppc_set_polarity(int port, int polarity) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_polarity) - rv = ppc->drv->set_polarity(port, polarity); - - return rv; -} -#endif - -int ppc_set_vbus_source_current_limit(int port, enum tcpc_rp_value rp) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_vbus_source_current_limit) - rv = ppc->drv->set_vbus_source_current_limit(port, rp); - - return rv; -} - -int ppc_discharge_vbus(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->discharge_vbus) - rv = ppc->drv->discharge_vbus(port, enable); - - return rv; -} - -#ifdef CONFIG_USBC_PPC_SBU -int ppc_set_sbu(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_sbu) - rv = ppc->drv->set_sbu(port, enable); - - return rv; -} -#endif /* defined(CONFIG_USBC_PPC_SBU) */ - -#ifdef CONFIG_USBC_PPC_VCONN -int ppc_set_vconn(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->set_vconn) - rv = ppc->drv->set_vconn(port, enable); - - return rv; -} -#endif - -int ppc_dev_is_connected(int port, enum ppc_device_role dev) -{ - int rv = EC_SUCCESS; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->dev_is_connected) - rv = ppc->drv->dev_is_connected(port, dev); - - return rv; -} - -int ppc_vbus_sink_enable(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->vbus_sink_enable) - rv = ppc->drv->vbus_sink_enable(port, enable); - - return rv; -} - -int ppc_enter_low_power_mode(int port) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->enter_low_power_mode) - rv = ppc->drv->enter_low_power_mode(port); - - return rv; -} - -int ppc_vbus_source_enable(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->vbus_source_enable) - rv = ppc->drv->vbus_source_enable(port, enable); - - return rv; -} - -#ifdef CONFIG_USB_PD_FRS_PPC -int ppc_set_frs_enable(int port, int enable) -{ - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - - if (ppc->drv->set_frs_enable) - rv = ppc->drv->set_frs_enable(port,enable); - - return rv; -} -#endif /* defined(CONFIG_USB_PD_FRS_PPC) */ - -#ifdef CONFIG_USB_PD_VBUS_DETECT_PPC -int ppc_is_vbus_present(int port) -{ - int rv = 0; - const struct ppc_config_t *ppc; - - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return 0; - } - - ppc = &ppc_chips[port]; - - if (ppc->drv->is_vbus_present) - rv = ppc->drv->is_vbus_present(port); - - return rv; -} -#endif /* defined(CONFIG_USB_PD_VBUS_DETECT_PPC) */ - -#ifdef CONFIG_CMD_PPC_DUMP -static int command_ppc_dump(int argc, char **argv) -{ - int port; - int rv = EC_ERROR_UNIMPLEMENTED; - const struct ppc_config_t *ppc; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - port = atoi(argv[1]); - if ((port < 0) || (port >= ppc_cnt)) { - CPRINTS("%s(%d) Invalid port!", __func__, port); - return EC_ERROR_INVAL; - } - - ppc = &ppc_chips[port]; - if (ppc->drv->reg_dump) - rv = ppc->drv->reg_dump(port); - - return rv; -} -DECLARE_CONSOLE_COMMAND(ppc_dump, command_ppc_dump, "<Type-C port>", - "dump the PPC regs"); -#endif /* defined(CONFIG_CMD_PPC_DUMP) */ diff --git a/common/vboot/common.c b/common/vboot/common.c deleted file mode 100644 index 39f8c193c7..0000000000 --- a/common/vboot/common.c +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2017 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 "common.h" -#include "console.h" -#include "rsa.h" -#include "sha256.h" -#include "shared_mem.h" -#include "vboot.h" - -#define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_VBOOT, format, ## args) - -int vboot_is_padding_valid(const uint8_t *data, uint32_t start, uint32_t end) -{ - const uint32_t *data32 = (const uint32_t *)data; - int i; - - if (start > end) - return EC_ERROR_INVAL; - - if (start % 4 || end % 4) - return EC_ERROR_INVAL; - - for (i = start / 4; i < end / 4; i++) { - if (data32[i] != 0xffffffff) - return EC_ERROR_INVAL; - } - - return EC_SUCCESS; -} - -int vboot_verify(const uint8_t *data, int len, - const struct rsa_public_key *key, const uint8_t *sig) -{ - struct sha256_ctx ctx; - uint8_t *hash; - uint32_t *workbuf; - int err = EC_SUCCESS; - - if (SHARED_MEM_ACQUIRE_CHECK(3 * RSANUMBYTES, (char **)&workbuf)) - return EC_ERROR_MEMORY_ALLOCATION; - - /* Compute hash of the RW firmware */ - SHA256_init(&ctx); - SHA256_update(&ctx, data, len); - hash = SHA256_final(&ctx); - - /* Verify the data */ - if (rsa_verify(key, sig, hash, workbuf) != 1) - err = EC_ERROR_VBOOT_DATA_VERIFY; - - shared_mem_release(workbuf); - - return err; -} diff --git a/common/vboot/efs2.c b/common/vboot/efs2.c deleted file mode 100644 index e5c3b64f04..0000000000 --- a/common/vboot/efs2.c +++ /dev/null @@ -1,352 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Early Firmware Selection ver.2. - * - * Verify and jump to a RW image. Register boot mode to Cr50. - */ - -#include "battery.h" -#include "chipset.h" -#include "clock.h" -#include "compile_time_macros.h" -#include "console.h" -#include "crc8.h" -#include "flash.h" -#include "hooks.h" -#include "sha256.h" -#include "system.h" -#include "task.h" -#include "usb_pd.h" -#include "uart.h" -#include "vboot.h" -#include "vboot_hash.h" - -#define CPRINTS(format, args...) cprints(CC_VBOOT,"VB " format, ## args) -#define CPRINTF(format, args...) cprintf(CC_VBOOT,"VB " format, ## args) - -static const char *boot_mode_to_string(uint8_t mode) -{ - static const char *boot_mode_str[] = { - [BOOT_MODE_NORMAL] = "NORMAL", - [BOOT_MODE_NO_BOOT] = "NO_BOOT", - }; - if (mode < ARRAY_SIZE(boot_mode_str)) - return boot_mode_str[mode]; - return "UNDEF"; -} - -/* - * Check whether the session has successfully ended or not. ERR_TIMEOUT is - * excluded because it's an internal error produced by EC itself. - */ -static bool is_valid_cr50_response(enum cr50_comm_err code) -{ - return code != CR50_COMM_ERR_TIMEOUT - && (code >> 8) == CR50_COMM_ERR_PREFIX; -} - -__overridable void board_enable_packet_mode(bool enable) -{ - /* - * This can be done by set_flags(INPUT|PULL_UP). We don't need it now - * because Cr50 never initiates communication. - */ - gpio_set_level(GPIO_PACKET_MODE_EN, enable ? 1 : 0); -} - -static enum cr50_comm_err send_to_cr50(const uint8_t *data, size_t size) -{ - timestamp_t until; - int i, timeout = 0; - uint32_t lock_key; - struct cr50_comm_response res = {}; - - /* This will wake up (if it's sleeping) and interrupt Cr50. */ - board_enable_packet_mode(true); - - uart_flush_output(); - uart_clear_input(); - - if (uart_shell_stop()) { - /* Failed to stop the shell. */ - board_enable_packet_mode(false); - return CR50_COMM_ERR_UNKNOWN; - } - - /* - * Send packet. No traffic control, assuming Cr50 consumes stream much - * faster. TX buffer shouldn't overflow because it's cleared above and - * much bigger than the max packet size. - * - * Disable interrupts so that the data frame will be stored in the Tx - * buffer in one piece. - */ - lock_key = irq_lock(); - uart_put_raw(data, size); - irq_unlock(lock_key); - - uart_flush_output(); - - until.val = get_time().val + CR50_COMM_TIMEOUT; - - /* - * Make sure console task won't steal the response in case we exchange - * packets after tasks start. - */ -#ifndef CONFIG_ZEPHYR - if (task_start_called()) - task_disable_task(TASK_ID_CONSOLE); -#endif /* !CONFIG_ZEPHYR */ - - /* Wait for response from Cr50 */ - for (i = 0; i < sizeof(res); i++) { - while (!timeout) { - int c = uart_getc(); - if (c != -1) { - res.error = res.error | c << (i*8); - break; - } - msleep(1); - timeout = timestamp_expired(until, NULL); - } - } - - uart_shell_start(); -#ifndef CONFIG_ZEPHYR - if (task_start_called()) - task_enable_task(TASK_ID_CONSOLE); -#endif /* CONFIG_ZEPHYR */ - - /* Exit packet mode */ - board_enable_packet_mode(false); - - CPRINTS("Received 0x%04x", res.error); - - if (timeout) { - CPRINTS("Timeout"); - return CR50_COMM_ERR_TIMEOUT; - } - - return res.error; -} - -static enum cr50_comm_err cmd_to_cr50(enum cr50_comm_cmd cmd, - const uint8_t *data, size_t size) -{ - /* - * This is on the stack instead of .bss because vboot_main currently is - * called only once (from main). Keeping the space unused in .bss would - * be wasteful. - */ - struct { - uint8_t preamble[CR50_UART_RX_BUFFER_SIZE]; - uint8_t packet[CR50_COMM_MAX_REQUEST_SIZE]; - } __packed s; - struct cr50_comm_request *p = (struct cr50_comm_request *)s.packet; - int retry = CR50_COMM_MAX_RETRY; - enum cr50_comm_err rv; - - /* compose a frame = preamble + packet */ - memset(s.preamble, CR50_COMM_PREAMBLE, sizeof(s.preamble)); - p->magic = CR50_PACKET_MAGIC; - p->struct_version = CR50_COMM_PACKET_VERSION; - p->type = cmd; - p->size = size; - memcpy(p->data, data, size); - p->crc = cros_crc8((uint8_t *)&p->type, - sizeof(p->type) + sizeof(p->size) + size); - - do { - rv = send_to_cr50((uint8_t *)&s, - sizeof(s.preamble) + sizeof(*p) + p->size); - if (is_valid_cr50_response(rv)) - break; - msleep(5); - } while (--retry); - - return rv; -} - -static enum cr50_comm_err verify_hash(void) -{ - const uint8_t *hash; - int rv; - - /* Wake up Cr50 beforehand in case it's asleep. */ - board_enable_packet_mode(true); - CPRINTS("Ping Cr50"); - msleep(1); - board_enable_packet_mode(false); - - rv = vboot_get_rw_hash(&hash); - if (rv) - return rv; - - CPRINTS("Verifying hash"); - return cmd_to_cr50(CR50_COMM_CMD_VERIFY_HASH, hash, SHA256_DIGEST_SIZE); -} - -static enum cr50_comm_err set_boot_mode(uint8_t mode) -{ - enum cr50_comm_err rv; - - CPRINTS("Setting boot mode to %s(%d)", boot_mode_to_string(mode), mode); - rv = cmd_to_cr50(CR50_COMM_CMD_SET_BOOT_MODE, - &mode, sizeof(enum boot_mode)); - if (rv != CR50_COMM_SUCCESS) - CPRINTS("Failed to set boot mode"); - return rv; -} - -static bool pd_comm_enabled; - -static void enable_pd(void) -{ - CPRINTS("Enable USB-PD"); - pd_comm_enabled = true; -} - -bool vboot_allow_usb_pd(void) -{ - return pd_comm_enabled; -} - -__overridable void show_critical_error(void) -{ - CPRINTS("%s", __func__); -} - -static void verify_and_jump(void) -{ - enum cr50_comm_err rv = verify_hash(); - - switch (rv) { - case CR50_COMM_ERR_BAD_PAYLOAD: - /* Cr50 should have set NO_BOOT. */ - CPRINTS("Hash mismatch"); - enable_pd(); - break; - case CR50_COMM_SUCCESS: - system_set_reset_flags(EC_RESET_FLAG_EFS); - rv = system_run_image_copy(EC_IMAGE_RW); - CPRINTS("Failed to jump (0x%x)", rv); - system_clear_reset_flags(EC_RESET_FLAG_EFS); - show_critical_error(); - break; - default: - CPRINTS("Failed to verify RW (0x%x)", rv); - show_critical_error(); - } -} - -__overridable void show_power_shortage(void) -{ - CPRINTS("%s", __func__); -} - -static bool is_battery_ready(void) -{ - /* TODO: Add battery check (https://crbug.com/1045216) */ - return true; -} - -void vboot_main(void) -{ - CPRINTS("Main"); - - if (system_is_in_rw()) { - /* - * We come here and immediately return. LED shows power shortage - * but it will be immediately corrected if the adapter can - * provide enough power. - */ - CPRINTS("Already in RW"); - show_power_shortage(); - return; - } - - if (system_is_manual_recovery() || - (system_get_reset_flags() & EC_RESET_FLAG_STAY_IN_RO)) { - if (system_is_manual_recovery()) - CPRINTS("In recovery mode"); - if (!IS_ENABLED(CONFIG_BATTERY) - && !IS_ENABLED(HAS_TASK_KEYSCAN)) { - /* - * For Chromeboxes, we relax security by allowing PD in - * RO. Attackers don't gain meaningful advantage on - * built-in-keyboard-less systems. - * - * Alternatively, we can use NO_BOOT to show a firmware - * screen, strictly requiring BJ adapter and keeping PD - * disabled. - */ - enable_pd(); - return; - } - - /* - * If battery is drained or bad, we will boot in NO_BOOT mode to - * inform the user of the problem. - */ - if (!is_battery_ready()) { - CPRINTS("Battery not ready or bad"); - if (set_boot_mode(BOOT_MODE_NO_BOOT) == - CR50_COMM_SUCCESS) - enable_pd(); - } - - /* We'll enter recovery mode immediately, later, or never. */ - return; - } - - verify_and_jump(); - - /* - * EFS failed. EC-RO may be able to boot AP if: - * - * - Battery is charged or - * - AC adapter supply in RO >= Boot threshold or - * - BJ adapter is plugged. - * - * Once AP boots, software sync will fix the mismatch. If that's the - * reason of the failure, we won't come back here next time. - */ - CPRINTS("Exit"); -} - -void hook_shutdown(void) -{ - CPRINTS("%s", __func__); - - /* - * We filter the cases which can be interfered with if we execute - * system_reset in HOOK_CHIPSET_SHUTDOWN context. Most cases are - * filtered out by system_is_in_rw (e.g. system_common_shutdown, - * check_pending_cutoff). - */ - if (system_is_in_rw()) - return; - - /* - * We can't reset here because it'll completely tear down the power - * and disturb the PCH's power sequence. We instead sysjump. - * - * Note that this does not reduce the security. Even if it's hijacked in - * NO_BOOT mode, an RO still needs to go through a cold reset to clear - * NO_BOOT flag since Cr50 rejects to switch from NO_BOOT to NORMAL. - * If a spoofed matching hash is passed to Cr50, Cr50 would reset EC. - */ - system_set_reset_flags(EC_RESET_FLAG_AP_IDLE); - verify_and_jump(); -} -/* - * There can be hooks which are needed to set external chips to a certain state - * in S5. If the initial state (i.e. AP_OFF state) is different from what those - * hooks realize, they need to be considered. This hook runs last (i.e. - * HOOK_PRIO_LAST) to make our landing on S5 as mild as possible. - */ -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN_COMPLETE, hook_shutdown, HOOK_PRIO_LAST); diff --git a/common/vboot/vb21_lib.c b/common/vboot/vb21_lib.c deleted file mode 100644 index 4e215c14e5..0000000000 --- a/common/vboot/vb21_lib.c +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright 2017 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. - */ - -/* - * Common utility APIs for vboot 2.1 - */ - -#include "common.h" -#include "host_command.h" -#include "rsa.h" -#include "rwsig.h" -#include "system.h" -#include "vb21_struct.h" -#include "vboot.h" - -int vb21_is_packed_key_valid(const struct vb21_packed_key *key) -{ - if (key->c.magic != VB21_MAGIC_PACKED_KEY) - return EC_ERROR_VBOOT_KEY_MAGIC; - if (key->key_size != sizeof(struct rsa_public_key)) - return EC_ERROR_VBOOT_KEY_SIZE; - return EC_SUCCESS; -} - -int vb21_is_signature_valid(const struct vb21_signature *sig, - const struct vb21_packed_key *key) -{ - if (sig->c.magic != VB21_MAGIC_SIGNATURE) - return EC_ERROR_VBOOT_SIG_MAGIC; - if (sig->sig_size != RSANUMBYTES) - return EC_ERROR_VBOOT_SIG_SIZE; - if (key->sig_alg != sig->sig_alg) - return EC_ERROR_VBOOT_SIG_ALGORITHM; - if (key->hash_alg != sig->hash_alg) - return EC_ERROR_VBOOT_HASH_ALGORITHM; - /* Validity check signature offset and data size. */ - if (sig->sig_offset < sizeof(*sig)) - return EC_ERROR_VBOOT_SIG_OFFSET; - if (sig->sig_offset + RSANUMBYTES > CONFIG_RW_SIG_SIZE) - return EC_ERROR_VBOOT_SIG_OFFSET; - if (sig->data_size > CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE) - return EC_ERROR_VBOOT_DATA_SIZE; - return EC_SUCCESS; -} - -const struct vb21_packed_key *vb21_get_packed_key(void) -{ - return (const struct vb21_packed_key *)(CONFIG_RO_PUBKEY_ADDR); -} - -static void read_rwsig_info(struct ec_response_rwsig_info *r) -{ - - const struct vb21_packed_key *vb21_key; - int rv; - - vb21_key = vb21_get_packed_key(); - - r->sig_alg = vb21_key->sig_alg; - r->hash_alg = vb21_key->hash_alg; - r->key_version = vb21_key->key_version; - { BUILD_ASSERT(sizeof(r->key_id) == sizeof(vb21_key->id), - "key ID sizes must match"); } - { BUILD_ASSERT(sizeof(vb21_key->id) == sizeof(vb21_key->id.raw), - "key ID sizes must match"); } - memcpy(r->key_id, vb21_key->id.raw, sizeof(r->key_id)); - - rv = vb21_is_packed_key_valid(vb21_key); - r->key_is_valid = (rv == EC_SUCCESS); -} - -static int command_rwsig_info(int argc, char **argv) -{ - int i; - struct ec_response_rwsig_info r; - - read_rwsig_info(&r); - - ccprintf("sig_alg: %d\n", r.sig_alg); - ccprintf("key_version: %d\n", r.key_version); - ccprintf("hash_alg: %d\n", r.hash_alg); - ccprintf("key_is_valid: %d\n", r.key_is_valid); - - ccprintf("key_id: "); - for (i = 0; i < sizeof(r.key_id); i++) - ccprintf("%x", r.key_id[i]); - ccprintf("\n"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(rwsiginfo, command_rwsig_info, NULL, - "Display rwsig info on console."); - -static enum ec_status -host_command_rwsig_info(struct host_cmd_handler_args *args) -{ - struct ec_response_rwsig_info *r = args->response; - - read_rwsig_info(r); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_RWSIG_INFO, host_command_rwsig_info, - EC_VER_MASK(EC_VER_RWSIG_INFO)); diff --git a/common/vboot/vboot.c b/common/vboot/vboot.c deleted file mode 100644 index 910156335d..0000000000 --- a/common/vboot/vboot.c +++ /dev/null @@ -1,228 +0,0 @@ -/* Copyright 2017 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. - */ - -/* - * Verify and jump to a RW image if power supply is not sufficient. - */ - -#include "battery.h" -#include "charge_manager.h" -#include "chipset.h" -#include "clock.h" -#include "console.h" -#include "flash.h" -#include "hooks.h" -#include "host_command.h" -#include "rsa.h" -#include "rwsig.h" -#include "stdbool.h" -#include "sha256.h" -#include "shared_mem.h" -#include "system.h" -#include "usb_pd.h" -#include "vboot.h" -#include "vb21_struct.h" - -#define CPRINTS(format, args...) cprints(CC_VBOOT,"VB " format, ## args) -#define CPRINTF(format, args...) cprintf(CC_VBOOT,"VB " format, ## args) - -static int has_matrix_keyboard(void) -{ - return 0; -} - -static int verify_slot(enum ec_image slot) -{ - const struct vb21_packed_key *vb21_key; - const struct vb21_signature *vb21_sig; - const struct rsa_public_key *key; - const uint8_t *sig; - const uint8_t *data; - int len; - int rv; - - CPRINTS("Verifying %s", ec_image_to_string(slot)); - - vb21_key = (const struct vb21_packed_key *)( - CONFIG_MAPPED_STORAGE_BASE + - CONFIG_EC_PROTECTED_STORAGE_OFF + - CONFIG_RO_PUBKEY_STORAGE_OFF); - rv = vb21_is_packed_key_valid(vb21_key); - if (rv) { - CPRINTS("Invalid key (%d)", rv); - return EC_ERROR_VBOOT_KEY; - } - key = (const struct rsa_public_key *) - ((const uint8_t *)vb21_key + vb21_key->key_offset); - - if (slot == EC_IMAGE_RW_A) { - data = (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE + - CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_A_STORAGE_OFF); - vb21_sig = (const struct vb21_signature *)( - CONFIG_MAPPED_STORAGE_BASE + - CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_A_SIGN_STORAGE_OFF); - } else { - data = (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE + - CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_B_STORAGE_OFF); - vb21_sig = (const struct vb21_signature *)( - CONFIG_MAPPED_STORAGE_BASE + - CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_B_SIGN_STORAGE_OFF); - } - - rv = vb21_is_signature_valid(vb21_sig, vb21_key); - if (rv) { - CPRINTS("Invalid signature (%d)", rv); - return EC_ERROR_INVAL; - } - sig = (const uint8_t *)vb21_sig + vb21_sig->sig_offset; - len = vb21_sig->data_size; - - if (vboot_is_padding_valid(data, len, - CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)) { - CPRINTS("Invalid padding"); - return EC_ERROR_INVAL; - } - - rv = vboot_verify(data, len, key, sig); - if (rv) { - CPRINTS("Invalid data (%d)", rv); - return EC_ERROR_INVAL; - } - - CPRINTS("Verified %s", ec_image_to_string(slot)); - - return EC_SUCCESS; -} - -static enum ec_status hc_verify_slot(struct host_cmd_handler_args *args) -{ - const struct ec_params_efs_verify *p = args->params; - enum ec_image slot; - - switch (p->region) { - case EC_FLASH_REGION_ACTIVE: - slot = system_get_active_copy(); - break; - case EC_FLASH_REGION_UPDATE: - slot = system_get_update_copy(); - break; - default: - return EC_RES_INVALID_PARAM; - } - return verify_slot(slot) ? EC_RES_ERROR : EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_EFS_VERIFY, hc_verify_slot, EC_VER_MASK(0)); - -static int verify_and_jump(void) -{ - enum ec_image slot; - int rv; - - /* 1. Decide which slot to try */ - slot = system_get_active_copy(); - - /* 2. Verify the slot */ - rv = verify_slot(slot); - if (rv) { - if (rv == EC_ERROR_VBOOT_KEY) - /* Key error. The other slot isn't worth trying. */ - return rv; - slot = system_get_update_copy(); - /* TODO(chromium:767050): Skip reading key again. */ - rv = verify_slot(slot); - if (rv) - /* Both slots failed */ - return rv; - - /* Proceed with the other slot. If this slot isn't expected, AP - * will catch it and request recovery after a few attempts. */ - if (system_set_active_copy(slot)) - CPRINTS("Failed to activate %s", - ec_image_to_string(slot)); - } - - /* 3. Jump (and reboot) */ - rv = system_run_image_copy(slot); - CPRINTS("Failed to jump (%d)", rv); - - return rv; -} - -/* Request more power: charging battery or more powerful AC adapter */ -__overridable void show_power_shortage(void) -{ - CPRINTS("%s", __func__); -} - -__overridable void show_critical_error(void) -{ - CPRINTS("%s", __func__); -} - -static bool pd_comm_enabled; - -bool vboot_allow_usb_pd(void) -{ - return pd_comm_enabled; -} - -void vboot_main(void) -{ - CPRINTS("Main"); - - if (system_is_in_rw()) { - /* - * We come here and immediately return. LED shows power shortage - * but it will be immediately corrected if the adapter can - * provide enough power. - */ - CPRINTS("Already in RW. Wait for power..."); - show_power_shortage(); - return; - } - - if (!(crec_flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED)) { - /* - * If hardware WP is disabled, PD communication is enabled. - * We can return and wait for more power. - * Note: If software WP is disabled, we still perform EFS even - * though PD communication is enabled. - */ - CPRINTS("HW-WP not asserted."); - show_power_shortage(); - return; - } - - if (system_is_manual_recovery() || - (system_get_reset_flags() & EC_RESET_FLAG_STAY_IN_RO)) { - if (system_is_manual_recovery()) - CPRINTS("Manual recovery"); - - if (battery_is_present() || has_matrix_keyboard()) { - show_power_shortage(); - return; - } - /* We don't request_power because we don't want to assume all - * devices support a non type-c charger. We open up a security - * hole by allowing EC-RO to do PD negotiation but attackers - * don't gain meaningful advantage on devices without a matrix - * keyboard */ - CPRINTS("Enable PD comm"); - pd_comm_enabled = true; - return; - } - - clock_enable_module(MODULE_FAST_CPU, 1); - /* If successful, this won't return. */ - verify_and_jump(); - clock_enable_module(MODULE_FAST_CPU, 0); - - /* Failed to jump. Need recovery. */ - show_critical_error(); -} diff --git a/common/vboot_hash.c b/common/vboot_hash.c deleted file mode 100644 index 33172e7c74..0000000000 --- a/common/vboot_hash.c +++ /dev/null @@ -1,498 +0,0 @@ -/* Copyright 2012 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. - */ - -/* Verified boot hash computing module for Chrome EC */ - -#include "clock.h" -#include "common.h" -#include "console.h" -#include "flash.h" -#include "hooks.h" -#include "host_command.h" -#include "sha256.h" -#include "shared_mem.h" -#include "stdbool.h" -#include "stdint.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "watchdog.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_VBOOT, outstr) -#define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args) - -struct vboot_hash_tag { - uint8_t hash[SHA256_DIGEST_SIZE]; - uint32_t offset; - uint32_t size; -}; - -#define CHUNK_SIZE 1024 /* Bytes to hash per deferred call */ -#define WORK_INTERVAL_US 100 /* Delay between deferred calls */ - -/* Check that CHUNK_SIZE fits in shared memory. */ -SHARED_MEM_CHECK_SIZE(CHUNK_SIZE); - -static uint32_t data_offset; -static uint32_t data_size; -static uint32_t curr_pos; -static const uint8_t *hash; /* Hash, or NULL if not valid */ -static int want_abort; -static int in_progress; -#define VBOOT_HASH_DEFERRED true -#define VBOOT_HASH_BLOCKING false - -static struct sha256_ctx ctx; - -int vboot_hash_in_progress(void) -{ - return in_progress; -} - -/** - * Abort hash currently in progress, and invalidate any completed hash. - */ -void vboot_hash_abort(void) -{ - if (in_progress) { - want_abort = 1; - } else { - CPRINTS("hash abort"); - want_abort = 0; - data_size = 0; - hash = NULL; -#ifdef CONFIG_SHA256_HW_ACCELERATE - SHA256_abort(&ctx); -#endif - } -} - -static void vboot_hash_next_chunk(void); -DECLARE_DEFERRED(vboot_hash_next_chunk); - -#ifndef CONFIG_MAPPED_STORAGE - -static int read_and_hash_chunk(int offset, int size) -{ - char *buf; - int rv; - - if (size == 0) - return EC_SUCCESS; - - rv = shared_mem_acquire(size, &buf); - if (rv == EC_ERROR_BUSY) { - /* Couldn't update hash right now; try again later */ - hook_call_deferred(&vboot_hash_next_chunk_data, - WORK_INTERVAL_US); - return rv; - } else if (rv != EC_SUCCESS) { - vboot_hash_abort(); - return rv; - } - - rv = crec_flash_read(offset, size, buf); - if (rv == EC_SUCCESS) - SHA256_update(&ctx, (const uint8_t *)buf, size); - else - vboot_hash_abort(); - - shared_mem_release(buf); - return rv; -} - -#endif - -#ifdef CONFIG_CONSOLE_VERBOSE -#define SHA256_PRINT_SIZE SHA256_DIGEST_SIZE -#else -#define SHA256_PRINT_SIZE 4 -#endif - -static void hash_next_chunk(size_t size) -{ -#ifdef CONFIG_MAPPED_STORAGE - crec_flash_lock_mapped_storage(1); - SHA256_update(&ctx, (const uint8_t *) - ((uintptr_t)CONFIG_MAPPED_STORAGE_BASE + - data_offset + curr_pos), size); - crec_flash_lock_mapped_storage(0); -#else - if (read_and_hash_chunk(data_offset + curr_pos, size) != EC_SUCCESS) - return; -#endif -} - -static void vboot_hash_all_chunks(void) -{ - do { - size_t size = MIN(CHUNK_SIZE, data_size - curr_pos); - hash_next_chunk(size); - curr_pos += size; - } while (curr_pos < data_size); - - hash = SHA256_final(&ctx); - CPRINTS("hash done %ph", HEX_BUF(hash, SHA256_PRINT_SIZE)); - in_progress = 0; - clock_enable_module(MODULE_FAST_CPU, 0); - - return; -} - -/** - * Do next chunk of hashing work, if any. - */ -static void vboot_hash_next_chunk(void) -{ - int size; - - /* Handle abort */ - if (want_abort) { - in_progress = 0; - clock_enable_module(MODULE_FAST_CPU, 0); - vboot_hash_abort(); - return; - } - - /* Compute the next chunk of hash */ - size = MIN(CHUNK_SIZE, data_size - curr_pos); - hash_next_chunk(size); - - curr_pos += size; - if (curr_pos >= data_size) { - /* Store the final hash */ - hash = SHA256_final(&ctx); - CPRINTS("hash done %ph", HEX_BUF(hash, SHA256_PRINT_SIZE)); - - in_progress = 0; - - clock_enable_module(MODULE_FAST_CPU, 0); - - /* Handle receiving abort during finalize */ - if (want_abort) - vboot_hash_abort(); - - return; - } - - /* If we're still here, more work to do; come back later */ - hook_call_deferred(&vboot_hash_next_chunk_data, WORK_INTERVAL_US); -} - -/** - * - * If nonce_size is non-zero, prefixes the <nonce> onto the data to be hashed. - * Returns non-zero if error. - */ -/** - * Start computing a hash of <size> bytes of data at flash offset <offset>. - * - * @param offset start address of data on flash to compute hash for. - * @param size size of data to compute hash for. - * @param nonce nonce to differentiate hash. - * @param nonce_size size of nonce. - * @param deferred True to hash progressively through deferred calls. - * False to hash with a blocking single call. - * @return ec_error_list. - */ -static int vboot_hash_start(uint32_t offset, uint32_t size, - const uint8_t *nonce, int nonce_size, bool deferred) -{ - /* Fail if hash computation is already in progress */ - if (in_progress) - return EC_ERROR_BUSY; - - /* - * Make sure request fits inside flash. That is, you can't use this - * command to peek at other memory. - */ - if (offset > CONFIG_FLASH_SIZE_BYTES || - size > CONFIG_FLASH_SIZE_BYTES || - offset + size > CONFIG_FLASH_SIZE_BYTES || nonce_size < 0) { - return EC_ERROR_INVAL; - } - - clock_enable_module(MODULE_FAST_CPU, 1); - /* Save new hash request */ - data_offset = offset; - data_size = size; - curr_pos = 0; - hash = NULL; - want_abort = 0; - in_progress = 1; - - /* Restart the hash computation */ - CPRINTS("hash start 0x%08x 0x%08x", offset, size); - SHA256_init(&ctx); - if (nonce_size) - SHA256_update(&ctx, nonce, nonce_size); - - if (deferred) - hook_call_deferred(&vboot_hash_next_chunk_data, 0); - else - vboot_hash_all_chunks(); - - return EC_SUCCESS; -} - -int vboot_hash_invalidate(int offset, int size) -{ - /* Don't invalidate if passed an invalid region */ - if (offset < 0 || size <= 0 || offset + size < 0) - return 0; - - /* Don't invalidate if hash is already invalid */ - if (!hash) - return 0; - - /* - * Always invalidate zero-size hash. No overlap if passed region is off - * either end of hashed region. - */ - if (data_size > 0 && - (offset + size <= data_offset || offset >= data_offset + data_size)) - return 0; - - /* Invalidate the hash */ - CPRINTS("hash invalidated 0x%08x 0x%08x", offset, size); - vboot_hash_abort(); - return 1; -} - -/*****************************************************************************/ -/* Hooks */ - -/** - * Returns the size of a RW copy to be hashed as expected by Softsync. - */ -static uint32_t get_rw_size(void) -{ -#ifdef CONFIG_VBOOT_EFS /* Only needed for EFS, which signs and verifies - * entire RW, thus not needed for EFS2, which - * verifies only the used image size. */ - return CONFIG_RW_SIZE; -#else - return system_get_image_used(EC_IMAGE_RW); -#endif -} - -static void vboot_hash_init(void) -{ -#ifdef CONFIG_HOSTCMD_EVENTS - /* - * Don't auto-start hash computation if we've asked the host to enter - * recovery mode since we probably won't need the hash. Although - * the host is capable of clearing this host event, the host is - * likely not even up and running yet in the case of cold boot, due to - * the power sequencing task not having run yet. - */ - if (!(host_get_events() & - EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY))) -#endif - { - /* Start computing the hash of RW firmware */ - vboot_hash_start(flash_get_rw_offset(system_get_active_copy()), - get_rw_size(), NULL, 0, VBOOT_HASH_DEFERRED); - } -} -DECLARE_HOOK(HOOK_INIT, vboot_hash_init, HOOK_PRIO_INIT_VBOOT_HASH); - -int vboot_get_rw_hash(const uint8_t **dst) -{ - int rv = vboot_hash_start(flash_get_rw_offset(system_get_active_copy()), - get_rw_size(), NULL, 0, VBOOT_HASH_BLOCKING); - *dst = hash; - return rv; -} - -/** - * Returns the offset of RO or RW image if the either region is specifically - * requested otherwise return the current hash offset. - */ -static int get_offset(int offset) -{ - if (offset == EC_VBOOT_HASH_OFFSET_RO) - return CONFIG_EC_PROTECTED_STORAGE_OFF + CONFIG_RO_STORAGE_OFF; - if (offset == EC_VBOOT_HASH_OFFSET_ACTIVE) - return flash_get_rw_offset(system_get_active_copy()); - if (offset == EC_VBOOT_HASH_OFFSET_UPDATE) - return flash_get_rw_offset(system_get_update_copy()); - return offset; -} - -/****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_HASH -static int command_hash(int argc, char **argv) -{ - uint32_t offset = CONFIG_EC_WRITABLE_STORAGE_OFF + - CONFIG_RW_STORAGE_OFF; - uint32_t size = CONFIG_RW_SIZE; - char *e; - - if (argc == 1) { - ccprintf("Offset: 0x%08x\n", data_offset); - ccprintf("Size: 0x%08x (%d)\n", data_size, data_size); - ccprintf("Digest: "); - if (want_abort) - ccprintf("(aborting)\n"); - else if (in_progress) - ccprintf("(in progress)\n"); - else if (hash) - ccprintf("%ph\n", HEX_BUF(hash, SHA256_DIGEST_SIZE)); - else - ccprintf("(invalid)\n"); - - return EC_SUCCESS; - } - - if (argc == 2) { - if (!strcasecmp(argv[1], "abort")) { - vboot_hash_abort(); - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "rw")) { - return vboot_hash_start( - get_offset(EC_VBOOT_HASH_OFFSET_ACTIVE), - get_rw_size(), - NULL, 0, VBOOT_HASH_DEFERRED); - } else if (!strcasecmp(argv[1], "ro")) { - return vboot_hash_start( - CONFIG_EC_PROTECTED_STORAGE_OFF + - CONFIG_RO_STORAGE_OFF, - system_get_image_used(EC_IMAGE_RO), - NULL, 0, VBOOT_HASH_DEFERRED); - } - return EC_ERROR_PARAM2; - } - - if (argc >= 3) { - offset = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - size = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - } - - if (argc == 4) { - int nonce = strtoi(argv[3], &e, 0); - if (*e) - return EC_ERROR_PARAM3; - - return vboot_hash_start(offset, size, - (const uint8_t *)&nonce, - sizeof(nonce), VBOOT_HASH_DEFERRED); - } else - return vboot_hash_start(offset, size, - NULL, 0, VBOOT_HASH_DEFERRED); -} -DECLARE_CONSOLE_COMMAND(hash, command_hash, - "[abort | ro | rw] | [<offset> <size> [<nonce>]]", - "Request hash recomputation"); -#endif /* CONFIG_CMD_HASH */ -/****************************************************************************/ -/* Host commands */ - -/* Fill in the response with the current hash status */ -static void fill_response(struct ec_response_vboot_hash *r, - int request_offset) -{ - if (in_progress) - r->status = EC_VBOOT_HASH_STATUS_BUSY; - else if (get_offset(request_offset) == data_offset && hash && - !want_abort) { - r->status = EC_VBOOT_HASH_STATUS_DONE; - r->hash_type = EC_VBOOT_HASH_TYPE_SHA256; - r->digest_size = SHA256_DIGEST_SIZE; - r->reserved0 = 0; - r->offset = data_offset; - r->size = data_size; - ASSERT(SHA256_DIGEST_SIZE < sizeof(r->hash_digest)); - memcpy(r->hash_digest, hash, SHA256_DIGEST_SIZE); - } else - r->status = EC_VBOOT_HASH_STATUS_NONE; -} - -/** - * Start computing a hash, with validity checking on params. - * - * @return EC_RES_SUCCESS if success, or other result code on error. - */ -static int host_start_hash(const struct ec_params_vboot_hash *p) -{ - int offset = p->offset; - int size = p->size; - int rv; - - /* Validity-check input params */ - if (p->hash_type != EC_VBOOT_HASH_TYPE_SHA256) - return EC_RES_INVALID_PARAM; - if (p->nonce_size > sizeof(p->nonce_data)) - return EC_RES_INVALID_PARAM; - - /* Handle special offset values */ - if (offset == EC_VBOOT_HASH_OFFSET_RO) - size = system_get_image_used(EC_IMAGE_RO); - else if ((offset == EC_VBOOT_HASH_OFFSET_ACTIVE) || - (offset == EC_VBOOT_HASH_OFFSET_UPDATE)) - size = get_rw_size(); - offset = get_offset(offset); - rv = vboot_hash_start(offset, size, p->nonce_data, p->nonce_size, - VBOOT_HASH_DEFERRED); - - if (rv == EC_SUCCESS) - return EC_RES_SUCCESS; - else if (rv == EC_ERROR_INVAL) - return EC_RES_INVALID_PARAM; - else - return EC_RES_ERROR; -} - -static enum ec_status -host_command_vboot_hash(struct host_cmd_handler_args *args) -{ - const struct ec_params_vboot_hash *p = args->params; - struct ec_response_vboot_hash *r = args->response; - int rv; - - switch (p->cmd) { - case EC_VBOOT_HASH_GET: - if (p->offset || p->size) - fill_response(r, p->offset); - else - fill_response(r, data_offset); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - - case EC_VBOOT_HASH_ABORT: - vboot_hash_abort(); - return EC_RES_SUCCESS; - - case EC_VBOOT_HASH_START: - case EC_VBOOT_HASH_RECALC: - rv = host_start_hash(p); - if (rv != EC_RES_SUCCESS) - return rv; - - /* Wait for hash to finish if command is RECALC */ - if (p->cmd == EC_VBOOT_HASH_RECALC) - while (in_progress) - usleep(1000); - - fill_response(r, p->offset); - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - } -} -DECLARE_HOST_COMMAND(EC_CMD_VBOOT_HASH, - host_command_vboot_hash, - EC_VER_MASK(0)); diff --git a/common/virtual_battery.c b/common/virtual_battery.c deleted file mode 100644 index 0f0167556c..0000000000 --- a/common/virtual_battery.c +++ /dev/null @@ -1,359 +0,0 @@ -/* Copyright 2016 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. - */ - -/* Virtual battery cross-platform code for Chrome EC */ - -#include "battery.h" -#include "charge_state.h" -#include "i2c.h" -#include "system.h" -#include "util.h" -#include "virtual_battery.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_I2C, outstr) -#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) - -#define BATT_MODE_UNINITIALIZED -1 - -/* - * The state machine used to parse smart battery command - * to support virtual battery. - */ -enum batt_cmd_parse_state { - IDLE = 0, /* initial state */ - START = 1, /* received the register address (command code) */ - WRITE_VB, /* writing data bytes to the peripheral */ - READ_VB, /* reading data bytes to the peripheral */ -}; - -static enum batt_cmd_parse_state sb_cmd_state; -static uint8_t cache_hit; -static const uint8_t *batt_cmd_head; -static int acc_write_len; - -int virtual_battery_handler(struct ec_response_i2c_passthru *resp, - int in_len, int *err_code, int xferflags, - int read_len, int write_len, - const uint8_t *out) -{ - -#if defined(CONFIG_BATTERY_PRESENT_GPIO) || \ - defined(CONFIG_BATTERY_PRESENT_CUSTOM) - /* - * If the battery isn't present, return a NAK (which we - * would have gotten anyways had we attempted to talk to - * the battery.) - */ - if (battery_is_present() != BP_YES) { - resp->i2c_status = EC_I2C_STATUS_NAK; - return EC_ERROR_INVAL; - } -#endif - switch (sb_cmd_state) { - case IDLE: - /* - * A legal battery command must start - * with a i2c write for reg index. - */ - if (write_len == 0) { - resp->i2c_status = EC_I2C_STATUS_NAK; - return EC_ERROR_INVAL; - } - /* Record the head of battery command. */ - batt_cmd_head = out; - sb_cmd_state = START; - *err_code = 0; - break; - case START: - if (write_len > 0) { - sb_cmd_state = WRITE_VB; - *err_code = 0; - } else { - sb_cmd_state = READ_VB; - *err_code = virtual_battery_operation(batt_cmd_head, - NULL, 0, 0); - /* - * If the reg is not handled by virtual battery, we - * do not support it. - */ - if (*err_code) - return EC_ERROR_INVAL; - cache_hit = 1; - } - break; - case WRITE_VB: - if (write_len == 0) { - resp->i2c_status = EC_I2C_STATUS_NAK; - reset_parse_state(); - return EC_ERROR_INVAL; - } - *err_code = 0; - break; - case READ_VB: - if (read_len == 0) { - resp->i2c_status = EC_I2C_STATUS_NAK; - reset_parse_state(); - return EC_ERROR_INVAL; - } - /* - * Do not send the command to battery - * if the reg is cached. - */ - if (cache_hit) - *err_code = 0; - break; - default: - reset_parse_state(); - return EC_ERROR_INVAL; - } - - acc_write_len += write_len; - - /* the last message */ - if (xferflags & I2C_XFER_STOP) { - switch (sb_cmd_state) { - /* write to virtual battery */ - case START: - case WRITE_VB: - virtual_battery_operation(batt_cmd_head, - NULL, - 0, - acc_write_len); - break; - /* read from virtual battery */ - case READ_VB: - if (cache_hit) { - read_len += in_len; - memset(&resp->data[0], 0, read_len); - virtual_battery_operation(batt_cmd_head, - &resp->data[0], - read_len, - 0); - } - break; - default: - reset_parse_state(); - return EC_ERROR_INVAL; - - } - /* Reset the state in the end of messages */ - reset_parse_state(); - } - return EC_RES_SUCCESS; -} - -void reset_parse_state(void) -{ - sb_cmd_state = IDLE; - cache_hit = 0; - acc_write_len = 0; -} - -/* - * Copy memmap string data from offset to dest, up to size len, in the format - * expected by SBS (first byte of dest contains strlen). - */ -void copy_memmap_string(uint8_t *dest, int offset, int len) -{ - uint8_t *memmap_str; - uint8_t memmap_strlen; - - if (len == 0) - return; - memmap_str = host_get_memmap(offset); - /* memmap_str might not be NULL terminated */ - memmap_strlen = *(memmap_str + EC_MEMMAP_TEXT_MAX - 1) == '\0' ? - strlen(memmap_str) : EC_MEMMAP_TEXT_MAX; - dest[0] = memmap_strlen; - memcpy(dest + 1, memmap_str, MIN(memmap_strlen, len - 1)); -} - -int virtual_battery_operation(const uint8_t *batt_cmd_head, - uint8_t *dest, - int read_len, - int write_len) -{ - int val; - int year, month, day; - /* - * We cache battery operational mode locally for both read and write - * commands. If MODE_CAPACITY bit is set, battery capacity will be - * reported in 10mW/10mWh, instead of the default unit, mA/mAh. - * Note that we don't update the cached capacity: We do a real-time - * conversion and return the converted values. - */ - static int batt_mode_cache = BATT_MODE_UNINITIALIZED; - const struct batt_params *curr_batt; - /* - * Don't allow host reads into arbitrary memory space, most params - * are two bytes. - */ - int bounded_read_len = MIN(read_len, 2); - - curr_batt = charger_current_battery_params(); - switch (*batt_cmd_head) { - case SB_BATTERY_MODE: - if (write_len == 3) { - batt_mode_cache = batt_cmd_head[1] | - (batt_cmd_head[2] << 8); - } else if (read_len > 0) { - if (batt_mode_cache == BATT_MODE_UNINITIALIZED) - /* - * Read the battery operational mode from - * the battery to initialize batt_mode_cache. - * This may cause an i2c transaction. - */ - if (battery_get_mode(&batt_mode_cache) == - EC_ERROR_UNIMPLEMENTED) - /* - * Register not supported, choose - * typical SB defaults. - */ - batt_mode_cache = - MODE_INTERNAL_CHARGE_CONTROLLER | - MODE_ALARM | - MODE_CHARGER; - - memcpy(dest, &batt_mode_cache, bounded_read_len); - } - break; - case SB_SERIAL_NUMBER: - val = strtoi(host_get_memmap(EC_MEMMAP_BATT_SERIAL), NULL, 16); - memcpy(dest, &val, bounded_read_len); - break; - case SB_VOLTAGE: - if (curr_batt->flags & BATT_FLAG_BAD_VOLTAGE) - return EC_ERROR_BUSY; - memcpy(dest, &(curr_batt->voltage), bounded_read_len); - break; - case SB_RELATIVE_STATE_OF_CHARGE: - if (curr_batt->flags & BATT_FLAG_BAD_STATE_OF_CHARGE) - return EC_ERROR_BUSY; - memcpy(dest, &(curr_batt->state_of_charge), bounded_read_len); - break; - case SB_TEMPERATURE: - if (curr_batt->flags & BATT_FLAG_BAD_TEMPERATURE) - return EC_ERROR_BUSY; - memcpy(dest, &(curr_batt->temperature), bounded_read_len); - break; - case SB_CURRENT: - if (curr_batt->flags & BATT_FLAG_BAD_CURRENT) - return EC_ERROR_BUSY; - memcpy(dest, &(curr_batt->current), bounded_read_len); - break; - case SB_AVERAGE_CURRENT: - /* This may cause an i2c transaction */ - if (curr_batt->flags & BATT_FLAG_BAD_AVERAGE_CURRENT) - return EC_ERROR_BUSY; - val = battery_get_avg_current(); - memcpy(dest, &val, bounded_read_len); - break; - case SB_MAX_ERROR: - /* report as 3% to make kernel happy */ - val = BATTERY_LEVEL_SHUTDOWN; - memcpy(dest, &val, bounded_read_len); - break; - case SB_FULL_CHARGE_CAPACITY: - if (curr_batt->flags & BATT_FLAG_BAD_FULL_CAPACITY || - curr_batt->flags & BATT_FLAG_BAD_VOLTAGE) - return EC_ERROR_BUSY; - val = curr_batt->full_capacity; - if (batt_mode_cache & MODE_CAPACITY) - val = val * curr_batt->voltage / 10000; - memcpy(dest, &val, bounded_read_len); - break; - case SB_BATTERY_STATUS: - if (curr_batt->flags & BATT_FLAG_BAD_STATUS) - return EC_ERROR_BUSY; - memcpy(dest, &(curr_batt->status), bounded_read_len); - break; - case SB_CYCLE_COUNT: - memcpy(dest, (int *)host_get_memmap(EC_MEMMAP_BATT_CCNT), - bounded_read_len); - break; - case SB_DESIGN_CAPACITY: - if (curr_batt->flags & BATT_FLAG_BAD_VOLTAGE) - return EC_ERROR_BUSY; - val = *(int *)host_get_memmap(EC_MEMMAP_BATT_DCAP); - if (batt_mode_cache & MODE_CAPACITY) - val = val * curr_batt->voltage / 10000; - memcpy(dest, &val, bounded_read_len); - break; - case SB_DESIGN_VOLTAGE: - memcpy(dest, (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT), - bounded_read_len); - break; - case SB_REMAINING_CAPACITY: - if (curr_batt->flags & BATT_FLAG_BAD_REMAINING_CAPACITY || - curr_batt->flags & BATT_FLAG_BAD_VOLTAGE) - return EC_ERROR_BUSY; - val = curr_batt->remaining_capacity; - if (batt_mode_cache & MODE_CAPACITY) - val = val * curr_batt->voltage / 10000; - memcpy(dest, &val, bounded_read_len); - break; - case SB_MANUFACTURER_NAME: - copy_memmap_string(dest, EC_MEMMAP_BATT_MFGR, read_len); - break; - case SB_DEVICE_NAME: - copy_memmap_string(dest, EC_MEMMAP_BATT_MODEL, read_len); - break; - case SB_DEVICE_CHEMISTRY: - copy_memmap_string(dest, EC_MEMMAP_BATT_TYPE, read_len); - break; - case SB_AVERAGE_TIME_TO_FULL: - /* This may cause an i2c transaction */ - if (battery_time_to_full(&val)) - return EC_ERROR_INVAL; - memcpy(dest, &val, bounded_read_len); - break; - case SB_AVERAGE_TIME_TO_EMPTY: - /* This may cause an i2c transaction */ - if (battery_time_to_empty(&val)) - return EC_ERROR_INVAL; - memcpy(dest, &val, bounded_read_len); - break; - case SB_CHARGING_CURRENT: - if (curr_batt->flags & BATT_FLAG_BAD_DESIRED_CURRENT) - return EC_ERROR_BUSY; - val = curr_batt->desired_current; - memcpy(dest, &val, bounded_read_len); - break; - case SB_CHARGING_VOLTAGE: - if (curr_batt->flags & BATT_FLAG_BAD_DESIRED_VOLTAGE) - return EC_ERROR_BUSY; - val = curr_batt->desired_voltage; - memcpy(dest, &val, bounded_read_len); - break; - case SB_MANUFACTURE_DATE: - /* This may cause an i2c transaction */ - if (!battery_manufacture_date(&year, &month, &day)) { - /* Encode in Smart Battery Spec format */ - val = ((year - 1980) << 9) + (month << 5) + day; - } else { - /* - * Return 0 on error. The kernel is unhappy with - * returning an error code. - */ - val = 0; - } - memcpy(dest, &val, bounded_read_len); - break; - case SB_MANUFACTURER_ACCESS: - /* No manuf. access reg access allowed over VB interface */ - return EC_ERROR_INVAL; - case SB_SPECIFICATION_INFO: - /* v1.1 without PEC, no scale factor to voltage and current */ - val = 0x0011; - memcpy(dest, &val, bounded_read_len); - break; - default: - CPRINTS("Unhandled VB reg %x", *batt_cmd_head); - return EC_ERROR_INVAL; - } - return EC_SUCCESS; -} - diff --git a/common/vstore.c b/common/vstore.c deleted file mode 100644 index 9b4636397c..0000000000 --- a/common/vstore.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright 2015 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. - */ - -/* - * Temporary secure storage commands for use by the host for verified boot - * related activities such as storing the hash of verified firmware for use - * in suspend/resume. - * - * There are a configurable number of vstore slots, with all slots having - * the same size of EC_VSTORE_SLOT_SIZE (64 bytes). - * - * Slots can be written once per AP power-on and will then be locked and - * cannot be written again until it is cleared in the CHIPSET_SHUTDOWN - * or CHIPSET_RESET hooks. - */ - -#include "common.h" -#include "hooks.h" -#include "host_command.h" -#include "system.h" -#include "util.h" - -#define VSTORE_SYSJUMP_TAG 0x5653 /* "VS" */ -#define VSTORE_HOOK_VERSION 1 - -struct vstore_slot { - uint8_t locked; - uint8_t data[EC_VSTORE_SLOT_SIZE]; -}; - -static struct vstore_slot vstore_slots[CONFIG_VSTORE_SLOT_COUNT]; -static const int vstore_size = - sizeof(struct vstore_slot) * CONFIG_VSTORE_SLOT_COUNT; -BUILD_ASSERT(ARRAY_SIZE(vstore_slots) <= EC_VSTORE_SLOT_MAX); - -/* - * vstore_info - Get slot count and mask of locked slots. - */ -static enum ec_status vstore_info(struct host_cmd_handler_args *args) -{ - struct ec_response_vstore_info *r = args->response; - int i; - - r->slot_count = CONFIG_VSTORE_SLOT_COUNT; - r->slot_locked = 0; - for (i = 0; i < CONFIG_VSTORE_SLOT_COUNT; i++) - if (vstore_slots[i].locked) - r->slot_locked |= 1 << i; - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_VSTORE_INFO, vstore_info, EC_VER_MASK(0)); - -/* - * vstore_read - Read slot from temporary secure storage. - * - * Response is EC_VSTORE_SLOT_SIZE bytes of data. - */ -static enum ec_status vstore_read(struct host_cmd_handler_args *args) -{ - const struct ec_params_vstore_read *p = args->params; - struct ec_response_vstore_read *r = args->response; - - if (p->slot >= CONFIG_VSTORE_SLOT_COUNT) - return EC_RES_INVALID_PARAM; - - memcpy(r->data, vstore_slots[p->slot].data, EC_VSTORE_SLOT_SIZE); - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_VSTORE_READ, vstore_read, EC_VER_MASK(0)); - -/* - * vstore_write - Write temporary secure storage slot and lock it. - */ -static enum ec_status vstore_write(struct host_cmd_handler_args *args) -{ - const struct ec_params_vstore_write *p = args->params; - struct vstore_slot *slot; - - if (p->slot >= CONFIG_VSTORE_SLOT_COUNT) - return EC_RES_INVALID_PARAM; - slot = &vstore_slots[p->slot]; - - if (slot->locked) - return EC_RES_ACCESS_DENIED; - slot->locked = 1; - memcpy(slot->data, p->data, EC_VSTORE_SLOT_SIZE); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_VSTORE_WRITE, vstore_write, EC_VER_MASK(0)); - -static void vstore_clear_lock(void) -{ - int i; - - for (i = 0; i < CONFIG_VSTORE_SLOT_COUNT; i++) - vstore_slots[i].locked = 0; -} -DECLARE_HOOK(HOOK_CHIPSET_RESET, vstore_clear_lock, HOOK_PRIO_DEFAULT); - -static void vstore_preserve_state(void) -{ - system_add_jump_tag(VSTORE_SYSJUMP_TAG, VSTORE_HOOK_VERSION, - vstore_size, vstore_slots); -} -DECLARE_HOOK(HOOK_SYSJUMP, vstore_preserve_state, HOOK_PRIO_DEFAULT); - -static void vstore_init(void) -{ - const struct vstore_slot *prev; - int version, size; - - prev = (const struct vstore_slot *)system_get_jump_tag( - VSTORE_SYSJUMP_TAG, &version, &size); - - if (prev && version == VSTORE_HOOK_VERSION && size == vstore_size) - memcpy(vstore_slots, prev, vstore_size); -} -DECLARE_HOOK(HOOK_INIT, vstore_init, HOOK_PRIO_DEFAULT); diff --git a/common/webusb_desc.c b/common/webusb_desc.c deleted file mode 100644 index 41d39006e0..0000000000 --- a/common/webusb_desc.c +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2017 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. - */ -/* WebUSB platform descriptor */ - -#include "common.h" -#include "usb_descriptor.h" -#include "util.h" - -#ifndef CONFIG_USB_BOS -#error "CONFIG_USB_BOS must be defined to use WebUSB descriptor" -#endif - -const void *webusb_url = USB_URL_DESC(HTTPS, CONFIG_WEBUSB_URL); - -/* - * Platform Descriptor in the device Binary Object Store - * as defined by USB 3.1 spec chapter 9.6.2. - */ -static struct { - struct usb_bos_hdr_descriptor bos; - struct usb_platform_descriptor platform; -} bos_desc = { - .bos = { - .bLength = USB_DT_BOS_SIZE, - .bDescriptorType = USB_DT_BOS, - .wTotalLength = (USB_DT_BOS_SIZE + USB_DT_PLATFORM_SIZE), - .bNumDeviceCaps = 1, /* platform caps */ - }, - .platform = { - .bLength = USB_DT_PLATFORM_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_DC_DTYPE_PLATFORM, - .bReserved = 0, - .PlatformCapUUID = USB_PLAT_CAP_WEBUSB, - .bcdVersion = 0x0100, - .bVendorCode = 0x01, - .iLandingPage = 1, - }, -}; - -const struct bos_context bos_ctx = { - .descp = (void *)&bos_desc, - .size = sizeof(bos_desc), -}; diff --git a/common/wireless.c b/common/wireless.c deleted file mode 100644 index d1f5cad645..0000000000 --- a/common/wireless.c +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Wireless power management */ - -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "host_command.h" -#include "util.h" -#include "wireless.h" - -/* Unless told otherwise, disable wireless in suspend */ -#ifndef CONFIG_WIRELESS_SUSPEND -#define CONFIG_WIRELESS_SUSPEND 0 -#endif - -/* - * Flags which will be left on when suspending. Other flags will be disabled - * when suspending. - */ -static int suspend_flags = CONFIG_WIRELESS_SUSPEND; - -/** - * Set wireless switch state. - * - * @param flags Enable flags from ec_commands.h (EC_WIRELESS_SWITCH_*), - * 0 to turn all wireless off, or -1 to turn all wireless - * on. - * @param mask Which of those flags to set - */ -static void wireless_enable(int flags) -{ -#ifdef WIRELESS_GPIO_WLAN - gpio_set_level(WIRELESS_GPIO_WLAN, - flags & EC_WIRELESS_SWITCH_WLAN); -#endif - -#ifdef WIRELESS_GPIO_WWAN - gpio_set_level(WIRELESS_GPIO_WWAN, - flags & EC_WIRELESS_SWITCH_WWAN); -#endif - -#ifdef WIRELESS_GPIO_BLUETOOTH - gpio_set_level(WIRELESS_GPIO_BLUETOOTH, - flags & EC_WIRELESS_SWITCH_BLUETOOTH); -#endif - -#ifdef WIRELESS_GPIO_WLAN_POWER -#ifndef CONFIG_WLAN_POWER_ACTIVE_LOW - gpio_set_level(WIRELESS_GPIO_WLAN_POWER, - flags & EC_WIRELESS_SWITCH_WLAN_POWER); -#else - gpio_set_level(WIRELESS_GPIO_WLAN_POWER, - !(flags & EC_WIRELESS_SWITCH_WLAN_POWER)); -#endif /* CONFIG_WLAN_POWER_ACTIVE_LOW */ -#endif - -} - -static int wireless_get(void) -{ - int flags = 0; - -#ifdef WIRELESS_GPIO_WLAN - if (gpio_get_level(WIRELESS_GPIO_WLAN)) - flags |= EC_WIRELESS_SWITCH_WLAN; -#endif - -#ifdef WIRELESS_GPIO_WWAN - if (gpio_get_level(WIRELESS_GPIO_WWAN)) - flags |= EC_WIRELESS_SWITCH_WWAN; -#endif - -#ifdef WIRELESS_GPIO_BLUETOOTH - if (gpio_get_level(WIRELESS_GPIO_BLUETOOTH)) - flags |= EC_WIRELESS_SWITCH_BLUETOOTH; -#endif - -#ifdef WIRELESS_GPIO_WLAN_POWER -#ifndef CONFIG_WLAN_POWER_ACTIVE_LOW - if (gpio_get_level(WIRELESS_GPIO_WLAN_POWER)) -#else - if (!gpio_get_level(WIRELESS_GPIO_WLAN_POWER)) -#endif /* CONFIG_WLAN_POWER_ACTIVE_LOW */ - flags |= EC_WIRELESS_SWITCH_WLAN_POWER; -#endif - - return flags; -} - -void wireless_set_state(enum wireless_power_state state) -{ - switch (state) { - case WIRELESS_OFF: - wireless_enable(0); - break; - case WIRELESS_SUSPEND: - /* - * When suspending, only turn things off. If the AP has - * disabled WiFi power, going into S3 should not re-enable it. - */ - wireless_enable(wireless_get() & suspend_flags); - break; - case WIRELESS_ON: - wireless_enable(EC_WIRELESS_SWITCH_ALL); - break; - } -} - -static enum ec_status wireless_enable_cmd(struct host_cmd_handler_args *args) -{ - const struct ec_params_switch_enable_wireless_v1 *p = args->params; - struct ec_response_switch_enable_wireless_v1 *r = args->response; - - if (args->version == 0) { - /* Ver.0 command just set all current flags */ - wireless_enable(p->now_flags); - return EC_RES_SUCCESS; - } - - /* Ver.1 can set flags based on mask */ - wireless_enable((wireless_get() & ~p->now_mask) | - (p->now_flags & p->now_mask)); - - suspend_flags = (suspend_flags & ~p->suspend_mask) | - (p->suspend_flags & p->suspend_mask); - - /* And return the current flags */ - r->now_flags = wireless_get(); - r->suspend_flags = suspend_flags; - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_SWITCH_ENABLE_WIRELESS, - wireless_enable_cmd, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -static int command_wireless(int argc, char **argv) -{ - char *e; - int i; - - if (argc >= 2) { - i = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - wireless_enable(i); - } - - if (argc >= 3) { - i = strtoi(argv[2], &e, 0); - if (*e) - return EC_ERROR_PARAM2; - - suspend_flags = i; - } - - ccprintf("Wireless flags: now=0x%x, suspend=0x%x\n", wireless_get(), - suspend_flags); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(wireless, command_wireless, - "[now [suspend]]", - "Get/set wireless flags"); |