summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/registers-stm32g4.h193
-rw-r--r--chip/stm32/ucpd-stm32gx.c277
-rw-r--r--chip/stm32/ucpd-stm32gx.h111
4 files changed, 582 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index a951c0eb21..f2d5720961 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -98,3 +98,4 @@ chip-$(CONFIG_USB_ISOCHRONOUS)+=usb_isochronous.o
chip-$(CONFIG_USB_PD_TCPC)+=usb_pd_phy.o
chip-$(CONFIG_USB_SPI)+=usb_spi.o
endif
+chip-$(CONFIG_USB_PD_TCPM_STM32GX)+=ucpd-stm32gx.o
diff --git a/chip/stm32/registers-stm32g4.h b/chip/stm32/registers-stm32g4.h
index 6e519bc9fb..4fe97e4c29 100644
--- a/chip/stm32/registers-stm32g4.h
+++ b/chip/stm32/registers-stm32g4.h
@@ -252,6 +252,195 @@
/* Register definitions */
+/* --- UCPD --- */
+#define STM32_UCPD_REG(port, offset) \
+ REG32(((STM32_UCPD1_BASE + ((port) * 0x400)) + (offset)))
+
+#define STM32_UCPD_CFGR1(port) STM32_UCPD_REG(port, 0x00)
+#define STM32_UCPD_CFGR2(port) STM32_UCPD_REG(port, 0x04)
+#define STM32_UCPD_CR(port) STM32_UCPD_REG(port, 0x0c)
+#define STM32_UCPD_IMR(port) STM32_UCPD_REG(port, 0x10)
+#define STM32_UCPD_SR(port) STM32_UCPD_REG(port, 0x14)
+#define STM32_UCPD_ICR(port) STM32_UCPD_REG(port, 0x18)
+#define STM32_UCPD_TX_ORDSETR(port) STM32_UCPD_REG(port, 0x1c)
+#define STM32_UCPD_TX_PAYSZR(port) STM32_UCPD_REG(port, 0x20)
+#define STM32_UCPD_TXDR(port) STM32_UCPD_REG(port, 0x24)
+#define STM32_UCPD_RX_ORDSETR(port) STM32_UCPD_REG(port, 0x28)
+#define STM32_UCPD_RX_PAYSZR(port) STM32_UCPD_REG(port, 0x2c)
+#define STM32_UCPD_RXDR(port) STM32_UCPD_REG(port, 0x30)
+#define STM32_UCPD_RX_ORDEXTR1(port) STM32_UCPD_REG(port, 0x34)
+#define STM32_UCPD_RX_ORDEXTR2(port) STM32_UCPD_REG(port, 0x38)
+
+/* --- UCPD CFGR1 Bit Definitions --- */
+#define STM32_UCPD_CFGR1_HBITCLKD_SHIFT 0
+#define STM32_UCPD_CFGR1_HBITCLKD_MASK ((0x3f) << \
+ (STM32_UCPD_CFGR1_HBITCLKD_SHIFT))
+#define STM32_UCPD_CFGR1_HBITCLKD_VAL(x) ((x) << \
+ STM32_UCPD_CFGR1_HBITCLKD_SHIFT)
+#define STM32_UCPD_CFGR1_IFRGAP_SHIFT 6
+#define STM32_UCPD_CFGR1_IFRGAP_MASK ((0x1f) << \
+ (STM32_UCPD_CFGR1_IFRGAP_SHIFT))
+#define STM32_UCPD_CFGR1_IFRGAP_VAL(x) ((x) << \
+ STM32_UCPD_CFGR1_IFRGAP_SHIFT)
+#define STM32_UCPD_CFGR1_TRANSWIN_SHIFT 11
+#define STM32_UCPD_CFGR1_TRANSWIN_MASK ((0x1f) << \
+ (STM32_UCPD_CFGR1_TRANSWIN_SHIFT))
+#define STM32_UCPD_CFGR1_TRANSWIN_VAL(x) ((x) << \
+ STM32_UCPD_CFGR1_TRANSWIN_SHIFT)
+#define STM32_UCPD_CFGR1_PSC_CLK_SHIFT 17
+#define STM32_UCPD_CFGR1_PSC_CLK_MASK ((0x7) << \
+ STM32_UCPD_CFGR1_PSC_CLK_SHIFT)
+#define STM32_UCPD_CFGR1_PSC_CLK_VAL(x) ((x) << \
+ STM32_UCPD_CFGR1_PSC_CLK_SHIFT)
+#define STM32_UCPD_CFGR1_RXORDSETEN_SHIFT 20
+#define STM32_UCPD_CFGR1_RXORDSETEN_MASK ((0x1ff) << \
+ STM32_UCPD_CFGR1_RXORDSETEN_SHIFT)
+#define STM32_UCPD_CFGR1_RXORDSETEN_VAL(x) ((x) << \
+ STM32_UCPD_CFGR1_RXORDSETEN_SHIFT)
+#define STM32_UCPD_CFGR1_TXDMAEN BIT(29)
+#define STM32_UCPD_CFGR1_RXDMAEN BIT(30)
+#define STM32_UCPD_CFGR1_UCPDEN BIT(31)
+
+/* --- UCPD CFGR2 Bit Definitions --- */
+#define STM32_UCPD_CFGR2_RXFILTDIS BIT(0)
+#define STM32_UCPD_CFGR2_RXFILT2N3 BIT(1)
+#define STM32_UCPD_CFGR2_FORCECLK BIT(2)
+#define STM32_UCPD_CFGR2_WUPEN BIT(3)
+
+/* --- UCPD CR Bit Definitions --- */
+#define STM32_UCPD_CR_TXMODE_SHIFT 0
+#define STM32_UCPD_CR_TXMODE_MASK ((0x3) << \
+ (STM32_UCPD_CR_TXMODE_SHIFT))
+#define STM32_UCPD_CR_TXMODE_VAL(x) ((x) << STM32_UCPD_CR_TXMODE_SHIFT)
+#define STM32_UCPD_CR_TXSEND BIT(2)
+#define STM32_UCPD_CR_TXHRST BIT(3)
+#define STM32_UCPD_CR_RXMODE BIT(4)
+#define STM32_UCPD_CR_PHYRXEN BIT(5)
+#define STM32_UCPD_CR_PHYCCSEL BIT(6)
+#define STM32_UCPD_CR_ANASUBMODE_SHIFT 7
+#define STM32_UCPD_CR_ANASUBMODE_MASK ((0x3) << \
+ (STM32_UCPD_CR_ANASUBMODE_SHIFT))
+#define STM32_UCPD_CR_ANASUBMODE_VAL(x) ((x) << \
+ STM32_UCPD_CR_ANASUBMODE_SHIFT)
+#define STM32_UCPD_CR_ANAMODE BIT(9)
+#define STM32_UCPD_CR_CCENABLE_SHIFT 10
+#define STM32_UCPD_CR_CCENABLE_MASK ((0x3) << \
+ (STM32_UCPD_CR_CCENABLE_SHIFT))
+#define STM32_UCPD_CR_CCENABLE_VAL(x) ((x) << \
+ STM32_UCPD_CR_CCENABLE_SHIFT)
+#define STM32_UCPD_CR_FRSRXEN BIT(16)
+#define STM32_UCPD_CR_FRSTX BIT(17)
+#define STM32_UCPD_CR_RDCH BIT(18)
+#define STM32_UCPD_CR_CC1TCDIS BIT(20)
+#define STM32_UCPD_CR_CC2TCDIS BIT(21)
+
+/* --- UCPD IMR Bit Definitions --- */
+#define STM32_UCPD_IMR_TXISIE BIT(0)
+#define STM32_UCPD_IMR_TXMSGDISCIE BIT(1)
+#define STM32_UCPD_IMR_TXMSGSENTIE BIT(2)
+#define STM32_UCPD_IMR_TXMSGABTIE BIT(3)
+#define STM32_UCPD_IMR_HRSTDISCIE BIT(4)
+#define STM32_UCPD_IMR_HRSTSENTIE BIT(5)
+#define STM32_UCPD_IMR_TXUNDIE BIT(6)
+#define STM32_UCPD_IMR_RXNEIE BIT(8)
+#define STM32_UCPD_IMR_RXORDDETIE BIT(9)
+#define STM32_UCPD_IMR_RXHRSTDETIE BIT(10)
+#define STM32_UCPD_IMR_RXOVRIE BIT(11)
+#define STM32_UCPD_IMR_RXMSGENDIE BIT(12)
+#define STM32_UCPD_IMR_TYPECEVT1IE BIT(14)
+#define STM32_UCPD_IMR_TYPECEVT2IE BIT(15)
+#define STM32_UCPD_IMR_FRSEVTIE BIT(20)
+
+/* --- UCPD SR Bit Definitions --- */
+#define STM32_UCPD_SR_TXIS BIT(0)
+#define STM32_UCPD_SR_TXMSGDISC BIT(1)
+#define STM32_UCPD_SR_TXMSGSENT BIT(2)
+#define STM32_UCPD_SR_TXMSGABT BIT(3)
+#define STM32_UCPD_SR_HRSTDISC BIT(4)
+#define STM32_UCPD_SR_HRSTSENT BIT(5)
+#define STM32_UCPD_SR_TXUND BIT(6)
+#define STM32_UCPD_SR_RXNE BIT(8)
+#define STM32_UCPD_SR_RXORDDET BIT(9)
+#define STM32_UCPD_SR_RXHRSTDET BIT(10)
+#define STM32_UCPD_SR_RXOVR BIT(11)
+#define STM32_UCPD_SR_RXMSGEND BIT(12)
+#define STM32_UCPD_SR_RXERR BIT(13)
+#define STM32_UCPD_SR_TYPECEVT1 BIT(14)
+#define STM32_UCPD_SR_TYPECEVT2 BIT(15)
+#define STM32_UCPD_SR_VSTATE_CC1_SHIFT 16
+#define STM32_UCPD_SR_VSTATE_CC1_MASK ((0x3) << \
+ (STM32_UCPD_SR_VSTATE_CC1_SHIFT))
+#define STM32_UCPD_SR_VSTATE_CC1_VAL(x) ((x) << \
+ STM32_UCPD_SR_VSTATE_CC1_SHIFT)
+#define STM32_UCPD_SR_VSTATE_CC2_SHIFT 18
+#define STM32_UCPD_SR_VSTATE_CC2_MASK ((0x3) << \
+ (STM32_UCPD_SR_VSTATE_CC2_SHIFT))
+#define STM32_UCPD_SR_VSTATE_CC2_VAL(x) ((x) << \
+ STM32_UCPD_SR_VSTATE_CC2_SHIFT)
+#define STM32_UCPD_SR_FRSEVT BIT(20)
+
+#define STM32_UCPD_SR_VSTATE_OPEN 3
+#define STM32_UCPD_SR_VSTATE_RA 0
+
+/* --- UCPD ICR Bit Definitions --- */
+#define STM32_UCPD_ICR_TXMSGDISCCF BIT(1)
+#define STM32_UCPD_ICR_TXMSGSENTCF BIT(2)
+#define STM32_UCPD_ICR_TXMSGABTCF BIT(3)
+#define STM32_UCPD_ICR_HRSTDISCCF BIT(4)
+#define STM32_UCPD_ICR_HRSTSENTCF BIT(5)
+#define STM32_UCPD_ICR_TXUNDCF BIT(6)
+#define STM32_UCPD_ICR_RXORDDETCF BIT(9)
+#define STM32_UCPD_ICR_RXHRSTDETCF BIT(10)
+#define STM32_UCPD_ICR_RXOVRCF BIT(11)
+#define STM32_UCPD_ICR_RXMSGENDCF BIT(12)
+#define STM32_UCPD_ICR_TYPECEVT1CF BIT(14)
+#define STM32_UCPD_ICR_TYPECEVT2CF BIT(15)
+#define STM32_UCPD_ICR_FRSEVTCF BIT(20)
+
+
+/* --- UCPD TX_ORDSETR Bit Definitions --- */
+#define STM32_UCPD_TX_ORDSETR_SHIFT 0
+#define STM32_UCPD_TX_ORDSETR_MASK ((0xfffff) << \
+ (STM32_UCPD_TX_ORDSETR_SHIFT))
+#define STM32_UCPD_TX_ORDSETR_VAL(x) ((x) << STM32_UCPD_TX_ORDSETR_SHIFT)
+
+/* --- UCPD TX_PAYSZR Bit Definitions --- */
+#define STM32_UCPD_TX_PAYSZR_SHIFT 0
+#define STM32_UCPD_TX_PAYSZR_MASK ((0x3ff) << \
+ (STM32_UCPD_TX_PAYSZR_SHIFT))
+#define STM32_UCPD_TX_PAYSZR_VAL(x) ((x) << STM32_UCPD_TX_PAYSZR_SHIFT)
+
+/* --- UCPD TXDR Bit Definitions --- */
+#define STM32_UCPD_TXDR_SHIFT 0
+#define STM32_UCPD_TXDR_MASK ((0xff) << \
+ (STM32_UCPD_TXDR_SHIFT))
+#define STM32_UCPD_TXDR_VAL(x) ((x) << STM32_UCPD_TXDR_SHIFT)
+
+/* --- UCPD RX_ORDSETR Bit Definitions --- */
+#define STM32_UCPD_RXORDSETR_SHIFT 0
+#define STM32_UCPD_RXORDSETR_MASK ((0x7) << \
+ (STM32_UCPD_RXORDSETR_SHIFT))
+#define STM32_UCPD_RXORDSETR_VAL(x) ((x) << STM32_UCPD_RXORDSETR_SHIFT)
+#define STM32_UCPD_RXSOP3OF4 BIT(3)
+#define STM32_UCPD_RXSOPKINVALID_SHIFT 4
+#define STM32_UCPD_RXSOPKINVALID_MASK ((0x7) << \
+ (STM32_UCPD_RXSOPKINVALID_SHIFT))
+#define STM32_UCPD_RXSOPKINVALID_VAL(x) ((x) << \
+ STM32_UCPD_RXSOPKINVALID_SHIFT)
+
+/* --- UCPD RX_PAYSZR Bit Definitions --- */
+#define STM32_UCPD_RX_PAYSZR_SHIFT 0
+#define STM32_UCPD_RX_PAYSZR_MASK ((0x3ff) << \
+ (STM32_UCPD_RX_PAYSZR_SHIFT))
+#define STM32_UCPD_RX_PAYSZR_VAL(x) ((x) << STM32_UCPD_RX_PAYSZR_SHIFT)
+
+/* --- UCPD TXDR Bit Definitions --- */
+#define STM32_UCPD_RXDR_SHIFT 0
+#define STM32_UCPD_RXDR_MASK ((0xff) << \
+ (STM32_UCPD_RXDR_SHIFT))
+#define STM32_UCPD_RXDR_VAL(x) ((x) << STM32_UCPD_RXDR_SHIFT)
+
+
/* --- USART --- */
#define STM32_USART_CR1(base) STM32_USART_REG(base, 0x00)
#define STM32_USART_CR2(base) STM32_USART_REG(base, 0x04)
@@ -508,6 +697,7 @@
/* --- RCC APB1ENR2 Bit Definitions --- */
#define STM32_RCC_APB1ENR2_LPUART1EN BIT(0)
#define STM32_RCC_APB1ENR2_I2C4EN BIT(1)
+#define STM32_RCC_APB1ENR2_UPCD1EN BIT(8)
/* --- RCC APB2ENR Bit Definitions --- */
#define STM32_RCC_APB2ENR_SYSCFGEN BIT(0)
@@ -591,6 +781,9 @@
#define STM32_PWR_RESET_CAUSE_CLR STM32_PWR_SCR
#define RESET_CAUSE_SBF_CLR STM32_PWR_SCR_CSBF
+#define STM32_PWR_CR3_UCPD1_STDBY BIT(13)
+#define STM32_PWR_CR3_UCPD1_DBDIS BIT(14)
+
/* --- System Config Registers --- */
#define STM32_SYSCFG_MEMRMP REG32(STM32_SYSCFG_BASE + 0x00)
#define STM32_SYSCFG_PMC REG32(STM32_SYSCFG_BASE + 0x04)
diff --git a/chip/stm32/ucpd-stm32gx.c b/chip/stm32/ucpd-stm32gx.c
new file mode 100644
index 0000000000..1355d9f042
--- /dev/null
+++ b/chip/stm32/ucpd-stm32gx.c
@@ -0,0 +1,277 @@
+/* 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 "common.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "registers.h"
+#include "task.h"
+#include "ucpd-stm32gx.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "util.h"
+
+
+/*
+ * UCPD is fed directly from HSI which is @ 16MHz. The ucpd_clk goes to
+ * a prescaler who's output feeds the 'half-bit' divider which is used
+ * to generate clock for delay counters and BMC Rx/Tx blocks. The rx is
+ * designed to work in freq ranges of 6 <--> 18 MHz, however recommended
+ * range is 9 <--> 18 MHz.
+ *
+ * ------- @ 16 MHz --------- @ ~600 kHz -------------
+ * HSI ---->| /psc |-------->| /hbit |--------------->| trans_cnt |
+ * ------- --------- | -------------
+ * | -------------
+ * |---------->| ifrgap_cnt|
+ * -------------
+ * Requirements:
+ * 1. hbit_clk ~= 600 kHz: 16 MHz / 600 kHz = 26.67
+ * 2. tTransitionWindow - 12 to 20 uSec
+ * 3. tInterframGap - uSec
+ *
+ * hbit_clk = HSI_clk / 26 = 615,385 kHz = 1.625 uSec period
+ * tTransitionWindow = 1.625 uS * 8 = 13 uS
+ * tInterFrameGap = 1.625 uS * 17 = 27.625 uS
+ */
+#define UCPD_PSC_DIV 1
+#define UCPD_HBIT_DIV 26
+#define UCPD_TRANSWIN_HBIT_CNT 8
+#define UCPD_IFRGAP_HBIT_CNT 17
+
+#define UCPD_ANASUB_TO_RP(r) ((r - 1) & 0x3)
+#define UCPD_RP_TO_ANASUB(r) ((r + 1) & 0x3)
+
+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);
+}
+
+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);
+
+ if (sr & (STM32_UCPD_SR_TYPECEVT1 | STM32_UCPD_SR_TYPECEVT2)) {
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0);
+ }
+ /* Clear interrupts now that PD events have been set */
+ STM32_UCPD_ICR(port) = sr;
+}
+DECLARE_IRQ(STM32_IRQ_UCPD1, stm32gx_ucpd1_irq, 1);
+
+int stm32gx_ucpd_init(int port)
+{
+ uint32_t cfgr1_reg;
+
+ /*
+ * 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;
+
+ /*
+ * 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;
+
+ /* 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;
+
+ /* 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;
+}
+
+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 by default */
+ 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 |= STM32_UCPD_CR_CCENABLE_MASK;
+ }
+
+ /* Update pull values */
+ STM32_UCPD_CR(port) = cr;
+
+ /* TODO(b/): Should this return error if cc_pull == Ra */
+ 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;
+
+ return EC_SUCCESS;
+}
+
diff --git a/chip/stm32/ucpd-stm32gx.h b/chip/stm32/ucpd-stm32gx.h
new file mode 100644
index 0000000000..f0a73cb29e
--- /dev/null
+++ b/chip/stm32/ucpd-stm32gx.h
@@ -0,0 +1,111 @@
+/* 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.
+ */
+#ifndef __CROS_EC_UCPD_STM32GX_H
+#define __CROS_EC_UCPD_STM32GX_H
+
+/* STM32 UCPD driver for Chrome EC */
+
+#include "usb_pd_tcpm.h"
+
+/*
+ * K-codes and ordered set defines. These codes and sets are used to encode
+ * which type of USB-PD message is being sent. This information can be found in
+ * the USB-PD spec section 5.4 - 5.6. This info is also included in the STM32G4
+ * TRM (RM0440) 45.4.3
+ */
+#define UCPD_SYNC1 0x18u
+#define UCPD_SYNC2 0x11u
+#define UCPD_SYNC3 0x06u
+#define UCPD_RST1 0x07u
+#define UCPD_RST2 0x19u
+#define UCPD_EOP 0x0Du
+
+enum ucpd_tx_ordset {
+ TX_ORDERSET_SOP = (UCPD_SYNC1 |
+ (UCPD_SYNC1<<5u) |
+ (UCPD_SYNC1<<10u) |
+ (UCPD_SYNC2<<15u)),
+ TX_ORDERSET_SOP1 = (UCPD_SYNC1 |
+ (UCPD_SYNC1<<5u) |
+ (UCPD_SYNC3<<10u) |
+ (UCPD_SYNC3<<15u)),
+ TX_ORDERSET_SOP2 = (UCPD_SYNC1 |
+ (UCPD_SYNC3<<5u) |
+ (UCPD_SYNC1<<10u) |
+ (UCPD_SYNC3<<15u)),
+ TX_ORDERSET_HARD_RESET = (UCPD_RST1 |
+ (UCPD_RST1<<5u) |
+ (UCPD_RST1<<10u) |
+ (UCPD_RST2<<15u)),
+ TX_ORDERSET_CABLE_RESET =
+ (UCPD_RST1 |
+ (UCPD_SYNC1<<5u) |
+ (UCPD_RST1<<10u) |
+ (UCPD_SYNC3<<15u)),
+ TX_ORDERSET_SOP1_DEBUG = (UCPD_SYNC1 |
+ (UCPD_RST2<<5u) |
+ (UCPD_RST2<<10u) |
+ (UCPD_SYNC3<<15u)),
+ TX_ORDERSET_SOP2_DEBUG = (UCPD_SYNC1 |
+ (UCPD_RST2<<5u) |
+ (UCPD_SYNC3<<10u) |
+ (UCPD_SYNC2<<15u)),
+};
+
+/**
+ * STM32Gx UCPD implementation of tcpci .init method
+ *
+ * @param usbc_port -> USB-C Port number
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_init(int usbc_port);
+
+/**
+ * STM32Gx UCPD implementation of tcpci .release method
+ *
+ * @param usbc_port -> USB-C Port number
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_release(int usbc_port);
+
+/**
+ * STM32Gx UCPD implementation of tcpci .get_cc method
+ *
+ * @param usbc_port -> USB-C Port number
+ * @param *cc1 -> pointer to cc1 result
+ * @param *cc2 -> pointer to cc2 result
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_get_cc(int usbc_port, enum tcpc_cc_voltage_status *cc1,
+ enum tcpc_cc_voltage_status *cc2);
+
+/**
+ * STM32Gx equivalent for TCPCI role_control register
+ *
+ * @param usbc_port -> USB-C Port number
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_get_role_control(int usbc_port);
+
+/**
+ * STM32Gx UCPD implementation of tcpci .set_cc method
+ *
+ * @param usbc_port -> USB-C Port number
+ * @param cc_pull -> Rp or Rd selection
+ * @param rp -> value of Rp (if cc_pull == Rp)
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_set_cc(int usbc_port, int cc_pull, int rp);
+
+/**
+ * STM32Gx UCPD implementation of tcpci .set_cc method
+ *
+ * @param usbc_port -> USB-C Port number
+ * @param polarity -> CC1 or CC2 selection
+ * @return EC_SUCCESS
+ */
+int stm32gx_ucpd_set_polarity(int usbc_port, enum tcpc_cc_polarity polarity);
+
+#endif /* __CROS_EC_UCPD_STM32GX_H */