diff options
-rw-r--r-- | board/dingdong/board.h | 2 | ||||
-rw-r--r-- | board/dingdong/usb_pd_policy.c | 13 | ||||
-rw-r--r-- | board/hoho/board.h | 2 | ||||
-rw-r--r-- | board/hoho/usb_pd_policy.c | 13 | ||||
-rw-r--r-- | board/samus_pd/board.h | 2 | ||||
-rw-r--r-- | board/zinger/board.h | 2 | ||||
-rw-r--r-- | board/zinger/runtime.c | 10 | ||||
-rw-r--r-- | board/zinger/usb_pd_policy.c | 3 | ||||
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/pd_log.c | 151 | ||||
-rw-r--r-- | include/config.h | 6 | ||||
-rw-r--r-- | include/ec_commands.h | 75 | ||||
-rw-r--r-- | include/usb_pd.h | 29 |
13 files changed, 302 insertions, 7 deletions
diff --git a/board/dingdong/board.h b/board/dingdong/board.h index 6247ae5fcc..666ab2699e 100644 --- a/board/dingdong/board.h +++ b/board/dingdong/board.h @@ -36,6 +36,8 @@ #define CONFIG_USB_PD_IDENTITY_HW_VERS 1 #define CONFIG_USB_PD_IDENTITY_SW_VERS 1 #define CONFIG_USB_PD_NO_VBUS_DETECT +#define CONFIG_USB_PD_LOGGING +#define CONFIG_USB_PD_LOG_SIZE 256 #undef CONFIG_WATCHDOG_HELP #undef CONFIG_LID_SWITCH #undef CONFIG_TASK_PROFILING diff --git a/board/dingdong/usb_pd_policy.c b/board/dingdong/usb_pd_policy.c index 5c14e4a6da..520732cd26 100644 --- a/board/dingdong/usb_pd_policy.c +++ b/board/dingdong/usb_pd_policy.c @@ -244,8 +244,17 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, *rpayload = payload; rsize = pd_custom_flash_vdm(port, cnt, payload); - if (!rsize) - return 0; + if (!rsize) { + int cmd = PD_VDO_CMD(payload[0]); + switch (cmd) { + case VDO_CMD_GET_LOG: + rsize = pd_vdm_get_log_entry(payload); + break; + default: + /* Unknown : do not answer */ + return 0; + } + } /* respond (positively) to the request */ payload[0] |= VDO_SRC_RESPONDER; diff --git a/board/hoho/board.h b/board/hoho/board.h index 74e3951b53..58dc4db20a 100644 --- a/board/hoho/board.h +++ b/board/hoho/board.h @@ -42,6 +42,8 @@ #define CONFIG_USB_PD_INTERNAL_COMP #define CONFIG_USB_PD_IDENTITY_HW_VERS 1 #define CONFIG_USB_PD_IDENTITY_SW_VERS 1 +#define CONFIG_USB_PD_LOGGING +#define CONFIG_USB_PD_LOG_SIZE 256 #define CONFIG_USB_PD_NO_VBUS_DETECT /* mcdp2850 serial interface */ #define CONFIG_MCDP28X0 usart3_hw diff --git a/board/hoho/usb_pd_policy.c b/board/hoho/usb_pd_policy.c index 9ecfd38611..efd1bbc243 100644 --- a/board/hoho/usb_pd_policy.c +++ b/board/hoho/usb_pd_policy.c @@ -243,8 +243,17 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, *rpayload = payload; rsize = pd_custom_flash_vdm(port, cnt, payload); - if (!rsize) - return 0; + if (!rsize) { + int cmd = PD_VDO_CMD(payload[0]); + switch (cmd) { + case VDO_CMD_GET_LOG: + rsize = pd_vdm_get_log_entry(payload); + break; + default: + /* Unknown : do not answer */ + return 0; + } + } /* respond (positively) to the request */ payload[0] |= VDO_SRC_RESPONDER; diff --git a/board/samus_pd/board.h b/board/samus_pd/board.h index 58e917d269..5adb1c46fc 100644 --- a/board/samus_pd/board.h +++ b/board/samus_pd/board.h @@ -45,6 +45,8 @@ #define CONFIG_USB_PD_DUAL_ROLE #define CONFIG_USB_PD_FLASH_ERASE_CHECK #define CONFIG_USB_PD_INTERNAL_COMP +#define CONFIG_USB_PD_LOGGING +#define CONFIG_USB_PD_LOG_SIZE 512 #define CONFIG_USB_SWITCH_PI3USB9281 #undef CONFIG_USB_SWITCH_PI3USB9281_MUX_GPIO #define CONFIG_USB_SWITCH_PI3USB9281_MUX_GPIO GPIO_USB_C_BC12_SEL diff --git a/board/zinger/board.h b/board/zinger/board.h index e307c6ff27..8eba23e214 100644 --- a/board/zinger/board.h +++ b/board/zinger/board.h @@ -51,6 +51,8 @@ #define CONFIG_USB_PD_CUSTOM_VDM #undef CONFIG_USB_PD_DUAL_ROLE #undef CONFIG_USB_PD_INTERNAL_COMP +#define CONFIG_USB_PD_LOGGING +#define CONFIG_USB_PD_LOG_SIZE 256 #undef CONFIG_USB_PD_RX_COMP_IRQ #define CONFIG_USB_PD_SIMPLE_DFP #undef CONFIG_WATCHDOG_HELP diff --git a/board/zinger/runtime.c b/board/zinger/runtime.c index 9d644e763a..0dfb51aed4 100644 --- a/board/zinger/runtime.c +++ b/board/zinger/runtime.c @@ -56,6 +56,16 @@ void task_clear_pending_irq(int irq) CPU_NVIC_UNPEND(0) = 1 << irq; } +void interrupt_disable(void) +{ + asm("cpsid i"); +} + +void interrupt_enable(void) +{ + asm("cpsie i"); +} + uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait) { last_event = event; diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c index 2e658428d9..d61023a13a 100644 --- a/board/zinger/usb_pd_policy.c +++ b/board/zinger/usb_pd_policy.c @@ -553,6 +553,9 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, payload[1] = ADC_TO_CURR_MA(vbus_amp); rsize = 2; break; + case VDO_CMD_GET_LOG: + rsize = pd_vdm_get_log_entry(payload); + break; default: /* Unknown : do not answer */ return 0; diff --git a/common/build.mk b/common/build.mk index 10d74c907d..2bb32b4f98 100644 --- a/common/build.mk +++ b/common/build.mk @@ -80,6 +80,7 @@ common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o throttle_ap.o common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o +common-$(CONFIG_USB_PD_LOGGING)+=pd_log.o common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o common-$(CONFIG_WIRELESS)+=wireless.o common-$(HAS_TASK_CHIPSET)+=chipset.o diff --git a/common/pd_log.c b/common/pd_log.c new file mode 100644 index 0000000000..e48b763b6c --- /dev/null +++ b/common/pd_log.c @@ -0,0 +1,151 @@ +/* Copyright (c) 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 "console.h" +#include "hooks.h" +#include "host_command.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Event log FIFO */ +#define UNIT_SIZE sizeof(struct ec_response_pd_log) +#define LOG_SIZE (CONFIG_USB_PD_LOG_SIZE/UNIT_SIZE) +static struct ec_response_pd_log log_events[LOG_SIZE]; +BUILD_ASSERT(POWER_OF_TWO(LOG_SIZE)); +/* + * 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 commited 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 + * pd_log_event(). Only one task is dequeuing events (host commands or VDM). + * When the FIFO is full, pd_log_event() will discard the oldest events, + * so "log_head" is incremented/decremented in a critical section since it is + * accessed from both pd_log_event() and pd_log_dequeue(). + * 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 pd_log_dequeue(). + */ +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 pd_log_event(uint8_t type, uint8_t size_port, + uint16_t data, void *payload) +{ + struct ec_response_pd_log *r; + size_t payload_size = PD_LOG_SIZE(size_port); + size_t total_size = ENTRY_SIZE(payload_size); + size_t current_tail, first; + + /* --- critical section : reserve queue space --- */ + interrupt_disable(); + current_tail = log_tail_next; + log_tail_next = current_tail + total_size; + interrupt_enable(); + /* --- end of critical section --- */ + + /* Out of space : discard the oldest entry */ + while ((LOG_SIZE - (current_tail - log_head)) < total_size) { + struct ec_response_pd_log *oldest; + /* --- critical section : atomically free-up space --- */ + interrupt_disable(); + oldest = log_events + (log_head & (LOG_SIZE - 1)); + log_head += ENTRY_SIZE(PD_LOG_SIZE(oldest->size_port)); + interrupt_enable(); + /* --- end of critical section --- */ + } + + r = log_events + (current_tail & (LOG_SIZE - 1)); + + r->timestamp = get_time().val >> PD_LOG_TIMESTAMP_SHIFT; + r->type = type; + r->size_port = size_port; + r->data = data; + /* copy the payload into the FIFO */ + first = MIN(total_size - 1, (LOG_SIZE - + (current_tail & (LOG_SIZE - 1))) - 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; +} + +static int pd_log_dequeue(struct ec_response_pd_log *r) +{ + uint32_t now = get_time().val >> PD_LOG_TIMESTAMP_SHIFT; + unsigned total_size, first; + struct ec_response_pd_log *entry; + size_t current_head; + +retry: + current_head = log_head; + /* The log FIFO is empty */ + if (log_tail == current_head) { + memset(r, 0, UNIT_SIZE); + r->type = PD_EVENT_NO_ENTRY; + return UNIT_SIZE; + } + + entry = log_events + (current_head & (LOG_SIZE - 1)); + total_size = ENTRY_SIZE(PD_LOG_SIZE(entry->size_port)); + first = MIN(total_size, LOG_SIZE - (current_head & (LOG_SIZE - 1))); + 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 --- */ + interrupt_disable(); + if (log_head != current_head) { /* our entry was thrown away */ + interrupt_enable(); + goto retry; + } + log_head += total_size; + interrupt_enable(); + /* --- end of critical section --- */ + + /* fixup the timestamp : number of milliseconds in the past */ + r->timestamp = now - r->timestamp; + + return total_size * UNIT_SIZE; +} + +#ifdef HAS_TASK_HOSTCMD +/* we are a PD MCU/EC, send back the events to the host */ +static int hc_pd_get_log_entry(struct host_cmd_handler_args *args) +{ + struct ec_response_pd_log *r = args->response; + + args->response_size = pd_log_dequeue(r); + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_PD_GET_LOG_ENTRY, + hc_pd_get_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 = pd_log_dequeue(r); + + return 1 + DIV_ROUND_UP(byte_size, sizeof(uint32_t)); +} +#endif /* !HAS_TASK_HOSTCMD */ diff --git a/include/config.h b/include/config.h index 2270277265..e1903ea68a 100644 --- a/include/config.h +++ b/include/config.h @@ -1165,6 +1165,12 @@ /* Define if using internal comparator for PD receive */ #undef CONFIG_USB_PD_INTERNAL_COMP +/* Record main PD events in a circular buffer */ +#undef CONFIG_USB_PD_LOGGING + +/* The size in bytes of the FIFO used for PD events logging */ +#undef CONFIG_USB_PD_LOG_SIZE + /* Define if USB-PD device has no way of detecting USB VBUS */ #undef CONFIG_USB_PD_NO_VBUS_DETECT diff --git a/include/ec_commands.h b/include/ec_commands.h index 898d502c6c..954a423f5d 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -2776,14 +2776,22 @@ enum usb_power_roles { USB_PD_PORT_POWER_SINK_NOT_CHARGING, }; +struct usb_chg_measures { + uint16_t voltage_max; + uint16_t voltage_now; + uint16_t current_max; + /* + * this structure is used below in struct ec_response_usb_pd_power_info, + * and currently expects an odd number of uint16_t for alignment. + */ +} __packed; + struct ec_response_usb_pd_power_info { uint8_t role; uint8_t type; uint8_t dualrole; uint8_t reserved1; - uint16_t voltage_max; - uint16_t voltage_now; - uint16_t current_max; + struct usb_chg_measures meas; uint16_t reserved2; uint32_t max_power; } __packed; @@ -2845,6 +2853,67 @@ enum usb_pd_override_ports { struct ec_params_charge_port_override { int16_t override_port; /* Override port# */ } __packed; + +/* Read (and delete) one entry of PD event log */ +#define EC_CMD_PD_GET_LOG_ENTRY 0x115 + +struct ec_response_pd_log { + uint32_t timestamp; /* relative timestamp in milliseconds */ + uint8_t type; /* event type : see PD_EVENT_xx below */ + uint8_t size_port; /* [7:5] port number [4:0] payload size in bytes */ + uint16_t data; /* type-defined data payload */ + uint8_t payload[0]; /* optional additional data payload: 0..16 bytes */ +} __packed; + + +/* The timestamp is the microsecond counter shifted to get about a ms. */ +#define PD_LOG_TIMESTAMP_SHIFT 10 /* 1 LSB = 1024us */ + +#define PD_LOG_SIZE_MASK 0x1F +#define PD_LOG_PORT_MASK 0xE0 +#define PD_LOG_PORT_SHIFT 5 +#define PD_LOG_PORT_SIZE(port, size) (((port) << PD_LOG_PORT_SHIFT) | \ + ((size) & PD_LOG_SIZE_MASK)) +#define PD_LOG_PORT(size_port) ((size_port) >> PD_LOG_PORT_SHIFT) +#define PD_LOG_SIZE(size_port) ((size_port) & PD_LOG_SIZE_MASK) + +/* PD event log : entry types */ +/* PD MCU events */ +#define PD_EVENT_MCU_BASE 0x00 +#define PD_EVENT_MCU_CHARGE (PD_EVENT_MCU_BASE+0) +#define PD_EVENT_MCU_CONNECT (PD_EVENT_MCU_BASE+1) +/* PD generic accessory events */ +#define PD_EVENT_ACC_BASE 0x20 +#define PD_EVENT_ACC_RW_FAIL (PD_EVENT_ACC_BASE+0) +#define PD_EVENT_ACC_RW_ERASE (PD_EVENT_ACC_BASE+1) +#define PD_EVENT_ACC_GFU_ENTER (PD_EVENT_ACC_BASE+2) +/* PD power supply events */ +#define PD_EVENT_PS_BASE 0x40 +#define PD_EVENT_PS_OCP (PD_EVENT_PS_BASE+0) +#define PD_EVENT_PS_OVP (PD_EVENT_PS_BASE+1) +#define PD_EVENT_PS_TEMP (PD_EVENT_PS_BASE+2) +/* PD video dongles events */ +#define PD_EVENT_VIDEO_BASE 0x60 +/* Returned in the "type" field, when there is no entry available */ +#define PD_EVENT_NO_ENTRY 0xFF + +/* + * PD_EVENT_MCU_CHARGE event definition : + * the payload is "struct usb_chg_measures" + * the data field contains the port state flags as defined below : + */ +/* Port partner is a dual role device */ +#define CHARGE_FLAGS_DUAL_ROLE (1 << 15) +/* Port is the pending override port */ +#define CHARGE_FLAGS_DELAYED_OVERRIDE (1 << 14) +/* Port is the override port */ +#define CHARGE_FLAGS_OVERRIDE (1 << 13) +/* Charger type */ +#define CHARGE_FLAGS_TYPE_SHIFT 3 +#define CHARGE_FLAGS_TYPE_MASK (0xF << CHARGE_FLAGS_TYPE_SHIFT) +/* Power delivery role */ +#define CHARGE_FLAGS_ROLE_MASK (7 << 0) + #endif /* !__ACPI__ */ /*****************************************************************************/ diff --git a/include/usb_pd.h b/include/usb_pd.h index 081e78c6f3..14b83ae41b 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -282,6 +282,7 @@ struct pd_policy { #define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10) #define VDO_CMD_CURRENT VDO_CMD_VENDOR(11) #define VDO_CMD_FLIP VDO_CMD_VENDOR(12) +#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13) #define PD_VDO_VID(vdo) ((vdo) >> 16) #define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1) @@ -1276,4 +1277,32 @@ void pd_prepare_sysjump(void); */ void pd_set_new_power_request(int port); +/* ----- Logging ----- */ +#ifdef CONFIG_USB_PD_LOGGING +/** + * Record one event in the PD logging FIFO. + * + * @param type event type as defined by PD_EVENT_xx in ec_commands.h + * @param size_port payload size and port num (defined by PD_LOG_PORT_SIZE) + * @param data type-defined information + * @param payload pointer to the optional payload (0..16 bytes) + */ +void pd_log_event(uint8_t type, uint8_t size_port, + uint16_t data, void *payload); + +/** + * Retrieve one logged event and prepare a VDM with it. + * + * Used to answer the VDO_CMD_GET_LOG unstructured VDM. + * + * @param payload pointer to the payload data buffer (must be 7 words) + * @return number of 32-bit words in the VDM payload. + */ +int pd_vdm_get_log_entry(uint32_t *payload); +#else /* CONFIG_USB_PD_LOGGING */ +static inline void pd_log_event(uint8_t type, uint8_t size_port, + uint16_t data, void *payload) {} +static inline int pd_vdm_get_log_entry(uint32_t *payload) { return 0; } +#endif /* CONFIG_USB_PD_LOGGING */ + #endif /* __USB_PD_H */ |