diff options
author | Jack Rosenthal <jrosenth@chromium.org> | 2021-11-04 12:11:58 -0600 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-11-05 04:22:34 +0000 |
commit | 252457d4b21f46889eebad61d4c0a65331919cec (patch) | |
tree | 01856c4d31d710b20e85a74c8d7b5836e35c3b98 /common | |
parent | 08f5a1e6fc2c9467230444ac9b582dcf4d9f0068 (diff) | |
download | chrome-ec-stabilize-14496.B-ish.tar.gz |
ish: Trim down the release branchstabilize-wristpin-14469.59.B-ishstabilize-voshyr-14637.B-ishstabilize-quickfix-14695.187.B-ishstabilize-quickfix-14695.124.B-ishstabilize-quickfix-14526.91.B-ishstabilize-14695.85.B-ishstabilize-14695.107.B-ishstabilize-14682.B-ishstabilize-14633.B-ishstabilize-14616.B-ishstabilize-14589.B-ishstabilize-14588.98.B-ishstabilize-14588.14.B-ishstabilize-14588.123.B-ishstabilize-14536.B-ishstabilize-14532.B-ishstabilize-14528.B-ishstabilize-14526.89.B-ishstabilize-14526.84.B-ishstabilize-14526.73.B-ishstabilize-14526.67.B-ishstabilize-14526.57.B-ishstabilize-14498.B-ishstabilize-14496.B-ishstabilize-14477.B-ishstabilize-14469.9.B-ishstabilize-14469.8.B-ishstabilize-14469.58.B-ishstabilize-14469.41.B-ishstabilize-14442.B-ishstabilize-14438.B-ishstabilize-14411.B-ishstabilize-14396.B-ishstabilize-14395.B-ishstabilize-14388.62.B-ishstabilize-14388.61.B-ishstabilize-14388.52.B-ishstabilize-14385.B-ishstabilize-14345.B-ishstabilize-14336.B-ishstabilize-14333.B-ishrelease-R99-14469.B-ishrelease-R98-14388.B-ishrelease-R102-14695.B-ishrelease-R101-14588.B-ishrelease-R100-14526.B-ishfirmware-cherry-14454.B-ishfirmware-brya-14505.B-ishfirmware-brya-14505.71.B-ishfactory-kukui-14374.B-ishfactory-guybrush-14600.B-ishfactory-cherry-14455.B-ishfactory-brya-14517.B-ish
In the interest of making long-term branch maintenance incur as little
technical debt on us as possible, we should not maintain any files on
the branch we are not actually using.
This has the added effect of making it extremely clear when merging CLs
from the main branch when changes have the possibility to affect us.
The follow-on CL adds a convenience script to actually pull updates from
the main branch and generate a CL for the update.
BUG=b:204206272
BRANCH=ish
TEST=make BOARD=arcada_ish && make BOARD=drallion_ish
Signed-off-by: Jack Rosenthal <jrosenth@chromium.org>
Change-Id: I17e4694c38219b5a0823e0a3e55a28d1348f4b18
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3262038
Reviewed-by: Jett Rink <jettrink@chromium.org>
Reviewed-by: Tom Hughes <tomhughes@chromium.org>
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"); |