summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2014-04-26 11:51:23 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-04-29 03:11:12 +0000
commitb57137dcac703ef9428032332ddff470ab424906 (patch)
treeb607e50db02155de643d5f38059f376b81f54a49
parent6f21e3f5c1c61caac1535ec433210aac213c1c1c (diff)
downloadchrome-ec-b57137dcac703ef9428032332ddff470ab424906.tar.gz
stm32: pd: add config option to use SPI master for USB PD trasmit
Added config option CONFIG_USB_PD_TX_USES_SPI_MASTER which switches to use SPI master for PD transmit. The advantage of SPI master mode is at the end of the tranmission, we don't have to send any dummy 0 bits. When the option is set, the CPU_CLOCK must be set to 38.4MHz, so that the SPI master can generate the correct clock frequency. BUG=chrome-os-partner:28309 BRANCH=none TEST=Tested by connecting two fruitpies together across CC1. One fruitpie has been modified such that the MISO and MOSI lines are swapped and is running PD TX in SPI master mode with 38.4MHz clock, while the other is running PD TX in SPI slave mode. On EC console ran pd charger on one board and pd dev on other board. Verified that communication works with no errors. Ran for 10 minutes in each configuration. Change-Id: Ib24030d34d95d59f4ac6c2dae98bf7adda1ada01 Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/197215 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/fruitpie/usb_pd_config.h4
-rw-r--r--chip/stm32/clock-stm32f0.c56
-rw-r--r--chip/stm32/registers.h2
-rw-r--r--chip/stm32/usb_pd_phy.c54
-rw-r--r--include/config.h3
5 files changed, 99 insertions, 20 deletions
diff --git a/board/fruitpie/usb_pd_config.h b/board/fruitpie/usb_pd_config.h
index e810c4025a..59c9852719 100644
--- a/board/fruitpie/usb_pd_config.h
+++ b/board/fruitpie/usb_pd_config.h
@@ -34,10 +34,6 @@ static inline void spi_enable_clock(void)
/* triggers packet detection on comparator falling edge */
#define EXTI_XTSR STM32_EXTI_FTSR
-
-/* Clock divider for RX edges timings (2.4Mhz counter from 48Mhz clock) */
-#define RX_CLOCK_DIV (20 - 1)
-
/* the pins used for communication need to be hi-speed */
static inline void pd_set_pins_speed(void)
{
diff --git a/chip/stm32/clock-stm32f0.c b/chip/stm32/clock-stm32f0.c
index df09e5b67b..3f503caad0 100644
--- a/chip/stm32/clock-stm32f0.c
+++ b/chip/stm32/clock-stm32f0.c
@@ -17,22 +17,18 @@
/* use 48Mhz USB-synchronized High-speed oscillator */
#define HSI48_CLOCK 48000000
+/* use PLL at 38.4MHz as system clock. */
+#define PLL_CLOCK 38400000
+
int clock_get_freq(void)
{
- return HSI48_CLOCK;
+ return CPU_CLOCK;
}
void clock_enable_module(enum module_id module, int enable)
{
}
-/*
- * system closk is HSI48 = 48MHz,
- * no prescaler, no MCO, no PLL
- * USB clock = HSI48
- */
-BUILD_ASSERT(CPU_CLOCK == HSI48_CLOCK);
-
void clock_init(void)
{
/*
@@ -52,10 +48,54 @@ void clock_init(void)
while (!(STM32_RCC_CR2 & (1 << 17)))
;
}
+
+#if (CPU_CLOCK == HSI48_CLOCK)
+ /*
+ * HSI48 = 48MHz, no prescaler, no MCO, no PLL
+ * therefore PCLK = FCLK = SYSCLK = 48MHz
+ * USB uses HSI48 = 48MHz
+ */
+
/* switch SYSCLK to HSI48 */
STM32_RCC_CFGR = 0x00000003;
/* wait until the HSI48 is the clock source */
while ((STM32_RCC_CFGR & 0xc) != 0xc)
;
+
+#elif (CPU_CLOCK == PLL_CLOCK)
+ /*
+ * HSI48 = 48MHz, no prescalar, no MCO, with PLL *4/5 => 38.4MHz SYSCLK
+ * therefore PCLK = FCLK = SYSCLK = 38.4MHz
+ * USB uses HSI48 = 48MHz
+ */
+
+ /* If PLL is the clock source, PLL has already been set up. */
+ if ((STM32_RCC_CFGR & 0xc) == 0x8)
+ return;
+
+ /*
+ * Specify HSI48 clock as input clock to PLL and set PLL multiplier
+ * and divider.
+ */
+ STM32_RCC_CFGR = 0x00098000;
+ STM32_RCC_CFGR2 = 0x4;
+
+ /* Enable the PLL. */
+ STM32_RCC_CR |= 0x01000000;
+
+ /* Wait until PLL is ready. */
+ while (!(STM32_RCC_CR & 0x02000000))
+ ;
+
+ /* Switch SYSCLK to PLL. */
+ STM32_RCC_CFGR |= 0x2;
+
+ /* wait until the PLL is the clock source */
+ while ((STM32_RCC_CFGR & 0xc) != 0x8)
+ ;
+
+#else
+#error "CPU_CLOCK must be either 48MHz or 38.4MHz"
+#endif
}
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 0752416a2f..9195fd3f81 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -686,6 +686,8 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t;
#define STM32_SPI_CR1_BR_DIV64R (5 << 3)
#define STM32_SPI_CR1_BR_DIV4R (1 << 3)
#define STM32_SPI_CR1_MSTR (1 << 2)
+#define STM32_SPI_CR1_CPOL (1 << 1)
+#define STM32_SPI_CR1_CPHA (1 << 0)
#define STM32_SPI_CR2_RXNEIE (1 << 6)
#define STM32_SPI_CR2_SSOE (1 << 2)
#define STM32_SPI_CR2_RXDMAEN (1 << 0)
diff --git a/chip/stm32/usb_pd_phy.c b/chip/stm32/usb_pd_phy.c
index f325c1be05..a3e78ef0da 100644
--- a/chip/stm32/usb_pd_phy.c
+++ b/chip/stm32/usb_pd_phy.c
@@ -263,10 +263,8 @@ void pd_start_tx(void *ctxt, int bit_len)
/* update DMA configuration */
dma_prepare_tx(&dma_tx_option, DIV_ROUND_UP(bit_len, 8), ctxt);
- /* Flush data in write buffer so that DMA can get the lastest data */
+ /* Flush data in write buffer so that DMA can get the latest data */
asm volatile("dmb;");
- /* Kick off the DMA to send the data */
- dma_go(tx);
/* disable RX detection interrupt */
pd_rx_disable_monitoring();
@@ -277,8 +275,13 @@ void pd_start_tx(void *ctxt, int bit_len)
*/
pd_tx_enable();
+ /* Kick off the DMA to send the data */
+ dma_go(tx);
+
+#ifndef CONFIG_USB_PD_TX_USES_SPI_MASTER
/* Start counting at 300Khz*/
STM32_TIM_CR1(TIM_TX) |= 1;
+#endif
}
void pd_tx_done(void)
@@ -294,14 +297,31 @@ void pd_tx_done(void)
while (!(spi->sr & (1<<1)))
; /* wait for TXE == 1 */
#endif
+
while (spi->sr & (1<<7))
; /* wait for BSY == 0 */
+
+ /*
+ * At the end of transmitting, the last bit is guaranteed by the
+ * protocol to be low, and it is necessary that the TX line stay low
+ * until pd_tx_disable().
+ *
+ * When using SPI slave mode for TX, this is done by writing out dummy
+ * 0 byte at end.
+ * When using SPI master mode, the CPOL and CPHA are set high, which
+ * means that after the last bit is transmitted there are no more
+ * clock edges. Hopefully, this is sufficient to guarantee that the
+ * MOSI line does not change before pd_tx_disable().
+ */
+#ifndef CONFIG_USB_PD_TX_USES_SPI_MASTER
/* ensure that we are not pushing out junk */
*(uint8_t *)&spi->dr = 0;
/* Stop counting */
STM32_TIM_CR1(TIM_TX) &= ~1;
- /* clear tranfer flag */
+#endif
+ /* clear transfer flag */
dma_clear_isr(DMAC_SPI_TX);
+
/* put TX pins and reference in Hi-Z */
pd_tx_disable();
}
@@ -380,14 +400,31 @@ void *pd_hw_init(void)
/* Enable Tx DMA for our first transaction */
spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_DATASIZE(8);
- /* Enable the salve SPI: LSB first, force NSS, TX only */
+#ifdef CONFIG_USB_PD_TX_USES_SPI_MASTER
+ /*
+ * Enable the master SPI: LSB first, force NSS, TX only, CPOL and CPHA
+ * high.
+ */
+ spi->cr1 = STM32_SPI_CR1_LSBFIRST | STM32_SPI_CR1_BIDIMODE
+ | STM32_SPI_CR1_SSM | STM32_SPI_CR1_SSI
+ | STM32_SPI_CR1_BIDIOE | STM32_SPI_CR1_MSTR
+ | STM32_SPI_CR1_BR_DIV64R | STM32_SPI_CR1_SPE
+ | STM32_SPI_CR1_CPOL | STM32_SPI_CR1_CPHA;
+
+#if CPU_CLOCK != 38400000
+#error "CPU_CLOCK must be 38.4MHz to use SPI master for USB PD Tx"
+#endif
+#else
+ /* Enable the slave SPI: LSB first, force NSS, TX only */
spi->cr1 = STM32_SPI_CR1_SPE | STM32_SPI_CR1_LSBFIRST
| STM32_SPI_CR1_SSM | STM32_SPI_CR1_BIDIMODE
| STM32_SPI_CR1_BIDIOE;
+#endif
/* configure TX DMA */
dma_prepare_tx(&dma_tx_option, PD_MAX_RAW_SIZE, raw_samples);
+#ifndef CONFIG_USB_PD_TX_USES_SPI_MASTER
/* --- set the TX timer with updates at 600KHz (BMC frequency) --- */
__hw_timer_enable_clock(TIM_TX, 1);
/* Timer configuration */
@@ -406,6 +443,7 @@ void *pd_hw_init(void)
STM32_TIM_PSC(TIM_TX) = 0;
/* Reload the pre-scaler and reset the counter */
STM32_TIM_EGR(TIM_TX) = 0x0001;
+#endif
/* --- set counter for RX timing : 2.4Mhz rate, free-running --- */
__hw_timer_enable_clock(TIM_RX, 1);
@@ -415,9 +453,9 @@ void *pd_hw_init(void)
STM32_TIM_DIER(TIM_RX) = 0x0000;
/* Auto-reload value : 16-bit free running counter */
STM32_TIM_ARR(TIM_RX) = 0xFFFF;
+
/* Timeout for message receive : 2.7ms */
- STM32_TIM_CCR2(TIM_RX) = clock_get_freq() / (RX_CLOCK_DIV + 1)
- * 27 / 10000 /* 2.7 ms */;
+ STM32_TIM_CCR2(TIM_RX) = 2400000 * 27 / 10000;
/* Timer ICx input configuration */
#if TIM_CCR_IDX == 1
STM32_TIM_CCMR1(TIM_RX) = TIM_CCR_CS << 0;
@@ -430,7 +468,7 @@ void *pd_hw_init(void)
/* configure DMA request on CCRx update */
STM32_TIM_DIER(TIM_RX) |= 1 << (8 + TIM_CCR_IDX); /* CCxDE */;
/* set prescaler to /26 (F=1.2Mhz, T=0.8us) */
- STM32_TIM_PSC(TIM_RX) = RX_CLOCK_DIV;
+ STM32_TIM_PSC(TIM_RX) = (clock_get_freq() / 2400000) - 1;
/* Reload the pre-scaler and reset the counter */
STM32_TIM_EGR(TIM_RX) = 0x0001 | (1 << TIM_CCR_IDX) /* clear CCRx */;
/* clear update event from reloading */
diff --git a/include/config.h b/include/config.h
index 56d18d26bb..ae51b24c26 100644
--- a/include/config.h
+++ b/include/config.h
@@ -843,6 +843,9 @@
/*****************************************************************************/
+/* USB PD transmit uses SPI master */
+#undef CONFIG_USB_PD_TX_USES_SPI_MASTER
+
/* Support simple control of power to the device's USB ports */
#undef CONFIG_USB_PORT_POWER_DUMB