summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/dingdong/board.h2
-rw-r--r--board/dingdong/usb_pd_policy.c13
-rw-r--r--board/hoho/board.h2
-rw-r--r--board/hoho/usb_pd_policy.c13
-rw-r--r--board/samus_pd/board.h2
-rw-r--r--board/zinger/board.h2
-rw-r--r--board/zinger/runtime.c10
-rw-r--r--board/zinger/usb_pd_policy.c3
-rw-r--r--common/build.mk1
-rw-r--r--common/pd_log.c151
-rw-r--r--include/config.h6
-rw-r--r--include/ec_commands.h75
-rw-r--r--include/usb_pd.h29
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 */