summaryrefslogtreecommitdiff
path: root/chip/stm32/ucpd-stm32gx.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/ucpd-stm32gx.c')
-rw-r--r--chip/stm32/ucpd-stm32gx.c1615
1 files changed, 0 insertions, 1615 deletions
diff --git a/chip/stm32/ucpd-stm32gx.c b/chip/stm32/ucpd-stm32gx.c
deleted file mode 100644
index ef6ec92a89..0000000000
--- a/chip/stm32/ucpd-stm32gx.c
+++ /dev/null
@@ -1,1615 +0,0 @@
-/* Copyright 2020 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.
- */
-
-/* STM32GX UCPD module for Chrome EC */
-
-#include "clock.h"
-#include "console.h"
-#include "common.h"
-#include "driver/tcpm/tcpm.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "hwtimer.h"
-#include "registers.h"
-#include "task.h"
-#include "timer.h"
-#include "ucpd-stm32gx.h"
-#include "usb_pd.h"
-#include "usb_pd_tcpm.h"
-#include "util.h"
-
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-
-#define USB_VID_STM32 0x0483
-
-/*
- * USB PD message buffer length. Absent extended messages, the longest PD
- * message will be 7 objects (4 bytes each) plus a 2 byte header. TCPMv2
- * suports extended messages via chunking so the data buffer length is
- * set assumign that extended messages are chunked.
- */
-#define UCPD_BUF_LEN 30
-
-#define UCPD_IMR_RX_INT_MASK (STM32_UCPD_IMR_RXNEIE| \
- STM32_UCPD_IMR_RXORDDETIE | \
- STM32_UCPD_IMR_RXHRSTDETIE | \
- STM32_UCPD_IMR_RXOVRIE | \
- STM32_UCPD_IMR_RXMSGENDIE)
-
-#define UCPD_IMR_TX_INT_MASK (STM32_UCPD_IMR_TXISIE | \
- STM32_UCPD_IMR_TXMSGDISCIE | \
- STM32_UCPD_IMR_TXMSGSENTIE | \
- STM32_UCPD_IMR_TXMSGABTIE | \
- STM32_UCPD_IMR_TXUNDIE)
-
-#define UCPD_ICR_TX_INT_MASK (STM32_UCPD_ICR_TXMSGDISCCF | \
- STM32_UCPD_ICR_TXMSGSENTCF | \
- STM32_UCPD_ICR_TXMSGABTCF | \
- STM32_UCPD_ICR_TXUNDCF)
-
-#define UCPD_ANASUB_TO_RP(r) ((r - 1) & 0x3)
-#define UCPD_RP_TO_ANASUB(r) ((r + 1) & 0x3)
-
-struct msg_header_info {
- enum pd_power_role pr;
- enum pd_data_role dr;
-};
-static struct msg_header_info msg_header;
-
-/* States for managing tx messages in ucpd task */
-enum ucpd_state {
- STATE_IDLE,
- STATE_ACTIVE_TCPM,
- STATE_ACTIVE_CRC,
- STATE_HARD_RESET,
- STATE_WAIT_CRC_ACK,
-};
-
-/* Events for pd_interrupt_handler_task */
-#define UCPD_EVT_GOOD_CRC_REQ BIT(0)
-#define UCPD_EVT_TCPM_MSG_REQ BIT(1)
-#define UCPD_EVT_HR_REQ BIT(2)
-#define UCPD_EVT_TX_MSG_FAIL BIT(3)
-#define UCPD_EVT_TX_MSG_DISC BIT(4)
-#define UCPD_EVT_TX_MSG_SUCCESS BIT(5)
-#define UCPD_EVT_HR_DONE BIT(6)
-#define UCPD_EVT_HR_FAIL BIT(7)
-#define UCPD_EVT_RX_GOOD_CRC BIT(8)
-#define UCPD_EVT_RX_MSG BIT(9)
-
-#define UCPD_T_RECEIVE_US (1 * MSEC)
-
-#define UCPD_N_RETRY_COUNT_REV20 3
-#define UCPD_N_RETRY_COUNT_REV30 2
-
-/*
- * Tx messages are iniated either by TCPM/PRL layer or from ucpd when a GoodCRC
- * ack message needs to be sent.
- */
-enum ucpd_tx_msg {
- TX_MSG_NONE = -1,
- TX_MSG_TCPM = 0,
- TX_MSG_GOOD_CRC = 1,
- TX_MSG_TOTAL = 2,
-};
-
-#define MSG_TCPM_MASK BIT(TX_MSG_TCPM)
-#define MSG_GOOD_CRC_MASK BIT(TX_MSG_GOOD_CRC)
-
-union buffer {
- uint16_t header;
- uint8_t msg[UCPD_BUF_LEN];
-};
-
-struct ucpd_tx_desc {
- enum tcpci_msg_type type;
- int msg_len;
- int msg_index;
- union buffer data;
-};
-
-/* Track VCONN on/off state */
-static int ucpd_vconn_enable;
-
-/* Tx message variables */
-struct ucpd_tx_desc ucpd_tx_buffers[TX_MSG_TOTAL];
-struct ucpd_tx_desc *ucpd_tx_active_buffer;
-static int ucpd_tx_request;
-static int ucpd_timeout_us;
-static enum ucpd_state ucpd_tx_state;
-static int msg_id_match;
-static int tx_retry_count;
-static int tx_retry_max;
-
-static int ucpd_txorderset[] = {
- TX_ORDERSET_SOP,
- TX_ORDERSET_SOP_PRIME,
- TX_ORDERSET_SOP_PRIME_PRIME,
- TX_ORDERSET_SOP_PRIME_DEBUG,
- TX_ORDERSET_SOP_PRIME_PRIME_DEBUG,
- TX_ORDERSET_HARD_RESET,
- TX_ORDERSET_CABLE_RESET,
-};
-
-/* PD Rx variables */
-static int ucpd_rx_byte_count;
-static uint8_t ucpd_rx_buffer[UCPD_BUF_LEN];
-static int ucpd_crc_id;
-static bool ucpd_rx_sop_prime_enabled;
-static int ucpd_rx_msg_active;
-static bool ucpd_rx_bist_mode;
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
-/* Defines and macros for ucpd state logging */
-#define TX_STATE_LOG_LEN BIT(5)
-#define TX_STATE_LOG_MASK (TX_STATE_LOG_LEN - 1)
-
-struct ucpd_tx_state {
- uint32_t ts;
- int tx_request;
- int timeout_us;
- enum ucpd_state enter_state;
- enum ucpd_state exit_state;
- uint32_t evt;
-};
-
-struct ucpd_tx_state ucpd_tx_statelog[TX_STATE_LOG_LEN];
-int ucpd_tx_state_log_idx;
-int ucpd_tx_state_log_freeze;
-
-static char ucpd_names[][12] = {
- "TX_IDLE",
- "ACT_TCPM",
- "ACT_CRC",
- "HARD_RST",
- "WAIT_CRC",
-};
-/* Defines and macros used for ucpd pd message logging */
-#define MSG_LOG_LEN 64
-#define MSG_BUF_LEN 10
-
-struct msg_info {
- uint8_t dir;
- uint8_t comp;
- uint8_t crc;
- uint16_t header;
- uint32_t ts;
- uint8_t buf[MSG_BUF_LEN];
-};
-static int msg_log_cnt;
-static int msg_log_idx;
-static struct msg_info msg_log[MSG_LOG_LEN];
-
-#define UCPD_CC_STRING_LEN 5
-
-static char ccx[4][UCPD_CC_STRING_LEN] = {
- "Ra",
- "Rp",
- "Rd",
- "Open",
-};
-static char rp_string[][8] = {
- "Rp_usb",
- "Rp_1.5",
- "Rp_3.0",
- "Open",
-};
-static int ucpd_sr_cc_event;
-static int ucpd_cc_set_save;
-static int ucpd_cc_change_log;
-
-static int ucpd_is_cc_pull_active(int port, enum usbpd_cc_pin cc_line);
-
-static void ucpd_log_add_msg(uint16_t header, int dir)
-{
- uint32_t ts = __hw_clock_source_read();
- int idx = msg_log_idx;
- uint8_t *buf = dir ? ucpd_rx_buffer : ucpd_tx_active_buffer->data.msg;
-
- /*
- * Add a msg entry in the history log. The log is currently designed to
- * be from reset until MSG_LOG_LEN messages have been added.
- * ts -> lower 32 bits of 1 uSec running clock
- * dir -> 0 = tx message, 1 = rx message
- * comp -> ucpd transmit success
- * crc -> GoodCrc received following tx message
- */
- if (msg_log_cnt++ < MSG_LOG_LEN) {
- int msg_bytes = MIN((PD_HEADER_CNT(header) << 2) + 2,
- MSG_BUF_LEN);
-
- msg_log[idx].header = header;
- msg_log[idx].ts = ts;
- msg_log[idx].dir = dir;
- msg_log[idx].comp = 0;
- msg_log[idx].crc = 0;
- msg_log_idx++;
- memcpy(msg_log[idx].buf, buf, msg_bytes);
- }
-}
-
-static void ucpd_log_mark_tx_comp(void)
-{
- /*
- * This msg logging utility function is used to mark when a message was
- * successfully transmitted when transmit interrupt occurs and the tx
- * message sent status was set. Because the transmit message is added
- * before it's sent by ucpd, the index has to back up one to mark the
- * correct log entry.
- */
- if (msg_log_cnt < MSG_LOG_LEN) {
- if (msg_log_idx > 0)
- msg_log[msg_log_idx - 1].comp = 1;
- }
-}
-
-static void ucpd_log_mark_crc(void)
-{
- /*
- * This msg logging utility function is used to mark when a GoodCRC
- * message is received following a tx message. This status is displayed
- * in column s2. Because this indication follows both transmit message
- * and GoodCRC rx, the index must be back up 2 rows to mark the correct
- * tx message entry.
- */
- if (msg_log_cnt < MSG_LOG_LEN) {
- if (msg_log_idx >= 2)
- msg_log[msg_log_idx - 2].crc = 1;
- }
-}
-
-static void ucpd_cc_status(int port)
-{
- int rc = stm32gx_ucpd_get_role_control(port);
- int cc1_pull, cc2_pull;
- enum tcpc_cc_voltage_status v_cc1, v_cc2;
- int rv;
- char *rp_name;
-
- cc1_pull = rc & 0x3;
- cc2_pull = (rc >> 2) & 0x3;
-
- /*
- * This function is used to display CC settings, including pull type,
- * and if Rp, what the Rp value is set to. In addition, the current
- * values of CC voltage detector, polarity, and PD enable status are
- * displayed.
- */
- rv = stm32gx_ucpd_get_cc(port,&v_cc1, &v_cc2);
- rp_name = rp_string[(rc >> 4) % 0x3];
- ccprintf("\tcc1\t = %s\n\tcc2\t = %s\n\tRp\t = %s\n",
- ccx[cc1_pull], ccx[cc2_pull], rp_name);
- if (!rv)
- ccprintf("\tcc1_v\t = %d\n\tcc2_v\t = %d\n", v_cc1, v_cc2);
-}
-
-void ucpd_cc_detect_notify_enable(int enable)
-{
- /*
- * This variable is used to control when a CC detach detector is
- * active.
- */
- ucpd_cc_change_log = enable;
-}
-
-static void ucpd_log_invalidate_entry(void)
-{
- /*
- * This is a msg log utility function which is triggered when an
- * unexpected detach event is detected.
- */
- if (msg_log_idx < (MSG_LOG_LEN - 1)) {
- int idx = msg_log_idx;
-
- msg_log[idx].header = 0xabcd;
- msg_log[idx].ts = __hw_clock_source_read();
- msg_log[idx].dir = 0;
- msg_log[idx].comp = 0;
- msg_log[idx].crc = 0;
- msg_log_cnt++;
- msg_log_idx++;
- }
-}
-
-/*
- * This function will mark in the msg log when a detach event occurs. It will
- * only be active if ucpd_cc_change_log is set which can be controlled via the
- * ucpd console command.
- */
-static void ucpd_cc_change_notify(void)
-{
- if (ucpd_cc_change_log) {
- uint32_t sr = ucpd_sr_cc_event;
-
- ucpd_log_invalidate_entry();
-
- ccprintf("vstate: cc1 = %x, cc2 = %x, Rp = %d\n",
- (sr >> STM32_UCPD_SR_VSTATE_CC1_SHIFT) & 0x3,
- (sr >> STM32_UCPD_SR_VSTATE_CC2_SHIFT) & 0x3,
- (ucpd_cc_set_save >> STM32_UCPD_CR_ANASUBMODE_SHIFT)
- & 0x3);
- /* Display CC status on EC console */
- ucpd_cc_status(0);
- }
-}
-DECLARE_DEFERRED(ucpd_cc_change_notify);
-#endif /* CONFIG_STM32G4_UCPD_DEBUG */
-
-static int ucpd_msg_is_good_crc(uint16_t header)
-{
- /*
- * Good CRC is a control message (no data objects) with GOOD_CRC message
- * type in the header.
- */
- return ((PD_HEADER_CNT(header) == 0) && (PD_HEADER_EXT(header) == 0) &&
- (PD_HEADER_TYPE(header) == PD_CTRL_GOOD_CRC)) ? 1 : 0;
-}
-
-static void ucpd_hard_reset_rx_log(void)
-{
- CPRINTS("ucpd: hard reset recieved");
-}
-DECLARE_DEFERRED(ucpd_hard_reset_rx_log);
-
-static void ucpd_port_enable(int port, int enable)
-{
- if (enable)
- STM32_UCPD_CFGR1(port) |= STM32_UCPD_CFGR1_UCPDEN;
- else
- STM32_UCPD_CFGR1(port) &= ~STM32_UCPD_CFGR1_UCPDEN;
-}
-
-static int ucpd_is_cc_pull_active(int port, enum usbpd_cc_pin cc_line)
-{
- int cc_enable = (STM32_UCPD_CR(port) & STM32_UCPD_CR_CCENABLE_MASK) >>
- STM32_UCPD_CR_CCENABLE_SHIFT;
-
- return ((cc_enable >> cc_line) & 0x1);
-}
-
-static void ucpd_tx_data_byte(int port)
-{
- int index = ucpd_tx_active_buffer->msg_index++;
-
- STM32_UCPD_TXDR(port) = ucpd_tx_active_buffer->data.msg[index];
-}
-
-static void ucpd_rx_data_byte(int port)
-{
- if (ucpd_rx_byte_count < UCPD_BUF_LEN)
- ucpd_rx_buffer[ucpd_rx_byte_count++] = STM32_UCPD_RXDR(port);
-}
-
-static void ucpd_tx_interrupts_enable(int port, int enable)
-{
- if (enable) {
- STM32_UCPD_ICR(port) = UCPD_ICR_TX_INT_MASK;
- STM32_UCPD_IMR(port) |= UCPD_IMR_TX_INT_MASK;
- } else {
- STM32_UCPD_IMR(port) &= ~UCPD_IMR_TX_INT_MASK;
- }
-}
-
-static void ucpd_rx_enque_error(void)
-{
- CPRINTS("ucpd: TCPM Enque Error!!");
-}
-DECLARE_DEFERRED(ucpd_rx_enque_error);
-
-static void stm32gx_ucpd_state_init(int port)
-{
- /* Init variables used to manage tx process */
- ucpd_tx_request = 0;
- tx_retry_count = 0;
- ucpd_tx_state = STATE_IDLE;
- ucpd_timeout_us = -1;
-
- /* Init variables used to manage rx */
- ucpd_rx_sop_prime_enabled = 0;
- ucpd_rx_msg_active = 0;
- ucpd_rx_bist_mode = 0;
-
- /* Vconn tracking variable */
- ucpd_vconn_enable = 0;
-}
-
-int stm32gx_ucpd_init(int port)
-{
- uint32_t cfgr1_reg;
- uint32_t moder_reg;
-
- /* Disable UCPD interrupts */
- task_disable_irq(STM32_IRQ_UCPD1);
-
- /*
- * After exiting reset, stm32gx will have dead battery mode enabled by
- * default which connects Rd to CC1/CC2. This should be disabled when EC
- * is powered up.
- */
- STM32_PWR_CR3 |= STM32_PWR_CR3_UCPD1_DBDIS;
-
- /* Ensure that clock to UCPD is enabled */
- STM32_RCC_APB1ENR2 |= STM32_RCC_APB1ENR2_UPCD1EN;
-
- /* Make sure CC1/CC2 pins PB4/PB6 are set for analog mode */
- moder_reg = STM32_GPIO_MODER(GPIO_B);
- moder_reg |= 0x3300;
- STM32_GPIO_MODER(GPIO_B) = moder_reg;
- /*
- * CFGR1 must be written when UCPD peripheral is disabled. Note that
- * disabling ucpd causes the peripheral to quit any ongoing activity and
- * sets all ucpd registers back their default values.
- */
- ucpd_port_enable(port, 0);
-
- cfgr1_reg = STM32_UCPD_CFGR1_PSC_CLK_VAL(UCPD_PSC_DIV - 1) |
- STM32_UCPD_CFGR1_TRANSWIN_VAL(UCPD_TRANSWIN_CNT - 1) |
- STM32_UCPD_CFGR1_IFRGAP_VAL(UCPD_IFRGAP_CNT - 1) |
- STM32_UCPD_CFGR1_HBITCLKD_VAL(UCPD_HBIT_DIV - 1);
- STM32_UCPD_CFGR1(port) = cfgr1_reg;
-
- /*
- * Set RXORDSETEN field to control which types of ordered sets the PD
- * receiver must receive.
- * SOP, SOP', Hard Reset Det, Cable Reset Det enabled
- */
- STM32_UCPD_CFGR1(port) |= STM32_UCPD_CFGR1_RXORDSETEN_VAL(0x1B);
-
- /* Enable ucpd */
- ucpd_port_enable(port, 1);
-
- /* Configure CC change interrupts */
- STM32_UCPD_IMR(port) = STM32_UCPD_IMR_TYPECEVT1IE |
- STM32_UCPD_IMR_TYPECEVT2IE;
- STM32_UCPD_ICR(port) = STM32_UCPD_ICR_TYPECEVT1CF |
- STM32_UCPD_ICR_TYPECEVT2CF;
-
- /* SOP'/SOP'' must be enabled via TCPCI call */
- ucpd_rx_sop_prime_enabled = false;
-
- stm32gx_ucpd_state_init(port);
-
- /* Enable UCPD interrupts */
- task_enable_irq(STM32_IRQ_UCPD1);
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_release(int port)
-{
- ucpd_port_enable(port, 0);
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_get_cc(int port, enum tcpc_cc_voltage_status *cc1,
- enum tcpc_cc_voltage_status *cc2)
-{
- int vstate_cc1;
- int vstate_cc2;
- int anamode;
- uint32_t sr;
-
- /*
- * cc_voltage_status is determined from vstate_cc bit field in the
- * status register. The meaning of the value vstate_cc depends on
- * current value of ANAMODE (src/snk).
- *
- * vstate_cc maps directly to cc_state from tcpci spec when ANAMODE = 1,
- * but needs to be modified slightly for case ANAMODE = 0.
- *
- * If presenting Rp (source), then need to to a circular shift of
- * vstate_ccx value:
- * vstate_cc | cc_state
- * ------------------
- * 0 -> 1
- * 1 -> 2
- * 2 -> 0
- */
-
- /* Get vstate_ccx values and power role */
- sr = STM32_UCPD_SR(port);
- /* Get Rp or Rd active */
- anamode = !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_ANAMODE);
- vstate_cc1 = (sr & STM32_UCPD_SR_VSTATE_CC1_MASK) >>
- STM32_UCPD_SR_VSTATE_CC1_SHIFT;
- vstate_cc2 = (sr & STM32_UCPD_SR_VSTATE_CC2_MASK) >>
- STM32_UCPD_SR_VSTATE_CC2_SHIFT;
-
- /* Do circular shift if port == source */
- if (anamode) {
- if (vstate_cc1 != STM32_UCPD_SR_VSTATE_RA)
- vstate_cc1 += 4;
- if (vstate_cc2 != STM32_UCPD_SR_VSTATE_RA)
- vstate_cc2 += 4;
- } else {
- if (vstate_cc1 != STM32_UCPD_SR_VSTATE_OPEN)
- vstate_cc1 = (vstate_cc1 + 1) % 3;
- if (vstate_cc2 != STM32_UCPD_SR_VSTATE_OPEN)
- vstate_cc2 = (vstate_cc2 + 1) % 3;
- }
-
- *cc1 = vstate_cc1;
- *cc2 = vstate_cc2;
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_get_role_control(int port)
-{
- int role_control;
- int cc1;
- int cc2;
- int anamode = !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_ANAMODE);
- int anasubmode = (STM32_UCPD_CR(port) & STM32_UCPD_CR_ANASUBMODE_MASK)
- >> STM32_UCPD_CR_ANASUBMODE_SHIFT;
-
- /*
- * Role control register is defined as:
- * R_cc1 -> b 1:0
- * R_cc2 -> b 3:2
- * Rp -> b 5:4
- *
- * In TCPCI, CCx is defined as:
- * 00b -> Ra
- * 01b -> Rp
- * 10b -> Rd
- * 11b -> Open (don't care)
- *
- * For ucpd, this information is encoded in ANAMODE and ANASUBMODE
- * fields as follows:
- * ANAMODE CCx
- * 0 -> Rp -> 1
- * 1 -> Rd -> 2
- *
- * ANASUBMODE:
- * 00b -> TYPEC_RP_RESERVED (open)
- * 01b -> TYPEC_RP_USB
- * 10b -> TYPEC_RP_1A5
- * 11b -> TYPEC_RP_3A0
- *
- * CCx = ANAMODE + 1, if CCx is enabled
- * Rp = (ANASUBMODE - 1) & 0x3
- */
- cc1 = ucpd_is_cc_pull_active(port, USBPD_CC_PIN_1) ? anamode + 1 :
- TYPEC_CC_OPEN;
- cc2 = ucpd_is_cc_pull_active(port, USBPD_CC_PIN_2) ? anamode + 1 :
- TYPEC_CC_OPEN;
- role_control = cc1 | (cc2 << 2);
- /* Circular shift anasubmode to convert to Rp range */
- role_control |= (UCPD_ANASUB_TO_RP(anasubmode) << 4);
-
- return role_control;
-}
-
-static uint32_t ucpd_get_cc_enable_mask(int port)
-{
- uint32_t mask = STM32_UCPD_CR_CCENABLE_MASK;
-
- if (ucpd_vconn_enable) {
- uint32_t cr = STM32_UCPD_CR(port);
- int pol = !!(cr & STM32_UCPD_CR_PHYCCSEL);
-
- mask &= ~(1 << (STM32_UCPD_CR_CCENABLE_SHIFT + !pol));
- }
-
- return mask;
-}
-
-int stm32gx_ucpd_vconn_disc_rp(int port, int enable)
-{
- int cr;
-
- /* Update VCONN on/off status. Do this before getting cc enable mask */
- ucpd_vconn_enable = enable;
-
- cr = STM32_UCPD_CR(port);
- cr &= ~STM32_UCPD_CR_CCENABLE_MASK;
- cr |= ucpd_get_cc_enable_mask(port);
-
- /* Apply cc pull resistor change */
- STM32_UCPD_CR(port) = cr;
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_set_cc(int port, int cc_pull, int rp)
-{
- uint32_t cr = STM32_UCPD_CR(port);
-
- /*
- * Always set ANASUBMODE to match desired Rp. TCPM layer has a valid
- * range of 0, 1, or 2. This range maps to 1, 2, or 3 in ucpd for
- * ANASUBMODE.
- */
- cr &= ~STM32_UCPD_CR_ANASUBMODE_MASK;
- cr |= STM32_UCPD_CR_ANASUBMODE_VAL(UCPD_RP_TO_ANASUB(rp));
-
- /* Disconnect both pull from both CC lines for R_open case */
- cr &= ~STM32_UCPD_CR_CCENABLE_MASK;
- /* Set ANAMODE if cc_pull is Rd */
- if (cc_pull == TYPEC_CC_RD) {
- cr |= STM32_UCPD_CR_ANAMODE | STM32_UCPD_CR_CCENABLE_MASK;
- /* Clear ANAMODE if cc_pull is Rp */
- } else if (cc_pull == TYPEC_CC_RP) {
- cr &= ~(STM32_UCPD_CR_ANAMODE);
- cr |= ucpd_get_cc_enable_mask(port);
- }
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- if (ucpd_cc_change_log) {
- CPRINTS("ucpd: set_cc: pull = %d, rp = %d", cc_pull, rp);
- }
-#endif
- /* Update pull values */
- STM32_UCPD_CR(port) = cr;
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_set_polarity(int port, enum tcpc_cc_polarity polarity) {
- /*
- * Polarity impacts the PHYCCSEL, CCENABLE, and CCxTCDIS fields. This
- * function is called when polarity is updated at TCPM layer. STM32Gx
- * only supports POLARITY_CC1 or POLARITY_CC2 and this is stored in the
- * PHYCCSEL bit in the CR register.
- */
- if (polarity > POLARITY_CC2)
- return EC_ERROR_UNIMPLEMENTED;
-
- if (polarity == POLARITY_CC1)
- STM32_UCPD_CR(port) &= ~STM32_UCPD_CR_PHYCCSEL;
- else if (polarity == POLARITY_CC2)
- STM32_UCPD_CR(port) |= STM32_UCPD_CR_PHYCCSEL;
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_cc_set_save = STM32_UCPD_CR(port);
-#endif
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_set_rx_enable(int port, int enable)
-{
- /*
- * USB PD receiver enable is controlled by the bit PHYRXEN in
- * UCPD_CR. Enable Rx interrupts when RX PD decoder is active.
- */
- if (enable) {
- STM32_UCPD_ICR(port) = UCPD_IMR_RX_INT_MASK;
- STM32_UCPD_IMR(port) |= UCPD_IMR_RX_INT_MASK;
- STM32_UCPD_CR(port) |= STM32_UCPD_CR_PHYRXEN;
- } else {
- STM32_UCPD_CR(port) &= ~STM32_UCPD_CR_PHYRXEN;
- STM32_UCPD_IMR(port) &= ~UCPD_IMR_RX_INT_MASK;
- }
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_set_msg_header(int port, int power_role, int data_role)
-{
- msg_header.pr = power_role;
- msg_header.dr = data_role;
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_sop_prime_enable(int port, bool enable)
-{
- /* Update static varialbe used to filter SOP//SOP'' messages */
- ucpd_rx_sop_prime_enabled = enable;
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_get_chip_info(int port, int live,
- struct ec_response_pd_chip_info_v1 *chip_info)
-{
- chip_info->vendor_id = USB_VID_STM32;
- chip_info->product_id = 0;
- chip_info->device_id = STM32_DBGMCU_IDCODE & 0xfff;
- chip_info->fw_version_number = 0xEC;
-
- return EC_SUCCESS;
-}
-
-static int stm32gx_ucpd_start_transmit(int port, enum ucpd_tx_msg msg_type)
-{
- enum tcpci_msg_type type;
-
- /* Select the correct tx desciptor */
- ucpd_tx_active_buffer = &ucpd_tx_buffers[msg_type];
- type = ucpd_tx_active_buffer->type;
-
- if (type == TCPCI_MSG_TX_HARD_RESET) {
- /*
- * From RM0440 45.4.4:
- * In order to facilitate generation of a Hard Reset, a special
- * code of TXMODE field is used. No other fields need to be
- * written. On writing the correct code, the hardware forces
- * Hard Reset Tx under the correct (optimal) timings with
- * respect to an on-going Tx message, which (if still in
- * progress) is cleanly terminated by truncating the current
- * sequence and directly appending an EOP K-code sequence. No
- * specific interrupt is generated relating to this truncation
- * event.
- *
- * Because Hard Reset can interrupt ongoing Tx operations, it is
- * started differently than all other tx messages. Only need to
- * enable hard reset interrupts, and then set a bit in the CR
- * register to initiate.
- */
- /* Enable interrupt for Hard Reset sent/discarded */
- STM32_UCPD_ICR(port) = STM32_UCPD_ICR_HRSTDISCCF |
- STM32_UCPD_ICR_HRSTSENTCF;
- STM32_UCPD_IMR(port) |= STM32_UCPD_IMR_HRSTDISCIE |
- STM32_UCPD_IMR_HRSTSENTIE;
- /* Initiate Hard Reset */
- STM32_UCPD_CR(port) |= STM32_UCPD_CR_TXHRST;
- } else if (type != TCPCI_MSG_INVALID) {
- int msg_len = 0;
- int mode;
-
- /*
- * These types are normal transmission, TXMODE = 0. To transmit
- * regular message, control or data, requires the following:
- * 1. Set TXMODE:
- * Normal -> 0
- * Cable Reset -> 1
- * Bist -> 2
- * 2. Set TX_ORDSETR based on message type
- * 3. Set TX_PAYSZR which must account for 2 bytes of header
- * 4. Configure DMA (optional if DMA is desired)
- * 5. Enable transmit interrupts
- * 6. Start TX by setting TXSEND in CR
- *
- */
-
- /*
- * Set tx length parameter (in bytes). Note the count field in
- * the header is number of 32 bit objects. Also, the length
- * field must account for the 2 header bytes.
- */
- if (type == TCPCI_MSG_TX_BIST_MODE_2) {
- mode = STM32_UCPD_CR_TXMODE_BIST;
- } else if (type == TCPCI_MSG_CABLE_RESET) {
- mode = STM32_UCPD_CR_TXMODE_CBL_RST;
- } else {
- mode = STM32_UCPD_CR_TXMODE_DEF;
- msg_len = ucpd_tx_active_buffer->msg_len;
- }
-
- STM32_UCPD_TX_PAYSZR(port) = msg_len;
-
- /* Set tx mode */
- STM32_UCPD_CR(port) &= ~STM32_UCPD_CR_TXMODE_MASK;
- STM32_UCPD_CR(port) |= STM32_UCPD_CR_TXMODE_VAL(mode);
-
- /* Index into ordset enum for start of packet */
- if (type <= TCPCI_MSG_CABLE_RESET)
- STM32_UCPD_TX_ORDSETR(port) = ucpd_txorderset[type];
- else
- STM32_UCPD_TX_ORDSETR(port) =
- ucpd_txorderset[TX_ORDERSET_SOP];
-
- /* Reset msg byte index */
- ucpd_tx_active_buffer-> msg_index = 0;
-
- /* Enable interrupts */
- ucpd_tx_interrupts_enable(port, 1);
-
- /* Trigger ucpd peripheral to start pd message transmit */
- STM32_UCPD_CR(port) |= STM32_UCPD_CR_TXSEND;
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_log_add_msg(ucpd_tx_active_buffer->data.header, 0);
-#endif
- }
-
- return EC_SUCCESS;
-}
-
-static void ucpd_set_tx_state(enum ucpd_state state)
-{
- ucpd_tx_state = state;
-}
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
-static void ucpd_task_log(int timeout, enum ucpd_state enter,
- enum ucpd_state exit, int req, uint32_t evt)
-{
- static int same_count = 0;
- int idx = ucpd_tx_state_log_idx;
-
- if (ucpd_tx_state_log_freeze)
- return;
-
- ucpd_tx_statelog[idx].ts = get_time().le.lo;
- ucpd_tx_statelog[idx].tx_request = req;
- ucpd_tx_statelog[idx].timeout_us = timeout;
- ucpd_tx_statelog[idx].enter_state = enter;
- ucpd_tx_statelog[idx].exit_state = exit;
- ucpd_tx_statelog[idx].evt = evt;
-
- ucpd_tx_state_log_idx = (idx + 1) & TX_STATE_LOG_MASK;
-
- if (enter == exit) {
- same_count++;
- } else {
- same_count = 0;
- }
-
- /*
- * Should not have same enter/exit states. If this happens, then freeze
- * state log to help in debugging.
- */
- if (same_count > 5)
- ucpd_tx_state_log_freeze = 1;
-}
-
-static void ucpd_task_log_dump(void)
-{
- int n;
- int idx;
-
- ucpd_tx_state_log_freeze = 1;
-
- /* current index will be oldest entry in the log */
- idx = ucpd_tx_state_log_idx;
-
- ccprintf("\n\t UCDP Task Log\n");
- for (n = 0; n < TX_STATE_LOG_LEN; n++) {
- ccprintf("[%d]:\t\%8s\t%8s\t%02x\t%08x\t%09d\t%d\n",
- n,
- ucpd_names[ucpd_tx_statelog[idx].enter_state],
- ucpd_names[ucpd_tx_statelog[idx].exit_state],
- ucpd_tx_statelog[idx].tx_request,
- ucpd_tx_statelog[idx].evt,
- ucpd_tx_statelog[idx].ts,
- ucpd_tx_statelog[idx].timeout_us);
-
- idx = (idx + 1) & TX_STATE_LOG_MASK;
- msleep(5);
- }
-
- ucpd_tx_state_log_freeze = 0;
-}
-#endif
-
-static void ucpd_manage_tx(int port, int evt)
-{
- enum ucpd_tx_msg msg_src = TX_MSG_NONE;
- uint16_t hdr;
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- enum ucpd_state enter = ucpd_tx_state;
- int req = ucpd_tx_request;
-#endif
-
- if (evt & UCPD_EVT_HR_REQ) {
- /*
- * Hard reset control messages are treated as a priority. The
- * control message will already be set up as it comes from the
- * PRL layer like any other PD ctrl/data message. So just need
- * to indicate the correct message source and set the state to
- * hard reset here.
- */
- ucpd_set_tx_state(STATE_HARD_RESET);
- msg_src = TX_MSG_TCPM;
- ucpd_tx_request &= ~(1 << msg_src);
- }
-
- switch (ucpd_tx_state) {
- case STATE_IDLE:
- if (ucpd_tx_request & MSG_GOOD_CRC_MASK) {
- ucpd_set_tx_state(STATE_ACTIVE_CRC);
- msg_src = TX_MSG_GOOD_CRC;
- } else if (ucpd_tx_request & MSG_TCPM_MASK) {
- if (evt & UCPD_EVT_RX_MSG) {
- /*
- * USB-PD Specification rev 3.0, section 6.10
- * On receiving a received message, the protocol
- * layer shall discard any pending message.
- *
- * Since the pending message from the PRL has
- * not been sent yet, it needs to be discarded
- * based on the received message event.
- */
- pd_transmit_complete(port,
- TCPC_TX_COMPLETE_DISCARDED);
- ucpd_tx_request &= ~MSG_TCPM_MASK;
- } else if (!ucpd_rx_msg_active) {
- ucpd_set_tx_state(STATE_ACTIVE_TCPM);
- msg_src = TX_MSG_TCPM;
- /* Save msgID required for GoodCRC check */
- hdr = ucpd_tx_buffers[TX_MSG_TCPM].data.header;
- msg_id_match = PD_HEADER_ID(hdr);
- tx_retry_max = PD_HEADER_REV(hdr) == PD_REV30 ?
- UCPD_N_RETRY_COUNT_REV30 :
- UCPD_N_RETRY_COUNT_REV20;
- }
- }
-
- /* If state is not idle, then start tx message */
- if (ucpd_tx_state != STATE_IDLE) {
- ucpd_tx_request &= ~(1 << msg_src);
- tx_retry_count = 0;
- }
- break;
-
- case STATE_ACTIVE_TCPM:
- /*
- * Check if tx msg has finsihed. For TCPM messages
- * transmit is not complete until a GoodCRC message
- * matching the msgID just sent is received. But, a tx
- * message can fail due to collision or underrun,
- * etc. If that failure occurs, dont' wait for GoodCrc
- * and just go to failure path.
- */
- if (evt & UCPD_EVT_TX_MSG_SUCCESS) {
- ucpd_set_tx_state(STATE_WAIT_CRC_ACK);
- ucpd_timeout_us = UCPD_T_RECEIVE_US;
- } else if (evt & UCPD_EVT_TX_MSG_DISC ||
- evt & UCPD_EVT_TX_MSG_FAIL) {
- if (tx_retry_count < tx_retry_max) {
- if (evt & UCPD_EVT_RX_MSG) {
- /*
- * A message was received so there is no
- * need to retry this tx message which
- * had failed to send previously.
- * Likely, due to the wire
- * being active from the message that
- * was just received.
- */
- ucpd_set_tx_state(STATE_IDLE);
- pd_transmit_complete(port,
- TCPC_TX_COMPLETE_DISCARDED);
- ucpd_set_tx_state(STATE_IDLE);
- } else {
- /*
- * Tx attempt failed. Remain in this
- * state, but trigger new tx attempt.
- */
- msg_src = TX_MSG_TCPM;
- tx_retry_count++;
- }
- } else {
- enum tcpc_transmit_complete status;
-
- status = (evt & UCPD_EVT_TX_MSG_FAIL) ?
- TCPC_TX_COMPLETE_FAILED :
- TCPC_TX_COMPLETE_DISCARDED;
- ucpd_set_tx_state(STATE_IDLE);
- pd_transmit_complete(port, status);
- }
- }
- break;
-
- case STATE_ACTIVE_CRC:
- if (evt & (UCPD_EVT_TX_MSG_SUCCESS | UCPD_EVT_TX_MSG_FAIL |
- UCPD_EVT_TX_MSG_DISC)) {
- ucpd_set_tx_state(STATE_IDLE);
- if (evt & UCPD_EVT_TX_MSG_FAIL)
- CPRINTS("ucpd: Failed to send GoodCRC!");
- else if (evt & UCPD_EVT_TX_MSG_DISC)
- CPRINTS("ucpd: GoodCRC message discarded!");
- }
- break;
-
- case STATE_WAIT_CRC_ACK:
- if (evt & UCPD_EVT_RX_GOOD_CRC &&
- ucpd_crc_id == msg_id_match) {
- /* GoodCRC with matching ID was received */
- pd_transmit_complete(port,
- TCPC_TX_COMPLETE_SUCCESS);
- ucpd_set_tx_state(STATE_IDLE);
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_log_mark_crc();
-#endif
- } else if ((evt & UCPD_EVT_RX_GOOD_CRC) ||
- (evt & TASK_EVENT_TIMER)) {
- /* GoodCRC w/out match or timeout waiting */
- if (tx_retry_count < tx_retry_max) {
- ucpd_set_tx_state(STATE_ACTIVE_TCPM);
- msg_src = TX_MSG_TCPM;
- tx_retry_count++;
- } else {
- ucpd_set_tx_state(STATE_IDLE);
- pd_transmit_complete(port,
- TCPC_TX_COMPLETE_FAILED);
- }
- } else if (evt & UCPD_EVT_RX_MSG) {
- /*
- * In the case of a collsion, it's possible the port
- * partner may not send a GoodCRC and instead send the
- * message that was colliding. If a message is received
- * in this state, then treat it as a discard from an
- * incoming message.
- */
- pd_transmit_complete(port,
- TCPC_TX_COMPLETE_DISCARDED);
- ucpd_set_tx_state(STATE_IDLE);
- }
- break;
-
- case STATE_HARD_RESET:
- if (evt & UCPD_EVT_HR_DONE) {
- /* HR complete, reset tx state values */
- ucpd_set_tx_state(STATE_IDLE);
- ucpd_tx_request = 0;
- tx_retry_count = 0;
- } else if (evt & UCPD_EVT_HR_FAIL) {
- ucpd_set_tx_state(STATE_IDLE);
- ucpd_tx_request = 0;
- tx_retry_count = 0;
- }
- break;
- }
-
- /* If msg_src is valid, then start transmit */
- if (msg_src > TX_MSG_NONE) {
- stm32gx_ucpd_start_transmit(port, msg_src);
- }
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_task_log(ucpd_timeout_us, enter, ucpd_tx_state, req, evt);
-#endif
-}
-
-/*
- * Main task entry point for UCPD task
- *
- * @param p The PD port number for which to handle interrupts (pointer is
- * reinterpreted as an integer directly).
- */
-void ucpd_task(void *p)
-{
- const int port = (int) ((intptr_t) p);
-
- /* Init variables used to manage tx process */
- stm32gx_ucpd_state_init(port);
-
- while (1) {
- /*
- * Note that ucpd_timeout_us is file scope and may be modified
- * in the tx state machine when entering the STATE_WAIT_CRC_ACK
- * state. Otherwise, the expectation is that the task is woken
- * only upon non-timer events.
- */
- int evt = task_wait_event(ucpd_timeout_us);
-
- /*
- * USB-PD messages are intiated in TCPM stack (PRL
- * layer). However, GoodCRC messages are initiated within the
- * UCPD driver based on USB-PD rx messages. These 2 types of
- * transmit paths are managed via task events.
- *
- * UCPD generated GoodCRC messages, are the priority path as
- * they must be sent immediately following a successful USB-PD
- * rx message. As long as a transmit operation is not underway,
- * then a transmit message will be started upon request. The ISR
- * routine sets the event to indicate that the transmit
- * operation is complete.
- *
- * Hard reset requests are sent as a TCPM message, but in terms
- * of the ucpd transmitter, they are treated as a 3rd tx msg
- * source since they can interrupt an ongoing tx msg, and there
- * is no requirement to wait for a GoodCRC reply message.
- */
-
- /* Assume there is no timer for next task wake */
- ucpd_timeout_us = -1;
-
- if (evt & UCPD_EVT_GOOD_CRC_REQ)
- ucpd_tx_request |= MSG_GOOD_CRC_MASK;
-
- if (evt & UCPD_EVT_TCPM_MSG_REQ)
- ucpd_tx_request |= MSG_TCPM_MASK;
-
- /*
- * Manage PD tx messages. The state machine may need to be
- * called more than once when the task wakes. For instance, if
- * the task is woken at the completion of sending a GoodCRC,
- * there may be a TCPM message request pending and just changing
- * the state back to idle would not trigger start of transmit.
- */
- do {
- ucpd_manage_tx(port, evt);
- /* Look at task events only once. */
- evt = 0;
- } while (ucpd_tx_request && ucpd_tx_state == STATE_IDLE
- && !ucpd_rx_msg_active);
- }
-}
-
-static void ucpd_send_good_crc(int port, uint16_t rx_header)
-{
- int msg_id;
- int rev_id;
- uint16_t tx_header;
- enum tcpci_msg_type tx_type;
- enum pd_power_role pr = 0;
- enum pd_data_role dr = 0;
-
- /*
- * A GoodCRC message shall be sent by receiver to ack that the previous
- * message was correctly received. The GoodCRC message shall return the
- * rx message's msg_id field. The one exception is for GoodCRC messages,
- * which do not generate a GoodCRC response
- */
- if (ucpd_msg_is_good_crc(rx_header)) {
- return;
- }
-
- /*
- * Get the rx ordered set code just detected. SOP -> SOP''_Debug are in
- * the same order as enum tcpci_msg_type and so can be used
- * directly.
- */
- tx_type = STM32_UCPD_RX_ORDSETR(port) & STM32_UCPD_RXORDSETR_MASK;
-
- /*
- * PD Header(SOP):
- * Extended b15 -> set to 0 for control messages
- * Count b14:12 -> number of 32 bit data objects = 0 for ctrl msg
- * MsgID b11:9 -> running byte counter (extracted from rx msg)
- * Power Role b8 -> stored in static, from set_msg_header()
- * Spec Rev b7:b6 -> PD spec revision (extracted from rx msg)
- * Data Role b5 -> stored in static, from set_msg_header
- * Msg Type b4:b0 -> data or ctrl type = PD_CTRL_GOOD_CRC
- */
- /* construct header message */
- msg_id = PD_HEADER_ID(rx_header);
- rev_id = PD_HEADER_REV(rx_header);
- if (tx_type == TCPCI_MSG_SOP) {
- pr = msg_header.pr;
- dr = msg_header.dr;
- }
- tx_header = PD_HEADER(PD_CTRL_GOOD_CRC, pr, dr, msg_id, 0, rev_id, 0);
-
- /* Good CRC is header with no other objects */
- ucpd_tx_buffers[TX_MSG_GOOD_CRC].msg_len = 2;
- ucpd_tx_buffers[TX_MSG_GOOD_CRC].data.header = tx_header;
- ucpd_tx_buffers[TX_MSG_GOOD_CRC].type = tx_type;
-
- /* Notify ucpd task that a GoodCRC message tx request is pending */
- task_set_event(TASK_ID_UCPD, UCPD_EVT_GOOD_CRC_REQ);
-}
-
-int stm32gx_ucpd_transmit(int port,
- enum tcpci_msg_type type,
- uint16_t header,
- const uint32_t *data)
-{
- /* Length in bytes = (4 * object len) + 2 header byes */
- int len = (PD_HEADER_CNT(header) << 2) + 2;
-
- if (len > UCPD_BUF_LEN)
- return EC_ERROR_OVERFLOW;
-
- /* Store tx msg info in TCPM msg descriptor */
- ucpd_tx_buffers[TX_MSG_TCPM].msg_len = len;
- ucpd_tx_buffers[TX_MSG_TCPM].type = type;
- ucpd_tx_buffers[TX_MSG_TCPM].data.header = header;
- /* Copy msg objects to ucpd data buffer, after 2 header bytes */
- memcpy(ucpd_tx_buffers[TX_MSG_TCPM].data.msg + 2, (uint8_t *)data,
- len - 2);
-
- /*
- * Check for hard reset message here. A different event is used for hard
- * resets as they are able to interrupt ongoing transmit, and should
- * have priority over any pending message.
- */
- if (type == TCPCI_MSG_TX_HARD_RESET)
- task_set_event(TASK_ID_UCPD, UCPD_EVT_HR_REQ);
- else
- task_set_event(TASK_ID_UCPD, UCPD_EVT_TCPM_MSG_REQ);
-
- return EC_SUCCESS;
-}
-
-int stm32gx_ucpd_get_message_raw(int port, uint32_t *payload, int *head)
-{
- uint16_t *rx_header = (uint16_t *)ucpd_rx_buffer;
- int rxpaysz;
-#ifdef CONFIG_USB_PD_DECODE_SOP
- int sop;
-#endif
-
- /* First 2 bytes of data buffer are the header */
- *head = *rx_header;
-
-#ifdef CONFIG_USB_PD_DECODE_SOP
-/*
- * The message header is a 16-bit value that's stored in a 32-bit data type.
- * SOP* is encoded in bits 31 to 28 of the 32-bit data type.
- * NOTE: The 4 byte header is not part of the PD spec.
- */
- /* Get SOP value */
- sop = STM32_UCPD_RX_ORDSETR(port) & STM32_UCPD_RXORDSETR_MASK;
- /* Put SOP in bits 31:28 of 32 bit header */
- *head |= PD_HEADER_SOP(sop);
-#endif
- rxpaysz = STM32_UCPD_RX_PAYSZR(port) & STM32_UCPD_RX_PAYSZR_MASK;
- /* This size includes 2 bytes for message header */
- rxpaysz -= 2;
- /* Copy payload (src/dst are both 32 bit aligned) */
- memcpy(payload, ucpd_rx_buffer + 2, rxpaysz);
-
- return EC_SUCCESS;
-}
-
-enum ec_error_list stm32gx_ucpd_set_bist_test_mode(const int port,
- const bool enable)
-{
- ucpd_rx_bist_mode = enable;
- CPRINTS("ucpd: Bist test mode = %d", enable);
-
- return EC_SUCCESS;
-}
-
-void stm32gx_ucpd1_irq(void)
-{
- /* STM32_IRQ_UCPD indicates this is from UCPD1, so port = 0 */
- int port = 0;
- uint32_t sr = STM32_UCPD_SR(port);
- uint32_t tx_done_mask = STM32_UCPD_SR_TXMSGSENT | STM32_UCPD_SR_TXMSGABT
- | STM32_UCPD_SR_TXMSGDISC | STM32_UCPD_SR_HRSTSENT |
- STM32_UCPD_SR_HRSTDISC;
-
- /* Check for CC events, set event to wake PD task */
- if (sr & (STM32_UCPD_SR_TYPECEVT1 | STM32_UCPD_SR_TYPECEVT2)) {
- task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC);
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_sr_cc_event = sr;
- hook_call_deferred(&ucpd_cc_change_notify_data, 0);
-#endif
- }
-
- /*
- * Check for Tx events. tx_mask includes all status bits related to the
- * end of a USB-PD tx message. If any of these bits are set, the
- * transmit attempt is completed. Set an event to notify ucpd tx state
- * machine that transmit operation is complete.
- */
- if (sr & tx_done_mask) {
- /* Check for tx message complete */
- if (sr & STM32_UCPD_SR_TXMSGSENT) {
- task_set_event(TASK_ID_UCPD, UCPD_EVT_TX_MSG_SUCCESS);
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_log_mark_tx_comp();
-#endif
- } else if (sr & (STM32_UCPD_SR_TXMSGABT |
- STM32_UCPD_SR_TXUND)) {
- task_set_event(TASK_ID_UCPD, UCPD_EVT_TX_MSG_FAIL);
- } else if (sr & STM32_UCPD_SR_TXMSGDISC) {
- task_set_event(TASK_ID_UCPD, UCPD_EVT_TX_MSG_DISC);
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_log_mark_tx_comp();
-#endif
- } else if (sr & STM32_UCPD_SR_HRSTSENT) {
- task_set_event(TASK_ID_UCPD, UCPD_EVT_HR_DONE);
- } else if (sr & STM32_UCPD_SR_HRSTDISC) {
- task_set_event(TASK_ID_UCPD, UCPD_EVT_HR_FAIL);
- }
- /* Disable Tx interrupts */
- ucpd_tx_interrupts_enable(port, 0);
- }
-
- /* Check for data register empty */
- if (sr & STM32_UCPD_SR_TXIS)
- ucpd_tx_data_byte(port);
-
- /* Check for Rx Events */
- /* Check first for start of new message */
- if (sr & STM32_UCPD_SR_RXORDDET) {
- ucpd_rx_byte_count = 0;
- ucpd_rx_msg_active = 1;
- }
- /* Check for byte received */
- if (sr & STM32_UCPD_SR_RXNE)
- ucpd_rx_data_byte(port);
-
- /* Check for end of message */
- if (sr & STM32_UCPD_SR_RXMSGEND) {
- ucpd_rx_msg_active = 0;
- /* Check for errors */
- if (!(sr & STM32_UCPD_SR_RXERR)) {
- uint16_t *rx_header = (uint16_t *)ucpd_rx_buffer;
- enum tcpci_msg_type type;
- int good_crc = 0;
-
- type = STM32_UCPD_RX_ORDSETR(port) &
- STM32_UCPD_RXORDSETR_MASK;
-
- good_crc = ucpd_msg_is_good_crc(*rx_header);
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
- ucpd_log_add_msg(*rx_header, 1);
-#endif
- /*
- * Don't pass GoodCRC control messages to the TCPM
- * layer. In addition, need to filter for SOP'/SOP''
- * packets if those are not enabled. SOP'/SOP''
- * reception is controlled by a static variable. The
- * hardware orderset detection pattern can't be changed
- * without disabling the ucpd peripheral.
- */
- if (!good_crc && (ucpd_rx_sop_prime_enabled ||
- type == TCPCI_MSG_SOP)) {
-
- /*
- * If BIST test mode is active, then still need
- * to send GoodCRC reply, but there is no need
- * to send the message up to the tcpm layer.
- */
- if(!ucpd_rx_bist_mode) {
- if (tcpm_enqueue_message(port))
- hook_call_deferred(&ucpd_rx_enque_error_data,
- 0);
- }
-
- task_set_event(TASK_ID_UCPD,
- UCPD_EVT_RX_MSG);
-
- /* Send GoodCRC message (if required) */
- ucpd_send_good_crc(port, *rx_header);
- } else if (good_crc) {
- task_set_event(TASK_ID_UCPD,
- UCPD_EVT_RX_GOOD_CRC);
- ucpd_crc_id = PD_HEADER_ID(*rx_header);
- }
- } else {
- /* Rx message is complete, but there were bit errors */
- CPRINTS("ucpd: rx message error");
- }
- }
- /* Check for fault conditions */
- if (sr & STM32_UCPD_SR_RXHRSTDET) {
- /* hard reset received */
- pd_execute_hard_reset(port);
- task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE);
- hook_call_deferred(&ucpd_hard_reset_rx_log_data, 0);
- }
-
- /* Clear interrupts now that PD events have been set */
- STM32_UCPD_ICR(port) = sr;
-}
-DECLARE_IRQ(STM32_IRQ_UCPD1, stm32gx_ucpd1_irq, 1);
-
-#ifdef CONFIG_STM32G4_UCPD_DEBUG
-static char ctrl_names[][12] = {
- "rsvd",
- "GoodCRC",
- "Goto Min",
- "Accept",
- "Reject",
- "Ping",
- "PS_Rdy",
- "Get_SRC",
- "Get_SNK",
- "DR_Swap",
- "PR_Swap",
- "VCONN_Swp",
- "Wait",
- "Soft_Rst",
- "RSVD",
- "RSVD",
- "Not_Sup",
- "Get_SRC_Ext",
- "Get_Status",
-};
-
-static char data_names[][10] = {
- "RSVD",
- "SRC_CAP",
- "REQUEST",
- "BIST",
- "SINK_CAP",
- "BATTERY",
- "ALERT",
- "GET_INFO",
- "ENTER_USB",
- "RSVD",
- "RSVD",
- "RSVD",
- "RSVD",
- "RSVD",
- "RSVD",
- "VDM",
-};
-
-static void ucpd_dump_msg_log(void)
-{
- int i;
- int type;
- int len;
- int dir;
- uint16_t header;
- char *name;
-
-
- ccprintf("ucpd: msg_total = %d\n", msg_log_cnt);
- ccprintf("Idx\t Delta(us)\tDir\t Type\t\tLen\t s1 s2 PR\t DR\n");
- ccprintf("-----------------------------------------------------------"
- "-----------------\n");
-
- for (i = 0; i < msg_log_idx; i++) {
- uint32_t delta_ts = 0;
- int j;
-
- header = msg_log[i].header;
-
- if (header != 0xabcd) {
- type = PD_HEADER_TYPE(header);
- len = PD_HEADER_CNT(header);
- name = len ? data_names[type] : ctrl_names[type];
- dir = msg_log[i].dir;
- if (i) {
- delta_ts = msg_log[i].ts - msg_log[i-1].ts;
- }
-
- ccprintf("msg[%02d]: %08d\t %s\t %8s\t %02d\t %d %d\t"
- "%s\t %s",
- i,
- delta_ts,
- dir ? "Rx" : "Tx",
- name,
- len,
- msg_log[i].comp,
- msg_log[i].crc,
- PD_HEADER_PROLE(header) ? "SRC" : "SNK",
- PD_HEADER_DROLE(header) ? "DFP" : "UFP");
- len = MIN((len * 4) + 2, MSG_BUF_LEN);
- for (j = 0; j < len; j++)
- ccprintf(" %02x", msg_log[i].buf[j]);
- } else {
- if (i) {
- delta_ts = msg_log[i].ts - msg_log[i-1].ts;
- }
- ccprintf("msg[%02d]: %08d\t CC Voltage Change!",
- i, delta_ts);
- }
- ccprintf("\n");
- msleep(5);
- }
-}
-
-static void stm32gx_ucpd_set_cc_debug(int port, int cc_mask, int pull, int rp)
-{
- int cc_enable;
- uint32_t cr = STM32_UCPD_CR(port);
-
- /*
- * Only update ANASUBMODE if specified pull type is Rp.
- */
- if (pull == TYPEC_CC_RP) {
- cr &= ~STM32_UCPD_CR_ANASUBMODE_MASK;
- cr |= STM32_UCPD_CR_ANASUBMODE_VAL(UCPD_RP_TO_ANASUB(rp));
- }
-
- /*
- * Can't independently set pull value for CC1 from CC2. But, can
- * independently connect or disconnect pull for CC1 and CC2. Enable here
- * the CC lines specified by cc_mask. If desired pull is TYPEC_CC_OPEN,
- * then the CC lines specified in cc_mask will be disabled.
- */
- /* Get existing cc enable value */
- cc_enable = (cr & STM32_UCPD_CR_CCENABLE_MASK) >>
- STM32_UCPD_CR_CCENABLE_SHIFT;
- /* Apply cc_mask (enable CC line specified) */
- cc_enable |= cc_mask;
-
- /* Set ANAMODE if cc_pull is Rd */
- if (pull == TYPEC_CC_RD)
- cr |= STM32_UCPD_CR_ANAMODE;
- /* Clear ANAMODE if cc_pull is Rp */
- else if (pull == TYPEC_CC_RP)
- cr &= ~(STM32_UCPD_CR_ANAMODE);
- else if (pull == TYPEC_CC_OPEN)
- cc_enable &= ~cc_mask;
-
- /* The value for this field needs to be OR'd in */
- cr &= ~STM32_UCPD_CR_CCENABLE_MASK;
- cr |= STM32_UCPD_CR_CCENABLE_VAL(cc_enable);
- /* Update pull values */
- STM32_UCPD_CR(port) = cr;
- /* Display updated settings */
- ucpd_cc_status(port);
-}
-
-void ucpd_info(int port)
-{
- ucpd_cc_status(port);
- ccprintf("\trx_en\t = %d\n\tpol\t = %d\n",
- !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_PHYRXEN),
- !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_PHYCCSEL));
-
- /* Dump ucpd task state info */
- ccprintf("ucpd: tx_state = %s, tx_req = %02x, timeout_us = %d\n",
- ucpd_names[ucpd_tx_state], ucpd_tx_request, ucpd_timeout_us);
-
- ucpd_task_log_dump();
-}
-
-static int command_ucpd(int argc, char **argv)
-{
- uint32_t tx_data = 0;
- char *e;
- int val;
- int port = 0;
-
- if (argc < 2)
- return EC_ERROR_PARAM_COUNT;
-
- if (!strcasecmp(argv[1], "rst")) {
- /* Force reset of ucpd peripheral */
- stm32gx_ucpd_init(port);
- pd_execute_hard_reset(port);
- task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE);
- } else if (!strcasecmp(argv[1], "info")) {
- ucpd_info(port);
- } else if (!strcasecmp(argv[1], "bist")) {
- /* Need to initiate via DPM to have a timer */
- /* TODO(b/182861002): uncomment when Gingerbread has
- * full PD support landed.
- * pd_dpm_request(port, DPM_REQUEST_BIST_TX);
- */
- } else if (!strcasecmp(argv[1], "hard")) {
- stm32gx_ucpd_transmit(port, TCPCI_MSG_TX_HARD_RESET, 0,
- &tx_data);
- } else if (!strcasecmp(argv[1], "pol")) {
- if (argc < 3)
- return EC_ERROR_PARAM_COUNT;
- val = strtoi(argv[2], &e, 10);
- if (val > 1)
- val = 0;
- stm32gx_ucpd_set_polarity(port, val);
- stm32gx_ucpd_set_rx_enable(port, 1);
- ccprintf("ucpd: set pol = %d, PHYRXEN = 1\n", val);
- } else if (!strcasecmp(argv[1], "cc")) {
- int cc_mask;
- int pull;
- int rp = 0; /* needs to be initialized */
-
- if (argc < 3) {
- ucpd_cc_status(port);
- return EC_SUCCESS;
- }
- cc_mask = strtoi(argv[2], &e, 10);
- if (cc_mask < 1 || cc_mask > 3)
- return EC_ERROR_PARAM2;
- /* cc_mask has determines which cc setting to apply */
- if (!strcasecmp(argv[3], "rd")) {
- pull = TYPEC_CC_RD;
- } else if (!strcasecmp(argv[3], "rp")) {
- pull = TYPEC_CC_RP;
- rp = strtoi(argv[4], &e, 10);
- if (rp < 0 || rp > 2)
- return EC_ERROR_PARAM4;
- } else if (!strcasecmp(argv[3], "open")) {
- pull = TYPEC_CC_OPEN;
- } else {
- return EC_ERROR_PARAM3;
- }
- stm32gx_ucpd_set_cc_debug(port, cc_mask, pull, rp);
-
- } else if (!strcasecmp(argv[1], "log")) {
- if (argc < 3) {
- ucpd_dump_msg_log();
- } else if (!strcasecmp(argv[2], "clr")) {
- msg_log_cnt = 0;
- msg_log_idx = 0;
- }
- } else {
- return EC_ERROR_PARAM1;
- }
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(ucpd, command_ucpd,
- "[rst|info|bist|hard|pol <0|1>|cc xx <rd|rp|open>|log",
- "ucpd peripheral debug and control options");
-#endif