summaryrefslogtreecommitdiff
path: root/common/usb_pd_tcpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_pd_tcpc.c')
-rw-r--r--common/usb_pd_tcpc.c1468
1 files changed, 0 insertions, 1468 deletions
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
deleted file mode 100644
index 1aaee29abc..0000000000
--- a/common/usb_pd_tcpc.c
+++ /dev/null
@@ -1,1468 +0,0 @@
-/* Copyright 2015 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "adc.h"
-#include "common.h"
-#include "config.h"
-#include "console.h"
-#include "crc.h"
-#include "ec_commands.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "registers.h"
-#include "system.h"
-#include "task.h"
-#include "tcpm/tcpci.h"
-#include "tcpm/tcpm.h"
-#include "timer.h"
-#include "util.h"
-#include "usb_pd.h"
-#include "usb_pd_config.h"
-#include "usb_pd_tcpm.h"
-
-#ifdef CONFIG_COMMON_RUNTIME
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-
-/*
- * Debug log level - higher number == more log
- * Level 0: Log state transitions
- * Level 1: Level 0, plus packet info
- * Level 2: Level 1, plus ping packet and packet dump on error
- *
- * Note that higher log level causes timing changes and thus may affect
- * performance.
- */
-static int debug_level;
-
-static struct mutex pd_crc_lock;
-#else
-#define CPRINTF(format, args...)
-static const int debug_level;
-#endif
-
-/* Encode 5 bits using Biphase Mark Coding */
-#define BMC(x) ((x & 1 ? 0x001 : 0x3FF) \
- ^ (x & 2 ? 0x004 : 0x3FC) \
- ^ (x & 4 ? 0x010 : 0x3F0) \
- ^ (x & 8 ? 0x040 : 0x3C0) \
- ^ (x & 16 ? 0x100 : 0x300))
-
-/* 4b/5b + Bimark Phase encoding */
-static const uint16_t bmc4b5b[] = {
-/* 0 = 0000 */ BMC(0x1E) /* 11110 */,
-/* 1 = 0001 */ BMC(0x09) /* 01001 */,
-/* 2 = 0010 */ BMC(0x14) /* 10100 */,
-/* 3 = 0011 */ BMC(0x15) /* 10101 */,
-/* 4 = 0100 */ BMC(0x0A) /* 01010 */,
-/* 5 = 0101 */ BMC(0x0B) /* 01011 */,
-/* 6 = 0110 */ BMC(0x0E) /* 01110 */,
-/* 7 = 0111 */ BMC(0x0F) /* 01111 */,
-/* 8 = 1000 */ BMC(0x12) /* 10010 */,
-/* 9 = 1001 */ BMC(0x13) /* 10011 */,
-/* A = 1010 */ BMC(0x16) /* 10110 */,
-/* B = 1011 */ BMC(0x17) /* 10111 */,
-/* C = 1100 */ BMC(0x1A) /* 11010 */,
-/* D = 1101 */ BMC(0x1B) /* 11011 */,
-/* E = 1110 */ BMC(0x1C) /* 11100 */,
-/* F = 1111 */ BMC(0x1D) /* 11101 */,
-/* Sync-1 K-code 11000 Startsynch #1 */
-/* Sync-2 K-code 10001 Startsynch #2 */
-/* RST-1 K-code 00111 Hard Reset #1 */
-/* RST-2 K-code 11001 Hard Reset #2 */
-/* EOP K-code 01101 EOP End Of Packet */
-/* Reserved Error 00000 */
-/* Reserved Error 00001 */
-/* Reserved Error 00010 */
-/* Reserved Error 00011 */
-/* Reserved Error 00100 */
-/* Reserved Error 00101 */
-/* Reserved Error 00110 */
-/* Reserved Error 01000 */
-/* Reserved Error 01100 */
-/* Reserved Error 10000 */
-/* Reserved Error 11111 */
-};
-
-static const uint8_t dec4b5b[] = {
-/* Error */ 0x10 /* 00000 */,
-/* Error */ 0x10 /* 00001 */,
-/* Error */ 0x10 /* 00010 */,
-/* Error */ 0x10 /* 00011 */,
-/* Error */ 0x10 /* 00100 */,
-/* Error */ 0x10 /* 00101 */,
-/* Error */ 0x10 /* 00110 */,
-/* RST-1 */ 0x13 /* 00111 K-code: Hard Reset #1 */,
-/* Error */ 0x10 /* 01000 */,
-/* 1 = 0001 */ 0x01 /* 01001 */,
-/* 4 = 0100 */ 0x04 /* 01010 */,
-/* 5 = 0101 */ 0x05 /* 01011 */,
-/* Error */ 0x10 /* 01100 */,
-/* EOP */ 0x15 /* 01101 K-code: EOP End Of Packet */,
-/* 6 = 0110 */ 0x06 /* 01110 */,
-/* 7 = 0111 */ 0x07 /* 01111 */,
-/* Error */ 0x10 /* 10000 */,
-/* Sync-2 */ 0x12 /* 10001 K-code: Startsynch #2 */,
-/* 8 = 1000 */ 0x08 /* 10010 */,
-/* 9 = 1001 */ 0x09 /* 10011 */,
-/* 2 = 0010 */ 0x02 /* 10100 */,
-/* 3 = 0011 */ 0x03 /* 10101 */,
-/* A = 1010 */ 0x0A /* 10110 */,
-/* B = 1011 */ 0x0B /* 10111 */,
-/* Sync-1 */ 0x11 /* 11000 K-code: Startsynch #1 */,
-/* RST-2 */ 0x14 /* 11001 K-code: Hard Reset #2 */,
-/* C = 1100 */ 0x0C /* 11010 */,
-/* D = 1101 */ 0x0D /* 11011 */,
-/* E = 1110 */ 0x0E /* 11100 */,
-/* F = 1111 */ 0x0F /* 11101 */,
-/* 0 = 0000 */ 0x00 /* 11110 */,
-/* Error */ 0x10 /* 11111 */,
-};
-
-/* Start of Packet sequence : three Sync-1 K-codes, then one Sync-2 K-code */
-#define PD_SOP (PD_SYNC1 | (PD_SYNC1<<5) | (PD_SYNC1<<10) | (PD_SYNC2<<15))
-#define PD_SOP_PRIME (PD_SYNC1 | (PD_SYNC1<<5) | \
- (PD_SYNC3<<10) | (PD_SYNC3<<15))
-#define PD_SOP_PRIME_PRIME (PD_SYNC1 | (PD_SYNC3<<5) | \
- (PD_SYNC1<<10) | (PD_SYNC3<<15))
-
-/* Hard Reset sequence : three RST-1 K-codes, then one RST-2 K-code */
-#define PD_HARD_RESET (PD_RST1 | (PD_RST1 << 5) |\
- (PD_RST1 << 10) | (PD_RST2 << 15))
-
-/*
- * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector
- * Specification)
- *
- * CC1 CC2 STATE POSITION
- * ----------------------------------------
- * open open NC N/A
- * Rd open UFP attached 1
- * open Rd UFP attached 2
- * open Ra pwr cable no UFP N/A
- * Ra open pwr cable no UFP N/A
- * Rd Ra pwr cable & UFP 1
- * Ra Rd pwr cable & UFP 2
- * Rd Rd dbg accessory N/A
- * Ra Ra audio accessory N/A
- *
- * Note, V(Rd) > V(Ra)
- */
-#ifndef PD_SRC_RD_THRESHOLD
-#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV
-#endif
-#ifndef PD_SRC_VNC
-#define PD_SRC_VNC PD_SRC_DEF_VNC_MV
-#endif
-
-#ifndef CC_RA
-#define CC_RA(port, cc, sel) (cc < PD_SRC_RD_THRESHOLD)
-#endif
-#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
-#ifndef CC_NC
-#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC)
-#endif
-
-/*
- * Polarity based on 'UFP Perspective'.
- *
- * CC1 CC2 STATE POSITION
- * ----------------------------------------
- * open open NC N/A
- * Rp open DFP attached 1
- * open Rp DFP attached 2
- * Rp Rp Accessory attached N/A
- */
-#ifndef PD_SNK_VA
-#define PD_SNK_VA PD_SNK_VA_MV
-#endif
-
-#define CC_RP(cc) (cc >= PD_SNK_VA)
-
-/*
- * Type C power source charge current limits are identified by their cc
- * voltage (set by selecting the proper Rd resistor). Any voltage below
- * TYPE_C_SRC_500_THRESHOLD will not be identified as a type C charger.
- */
-#define TYPE_C_SRC_500_THRESHOLD PD_SRC_RD_THRESHOLD
-#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */
-#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */
-
-/* Convert TCPC Alert register to index into pd.alert[] */
-#define ALERT_REG_TO_INDEX(reg) (reg - TCPC_REG_ALERT)
-
-/* PD transmit errors */
-enum pd_tx_errors {
- PD_TX_ERR_GOODCRC = -1, /* Failed to receive goodCRC */
- PD_TX_ERR_DISABLED = -2, /* Attempted transmit even though disabled */
- PD_TX_ERR_INV_ACK = -4, /* Received different packet instead of gCRC */
- PD_TX_ERR_COLLISION = -5 /* Collision detected during transmit */
-};
-
-/* PD Header with SOP* encoded in bits 31 - 28 */
-union pd_header_sop {
- uint16_t pd_header;
- uint32_t head;
-};
-
-/*
- * If TCPM is not on this chip, and PD low power is defined, then use low
- * power task delay logic.
- */
-#if !defined(CONFIG_USB_POWER_DELIVERY) && defined(CONFIG_USB_PD_LOW_POWER)
-#define TCPC_LOW_POWER
-#endif
-
-/*
- * Receive message buffer size. Buffer physical size is RX_BUFFER_SIZE + 1,
- * but only RX_BUFFER_SIZE of that memory is used to store messages that can
- * be retrieved from TCPM. The last slot is a temporary buffer for collecting
- * a message before deciding whether or not to keep it.
- */
-#ifdef CONFIG_USB_POWER_DELIVERY
-#define RX_BUFFER_SIZE 1
-#else
-#define RX_BUFFER_SIZE 2
-#endif
-
-static struct pd_port_controller {
- /* current port power role (SOURCE or SINK) */
- uint8_t power_role;
- /* current port data role (DFP or UFP) */
- uint8_t data_role;
- /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */
- uint8_t polarity;
- /* Our CC pull resistor setting */
- uint8_t cc_pull;
- /* CC status */
- uint8_t cc_status[2];
- /* TCPC alert status */
- uint16_t alert;
- uint16_t alert_mask;
- /* RX enabled */
- uint8_t rx_enabled;
- /* Power status */
- uint8_t power_status;
- uint8_t power_status_mask;
-
-#ifdef TCPC_LOW_POWER
- /* Timestamp beyond which we allow low power task sampling */
- timestamp_t low_power_ts;
-#endif
-
- /* Last received */
- int rx_head[RX_BUFFER_SIZE+1];
- uint32_t rx_payload[RX_BUFFER_SIZE+1][7];
- int rx_buf_head, rx_buf_tail;
-
- /* Next transmit */
- enum tcpci_msg_type tx_type;
- uint16_t tx_head;
- uint32_t tx_payload[7];
- const uint32_t *tx_data;
-} pd[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-static int rx_buf_is_full(int port)
-{
- /*
- * TODO: Refactor these to use the incrementing-counter idiom instead of
- * the wrapping-counter idiom to reclaim the last buffer entry.
- *
- * Buffer is full if the tail is 1 ahead of head.
- */
- int diff = pd[port].rx_buf_tail - pd[port].rx_buf_head;
- return (diff == 1) || (diff == -RX_BUFFER_SIZE);
-}
-
-int rx_buf_is_empty(int port)
-{
- /* Buffer is empty if the head and tail are the same */
- return pd[port].rx_buf_tail == pd[port].rx_buf_head;
-}
-
-void rx_buf_clear(int port)
-{
- pd[port].rx_buf_tail = pd[port].rx_buf_head;
-}
-
-static void rx_buf_increment(int port, int *buf_ptr)
-{
- *buf_ptr = *buf_ptr == RX_BUFFER_SIZE ? 0 : *buf_ptr + 1;
-}
-
-static inline int encode_short(int port, int off, uint16_t val16)
-{
- off = pd_write_sym(port, off, bmc4b5b[(val16 >> 0) & 0xF]);
- off = pd_write_sym(port, off, bmc4b5b[(val16 >> 4) & 0xF]);
- off = pd_write_sym(port, off, bmc4b5b[(val16 >> 8) & 0xF]);
- return pd_write_sym(port, off, bmc4b5b[(val16 >> 12) & 0xF]);
-}
-
-int encode_word(int port, int off, uint32_t val32)
-{
- off = encode_short(port, off, (val32 >> 0) & 0xFFFF);
- return encode_short(port, off, (val32 >> 16) & 0xFFFF);
-}
-
-/* prepare a 4b/5b-encoded PD message to send */
-int prepare_message(int port, uint16_t header, uint8_t cnt,
- const uint32_t *data)
-{
- int off, i;
- /* 64-bit preamble */
- off = pd_write_preamble(port);
-#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD)
- /* Start Of Packet Prime: 2x Sync-1 + 2x Sync-3 */
- off = pd_write_sym(port, off, BMC(PD_SYNC1));
- off = pd_write_sym(port, off, BMC(PD_SYNC1));
- off = pd_write_sym(port, off, BMC(PD_SYNC3));
- off = pd_write_sym(port, off, BMC(PD_SYNC3));
-#else
- /* Start Of Packet: 3x Sync-1 + 1x Sync-2 */
- off = pd_write_sym(port, off, BMC(PD_SYNC1));
- off = pd_write_sym(port, off, BMC(PD_SYNC1));
- off = pd_write_sym(port, off, BMC(PD_SYNC1));
- off = pd_write_sym(port, off, BMC(PD_SYNC2));
-#endif
- /* header */
- off = encode_short(port, off, header);
-
-#ifdef CONFIG_COMMON_RUNTIME
- mutex_lock(&pd_crc_lock);
-#endif
-
- crc32_init();
- crc32_hash16(header);
- /* data payload */
- for (i = 0; i < cnt; i++) {
- off = encode_word(port, off, data[i]);
- crc32_hash32(data[i]);
- }
- /* CRC */
- off = encode_word(port, off, crc32_result());
-
-#ifdef CONFIG_COMMON_RUNTIME
- mutex_unlock(&pd_crc_lock);
-#endif
-
- /* End Of Packet */
- off = pd_write_sym(port, off, BMC(PD_EOP));
- /* Ensure that we have a final edge */
- return pd_write_last_edge(port, off);
-}
-
-static int send_hard_reset(int port)
-{
- int off;
-
- if (debug_level >= 1)
- CPRINTF("C%d Send hard reset\n", port);
-
- /* 64-bit preamble */
- off = pd_write_preamble(port);
- /* Hard-Reset: 3x RST-1 + 1x RST-2 */
- off = pd_write_sym(port, off, BMC(PD_RST1));
- off = pd_write_sym(port, off, BMC(PD_RST1));
- off = pd_write_sym(port, off, BMC(PD_RST1));
- off = pd_write_sym(port, off, BMC(PD_RST2));
- /* Ensure that we have a final edge */
- off = pd_write_last_edge(port, off);
- /* Transmit the packet */
- if (pd_start_tx(port, pd[port].polarity, off) < 0)
- return PD_TX_ERR_COLLISION;
- pd_tx_done(port, pd[port].polarity);
- /* Keep RX monitoring on */
- pd_rx_enable_monitoring(port);
- return 0;
-}
-
-static int send_validate_message(int port, uint16_t header,
- const uint32_t *data)
-{
- int r;
- static uint32_t payload[7];
- uint8_t expected_msg_id = PD_HEADER_ID(header);
- uint8_t cnt = PD_HEADER_CNT(header);
- int retries = PD_HEADER_TYPE(header) == PD_DATA_SOURCE_CAP ?
- 0 :
- CONFIG_PD_RETRY_COUNT;
-
- /* retry 3 times if we are not getting a valid answer */
- for (r = 0; r <= retries; r++) {
- int bit_len, head;
- /* write the encoded packet in the transmission buffer */
- bit_len = prepare_message(port, header, cnt, data);
- /* Transmit the packet */
- if (pd_start_tx(port, pd[port].polarity, bit_len) < 0) {
- /*
- * Collision detected, return immediately so we can
- * respond to what we have received.
- */
- return PD_TX_ERR_COLLISION;
- }
- pd_tx_done(port, pd[port].polarity);
- /*
- * If this is the first attempt, leave RX monitoring off,
- * and do a blocking read of the channel until timeout or
- * packet received. If we failed the first try, enable
- * interrupt and yield to other tasks, so that we don't
- * starve them.
- */
- if (r) {
- pd_rx_enable_monitoring(port);
- /* Wait for message receive timeout */
- if (task_wait_event(USB_PD_RX_TMOUT_US) ==
- TASK_EVENT_TIMER)
- continue;
- /*
- * Make sure we woke up due to rx recd, otherwise
- * we need to manually start
- */
- if (!pd_rx_started(port)) {
- pd_rx_disable_monitoring(port);
- pd_rx_start(port);
- }
- } else {
- /* starting waiting for GoodCrc */
- pd_rx_start(port);
- }
- /* read the incoming packet if any */
- head = pd_analyze_rx(port, payload);
- pd_rx_complete(port);
- /* keep RX monitoring on to avoid collisions */
- pd_rx_enable_monitoring(port);
- if (head > 0) { /* we got a good packet, analyze it */
- int type = PD_HEADER_TYPE(head);
- int nb = PD_HEADER_CNT(head);
- uint8_t id = PD_HEADER_ID(head);
- if (type == PD_CTRL_GOOD_CRC && nb == 0 &&
- id == expected_msg_id) {
- /* got the GoodCRC we were expecting */
- /* do not catch last edges as a new packet */
- udelay(20);
- return bit_len;
- } else {
- /*
- * we have received a good packet
- * but not the expected GoodCRC,
- * the other side is trying to contact us,
- * bail out immediately so we can get the retry.
- */
- return PD_TX_ERR_INV_ACK;
- }
- }
- }
- /* we failed all the re-transmissions */
- if (debug_level >= 1)
- CPRINTF("TX NOACK%d %04x/%d\n", port, header, cnt);
- return PD_TX_ERR_GOODCRC;
-}
-
-static void send_goodcrc(int port, int id)
-{
- uint16_t header = PD_HEADER(PD_CTRL_GOOD_CRC, pd[port].power_role,
- pd[port].data_role, id, 0, 0, 0);
- int bit_len = prepare_message(port, header, 0, NULL);
-
- if (pd_start_tx(port, pd[port].polarity, bit_len) < 0)
- /* another packet recvd before we could send goodCRC */
- return;
- pd_tx_done(port, pd[port].polarity);
- /* Keep RX monitoring on */
- pd_rx_enable_monitoring(port);
-}
-
-#if 0
-/* TODO: when/how do we trigger this ? */
-static int analyze_rx_bist(int port);
-
-void bist_mode_2_rx(int port)
-{
- int analyze_bist = 0;
- int num_bits;
- timestamp_t start_time;
-
- /* monitor for incoming packet */
- pd_rx_enable_monitoring(port);
-
- /* loop until we start receiving data */
- start_time.val = get_time().val;
- while ((get_time().val - start_time.val) < (500*MSEC)) {
- task_wait_event(10*MSEC);
- /* incoming packet ? */
- if (pd_rx_started(port)) {
- analyze_bist = 1;
- break;
- }
- }
-
- if (analyze_bist) {
- /*
- * once we start receiving bist data, analyze 40 bytes
- * every 10 msec. Continue analyzing until BIST data
- * is no longer received. The standard limits the max
- * BIST length to 60 msec.
- */
- start_time.val = get_time().val;
- while ((get_time().val - start_time.val)
- < (PD_T_BIST_RECEIVE)) {
- num_bits = analyze_rx_bist(port);
- pd_rx_complete(port);
- /*
- * If no data was received, then analyze_rx_bist()
- * will return a -1 and there is no need to stay
- * in this mode
- */
- if (num_bits == -1)
- break;
- msleep(10);
- pd_rx_enable_monitoring(port);
- }
- } else {
- CPRINTF("BIST RX TO\n");
- }
-}
-#endif
-
-static void bist_mode_2_tx(int port)
-{
- int bit;
-
- CPRINTF("BIST 2: p%d\n", port);
- /*
- * build context buffer with 5 bytes, where the data is
- * alternating 1's and 0's.
- */
- bit = pd_write_sym(port, 0, BMC(0x15));
- bit = pd_write_sym(port, bit, BMC(0x0a));
- bit = pd_write_sym(port, bit, BMC(0x15));
- bit = pd_write_sym(port, bit, BMC(0x0a));
-
- /* start a circular DMA transfer */
- pd_tx_set_circular_mode(port);
- pd_start_tx(port, pd[port].polarity, bit);
-
- task_wait_event(PD_T_BIST_TRANSMIT);
-
- /* clear dma circular mode, will also stop dma */
- pd_tx_clear_circular_mode(port);
- /* finish and cleanup transmit */
- pd_tx_done(port, pd[port].polarity);
-}
-
-static inline int decode_short(int port, int off, uint16_t *val16)
-{
- uint32_t w;
- int end;
-
- end = pd_dequeue_bits(port, off, 20, &w);
-
-#if 0 /* DEBUG */
- CPRINTS("%d-%d: %05x %x:%x:%x:%x",
- off, end, w,
- dec4b5b[(w >> 15) & 0x1f], dec4b5b[(w >> 10) & 0x1f],
- dec4b5b[(w >> 5) & 0x1f], dec4b5b[(w >> 0) & 0x1f]);
-#endif
- *val16 = dec4b5b[w & 0x1f] |
- (dec4b5b[(w >> 5) & 0x1f] << 4) |
- (dec4b5b[(w >> 10) & 0x1f] << 8) |
- (dec4b5b[(w >> 15) & 0x1f] << 12);
- return end;
-}
-
-static inline int decode_word(int port, int off, uint32_t *val32)
-{
- off = decode_short(port, off, (uint16_t *)val32);
- return decode_short(port, off, ((uint16_t *)val32 + 1));
-}
-
-#ifdef CONFIG_COMMON_RUNTIME
-#if 0
-/*
- * TODO: when/how do we trigger this ? Could add custom vendor command
- * to TCPCI to enter bist verification? Is there an easier way?
- */
-static int count_set_bits(int n)
-{
- int count = 0;
- while (n) {
- n &= (n - 1);
- count++;
- }
- return count;
-}
-
-static int analyze_rx_bist(int port)
-{
- int i = 0, bit = -1;
- uint32_t w, match;
- int invalid_bits = 0;
- int bits_analyzed = 0;
- static int total_invalid_bits;
-
- /* dequeue bits until we see a full byte of alternating 1's and 0's */
- while (i < 10 && (bit < 0 || (w != 0xaa && w != 0x55)))
- bit = pd_dequeue_bits(port, i++, 8, &w);
-
- /* if we didn't find any bytes that match criteria, display error */
- if (i == 10) {
- CPRINTF("invalid pattern\n");
- return -1;
- }
- /*
- * now we know what matching byte we are looking for, dequeue a bunch
- * more data and count how many bits differ from expectations.
- */
- match = w;
- bit = i - 1;
- for (i = 0; i < 40; i++) {
- bit = pd_dequeue_bits(port, bit, 8, &w);
- if (i && (i % 20 == 0))
- CPRINTF("\n");
- CPRINTF("%02x ", w);
- bits_analyzed += 8;
- invalid_bits += count_set_bits(w ^ match);
- }
-
- total_invalid_bits += invalid_bits;
-
- CPRINTF("\nInvalid: %d/%d\n",
- invalid_bits, total_invalid_bits);
- return bits_analyzed;
-}
-#endif
-#endif
-
-int pd_analyze_rx(int port, uint32_t *payload)
-{
- int bit;
- char *msg = "---";
- uint32_t val = 0;
- union pd_header_sop phs;
- uint32_t pcrc, ccrc;
- int p, cnt;
- uint32_t eop;
-
- pd_init_dequeue(port);
-
- /* Detect preamble */
- bit = pd_find_preamble(port);
- if (bit == PD_RX_ERR_HARD_RESET || bit == PD_RX_ERR_CABLE_RESET) {
- /* Hard reset or cable reset */
- return bit;
- } else if (bit < 0) {
- msg = "Preamble";
- goto packet_err;
- }
-
- /* Find the Start Of Packet sequence */
- while (bit > 0) {
- bit = pd_dequeue_bits(port, bit, 20, &val);
-#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD)
- if (val == PD_SOP_PRIME) {
- break;
- } else if (val == PD_SOP) {
- CPRINTF("SOP\n");
- return PD_RX_ERR_UNSUPPORTED_SOP;
- } else if (val == PD_SOP_PRIME_PRIME) {
- CPRINTF("SOP''\n");
- return PD_RX_ERR_UNSUPPORTED_SOP;
- }
-#else /* CONFIG_USB_VPD || CONFIG_USB_CTVPD */
-#ifdef CONFIG_USB_PD_DECODE_SOP
- if (val == PD_SOP || val == PD_SOP_PRIME ||
- val == PD_SOP_PRIME_PRIME)
- break;
-#else
- if (val == PD_SOP) {
- break;
- } else if (val == PD_SOP_PRIME) {
- CPRINTF("SOP'\n");
- return PD_RX_ERR_UNSUPPORTED_SOP;
- } else if (val == PD_SOP_PRIME_PRIME) {
- CPRINTF("SOP''\n");
- return PD_RX_ERR_UNSUPPORTED_SOP;
- }
-#endif /* CONFIG_USB_PD_DECODE_SOP */
-#endif /* CONFIG_USB_VPD || CONFIG_USB_CTVPD */
- }
- if (bit < 0) {
-#ifdef CONFIG_USB_PD_DECODE_SOP
- if (val == PD_SOP)
- msg = "SOP";
- else if (val == PD_SOP_PRIME)
- msg = "SOP'";
- else if (val == PD_SOP_PRIME_PRIME)
- msg = "SOP''";
- else
- msg = "SOP*";
-#else
- msg = "SOP";
-#endif
- goto packet_err;
- }
-
- phs.head = 0;
-
- /* read header */
- bit = decode_short(port, bit, &phs.pd_header);
-
-#ifdef CONFIG_COMMON_RUNTIME
- mutex_lock(&pd_crc_lock);
-#endif
-
- crc32_init();
- crc32_hash16(phs.pd_header);
- cnt = PD_HEADER_CNT(phs.pd_header);
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
- /* Encode message address */
- if (val == PD_SOP) {
- phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP);
- } else if (val == PD_SOP_PRIME) {
- phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP_PRIME);
- } else if (val == PD_SOP_PRIME_PRIME) {
- phs.head |= PD_HEADER_SOP(TCPCI_MSG_SOP_PRIME_PRIME);
- } else {
- msg = "SOP*";
- goto packet_err;
- }
-#endif
-
- /* read payload data */
- for (p = 0; p < cnt && bit > 0; p++) {
- bit = decode_word(port, bit, payload+p);
- crc32_hash32(payload[p]);
- }
- ccrc = crc32_result();
-
-#ifdef CONFIG_COMMON_RUNTIME
- mutex_unlock(&pd_crc_lock);
-#endif
-
- if (bit < 0) {
- msg = "len";
- goto packet_err;
- }
-
- /* check transmitted CRC */
- bit = decode_word(port, bit, &pcrc);
- if (bit < 0 || pcrc != ccrc) {
- msg = "CRC";
- if (pcrc != ccrc)
- bit = PD_RX_ERR_CRC;
- if (debug_level >= 1)
- CPRINTF("CRC%d %08x <> %08x\n", port, pcrc, ccrc);
- goto packet_err;
- }
-
- /*
- * Check EOP. EOP is 5 bits, but last bit may not be able to
- * be dequeued, depending on ending state of CC line, so stop
- * at 4 bits (assumes last bit is 0).
- */
- bit = pd_dequeue_bits(port, bit, 4, &eop);
- if (bit < 0 || eop != PD_EOP) {
- msg = "EOP";
- goto packet_err;
- }
-
- return phs.head;
-packet_err:
- if (debug_level >= 2)
- pd_dump_packet(port, msg);
- else
- CPRINTF("RXERR%d %s\n", port, msg);
- return bit;
-}
-
-static void handle_request(int port, uint16_t head)
-{
- int cnt = PD_HEADER_CNT(head);
-
- if (PD_HEADER_TYPE(head) != PD_CTRL_GOOD_CRC || cnt)
- send_goodcrc(port, PD_HEADER_ID(head));
- else
- /* keep RX monitoring on to avoid collisions */
- pd_rx_enable_monitoring(port);
-}
-
-/* Convert CC voltage to CC status */
-static int cc_voltage_to_status(int port, int cc_volt, int cc_sel)
-{
- /* If we have a pull-up, then we are source, check for Rd. */
- if (pd[port].cc_pull == TYPEC_CC_RP) {
- if (CC_NC(port, cc_volt, cc_sel))
- return TYPEC_CC_VOLT_OPEN;
- else if (CC_RA(port, cc_volt, cc_sel))
- return TYPEC_CC_VOLT_RA;
- else
- return TYPEC_CC_VOLT_RD;
- /* If we have a pull-down, then we are sink, check for Rp. */
- }
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- else if (pd[port].cc_pull == TYPEC_CC_RD) {
- if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD)
- return TYPEC_CC_VOLT_RP_3_0;
- else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD)
- return TYPEC_CC_VOLT_RP_1_5;
- else if (CC_RP(cc_volt))
- return TYPEC_CC_VOLT_RP_DEF;
- else
- return TYPEC_CC_VOLT_OPEN;
- }
-#endif
- /* If we are open, then always return 0 */
- else
- return 0;
-}
-
-static void alert(int port, int mask)
-{
- /* Always update the Alert status register */
- pd[port].alert |= mask;
- /*
- * Only send interrupt to TCPM if corresponding
- * bit in the alert_enable register is set.
- */
- if (pd[port].alert_mask & mask)
- tcpc_alert(port);
-}
-
-int tcpc_run(int port, int evt)
-{
- int cc, i, res;
-
- /* Don't do anything when port is not available */
- if (port >= board_get_usb_pd_port_count())
- return -1;
-
- /* incoming packet ? */
- if (pd_rx_started(port) && pd[port].rx_enabled) {
- /* Get message and place at RX buffer head */
- res = pd[port].rx_head[pd[port].rx_buf_head] =
- pd_analyze_rx(port,
- pd[port].rx_payload[pd[port].rx_buf_head]);
- pd_rx_complete(port);
-
- /*
- * If there is space in buffer, then increment head to keep
- * the message and send goodCRC. If this is a hard reset,
- * send alert regardless of rx buffer status. Else if there is
- * no space in buffer, then do not send goodCRC and drop
- * message.
- */
- if (res > 0 && !rx_buf_is_full(port)) {
- rx_buf_increment(port, &pd[port].rx_buf_head);
- handle_request(port, res);
- alert(port, TCPC_REG_ALERT_RX_STATUS);
- } else if (res == PD_RX_ERR_HARD_RESET) {
- alert(port, TCPC_REG_ALERT_RX_HARD_RST);
- }
- }
-
- /* outgoing packet ? */
- if ((evt & PD_EVENT_TX) && pd[port].rx_enabled) {
- switch (pd[port].tx_type) {
-#if defined(CONFIG_USB_VPD) || defined(CONFIG_USB_CTVPD)
- case TCPCI_MSG_SOP_PRIME:
-#else
- case TCPCI_MSG_SOP:
-#endif
- res = send_validate_message(port,
- pd[port].tx_head,
- pd[port].tx_data);
- break;
- case TCPCI_MSG_TX_BIST_MODE_2:
- bist_mode_2_tx(port);
- res = 0;
- break;
- case TCPCI_MSG_TX_HARD_RESET:
- res = send_hard_reset(port);
- break;
- default:
- res = PD_TX_ERR_DISABLED;
- break;
- }
-
- /* send appropriate alert for tx completion */
- if (res >= 0)
- alert(port, TCPC_REG_ALERT_TX_SUCCESS);
- else if (res == PD_TX_ERR_GOODCRC)
- alert(port, TCPC_REG_ALERT_TX_FAILED);
- else
- alert(port, TCPC_REG_ALERT_TX_DISCARDED);
- } else {
- /* If we have nothing to transmit, then sample CC lines */
-
- /* CC pull changed, wait 1ms for CC voltage to stabilize */
- if (evt & PD_EVENT_CC)
- usleep(MSEC);
-
- /* check CC lines */
- for (i = 0; i < 2; i++) {
- /* read CC voltage */
- cc = pd_adc_read(port, i);
-
- /* convert voltage to status, and check status change */
- cc = cc_voltage_to_status(port, cc, i);
- if (pd[port].cc_status[i] != cc) {
- pd[port].cc_status[i] = cc;
- alert(port, TCPC_REG_ALERT_CC_STATUS);
- }
- }
- }
-
- /* make sure PD monitoring is enabled to wake on PD RX */
- if (pd[port].rx_enabled)
- pd_rx_enable_monitoring(port);
-
-#ifdef TCPC_LOW_POWER
- /*
- * If we are presenting Rd with no connection, and timestamp is
- * past the low power timestamp, then we don't need to sample
- * CC lines as often. In this case, our connection delay should not
- * actually increased because we will get an interrupt on VBUS detect.
- */
- return (get_time().val >= pd[port].low_power_ts.val &&
- pd[port].cc_pull == TYPEC_CC_RD &&
- cc_is_open(pd[port].cc_status[0], pd[port].cc_status[1]))
- ? 200 * MSEC
- : 10 * MSEC;
-#else
- return 10*MSEC;
-#endif
-}
-
-#if !defined(CONFIG_USB_POWER_DELIVERY)
-void pd_task(void *u)
-{
- int port = TASK_ID_TO_PD_PORT(task_get_current());
- int timeout = 10*MSEC;
- int evt;
-
- /* initialize phy task */
- tcpc_init(port);
-
- /* we are now initialized */
- pd[port].power_status &= ~TCPC_REG_POWER_STATUS_UNINIT;
-
- while (1) {
- /* wait for next event/packet or timeout expiration */
- evt = task_wait_event(timeout);
-
- /* run phy task once */
- timeout = tcpc_run(port, evt);
- }
-}
-#endif
-
-void pd_rx_event(int port)
-{
- task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE);
-}
-
-int tcpc_alert_status(int port, int *alert)
-{
- /* return the value of the TCPC Alert register */
- uint16_t ret = pd[port].alert;
- *alert = ret;
- return EC_SUCCESS;
-}
-
-int tcpc_alert_status_clear(int port, uint16_t mask)
-{
- /*
- * If the RX status alert is attempting to be cleared, then increment
- * rx buffer tail pointer. if the RX buffer is not empty, then keep
- * the RX status alert active.
- */
- if (mask & TCPC_REG_ALERT_RX_STATUS) {
- if (!rx_buf_is_empty(port)) {
- rx_buf_increment(port, &pd[port].rx_buf_tail);
- if (!rx_buf_is_empty(port))
- /* buffer is not empty, keep alert active */
- mask &= ~TCPC_REG_ALERT_RX_STATUS;
- }
- }
-
- /* clear only the bits specified by the TCPM */
- pd[port].alert &= ~mask;
-#ifndef CONFIG_USB_POWER_DELIVERY
- /* Set Alert# inactive if all alert bits clear */
- if (!pd[port].alert)
- tcpc_alert_clear(port);
-#endif
- return EC_SUCCESS;
-}
-
-int tcpc_alert_mask_set(int port, uint16_t mask)
-{
- /* Update the alert mask as specificied by the TCPM */
- pd[port].alert_mask = mask;
- return EC_SUCCESS;
-}
-
-int tcpc_set_cc(int port, int pull)
-{
- /* If CC pull resistor not changing, then nothing to do */
- if (pd[port].cc_pull == pull)
- return EC_SUCCESS;
-
- /* Change CC pull resistor */
- pd[port].cc_pull = pull;
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- pd_set_host_mode(port, pull == TYPEC_CC_RP);
-#endif
-
-#ifdef TCPC_LOW_POWER
- /*
- * Reset the low power timestamp every time CC termination toggles,
- * because we only want to go into low power mode when we are not
- * dual-role toggling.
- */
- pd[port].low_power_ts.val = get_time().val +
- 2*(PD_T_DRP_SRC + PD_T_DRP_SNK);
-#endif
-
- /*
- * Before CC pull can be changed and the task can read the new
- * status, we should set the CC status to open, in case TCPM
- * asks before it is known for sure.
- */
- pd[port].cc_status[0] = TYPEC_CC_VOLT_OPEN;
- pd[port].cc_status[1] = pd[port].cc_status[0];
-
- /* Wake the PD phy task with special CC event mask */
- /* TODO: use top case if no TCPM on same CPU */
-#ifdef CONFIG_USB_POWER_DELIVERY
- tcpc_run(port, PD_EVENT_CC);
-#else
- task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC);
-#endif
- return EC_SUCCESS;
-}
-
-int tcpc_get_cc(int port, enum tcpc_cc_voltage_status *cc1,
- enum tcpc_cc_voltage_status *cc2)
-{
- *cc2 = pd[port].cc_status[1];
- *cc1 = pd[port].cc_status[0];
-
- return EC_SUCCESS;
-}
-
-int board_select_rp_value(int port, int rp) __attribute__((weak));
-
-int tcpc_select_rp_value(int port, int rp)
-{
- if (board_select_rp_value)
- return board_select_rp_value(port, rp);
- else
- return EC_ERROR_UNIMPLEMENTED;
-}
-
-int tcpc_set_polarity(int port, int polarity)
-{
- pd[port].polarity = polarity;
- pd_select_polarity(port, pd[port].polarity);
-
- return EC_SUCCESS;
-}
-
-#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS
-static int tcpc_set_power_status(int port, int vbus_present)
-{
- /* Update VBUS present bit */
- if (vbus_present)
- pd[port].power_status |= TCPC_REG_POWER_STATUS_VBUS_PRES;
- else
- pd[port].power_status &= ~TCPC_REG_POWER_STATUS_VBUS_PRES;
-
- /* Set bit Port Power Status bit in Alert register */
- if (pd[port].power_status_mask & TCPC_REG_POWER_STATUS_VBUS_PRES)
- alert(port, TCPC_REG_ALERT_POWER_STATUS);
-
- return EC_SUCCESS;
-}
-#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */
-
-int tcpc_set_power_status_mask(int port, uint8_t mask)
-{
- pd[port].power_status_mask = mask;
- return EC_SUCCESS;
-}
-
-int tcpc_set_vconn(int port, int enable)
-{
-#ifdef CONFIG_USBC_VCONN
- pd_set_vconn(port, pd[port].polarity, enable);
-#endif
- return EC_SUCCESS;
-}
-
-int tcpc_set_rx_enable(int port, int enable)
-{
-#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY)
- int i;
-#endif
- pd[port].rx_enabled = enable;
-
- if (!enable)
- pd_rx_disable_monitoring(port);
-
-#if defined(CONFIG_LOW_POWER_IDLE) && !defined(CONFIG_USB_POWER_DELIVERY)
- /* If any PD port is connected, then disable deep sleep */
- for (i = 0; i < board_get_usb_pd_port_count(); ++i)
- if (pd[i].rx_enabled)
- break;
-
- if (i == board_get_usb_pd_port_count())
- enable_sleep(SLEEP_MASK_USB_PD);
- else
- disable_sleep(SLEEP_MASK_USB_PD);
-#endif
- return EC_SUCCESS;
-}
-
-int tcpc_transmit(int port, enum tcpci_msg_type type, uint16_t header,
- const uint32_t *data)
-{
- /* Store data to transmit and wake task to send it */
- pd[port].tx_type = type;
- pd[port].tx_head = header;
- pd[port].tx_data = data;
- /* TODO: use top case if no TCPM on same CPU */
-#ifdef CONFIG_USB_POWER_DELIVERY
- tcpc_run(port, PD_EVENT_TX);
-#else
- task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX);
-#endif
- return EC_SUCCESS;
-}
-
-int tcpc_set_msg_header(int port, int power_role, int data_role)
-{
- pd[port].power_role = power_role;
- pd[port].data_role = data_role;
-
- return EC_SUCCESS;
-}
-
-int tcpc_get_message(int port, uint32_t *payload, int *head)
-{
- /* Get message at tail of RX buffer */
- int idx = pd[port].rx_buf_tail;
-
- memcpy(payload, pd[port].rx_payload[idx],
- sizeof(pd[port].rx_payload[idx]));
- *head = pd[port].rx_head[idx];
- return EC_SUCCESS;
-}
-
-void tcpc_pre_init(void)
-{
- int i;
-
- /* Mark as uninitialized */
- for (i = 0; i < board_get_usb_pd_port_count(); i++)
- pd[i].power_status |= TCPC_REG_POWER_STATUS_UNINIT |
- TCPC_REG_POWER_STATUS_VBUS_DET;
-}
-/* Must be prioritized above i2c init */
-DECLARE_HOOK(HOOK_INIT, tcpc_pre_init, HOOK_PRIO_INIT_I2C - 1);
-
-void tcpc_init(int port)
-{
- int i;
-
- if (port >= board_get_usb_pd_port_count())
- return;
-
- /* Initialize physical layer */
- pd_hw_init(port, PD_ROLE_DEFAULT(port));
- pd[port].cc_pull = PD_ROLE_DEFAULT(port) ==
- PD_ROLE_SOURCE ? TYPEC_CC_RP : TYPEC_CC_RD;
-#ifdef TCPC_LOW_POWER
- /* Don't use low power immediately after boot */
- pd[port].low_power_ts.val = get_time().val + SECOND;
-#endif
-
- /* make sure PD monitoring is disabled initially */
- pd[port].rx_enabled = 0;
-
- /* make initial readings of CC voltages */
- for (i = 0; i < 2; i++) {
- pd[port].cc_status[i] = cc_voltage_to_status(port,
- pd_adc_read(port, i),
- i);
- }
-
-#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS
-#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2
- tcpc_set_power_status(port, !gpio_get_level(port ?
- GPIO_USB_C1_VBUS_WAKE_L :
- GPIO_USB_C0_VBUS_WAKE_L));
-#else
- tcpc_set_power_status(port, !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L));
-#endif /* CONFIG_USB_PD_PORT_MAX_COUNT >= 2 */
-#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */
-
- /* set default alert and power mask register values */
- pd[port].alert_mask = TCPC_REG_ALERT_MASK_ALL;
- pd[port].power_status_mask = TCPC_REG_POWER_STATUS_MASK_ALL;
-
- /* set power status alert since the UNINIT bit has been set */
- alert(port, TCPC_REG_ALERT_POWER_STATUS);
-}
-
-#ifdef CONFIG_USB_PD_TCPC_TRACK_VBUS
-void pd_vbus_evt_p0(enum gpio_signal signal)
-{
- tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C0),
- !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L));
- task_wake(TASK_ID_PD_C0);
-}
-
-#if CONFIG_USB_PD_PORT_MAX_COUNT >= 2
-void pd_vbus_evt_p1(enum gpio_signal signal)
-{
- if (board_get_usb_pd_port_count() == 1)
- return;
-
- tcpc_set_power_status(TASK_ID_TO_PD_PORT(TASK_ID_PD_C1),
- !gpio_get_level(GPIO_USB_C1_VBUS_WAKE_L));
- task_wake(TASK_ID_PD_C1);
-}
-#endif /* PD_PORT_COUNT >= 2 */
-#endif /* CONFIG_USB_PD_TCPC_TRACK_VBUS */
-
-#ifndef CONFIG_USB_POWER_DELIVERY
-static void tcpc_i2c_write(int port, int reg, int len, uint8_t *payload)
-{
- uint16_t alert;
-
- /* If we are not yet initialized, ignore any write command */
- if (pd[port].power_status & TCPC_REG_POWER_STATUS_UNINIT)
- return;
-
- switch (reg) {
- case TCPC_REG_ROLE_CTRL:
- tcpc_set_cc(port, TCPC_REG_ROLE_CTRL_CC1(payload[1]));
- break;
- case TCPC_REG_POWER_CTRL:
- tcpc_set_vconn(port, TCPC_REG_POWER_CTRL_VCONN(payload[1]));
- break;
- case TCPC_REG_TCPC_CTRL:
- tcpc_set_polarity(port,
- TCPC_REG_TCPC_CTRL_POLARITY(payload[1]));
- break;
- case TCPC_REG_MSG_HDR_INFO:
- tcpc_set_msg_header(port,
- TCPC_REG_MSG_HDR_INFO_PROLE(payload[1]),
- TCPC_REG_MSG_HDR_INFO_DROLE(payload[1]));
- break;
- case TCPC_REG_ALERT:
- alert = payload[1];
- alert |= (payload[2] << 8);
- /* clear alert bits specified by the TCPM */
- tcpc_alert_status_clear(port, alert);
- break;
- case TCPC_REG_ALERT_MASK:
- alert = payload[1];
- alert |= (payload[2] << 8);
- tcpc_alert_mask_set(port, alert);
- break;
- case TCPC_REG_RX_DETECT:
- tcpc_set_rx_enable(port, payload[1] &
- TCPC_REG_RX_DETECT_SOP_HRST_MASK);
- break;
- case TCPC_REG_POWER_STATUS_MASK:
- tcpc_set_power_status_mask(port, payload[1]);
- break;
- case TCPC_REG_TX_HDR:
- pd[port].tx_head = (payload[2] << 8) | payload[1];
- break;
- case TCPC_REG_TX_DATA:
- memcpy(pd[port].tx_payload, &payload[1], len - 1);
- break;
- case TCPC_REG_TRANSMIT:
- tcpc_transmit(port, TCPC_REG_TRANSMIT_TYPE(payload[1]),
- pd[port].tx_head, pd[port].tx_payload);
- break;
- }
-}
-
-static int tcpc_i2c_read(int port, int reg, uint8_t *payload)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
- int alert;
-
- switch (reg) {
- case TCPC_REG_VENDOR_ID:
- *(uint16_t *)payload = USB_VID_GOOGLE;
- return 2;
- case TCPC_REG_CC_STATUS:
- tcpc_get_cc(port, &cc1, &cc2);
- payload[0] = TCPC_REG_CC_STATUS_SET(
- pd[port].cc_pull == TYPEC_CC_RD,
- pd[port].cc_status[0], pd[port].cc_status[1]);
- return 1;
- case TCPC_REG_ROLE_CTRL:
- payload[0] = TCPC_REG_ROLE_CTRL_SET(0, 0,
- pd[port].cc_pull,
- pd[port].cc_pull);
- return 1;
- case TCPC_REG_TCPC_CTRL:
- payload[0] = TCPC_REG_TCPC_CTRL_SET(pd[port].polarity);
- return 1;
- case TCPC_REG_MSG_HDR_INFO:
- payload[0] = TCPC_REG_MSG_HDR_INFO_SET(pd[port].data_role,
- pd[port].power_role);
- return 1;
- case TCPC_REG_RX_DETECT:
- payload[0] = pd[port].rx_enabled ?
- TCPC_REG_RX_DETECT_SOP_HRST_MASK : 0;
- return 1;
- case TCPC_REG_ALERT:
- tcpc_alert_status(port, &alert);
- payload[0] = alert & 0xff;
- payload[1] = (alert >> 8) & 0xff;
- return 2;
- case TCPC_REG_ALERT_MASK:
- payload[0] = pd[port].alert_mask & 0xff;
- payload[1] = (pd[port].alert_mask >> 8) & 0xff;
- return 2;
- case TCPC_REG_RX_BYTE_CNT:
- payload[0] = 3 + 4 *
- PD_HEADER_CNT(pd[port].rx_head[pd[port].rx_buf_tail]);
- return 1;
- case TCPC_REG_RX_HDR:
- payload[0] = pd[port].rx_head[pd[port].rx_buf_tail] & 0xff;
- payload[1] =
- (pd[port].rx_head[pd[port].rx_buf_tail] >> 8) & 0xff;
- return 2;
- case TCPC_REG_RX_DATA:
- memcpy(payload, pd[port].rx_payload[pd[port].rx_buf_tail],
- sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]));
- return sizeof(pd[port].rx_payload[pd[port].rx_buf_tail]);
- case TCPC_REG_POWER_STATUS:
- payload[0] = pd[port].power_status;
- return 1;
- case TCPC_REG_POWER_STATUS_MASK:
- payload[0] = pd[port].power_status_mask;
- return 1;
- case TCPC_REG_TX_HDR:
- payload[0] = pd[port].tx_head & 0xff;
- payload[1] = (pd[port].tx_head >> 8) & 0xff;
- return 2;
- case TCPC_REG_TX_DATA:
- memcpy(payload, pd[port].tx_payload,
- sizeof(pd[port].tx_payload));
- return sizeof(pd[port].tx_payload);
- default:
- return 0;
- }
-}
-
-void tcpc_i2c_process(int read, int port, int len, uint8_t *payload,
- void (*send_response)(int))
-{
- int i, reg;
-
- if (debug_level >= 1) {
- CPRINTF("tcpci p%d: ", port);
- for (i = 0; i < len; i++)
- CPRINTF("0x%02x ", payload[i]);
- CPRINTF("\n");
- }
-
- /* length must always be at least 1 */
- if (len == 0) {
- /*
- * if this is a read, we must call send_response() for
- * i2c transaction to finishe properly
- */
- if (read)
- (*send_response)(0);
- }
-
- /* if this is a write, length must be at least 2 */
- if (!read && len < 2)
- return;
-
- /* register is always first byte */
- reg = payload[0];
-
- /* perform read or write */
- if (read) {
- len = tcpc_i2c_read(port, reg, payload);
- (*send_response)(len);
- } else {
- tcpc_i2c_write(port, reg, len, payload);
- }
-}
-#endif
-
-#ifdef CONFIG_COMMON_RUNTIME
-static int command_tcpc(int argc, char **argv)
-{
- int port;
- char *e;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- if (!strcasecmp(argv[1], "dump")) {
- int level;
-
- if (argc < 3)
- ccprintf("lvl: %d\n", debug_level);
- else {
- level = strtoi(argv[2], &e, 10);
- if (*e)
- return EC_ERROR_PARAM2;
- debug_level = level;
- }
- return EC_SUCCESS;
- }
-
- /* command: pd <port> <subcmd> [args] */
- port = strtoi(argv[1], &e, 10);
- if (argc < 3)
- return EC_ERROR_PARAM_COUNT;
- if (*e || port >= board_get_usb_pd_port_count())
- return EC_ERROR_PARAM2;
-
- if (!strcasecmp(argv[2], "clock")) {
- int freq;
-
- if (argc < 4)
- return EC_ERROR_PARAM2;
-
- freq = strtoi(argv[3], &e, 10);
- if (*e)
- return EC_ERROR_PARAM2;
- pd_set_clock(port, freq);
- ccprintf("set TX frequency to %d Hz\n", freq);
- return EC_SUCCESS;
- } else if (!strncasecmp(argv[2], "state", 5)) {
- ccprintf("Port C%d, %s - CC:%d, CC0:%d, CC1:%d\n"
- "Alert: 0x%02x Mask: 0x%04x\n"
- "Power Status: 0x%02x Mask: 0x%02x\n", port,
- pd[port].rx_enabled ? "Ena" : "Dis",
- pd[port].cc_pull,
- pd[port].cc_status[0], pd[port].cc_status[1],
- pd[port].alert, pd[port].alert_mask,
- pd[port].power_status, pd[port].power_status_mask);
- }
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(tcpc, command_tcpc,
- "dump [0|1]\n\t<port> [clock|state]",
- "Type-C Port Controller");
-#endif