mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
Move parts of usb_pd_config.h that are not part of the phy layer out of usb_pd_config.h and into board.h. This cleans up the division between the TCPC and TCPM as only the TCPC needs to use usb_pd_config.h. Also cleans up the use of the CC detection voltage thresholds by creating standard macros to use based on Rp strength for the board. BUG=none BRANCH=none TEST=make -j buildall Change-Id: I946cceb38bea8233095b8a4b287102bb8a3a296d Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/270337 Reviewed-by: Todd Broch <tbroch@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
238 lines
7.3 KiB
C
238 lines
7.3 KiB
C
/* 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 "charge_manager.h"
|
|
#include "console.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "usb_pd.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))
|
|
|
|
static void log_add_event(uint8_t type, uint8_t size_port, uint16_t data,
|
|
void *payload, uint32_t timestamp)
|
|
{
|
|
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 = timestamp;
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
|
|
/* 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 int hc_pd_get_log_entry(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_pd_log *r = args->response;
|
|
|
|
dequeue_retry:
|
|
args->response_size = pd_log_dequeue(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 < CONFIG_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 int 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 >= CONFIG_USB_PD_PORT_COUNT)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
switch (type) {
|
|
/* Charge event: Log data for all ports */
|
|
case PD_EVENT_MCU_CHARGE:
|
|
charge_manager_save_log(port);
|
|
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 = pd_log_dequeue(r);
|
|
|
|
return 1 + DIV_ROUND_UP(byte_size, sizeof(uint32_t));
|
|
}
|
|
#endif /* !HAS_TASK_HOSTCMD */
|