pd: clean up beg/end transitions of PD comms

Fix the beginning and end of BMC PD communication:
- Initial transmission within 1us of taking control of CC line
- CC line released between 1us and 23us after last edge
- If final bit is a 0, then add two 1 bits to the end
- No garbage after the final bit

BUG=chrome-os-partner:30132
BRANCH=none
TEST=tested with a fruitpie, samus, and zinger.
verified timing on scope.

Change-Id: Ie45695eb367a7554cf5d5b76b6fbdf1e3fc85d29
Signed-off-by: Alec Berg <alecaberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/206453
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
Alec Berg
2014-07-01 11:31:41 -07:00
committed by chrome-internal-fetch
parent 6473343075
commit 9ba7bd4284
6 changed files with 175 additions and 53 deletions

View File

@@ -44,16 +44,25 @@ static inline void pd_set_pins_speed(void)
STM32_GPIO_OSPEEDR(GPIO_B) |= 0x000C0000;
}
/* Reset SPI peripheral used for TX */
static inline void pd_tx_spi_reset(void)
{
/* Reset SPI1 */
STM32_RCC_APB2RSTR |= (1 << 12);
STM32_RCC_APB2RSTR &= ~(1 << 12);
}
/* Drive the CC line from the TX block */
static inline void pd_tx_enable(int polarity)
{
/* set the low level reference */
gpio_set_level(polarity ? GPIO_PD_CC2_TX_EN : GPIO_PD_CC1_TX_EN, 0);
/* put SPI function on TX pin */
if (polarity) /* PB4 is SPI1 MISO */
gpio_set_alternate_function(GPIO_B, 0x0010, 0);
else /* PA6 is SPI1 MISO */
gpio_set_alternate_function(GPIO_A, 0x0040, 0);
/* set the low level reference */
gpio_set_level(polarity ? GPIO_PD_CC2_TX_EN : GPIO_PD_CC1_TX_EN, 0);
}
/* Put the TX driver in Hi-Z state */

View File

@@ -43,12 +43,21 @@ static inline void pd_set_pins_speed(void)
STM32_GPIO_OSPEEDR(GPIO_B) |= 0x000C0000;
}
/* Reset SPI peripheral used for TX */
static inline void pd_tx_spi_reset(void)
{
/* Reset SPI2 */
STM32_RCC_APB1RSTR |= (1 << 14);
STM32_RCC_APB1RSTR &= ~(1 << 14);
}
/* Drive the CC line from the TX block */
static inline void pd_tx_enable(int polarity)
{
gpio_set_level(GPIO_PD_TX_EN, 1);
/* TX_DATA on PB14 is now connected to SPI2 */
gpio_set_alternate_function(GPIO_B, 0x4000, 0);
gpio_set_level(GPIO_PD_TX_EN, 1);
}
/* Put the TX driver in Hi-Z state */

View File

@@ -42,18 +42,26 @@ static inline void pd_set_pins_speed(void)
STM32_GPIO_OSPEEDR(GPIO_B) |= 0x0000000C;
}
/* Reset SPI peripheral used for TX */
static inline void pd_tx_spi_reset(void)
{
/* Reset SPI1 */
STM32_RCC_APB2RSTR |= (1 << 12);
STM32_RCC_APB2RSTR &= ~(1 << 12);
}
/* Drive the CC line from the TX block */
static inline void pd_tx_enable(int polarity)
{
/* set the low level reference */
gpio_set_level(polarity ? GPIO_USB_C0_CC2_TX_EN :
GPIO_USB_C0_CC1_TX_EN, 1);
/* put SPI function on TX pin */
if (polarity) /* PE14 is SPI1 MISO */
gpio_set_alternate_function(GPIO_E, 0x4000, 1);
else /* PB4 is SPI1 MISO */
gpio_set_alternate_function(GPIO_B, 0x0010, 0);
/* set the low level reference */
gpio_set_level(polarity ? GPIO_USB_C0_CC2_TX_EN :
GPIO_USB_C0_CC1_TX_EN, 1);
}
/* Put the TX driver in Hi-Z state */

View File

@@ -43,13 +43,21 @@ static inline void pd_set_pins_speed(void)
/* Already done in hardware_init() */
}
/* Reset SPI peripheral used for TX */
static inline void pd_tx_spi_reset(void)
{
/* Reset SPI1 */
STM32_RCC_APB2RSTR |= (1 << 12);
STM32_RCC_APB2RSTR &= ~(1 << 12);
}
/* Drive the CC line from the TX block */
static inline void pd_tx_enable(int polarity)
{
/* Drive TX GND on PA4 */
STM32_GPIO_BSRR(GPIO_A) = 1 << (4 + 16 /* Reset */);
/* Drive SPI MISO on PA6 by putting it in AF mode */
STM32_GPIO_MODER(GPIO_A) |= 0x2 << (2*6);
/* Drive TX GND on PA4 */
STM32_GPIO_BSRR(GPIO_A) = 1 << (4 + 16 /* Reset */);
}
/* Put the TX driver in Hi-Z state */

View File

@@ -27,7 +27,16 @@ static task_id_t id[STM32_DMAC_COUNT];
*/
static int dma_get_irq(enum dma_channel channel)
{
#ifdef CHIP_FAMILY_STM32F0
if (channel == STM32_DMAC_CH1)
return STM32_IRQ_DMA_CHANNEL_1;
return channel > STM32_DMAC_CH3 ?
STM32_IRQ_DMA_CHANNEL_4_7 :
STM32_IRQ_DMA_CHANNEL_2_3;
#else
return STM32_IRQ_DMA_CHANNEL_1 + channel;
#endif
}
/*
@@ -241,7 +250,47 @@ void dma_clear_isr(enum dma_channel channel)
dma->ifcr |= STM32_DMA_ISR_ALL(channel);
}
#ifndef CHIP_FAMILY_STM32F0
#ifdef CHIP_FAMILY_STM32F0
void dma_event_interrupt_channel_1(void)
{
if (STM32_DMA1_REGS->isr & STM32_DMA_ISR_TCIF(STM32_DMAC_CH1)) {
dma_clear_isr(STM32_DMAC_CH1);
if (id[STM32_DMAC_CH1] != TASK_ID_INVALID)
task_wake(id[STM32_DMAC_CH1]);
}
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_1, dma_event_interrupt_channel_1, 3);
void dma_event_interrupt_channel_2_3(void)
{
int i;
for (i = STM32_DMAC_CH2; i <= STM32_DMAC_CH3; i++) {
if (STM32_DMA1_REGS->isr & STM32_DMA_ISR_TCIF(i)) {
dma_clear_isr(i);
if (id[i] != TASK_ID_INVALID)
task_wake(id[i]);
}
}
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_2_3, dma_event_interrupt_channel_2_3, 3);
void dma_event_interrupt_channel_4_7(void)
{
int i;
for (i = STM32_DMAC_CH4; i <= STM32_DMAC_CH7; i++) {
if (STM32_DMA1_REGS->isr & STM32_DMA_ISR_TCIF(i)) {
dma_clear_isr(i);
if (id[i] != TASK_ID_INVALID)
task_wake(id[i]);
}
}
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_4_7, dma_event_interrupt_channel_4_7, 3);
#else /* !CHIP_FAMILY_STM32F0 */
void dma_event_interrupt_channel_4(void)
{
dma_clear_isr(STM32_DMAC_CH4);
@@ -273,4 +322,4 @@ void dma_event_interrupt_channel_7(void)
task_wake(id[STM32_DMAC_CH7]);
}
DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_7, dma_event_interrupt_channel_7, 3);
#endif /* !CHIP_FAMILY_STM32F0 */
#endif /* CHIP_FAMILY_STM32F0 */

View File

@@ -214,14 +214,20 @@ int pd_write_last_edge(void *ctxt, int bit_off)
if (bit_idx == 0)
msg[word_idx] = 0;
if (!b_toggle /* last bit was 0 */) {
/* transition to 1, then 0 */
msg[word_idx] |= 1 << bit_idx;
/* transition to 1, another 1, then 0 */
if (bit_idx == 31) {
msg[word_idx++] |= 1 << bit_idx;
msg[word_idx] = 1;
} else {
msg[word_idx] |= 3 << bit_idx;
}
}
/* ensure that the trailer is 0 */
msg[word_idx+1] = 0;
return bit_off + 2;
return bit_off + 3;
}
#ifdef CONFIG_COMMON_RUNTIME
@@ -257,6 +263,35 @@ static struct dma_option dma_tx_option = {
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
};
void pd_tx_spi_init(void)
{
stm32_spi_regs_t *spi = SPI_REGS;
/* Enable Tx DMA for our first transaction */
spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_DATASIZE(8);
#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, CPHA */
spi->cr1 = STM32_SPI_CR1_SPE | STM32_SPI_CR1_LSBFIRST
| STM32_SPI_CR1_SSM | STM32_SPI_CR1_BIDIMODE
| STM32_SPI_CR1_BIDIOE | STM32_SPI_CR1_CPHA;
#endif
}
void pd_tx_set_circular_mode(void)
{
dma_tx_option.flags |= STM32_DMA_CCR_CIRC;
@@ -266,6 +301,15 @@ void pd_start_tx(void *ctxt, int polarity, int bit_len)
{
stm32_dma_chan_t *tx = dma_get_channel(DMAC_SPI_TX);
/* Initialize spi peripheral to prepare for transmission. */
pd_tx_spi_init();
/*
* Set timer to one tick before reset so that the first tick causes
* a rising edge on the output.
*/
STM32_TIM_CNT(TIM_TX) = TX_CLOCK_DIV - 1;
/* 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 latest data */
@@ -273,16 +317,23 @@ void pd_start_tx(void *ctxt, int polarity, int bit_len)
/* disable RX detection interrupt */
pd_rx_disable_monitoring();
/*
* Drive the CC line from the TX block :
* - set the low level reference.
* - put SPI function on TX pin.
*/
pd_tx_enable(polarity);
/* Kick off the DMA to send the data */
dma_clear_isr(DMAC_SPI_TX);
#ifdef CONFIG_COMMON_RUNTIME
dma_enable_tc_interrupt(DMAC_SPI_TX);
#endif
dma_go(tx);
/*
* Drive the CC line from the TX block :
* - put SPI function on TX pin.
* - set the low level reference.
* Call this last before enabling timer in order to meet spec on
* timing between enabling TX and clocking out bits.
*/
pd_tx_enable(polarity);
#ifndef CONFIG_USB_PD_TX_USES_SPI_MASTER
/* Start counting at 300Khz*/
STM32_TIM_CR1(TIM_TX) |= 1;
@@ -293,7 +344,12 @@ void pd_tx_done(int polarity)
{
stm32_spi_regs_t *spi = SPI_REGS;
dma_wait(DMAC_SPI_TX);
/* wait for DMA */
#ifdef CONFIG_COMMON_RUNTIME
task_wait_event(DMA_TRANSFER_TIMEOUT_US);
dma_disable_tc_interrupt(DMAC_SPI_TX);
#endif
/* wait for real end of transmission */
#ifdef CHIP_FAMILY_STM32F0
while (spi->sr & STM32_SPI_SR_FTLVL)
@@ -303,9 +359,6 @@ void pd_tx_done(int polarity)
; /* wait for TXE == 1 */
#endif
while (spi->sr & STM32_SPI_SR_BSY)
; /* 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
@@ -321,14 +374,23 @@ void pd_tx_done(int polarity)
#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;
while (spi->sr & STM32_SPI_SR_FTLVL)
; /* wait for TX FIFO empty */
#else
while (spi->sr & STM32_SPI_SR_BSY)
; /* wait for BSY == 0 */
#endif
/* clear transfer flag */
dma_clear_isr(DMAC_SPI_TX);
/* put TX pins and reference in Hi-Z */
pd_tx_disable(polarity);
#ifndef CONFIG_USB_PD_TX_USES_SPI_MASTER
/* Stop counting */
STM32_TIM_CR1(TIM_TX) &= ~1;
/* Reset SPI to clear remaining data in buffer */
pd_tx_spi_reset();
#endif
}
/* --- RX operation using comparator linked to timer --- */
@@ -405,8 +467,6 @@ void pd_hw_release(void)
/* --- Startup initialization --- */
void *pd_hw_init(void)
{
stm32_spi_regs_t *spi = SPI_REGS;
/* set 40 MHz pin speed on communication pins */
pd_set_pins_speed();
@@ -418,29 +478,8 @@ void *pd_hw_init(void)
/* Initialize TX pins and put them in Hi-Z */
pd_tx_init();
/* Enable Tx DMA for our first transaction */
spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_DATASIZE(8);
#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
/* Initialize SPI peripheral registers */
pd_tx_spi_init();
/* configure TX DMA */
dma_prepare_tx(&dma_tx_option, PD_MAX_RAW_SIZE, raw_samples);