diff options
Diffstat (limited to 'common')
36 files changed, 620 insertions, 134 deletions
diff --git a/common/battery_fuel_gauge.c b/common/battery_fuel_gauge.c index 528713d68f..5555255188 100644 --- a/common/battery_fuel_gauge.c +++ b/common/battery_fuel_gauge.c @@ -14,11 +14,83 @@ #define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) +/* + * Authenticate the battery connected. + * + * 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. + */ +static bool authenticate_battery_type(int index, char *manuf_name) +{ + char device_name[32]; + + const struct fuel_gauge_info * const fuel_gauge = + &board_battery_info[index].fuel_gauge; + int len = 0; + + /* check for valid index */ + if (index >= BATTERY_TYPE_COUNT) + return false; + + /* manufacturer name mismatch */ + if (strcasecmp(manuf_name, fuel_gauge->manuf_name)) + return false; + + /* device name is specified in table */ + if (fuel_gauge->device_name != NULL) { + + /* Get the device name */ + if (battery_device_name(device_name, + sizeof(device_name))) + return false; + + len = strlen(fuel_gauge->device_name); + + /* device name mismatch */ + if (strncasecmp(device_name, fuel_gauge->device_name, + len)) + return false; + } + + CPRINTS("found batt:%s", fuel_gauge->manuf_name); + return true; +} + +#ifdef CONFIG_BATTERY_TYPE_NO_AUTO_DETECT + +/* Variable to decide the battery type */ +static int fixed_battery_type = BATTERY_TYPE_UNINITIALIZED; + +/* + * Function to get the fixed battery type. + */ +static int battery_get_fixed_battery_type(void) +{ + if (fixed_battery_type == BATTERY_TYPE_UNINITIALIZED) { + CPRINTS("Warning: Battery type is not Initialized! " + "Setting to default battery type.\n"); + fixed_battery_type = DEFAULT_BATTERY_TYPE; + } + + return fixed_battery_type; +} + +/* + * Function to set the battery type, when auto-detection cannot be used. + */ +void battery_set_fixed_battery_type(int type) +{ + if (type < BATTERY_TYPE_COUNT) + fixed_battery_type = type; +} +#endif /* CONFIG_BATTERY_TYPE_NO_AUTO_DETECT */ /* Get type of the battery connected on the board */ static int get_battery_type(void) { - char manuf_name[32], device_name[32]; + char manuf_name[32]; int i; static enum battery_type battery_type = BATTERY_TYPE_COUNT; @@ -33,36 +105,18 @@ static int get_battery_type(void) 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. - */ +#if defined(CONFIG_BATTERY_TYPE_NO_AUTO_DETECT) + i = battery_get_fixed_battery_type(); + if (authenticate_battery_type(i, manuf_name)) + battery_type = i; +#else 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; + if (authenticate_battery_type(i, manuf_name)) { + battery_type = i; + break; } - - CPRINTS("found batt:%s", fuel_gauge->manuf_name); - battery_type = i; - break; } +#endif return battery_type; } diff --git a/common/build.mk b/common/build.mk index 901d0d3c9d..d00a7f8bb1 100644 --- a/common/build.mk +++ b/common/build.mk @@ -30,6 +30,7 @@ common-$(CONFIG_ACCEL_LIS2DS)+=math_util.o common-$(CONFIG_ACCEL_KXCJ9)+=math_util.o common-$(CONFIG_ACCEL_KX022)+=math_util.o common-$(CONFIG_TEMP_SENSOR_TMP112)+=math_util.o +common-$(CONFIG_TEMP_SENSOR_PCT2075)+=math_util.o ifneq ($(CORE),cortex-m) common-$(CONFIG_AES)+=aes.o endif @@ -83,7 +84,7 @@ common-$(CONFIG_DEVICE_STATE)+=device_state.o common-$(CONFIG_DPTF)+=dptf.o common-$(CONFIG_EC_EC_COMM_CLIENT)+=ec_ec_comm_client.o common-$(CONFIG_EC_EC_COMM_SERVER)+=ec_ec_comm_server.o -common-$(CONFIG_HOSTCMD_ESPI)+=espi.o +common-$(CONFIG_HOST_INTERFACE_ESPI)+=espi.o common-$(CONFIG_EXTPOWER_GPIO)+=extpower_gpio.o common-$(CONFIG_EXTPOWER)+=extpower_common.o common-$(CONFIG_FANS)+=fan.o pwm.o diff --git a/common/button.c b/common/button.c index d30115066d..145cd9db74 100644 --- a/common/button.c +++ b/common/button.c @@ -43,7 +43,7 @@ static uint64_t next_deferred_time; /* Bitmask to keep track of simulated state of each button. * Bit numbers are aligned to enum button. */ -static int sim_button_state; +static atomic_t sim_button_state; /* * Flip state of associated button type in sim_button_state bitmask. @@ -62,7 +62,7 @@ static int sim_button_state; */ static int simulated_button_pressed(const struct button_config *button) { - return !!(sim_button_state & BIT(button->type)); + return !!((uint32_t)sim_button_state & BIT(button->type)); } #endif @@ -389,7 +389,8 @@ static void simulate_button_release_deferred(void) /* 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)) { + if ((uint32_t)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)); diff --git a/common/charge_manager.c b/common/charge_manager.c index 3d41c3a08d..041f41f1bc 100644 --- a/common/charge_manager.c +++ b/common/charge_manager.c @@ -1388,9 +1388,9 @@ void charge_manager_source_port(int port, int enable) int p, rp; if (enable) - atomic_or((uint32_t *)&source_port_bitmap, 1 << port); + atomic_or((atomic_t *)&source_port_bitmap, 1 << port); else - atomic_clear_bits((uint32_t *)&source_port_bitmap, 1 << port); + atomic_clear_bits((atomic_t *)&source_port_bitmap, 1 << port); /* No change, exit early. */ if (prev_bitmap == source_port_bitmap) diff --git a/common/charge_ramp.c b/common/charge_ramp.c index a408771f40..32e0d21ddb 100644 --- a/common/charge_ramp.c +++ b/common/charge_ramp.c @@ -18,16 +18,29 @@ test_mockable int chg_ramp_allowed(int port, int supplier) return 0; switch (supplier) { - /* Use ramping for USB-C DTS suppliers (debug accessory eg suzy-q). */ + /* + * Use ramping for USB-C DTS suppliers (debug accessory eg suzy-q). + * The suzy-q simply passes through the VBUS. The power supplier behind + * may be a SDP/CDP which requires ramping. + */ 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). + * Don't regulate the input voltage for USB-C chargers. It is + * unnecessary as the USB-C compliant adapters should never trigger it + * active. + * + * The USB-C spec defines their load curves should not be below + * 4.75V @0A and 4V @3A. We can't define the voltage regulation value + * higher than 4V since it limits the current reaching its max 3A. If + * we define the voltage regulation value lower than 4V, their load + * curves will never be below the voltage regulation line. + * + * Check go/charge_ramp_typec for detail. */ case CHARGE_SUPPLIER_PD: case CHARGE_SUPPLIER_TYPEC: - return IS_ENABLED(CONFIG_CHARGE_RAMP_HW); + return 0; /* default: fall through */ } diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c index abb271cad4..75ffad576b 100644 --- a/common/charge_state_v2.c +++ b/common/charge_state_v2.c @@ -1200,7 +1200,12 @@ static int shutdown_on_critical_battery(void) switch (board_critical_shutdown_check(&curr)) { case CRITICAL_SHUTDOWN_HIBERNATE: if (IS_ENABLED(CONFIG_HIBERNATE)) { - if (power_get_state() == POWER_S3S5) + /* + * If the chipset is on its way down but not + * quite there yet, give it a little time to + * get there. + */ + if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) sleep(1); CPRINTS("Hibernate due to critical battery"); cflush(); @@ -1208,7 +1213,11 @@ static int shutdown_on_critical_battery(void) } break; case CRITICAL_SHUTDOWN_CUTOFF: - if (power_get_state() == POWER_S3S5) + /* + * Give the chipset just a sec to get to off if + * it's trying. + */ + if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) sleep(1); CPRINTS("Cutoff due to critical battery"); cflush(); diff --git a/common/device_event.c b/common/device_event.c index f7944ae930..748a98ae8f 100644 --- a/common/device_event.c +++ b/common/device_event.c @@ -17,8 +17,8 @@ #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; +static atomic_t device_current_events; +static atomic_t device_enabled_events; uint32_t device_get_current_events(void) { @@ -40,7 +40,7 @@ void device_set_events(uint32_t mask) /* Ignore events that are not enabled */ mask &= device_enabled_events; - if ((device_current_events & mask) != mask) { + if (((uint32_t)device_current_events & mask) != mask) { CPRINTS("device event set 0x%08x", mask); } else { /* @@ -64,7 +64,7 @@ void device_set_events(uint32_t mask) void device_clear_events(uint32_t mask) { /* Only print if something's about to change */ - if (device_current_events & mask) + if ((uint32_t)device_current_events & mask) CPRINTS("device event clear 0x%08x", mask); atomic_clear_bits(&device_current_events, mask); diff --git a/common/dptf.c b/common/dptf.c index 33a42ba5af..28ccff34f2 100644 --- a/common/dptf.c +++ b/common/dptf.c @@ -28,6 +28,8 @@ 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_ASSERT(TEMP_SENSOR_COUNT > 0, + "CONFIG_PLATFORM_EC_DPTF enabled, but no temp sensors"); static void dptf_init(void) { @@ -43,14 +45,14 @@ static void dptf_init(void) 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; +static atomic_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? */ + if ((uint32_t)dptf_seen & BIT(id)) { atomic_clear_bits(&dptf_seen, BIT(id)); return id; } @@ -196,7 +198,7 @@ static int command_dptftemp(int argc, char **argv) ccprintf(" %s\n", temp_sensors[id].name); } - ccprintf("AP seen mask: 0x%08x\n", dptf_seen); + ccprintf("AP seen mask: 0x%08x\n", (int)dptf_seen); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(dptftemp, command_dptftemp, diff --git a/common/ec_features.c b/common/ec_features.c index a7e097e733..2147c1b48a 100644 --- a/common/ec_features.c +++ b/common/ec_features.c @@ -147,6 +147,9 @@ uint32_t get_feature_flags1(void) #ifdef CONFIG_USB_MUX_AP_ACK_REQUEST | EC_FEATURE_MASK_1(EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK) #endif +#ifdef CONFIG_POWER_S4_RESIDENCY + | EC_FEATURE_MASK_1(EC_FEATURE_S4_RESIDENCY) +#endif ; return board_override_feature_flags1(result); } diff --git a/common/fmap.c b/common/fmap.c index 47fa75f0e9..6bae9c7f85 100644 --- a/common/fmap.c +++ b/common/fmap.c @@ -13,7 +13,7 @@ /* * FMAP structs. - * See https://chromium.googlesource.com/chromiumos/third_party/flashmap/+/master/lib/fmap.h + * See https://chromium.googlesource.com/chromiumos/third_party/flashmap/+/HEAD/lib/fmap.h */ #define FMAP_NAMELEN 32 #define FMAP_SIGNATURE "__FMAP__" diff --git a/common/fpsensor/fpsensor.c b/common/fpsensor/fpsensor.c index 8d4f88c8eb..58b1bd71cf 100644 --- a/common/fpsensor/fpsensor.c +++ b/common/fpsensor/fpsensor.c @@ -754,7 +754,7 @@ static enum ec_error_list fp_console_action(uint32_t mode) while (tries--) { if (!(sensor_mode & FP_MODE_ANY_CAPTURE)) { - CPRINTS("done (events:%x)", fp_events); + CPRINTS("done (events:%x)", (int)fp_events); return 0; } usleep(100 * MSEC); diff --git a/common/fpsensor/fpsensor_state.c b/common/fpsensor/fpsensor_state.c index db64110b56..bd907e2c00 100644 --- a/common/fpsensor/fpsensor_state.c +++ b/common/fpsensor/fpsensor_state.c @@ -3,6 +3,7 @@ * found in the LICENSE file. */ +#include "atomic.h" #include "common.h" #include "cryptoc/util.h" #include "ec_commands.h" @@ -51,7 +52,7 @@ uint8_t tpm_seed[FP_CONTEXT_TPM_BYTES]; /* Status of the FP encryption engine. */ static uint32_t fp_encryption_status; -uint32_t fp_events; +atomic_t fp_events; uint32_t sensor_mode; diff --git a/common/host_event_commands.c b/common/host_event_commands.c index 1856c88c37..532cfe3be3 100644 --- a/common/host_event_commands.c +++ b/common/host_event_commands.c @@ -264,7 +264,7 @@ static struct lazy_wake_masks { static void host_events_atomic_or(host_event_t *e, host_event_t m) { - uint32_t *ptr = (uint32_t *)e; + atomic_t *ptr = (atomic_t *)e; atomic_or(ptr, (uint32_t)m); #ifdef CONFIG_HOST_EVENT64 @@ -274,7 +274,7 @@ static void host_events_atomic_or(host_event_t *e, host_event_t m) static void host_events_atomic_clear(host_event_t *e, host_event_t m) { - uint32_t *ptr = (uint32_t *)e; + atomic_t *ptr = (atomic_t *)e; atomic_clear_bits(ptr, (uint32_t)m); #ifdef CONFIG_HOST_EVENT64 diff --git a/common/i2c_controller.c b/common/i2c_controller.c index 90d8a8da80..a99952943e 100644 --- a/common/i2c_controller.c +++ b/common/i2c_controller.c @@ -272,8 +272,8 @@ int i2c_xfer_unlocked(const int port, num_msgs++; } - - if (no_pec_af & ~I2C_ADDR_MASK) + /* Big endian flag is used in wrappers for this call */ + if (no_pec_af & ~(I2C_ADDR_MASK | I2C_FLAG_BIG_ENDIAN)) ccprintf("Ignoring flags from i2c addr_flags: %04x", no_pec_af); diff --git a/common/i2c_trace.c b/common/i2c_trace.c index 67b8864b22..e853a834bd 100644 --- a/common/i2c_trace.c +++ b/common/i2c_trace.c @@ -67,7 +67,11 @@ static int command_i2ctrace_list(void) ccprintf("%-2zd %d %-8s 0x%X", i, trace_entries[i].port, +#ifndef CONFIG_ZEPHYR i2c_port->name, +#else + "", +#endif /* CONFIG_ZEPHYR */ trace_entries[i].addr_lo); if (trace_entries[i].addr_hi != trace_entries[i].addr_lo) diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c index 071b441cfa..8d59fb0a33 100644 --- a/common/keyboard_scan.c +++ b/common/keyboard_scan.c @@ -140,9 +140,9 @@ 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); + atomic_clear_bits((atomic_t *)&disable_scanning_mask, mask); } else { - atomic_or((uint32_t *)&disable_scanning_mask, mask); + atomic_or((atomic_t *)&disable_scanning_mask, mask); clear_typematic_key(); } @@ -954,9 +954,9 @@ int keyboard_factory_test_scan(void) gpio_set_flags_by_mask(port, 1 << id, GPIO_OUT_LOW); - for (j = 0; j < i; j++) { + for (j = 0; j < keyboard_factory_scan_pins_used; j++) { - if (keyboard_factory_scan_pins[j][0] < 0) + if (keyboard_factory_scan_pins[j][0] < 0 || i == j) continue; if (keyboard_raw_is_input_low( diff --git a/common/led_common.c b/common/led_common.c index 85879b148f..6c0e2ac426 100644 --- a/common/led_common.c +++ b/common/led_common.c @@ -47,6 +47,16 @@ int led_auto_control_is_enabled(enum ec_led_id led_id) return (led_auto_control_flags & LED_AUTO_CONTROL_FLAG(led_id)) != 0; } +__attribute__((weak)) void board_led_auto_control(void) +{ + /* + * The projects have only power led won't change the led + * state immediately as the auto command is called for + * they only check the led state while the power state + * is changed. + */ +} + static enum ec_status led_command_control(struct host_cmd_handler_args *args) { const struct ec_params_led_control *p = args->params; @@ -69,6 +79,8 @@ static enum ec_status led_command_control(struct host_cmd_handler_args *args) if (p->flags & EC_LED_FLAGS_AUTO) { led_auto_control(p->led_id, 1); + if (!IS_ENABLED(CONFIG_LED_ONOFF_STATES)) + board_led_auto_control(); } else { if (led_set_brightness(p->led_id, p->brightness) != EC_SUCCESS) return EC_RES_INVALID_PARAM; diff --git a/common/mkbp_fifo.c b/common/mkbp_fifo.c index 428d6412fc..c394d9fc77 100644 --- a/common/mkbp_fifo.c +++ b/common/mkbp_fifo.c @@ -28,7 +28,7 @@ static uint32_t fifo_start; /* first entry */ static uint32_t fifo_end; /* last entry */ -static uint32_t fifo_entries; /* number of existing entries */ +static atomic_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]; diff --git a/common/mock/usb_pd_dpm_mock.c b/common/mock/usb_pd_dpm_mock.c index 8b6fbaa30e..766cdcecf4 100644 --- a/common/mock/usb_pd_dpm_mock.c +++ b/common/mock/usb_pd_dpm_mock.c @@ -31,6 +31,10 @@ void dpm_init(int port) dpm[port].mode_exit_request = false; } +void dpm_mode_exit_complete(int port) +{ +} + void dpm_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, uint32_t *vdm) { diff --git a/common/motion_sense.c b/common/motion_sense.c index a9b15fd071..afffb02e78 100644 --- a/common/motion_sense.c +++ b/common/motion_sense.c @@ -73,7 +73,7 @@ STATIC_IF(CONFIG_MOTION_FILL_LPC_SENSE_DATA) void update_sense_data( uint8_t *lpc_status, int *psample_id); /* Flags to control whether to send an ODR change event for a sensor */ -static uint32_t odr_event_required; +static atomic_t odr_event_required; /* Whether or not the FIFO interrupt should be enabled (set from the AP). */ __maybe_unused static int fifo_int_enabled; diff --git a/common/ocpc.c b/common/ocpc.c index 3bc2a265d3..d63e6f8793 100644 --- a/common/ocpc.c +++ b/common/ocpc.c @@ -379,6 +379,7 @@ int ocpc_config_secondary_charger(int *desired_input_current, if (batt.desired_voltage) { if (((batt.voltage < batt_info->voltage_min) || ((batt.voltage < batt_info->voltage_normal) && + (current_ma >= 0) && (current_ma <= batt_info->precharge_current))) && (ph != PHASE_PRECHARGE)) { /* diff --git a/common/peripheral_charger.c b/common/peripheral_charger.c index 0a597ad6bd..9ed8d8394d 100644 --- a/common/peripheral_charger.c +++ b/common/peripheral_charger.c @@ -22,7 +22,7 @@ #define CPRINTS(fmt, args...) cprints(CC_PCHG, "PCHG: " fmt, ##args) /* Currently only used for FW update. */ -static uint32_t pchg_host_events; +static atomic_t pchg_host_events; static void pchg_queue_event(struct pchg *ctx, enum pchg_event event) { diff --git a/common/system.c b/common/system.c index 5e18170b59..8f85e85e3c 100644 --- a/common/system.c +++ b/common/system.c @@ -72,11 +72,11 @@ STATIC_IF(CONFIG_HIBERNATE) uint32_t hibernate_seconds; STATIC_IF(CONFIG_HIBERNATE) uint32_t hibernate_microseconds; /* On-going actions preventing going into deep-sleep mode */ -uint32_t sleep_mask; +atomic_t sleep_mask; #ifdef CONFIG_LOW_POWER_IDLE_LIMITED /* Set it to prevent going into idle mode */ -uint32_t idle_disabled; +atomic_t idle_disabled; #endif /* SKU ID sourced from AP */ @@ -1491,7 +1491,7 @@ static int command_sleepmask(int argc, char **argv) } } #endif - ccprintf("sleep mask: %08x\n", sleep_mask); + ccprintf("sleep mask: %08x\n", (int)sleep_mask); return EC_SUCCESS; } diff --git a/common/thermal.c b/common/thermal.c index dba7334b74..50bf3e27f1 100644 --- a/common/thermal.c +++ b/common/thermal.c @@ -54,7 +54,7 @@ BUILD_ASSERT(EC_TEMP_THRESH_COUNT == 3); static cond_t cond_hot[EC_TEMP_THRESH_COUNT]; /* thermal sensor read delay */ -#if defined(CONFIG_TEMP_SENSOR_POWER_GPIO) && \ +#if defined(CONFIG_TEMP_SENSOR_POWER) && \ defined(CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS) static int first_read_delay = CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS; #endif @@ -77,7 +77,7 @@ static void thermal_control(void) #endif /* add delay to ensure thermal sensor is ready when EC boot */ -#if defined(CONFIG_TEMP_SENSOR_POWER_GPIO) && \ +#if defined(CONFIG_TEMP_SENSOR_POWER) && \ defined(CONFIG_TEMP_SENSOR_FIRST_READ_DELAY_MS) if (first_read_delay != 0) { msleep(first_read_delay); diff --git a/common/timer.c b/common/timer.c index 0490741c4c..0cb0d97289 100644 --- a/common/timer.c +++ b/common/timer.c @@ -49,7 +49,7 @@ static int timer_irq; static void expire_timer(task_id_t tskid) { /* we are done with this timer */ - atomic_clear_bits(&timer_running, 1 << tskid); + atomic_clear_bits((atomic_t *)&timer_running, 1 << tskid); /* wake up the taks waiting for this timer */ task_set_event(tskid, TASK_EVENT_TIMER); } @@ -142,7 +142,7 @@ int timer_arm(timestamp_t event, task_id_t tskid) return EC_ERROR_BUSY; timer_deadline[tskid] = event; - atomic_or(&timer_running, BIT(tskid)); + atomic_or((atomic_t *)&timer_running, BIT(tskid)); /* Modify the next event if needed */ if ((event.le.hi < now.le.hi) || @@ -156,7 +156,7 @@ void timer_cancel(task_id_t tskid) { ASSERT(tskid < TASK_ID_COUNT); - atomic_clear_bits(&timer_running, BIT(tskid)); + atomic_clear_bits((atomic_t *)&timer_running, BIT(tskid)); /* * Don't need to cancel the hardware timer interrupt, instead do * timer-related housekeeping when the next timer interrupt fires. diff --git a/common/usb_common.c b/common/usb_common.c index d5f42edca2..be54c62eff 100644 --- a/common/usb_common.c +++ b/common/usb_common.c @@ -132,7 +132,7 @@ int remote_flashing(int argc, char **argv) struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; #endif /* CONFIG_COMMON_RUNTIME */ -static __maybe_unused uint32_t pd_host_event_status __aligned(4); +static __maybe_unused atomic_t pd_host_event_status __aligned(4); bool pd_firmware_upgrade_check_power_readiness(int port) { @@ -578,7 +578,7 @@ static void pd_send_hard_reset(int port) #ifdef CONFIG_USBC_OCP -static uint32_t port_oc_reset_req; +static atomic_t port_oc_reset_req; static void re_enable_ports(void) { @@ -838,7 +838,7 @@ void pd_set_vbus_discharge(int port, int enable) 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 +#ifdef CONFIG_USB_PD_DISCHARGE_TCPC tcpc_discharge_vbus(port, enable); #endif } else if (get_usb_pd_discharge() == USB_PD_DISCHARGE_PPC) { @@ -852,7 +852,7 @@ void pd_set_vbus_discharge(int port, int enable) #endif /* CONFIG_USB_PD_DISCHARGE */ #ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; +static atomic_t pd_ports_to_resume; static void resume_pd_port(void) { uint32_t port; diff --git a/common/usb_pd_host_cmd.c b/common/usb_pd_host_cmd.c index 47ec36ad5f..3cc3584e92 100644 --- a/common/usb_pd_host_cmd.c +++ b/common/usb_pd_host_cmd.c @@ -548,7 +548,7 @@ DECLARE_HOST_COMMAND(EC_CMD_PD_CONTROL, pd_control, EC_VER_MASK(0)); * 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 atomic_t pd_host_event_status __aligned(4); static enum ec_status hc_pd_host_event_status(struct host_cmd_handler_args *args) diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index abf75e8004..94eb1a3aa7 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -243,9 +243,9 @@ static struct pd_protocol { /* 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; + atomic_t tasks_waiting_on_reset; /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; + atomic_t tasks_preventing_lpm; #endif #ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE diff --git a/common/usbc/dp_alt_mode.c b/common/usbc/dp_alt_mode.c index 2a532466ac..1ebc5d3d04 100644 --- a/common/usbc/dp_alt_mode.c +++ b/common/usbc/dp_alt_mode.c @@ -58,10 +58,12 @@ static const uint8_t state_vdm_cmd[DP_STATE_COUNT] = { */ #define DP_FLAG_RETRY BIT(0) -static uint32_t dpm_dp_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; +static atomic_t dpm_dp_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; -#define DP_SET_FLAG(port, flag) atomic_or(&dpm_dp_flags[port], (flag)) -#define DP_CLR_FLAG(port, flag) atomic_clear_bits(&dpm_dp_flags[port], (flag)) +#define DP_SET_FLAG(port, flag) \ + atomic_or(&dpm_dp_flags[port], (flag)) +#define DP_CLR_FLAG(port, flag) \ + atomic_clear_bits(&dpm_dp_flags[port], (flag)) #define DP_CHK_FLAG(port, flag) (dpm_dp_flags[port] & (flag)) bool dp_is_active(int port) diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c index 36cfdf0f75..5deea53d5d 100644 --- a/common/usbc/usb_pd_dpm.c +++ b/common/usbc/usb_pd_dpm.c @@ -37,7 +37,7 @@ #define DPM_ATTENION_MAX_VDO 2 static struct { - uint32_t flags; + atomic_t flags; uint32_t vdm_attention[DPM_ATTENION_MAX_VDO]; int vdm_cnt; mutex_t vdm_attention_mutex; @@ -48,12 +48,16 @@ static struct { #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) +#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_ENTER_ANY (DPM_FLAG_ENTER_DP | DPM_FLAG_ENTER_TBT \ + | DPM_FLAG_ENTER_USB4) +#define DPM_FLAG_SEND_ATTENTION BIT(5) +#define DPM_FLAG_DATA_RESET_REQUESTED BIT(6) +#define DPM_FLAG_DATA_RESET_DONE BIT(7) #ifdef CONFIG_ZEPHYR static int init_vdm_attention_mutex(const struct device *dev) @@ -134,6 +138,7 @@ enum ec_status pd_request_enter_mode(int port, enum typec_mode mode) DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); + DPM_CLR_FLAG(port, DPM_FLAG_DATA_RESET_DONE); return EC_RES_SUCCESS; } @@ -143,6 +148,12 @@ void dpm_init(int port) dpm[port].flags = 0; } +void dpm_mode_exit_complete(int port) +{ + DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE | DPM_FLAG_EXIT_REQUEST | + DPM_FLAG_SEND_ATTENTION); +} + static void dpm_set_mode_entry_done(int port) { DPM_SET_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); @@ -155,6 +166,13 @@ void dpm_set_mode_exit_request(int port) DPM_SET_FLAG(port, DPM_FLAG_EXIT_REQUEST); } +void dpm_data_reset_complete(int port) +{ + DPM_CLR_FLAG(port, DPM_FLAG_DATA_RESET_REQUESTED); + DPM_SET_FLAG(port, DPM_FLAG_DATA_RESET_DONE); + DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); +} + static void dpm_clear_mode_exit_request(int port) { DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); @@ -287,6 +305,22 @@ static void dpm_attempt_mode_entry(int port) if (IS_ENABLED(CONFIG_USBC_SS_MUX) && !usb_mux_set_completed(port)) return; + if (IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) && + IS_ENABLED(CONFIG_USB_PD_DATA_RESET_MSG) && + DPM_CHK_FLAG(port, DPM_FLAG_ENTER_ANY) && + !DPM_CHK_FLAG(port, DPM_FLAG_DATA_RESET_REQUESTED) && + !DPM_CHK_FLAG(port, DPM_FLAG_DATA_RESET_DONE)) { + pd_dpm_request(port, DPM_REQUEST_DATA_RESET); + DPM_SET_FLAG(port, DPM_FLAG_DATA_RESET_REQUESTED); + return; + } + + if (IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) && + IS_ENABLED(CONFIG_USB_PD_DATA_RESET_MSG) && + !DPM_CHK_FLAG(port, DPM_FLAG_DATA_RESET_DONE)) { + return; + } + /* Check if port, port partner and cable support USB4. */ if (IS_ENABLED(CONFIG_USB_PD_USB4) && board_is_tbt_usb4_port(port) && @@ -469,11 +503,11 @@ 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; +static atomic_t sink_max_pdo_requested; /* Ports with FRS source needing > 1.5 A */ -static uint32_t source_frs_max_requested; +static atomic_t source_frs_max_requested; /* Ports with non-PD sinks, so current requirements are unknown */ -static uint32_t non_pd_sink_max_requested; +static atomic_t non_pd_sink_max_requested; #define LOWEST_PORT(p) __builtin_ctz(p) /* Undefined behavior if p == 0 */ @@ -674,8 +708,8 @@ 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)) + if (!(BIT(port) & (uint32_t)sink_max_pdo_requested) && + !(BIT(port) & (uint32_t)non_pd_sink_max_requested)) return; atomic_clear_bits(&sink_max_pdo_requested, BIT(port)); @@ -696,7 +730,7 @@ void dpm_remove_source(int port) if (!IS_ENABLED(CONFIG_USB_PD_FRS)) return; - if (!(BIT(port) & source_frs_max_requested)) + if (!(BIT(port) & (uint32_t)source_frs_max_requested)) return; atomic_clear_bits(&source_frs_max_requested, BIT(port)); diff --git a/common/usbc/usb_pd_timer.c b/common/usbc/usb_pd_timer.c index 97aa699737..4d284024be 100644 --- a/common/usbc/usb_pd_timer.c +++ b/common/usbc/usb_pd_timer.c @@ -21,30 +21,35 @@ #define NO_TIMEOUT (-1) #define EXPIRE_NOW (0) -#define PD_SET_ACTIVE(p, m) pd_timer_atomic_op( \ - atomic_or, \ - timer_active[p], \ +#define PD_SET_ACTIVE(p, m) pd_timer_atomic_op( \ + atomic_or, \ + (atomic_t *)timer_active[p], \ (m)) -#define PD_CLR_ACTIVE(p, m) pd_timer_atomic_op( \ - atomic_clear_bits, \ - timer_active[p], \ +#define PD_CLR_ACTIVE(p, m) pd_timer_atomic_op( \ + atomic_clear_bits, \ + (atomic_t *)timer_active[p], \ (m)) -#define PD_CHK_ACTIVE(p, m) ((timer_active[p][0] & ((m) >> 32)) | \ +#define PD_CHK_ACTIVE(p, m) ((timer_active[p][0] & ((m) >> 32)) | \ (timer_active[p][1] & (m))) -#define PD_SET_DISABLED(p, m) pd_timer_atomic_op( \ - atomic_or, \ - timer_disabled[p], \ +#define PD_SET_DISABLED(p, m) pd_timer_atomic_op( \ + atomic_or, \ + (atomic_t *)timer_disabled[p], \ (m)) -#define PD_CLR_DISABLED(p, m) pd_timer_atomic_op( \ - atomic_clear_bits, \ - timer_disabled[p], \ +#define PD_CLR_DISABLED(p, m) pd_timer_atomic_op( \ + atomic_clear_bits, \ + (atomic_t *)timer_disabled[p], \ (m)) #define PD_CHK_DISABLED(p, m) ((timer_disabled[p][0] & ((m) >> 32)) | \ (timer_disabled[p][1] & (m))) #define TIMER_FIELD_NUM_UINT32S 2 +/* + * Use uint32_t for timer_active and timer_disabled instead of atomic_t, + * because mixing types signed and unsigned around shifting may lead to + * undefined behavior. + */ test_mockable_static uint32_t timer_active[MAX_PD_PORTS][TIMER_FIELD_NUM_UINT32S]; test_mockable_static @@ -77,6 +82,9 @@ __maybe_unused static __const_data const char * const pd_timer_names[] = { [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", + [PE_TIMER_VCONN_DISCHARGE] = "PE-VCONN_DISCHARGE", + [PE_TIMER_VCONN_REAPPLIED] = "PE-VCONN_REAPPLIED", + [PE_TIMER_DATA_RESET_FAIL] = "PE-DATA_RESET_FAIL", [PR_TIMER_CHUNK_SENDER_REQUEST] = "PR-CHUNK_SENDER_REQUEST", [PR_TIMER_CHUNK_SENDER_RESPONSE] = "PR-CHUNK_SENDER_RESPONSE", @@ -116,9 +124,9 @@ __maybe_unused static __const_data const char * const pd_timer_names[] = { */ test_mockable_static void pd_timer_atomic_op( atomic_val_t (*op)(atomic_t*, atomic_val_t), - uint32_t *const timer_field, const uint64_t mask_val) + atomic_t *const timer_field, const uint64_t mask_val) { - uint32_t *atomic_timer_field; + atomic_t *atomic_timer_field; union mask64_t { struct { #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c index 431dcdc9af..60a2255215 100644 --- a/common/usbc/usb_pe_drp_sm.c +++ b/common/usbc/usb_pe_drp_sm.c @@ -20,9 +20,11 @@ #include "task.h" #include "tcpm/tcpm.h" #include "util.h" +#include "usb_charge.h" #include "usb_common.h" #include "usb_dp_alt_mode.h" #include "usb_mode.h" +#include "usb_mux.h" #include "usb_pd_dpm.h" #include "usb_pd_policy.h" #include "usb_pd.h" @@ -153,6 +155,8 @@ #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) +/* FLAG to note that a data reset is complete */ +#define PE_FLAGS_DATA_RESET_COMPLETE BIT(31) /* Message flags which should not persist on returning to ready state */ #define PE_FLAGS_READY_CLR (PE_FLAGS_LOCALLY_INITIATED_AMS \ @@ -171,7 +175,6 @@ */ #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 @@ -319,6 +322,13 @@ enum usb_pe_state { PE_DR_SRC_GET_SOURCE_CAP, /* PD3.0 only states below here*/ +#ifdef CONFIG_USB_PD_DATA_RESET_MSG + /* DFP Data Reset States */ + PE_DDR_SEND_DATA_RESET, + PE_DDR_WAIT_FOR_VCONN_OFF, + PE_DDR_PERFORM_DATA_RESET, +#endif /* CONFIG_USB_PD_DATA_RESET_MSG */ + PE_FRS_SNK_SRC_START_AMS, PE_GIVE_BATTERY_CAP, PE_GIVE_BATTERY_STATUS, @@ -452,6 +462,11 @@ __maybe_unused static __const_data const char * const pe_state_names[] = { #ifdef CONFIG_USBC_VCONN [PE_VCS_FORCE_VCONN] = "PE_VCS_Force_Vconn", #endif +#ifdef CONFIG_USB_PD_DATA_RESET_MSG + [PE_DDR_SEND_DATA_RESET] = "PE_DDR_Send_Data_Reset", + [PE_DDR_WAIT_FOR_VCONN_OFF] = "PE_DDR_Wait_For_VCONN_Off", + [PE_DDR_PERFORM_DATA_RESET] = "PE_DDR_Perform_Data_Reset", +#endif /* CONFIG_USB_PD_DATA_RESET_MSG */ #endif /* CONFIG_USB_PD_REV30 */ }; @@ -562,9 +577,9 @@ static struct policy_engine { /* current port data role (DFP or UFP) */ enum pd_data_role data_role; /* state machine flags */ - uint32_t flags; + atomic_t flags; /* Device Policy Manager Request */ - uint32_t dpm_request; + atomic_t dpm_request; uint32_t dpm_curr_request; /* last requested voltage PDO index */ int requested_idx; @@ -573,7 +588,7 @@ static struct policy_engine { * Port events - PD_STATUS_EVENT_* values * Set from PD task but may be cleared by host command */ - uint32_t events; + atomic_t events; /* port address where soft resets are sent */ enum tcpci_msg_type soft_reset_sop; @@ -1172,6 +1187,11 @@ void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) 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) || +#ifdef CONFIG_USB_PD_DATA_RESET_MSG + get_state_pe(port) == PE_DDR_SEND_DATA_RESET || + get_state_pe(port) == PE_DDR_WAIT_FOR_VCONN_OFF || + get_state_pe(port) == PE_DDR_PERFORM_DATA_RESET || +#endif (pe_in_frs_mode(port) && get_state_pe(port) == PE_PRS_SNK_SRC_SEND_SWAP) ) { @@ -1529,7 +1549,21 @@ static bool common_src_snk_dpm_requests(int port) set_state_pe(port, PE_VCS_CBL_SEND_SOFT_RESET); return true; } +#ifdef CONFIG_USB_PD_DATA_RESET_MSG + else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_DATA_RESET)) { + if (prl_get_rev(port, TCPCI_MSG_SOP) < PD_REV30) { + dpm_data_reset_complete(port); + return false; + } + pe_set_dpm_curr_request(port, DPM_REQUEST_DATA_RESET); + if (pe[port].data_role == PD_ROLE_DFP) + set_state_pe(port, PE_DDR_SEND_DATA_RESET); + else + return false; + return true; + } +#endif /* CONFIG_USB_PD_DATA_RESET_MSG */ return false; } @@ -2167,6 +2201,7 @@ static void pe_src_startup_entry(int port) /* Clear port discovery/mode flags */ pd_dfp_discovery_init(port); pd_dfp_mode_init(port); + dpm_init(port); pe[port].ama_vdo = PD_VDO_INVALID; pe[port].vpd_vdo = PD_VDO_INVALID; pe[port].discover_identity_counter = 0; @@ -3021,6 +3056,7 @@ static void pe_snk_startup_entry(int port) /* Clear port discovery/mode flags */ pd_dfp_discovery_init(port); pd_dfp_mode_init(port); + dpm_init(port); pe[port].discover_identity_counter = 0; /* Reset dr swap attempt counter */ @@ -6974,6 +7010,255 @@ static void pe_dr_src_get_source_cap_exit(int port) pe_sender_response_msg_exit(port); } +#ifdef CONFIG_USB_PD_DATA_RESET_MSG +/* + * PE_DDR_Send_Data_Reset + * See PD rev 3.1, v. 1.2, Figure 8-88. + */ +static void pe_ddr_send_data_reset_entry(int port) +{ + print_current_state(port); + /* Send Data Reset message */ + send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_DATA_RESET); + pe_sender_response_msg_entry(port); +} + +static void pe_ddr_send_data_reset_run(int port) +{ + enum pe_msg_check 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; + } + + /* + * Transition to the next Data Reset state after receiving Accept. + * Return to the ready state after receiving Not Supported. After + * receiving Reject or any other message type (Protocol Error), + * transition to Error Recovery. + */ + if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { + const uint32_t hdr = rx_emsg[port].header; + + PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); + + if (PD_HEADER_GET_SOP(hdr) == TCPCI_MSG_SOP && + PD_HEADER_CNT(hdr) == 0 && + !PD_HEADER_EXT(hdr) && + PD_HEADER_TYPE(hdr) == PD_CTRL_ACCEPT) { + /* + * Start DataResetFailTimer NOTE: This timer continues + * to run in every state until it is stopped or it times + * out. + */ + pd_timer_enable(port, PE_TIMER_DATA_RESET_FAIL, + PD_T_DATA_RESET_FAIL); + set_state_pe(port, tc_is_vconn_src(port) ? + PE_DDR_PERFORM_DATA_RESET : + PE_DDR_WAIT_FOR_VCONN_OFF); + return; + } else if (PD_HEADER_GET_SOP(hdr) == TCPCI_MSG_SOP && + PD_HEADER_CNT(hdr) == 0 && + !PD_HEADER_EXT(hdr) && + PD_HEADER_TYPE(hdr) == PD_CTRL_NOT_SUPPORTED) { + /* Just pretend it worked. */ + dpm_data_reset_complete(port); + pe_set_ready_state(port); + return; + } + + /* Otherwise, it's a protocol error. */ + PE_SET_FLAG(port, PE_FLAGS_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); + set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); + return; + } +} + +static void pe_ddr_send_data_reset_exit(int port) +{ + pe_sender_response_msg_exit(port); +} + +/* + * PE_DDR_Wait_For_VCONN_Off + */ +static void pe_ddr_wait_for_vconn_off_entry(int port) +{ + print_current_state(port); + /* Initialize and start VCONNDischargeTimer */ + pd_timer_enable(port, PE_TIMER_VCONN_DISCHARGE, PD_T_VCONN_DISCHARGE); +} + +static void pe_ddr_wait_for_vconn_off_run(int port) +{ + if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { + const uint32_t hdr = rx_emsg[port].header; + + PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); + + if (PD_HEADER_GET_SOP(hdr) == TCPCI_MSG_SOP && + PD_HEADER_CNT(hdr) == 0 && + !PD_HEADER_EXT(hdr) && + PD_HEADER_TYPE(hdr) == PD_CTRL_PS_RDY) { + /* PS_RDY message received */ + pd_timer_enable(port, PE_TIMER_VCONN_REAPPLIED, + PD_T_VCONN_REAPPLIED); + set_state_pe(port, PE_DDR_PERFORM_DATA_RESET); + return; + } + + /* Otherwise, it's a protocol error. */ + PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); + } + + if (pd_timer_is_expired(port, PE_TIMER_VCONN_DISCHARGE) || + 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_ddr_wait_for_vconn_off_exit(int port) +{ + pd_timer_disable(port, PE_TIMER_VCONN_DISCHARGE); +} + +/* + * PE_DDR_Perform_Data_Reset + * See PD rev 3.1, v. 1.2, section 6.3.14. + */ +static void pe_ddr_perform_data_reset_entry(int port) +{ + print_current_state(port); + + /* + * 1) The DFP shall: + * a) Disconnect the Port’s USB 2.0 D+/D- signals. + * b) If operating in USB 3.2 remove the port’s Rx Terminations. + * c) If operating in [USB4] drive the port’s SBTX to a logic low. + */ + usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_DISCONNECT, + polarity_rm_dts(pd_get_polarity(port))); + + /* 2) Both the DFP and UFP Shall exit all Alternate Modes if any. */ + if (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); + } + + /* 3) Reset the cable */ + /* + * TODO(b/141363146): The PD spec is ambiguous about which state the PE + * should be in during the cable reset (step 3 in section 6.3.14). If + * the DFP is not the initial VCONN Source, the cable reset presumably + * starts in PE_DDR_Wait_for_VCONN_Off and finishes in + * PE_DDR_Perform_Data_Reset. To be consistent with the steps in 6.3.14, + * that would imply that steps 1 and 2 take place in + * PE_DDR_Send_Data_Reset. However, this would be inconsistent with the, + * "Tell the Policy Manager to Perform Data Reset," action in + * PE_DDR_Perform_Data_Reset in figure 8-88, DFP Data_Reset Message + * State Diagram, since the Data Reset process would have had to start + * before then. Resolve this ambiguity and update this implementation. + */ + if (IS_ENABLED(CONFIG_USBC_VCONN) && tc_is_vconn_src(port)) + pd_request_vconn_swap_off(port); + else + PE_SET_FLAG(port, PE_FLAGS_DATA_RESET_COMPLETE); +} + +static void pe_ddr_perform_data_reset_run(int port) +{ + /* + * PE_FLAGS_VCONN_SWAP_COMPLETE may be set in 2 cases: + * a) If the PE requested to turn VCONN off while entering this state, + * i.e. if the TCPM was VCONN Source at that time. If the TCPM did not + * start out as VCONN Source, then PE_DDR_Wait_For_VCONN_Off will have + * already started the VCONN reapplied timer. + * b) When this state requests to turn VCONN on after tVCONNReapplied + * expires. At this point, the Data Reset process is complete. + */ + if (IS_ENABLED(CONFIG_USBC_VCONN) && !tc_is_vconn_src(port) && + PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { + PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); + pd_timer_enable(port, PE_TIMER_VCONN_REAPPLIED, + PD_T_VCONN_REAPPLIED); + } else if (IS_ENABLED(CONFIG_USBC_VCONN) && + pd_timer_is_expired(port, PE_TIMER_VCONN_REAPPLIED)) { + pd_request_vconn_swap_on(port); + pd_timer_disable(port, PE_TIMER_VCONN_REAPPLIED); + + /* + * 4) After tDataReset the DFP shall: + * a) Reconnect the [USB 2.0] D+/D- signals + * b) If the Port was operating in [USB 3.2] or [USB4] + * reapply the port’s Rx Terminations + * TODO: Section 6.3.14 implies that tDataReset is a minimum + * time for the DFP to leave the lines disconnected during Data + * Reset, possibly starting after the cable reset. Section + * 6.6.10.2 implies that tDataReset is the maximum time for the + * DFP to send Data_Reset_Complete after receiving Accept. These + * interpretations are mutually exclusive. Resolve that + * ambiguity and update this implementation. + */ + usb_mux_set(port, USB_PD_MUX_NONE, USB_SWITCH_DISCONNECT, + polarity_rm_dts(pd_get_polarity(port))); + } else if (IS_ENABLED(CONFIG_USBC_VCONN) && + PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE) && + tc_is_vconn_src(port)) { + PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); + PE_SET_FLAG(port, PE_FLAGS_DATA_RESET_COMPLETE); + } else if (PE_CHK_FLAG(port, PE_FLAGS_DATA_RESET_COMPLETE) && + !pd_timer_is_disabled(port, PE_TIMER_DATA_RESET_FAIL)) { + pd_timer_disable(port, PE_TIMER_DATA_RESET_FAIL); + send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_DATA_RESET_COMPLETE); + } else if (PE_CHK_FLAG(port, PE_FLAGS_DATA_RESET_COMPLETE)) { + /* + * There is no specified response to Data_Reset_Complete, but + * make sure the port partner receives it before returning to a + * ready state. + */ + if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) + set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); + else if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) + pe_set_ready_state(port); + return; + } else if (pd_timer_is_expired(port, PE_TIMER_DATA_RESET_FAIL) || + 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; + } + + /* + * No messages are expected, so any received would be a protocol error. + */ + if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { + PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); + set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); + } +} + +static void pe_ddr_perform_data_reset_exit(int port) +{ + pd_timer_disable(port, PE_TIMER_VCONN_REAPPLIED); + pd_timer_disable(port, PE_TIMER_DATA_RESET_FAIL); + PE_CLR_FLAG(port, PE_FLAGS_DATA_RESET_COMPLETE); + dpm_data_reset_complete(port); +} +#endif /* CONFIG_USB_PD_DATA_RESET_MSG */ + const uint32_t * const pd_get_src_caps(int port) { return pe[port].src_caps; @@ -6998,7 +7283,7 @@ uint8_t pd_get_src_cap_cnt(int port) } /* Track access to the PD discovery structures during HC execution */ -uint32_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT]; +atomic_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT]; void pd_dfp_discovery_init(int port) { @@ -7022,7 +7307,7 @@ void pd_dfp_mode_init(int port) 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); + dpm_mode_exit_complete(port); dp_init(port); if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) @@ -7446,6 +7731,23 @@ static __const_data const struct usb_state pe_states[] = { .exit = pe_vcs_force_vconn_exit, }, #endif /* CONFIG_USBC_VCONN */ +#ifdef CONFIG_USB_PD_DATA_RESET_MSG + [PE_DDR_SEND_DATA_RESET] = { + .entry = pe_ddr_send_data_reset_entry, + .run = pe_ddr_send_data_reset_run, + .exit = pe_ddr_send_data_reset_exit, + }, + [PE_DDR_WAIT_FOR_VCONN_OFF] = { + .entry = pe_ddr_wait_for_vconn_off_entry, + .run = pe_ddr_wait_for_vconn_off_run, + .exit = pe_ddr_wait_for_vconn_off_exit, + }, + [PE_DDR_PERFORM_DATA_RESET] = { + .entry = pe_ddr_perform_data_reset_entry, + .run = pe_ddr_perform_data_reset_run, + .exit = pe_ddr_perform_data_reset_exit, + }, +#endif /* CONFIG_USB_PD_DATA_RESET_MSG */ #endif /* CONFIG_USB_PD_REV30 */ }; diff --git a/common/usbc/usb_prl_sm.c b/common/usbc/usb_prl_sm.c index b85a7ee553..d9c3849f58 100644 --- a/common/usbc/usb_prl_sm.c +++ b/common/usbc/usb_prl_sm.c @@ -294,7 +294,7 @@ static struct rx_chunked { /* state machine context */ struct sm_ctx ctx; /* PRL_FLAGS */ - uint32_t flags; + atomic_t flags; /* error to report when moving to rch_report_error state */ enum pe_error error; } rch[CONFIG_USB_PD_PORT_MAX_COUNT]; @@ -304,7 +304,7 @@ static struct tx_chunked { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ - uint32_t flags; + atomic_t flags; /* error to report when moving to tch_report_error state */ enum pe_error error; } tch[CONFIG_USB_PD_PORT_MAX_COUNT]; @@ -322,7 +322,7 @@ static struct protocol_layer_tx { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ - uint32_t flags; + atomic_t flags; /* last message type we transmitted */ enum tcpci_msg_type last_xmit_type; /* message id counters for all 6 port partners */ @@ -336,13 +336,13 @@ static struct protocol_hard_reset { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ - uint32_t flags; + atomic_t flags; } prl_hr[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Chunking Message Object */ static struct pd_message { /* message status flags */ - uint32_t flags; + atomic_t flags; /* SOP* */ enum tcpci_msg_type xmit_type; /* type of message */ diff --git a/common/usbc/usb_retimer_fw_update.c b/common/usbc/usb_retimer_fw_update.c index 1ff198c78f..87a8f786c5 100644 --- a/common/usbc/usb_retimer_fw_update.c +++ b/common/usbc/usb_retimer_fw_update.c @@ -96,8 +96,29 @@ static inline mux_state_t retimer_fw_update_usb_mux_get(int port) return usb_mux_get(port) & USB_RETIMER_FW_UPDATE_MUX_MASK; } +/* Allow mux results to be filled in during HOOKS if needed */ +static void last_result_mux_get(void); +DECLARE_DEFERRED(last_result_mux_get); + +static void last_result_mux_get(void) +{ + if (!usb_mux_set_completed(cur_port)) { + hook_call_deferred(&last_result_mux_get_data, 20 * MSEC); + return; + } + + last_result = retimer_fw_update_usb_mux_get(cur_port); +} + void usb_retimer_fw_update_process_op_cb(int port) { + bool result_mux_get = false; + + if (port != cur_port) { + CPRINTS("Unexpected FW op: port %d, cur %d", port, cur_port); + return; + } + switch (last_op) { case USB_RETIMER_FW_UPDATE_SUSPEND_PD: last_result = 0; @@ -123,30 +144,37 @@ void usb_retimer_fw_update_process_op_cb(int port) pd_set_suspend(port, RESUME); break; case USB_RETIMER_FW_UPDATE_GET_MUX: - last_result = retimer_fw_update_usb_mux_get(port); + result_mux_get = true; 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); + result_mux_get = true; break; case USB_RETIMER_FW_UPDATE_SET_SAFE: usb_mux_set_safe_mode(port); - last_result = retimer_fw_update_usb_mux_get(port); + result_mux_get = true; 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); + result_mux_get = true; 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); + result_mux_get = true; break; default: break; } + + /* + * Fill in our mux result if available, or set up a deferred retrieval + * if the set is still pending. + */ + if (result_mux_get) + last_result_mux_get(); } void usb_retimer_fw_update_process_op(int port, int op) @@ -158,6 +186,7 @@ void usb_retimer_fw_update_process_op(int port, int op) * not change cur_port if retimer scan is in progress */ last_op = op; + cur_port = port; switch (op) { case USB_RETIMER_FW_UPDATE_QUERY_PORT: @@ -169,7 +198,6 @@ void usb_retimer_fw_update_process_op(int port, int op) 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: diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c index 3fa9528699..726958ba03 100644 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c @@ -401,7 +401,7 @@ static struct type_c { * else they're disabled if bits PD_DISABLED_NO_CONNECTION or * PD_DISABLED_BY_POLICY are set. */ - uint32_t pd_disabled_mask; + atomic_t pd_disabled_mask; /* * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle * enabled. See drp_auto_toggle_next_state() for details. @@ -414,13 +414,13 @@ static struct type_c { /* Port polarity */ enum tcpc_cc_polarity polarity; /* port flags, see TC_FLAGS_* */ - uint32_t flags; + atomic_t flags; /* The cc state */ enum pd_cc_states cc_state; /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; + atomic_t tasks_waiting_on_reset; /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; + atomic_t tasks_preventing_lpm; /* Voltage on CC pin */ enum tcpc_cc_voltage_status cc_voltage; /* Type-C current */ @@ -457,7 +457,7 @@ 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 atomic_t pd_try_src; static volatile enum try_src_override_t pd_try_src_override; static void pd_update_try_source(void); @@ -825,6 +825,10 @@ int tc_is_attached_snk(int port) return IS_ATTACHED_SNK(port); } +__overridable void tc_update_pd_sleep_mask(int port) +{ +} + void tc_pd_connection(int port, int en) { if (en) { @@ -836,9 +840,10 @@ void tc_pd_connection(int port, int en) 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)) { + IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) + tc_update_pd_sleep_mask(port); + else if (IS_ENABLED(CONFIG_LOW_POWER_IDLE)) disable_sleep(SLEEP_MASK_USB_PD); - } /* * Update the mux state, only when the PD capable flag @@ -852,7 +857,9 @@ void tc_pd_connection(int port, int en) 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)) { + IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) + tc_update_pd_sleep_mask(port); + else if (IS_ENABLED(CONFIG_LOW_POWER_IDLE)) { int i; /* If all ports are not connected, allow the sleep */ diff --git a/common/usbc_ocp.c b/common/usbc_ocp.c index e20cf9f1f8..c7c977bf12 100644 --- a/common/usbc_ocp.c +++ b/common/usbc_ocp.c @@ -42,7 +42,7 @@ 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 atomic_t snk_connected_ports; static void clear_oc_tbl(void) { @@ -53,7 +53,7 @@ static void clear_oc_tbl(void) * Only clear the table if the port partner is no longer * attached after debouncing. */ - if ((!(BIT(port) & snk_connected_ports)) && + if ((!(BIT(port) & (uint32_t)snk_connected_ports)) && oc_event_cnt_tbl[port]) { oc_event_cnt_tbl[port] = 0; CPRINTS("C%d: OC events cleared", port); |