[U-Boot] [PATCH] net/designware: make driver compatible with data cache

Up until now this driver only worked with data cache disabled. To make it work with enabled data cache following changes were required:
* Implement all accesses to shared structures between CPU and GMAC via uncached reads/writes ("readl"/"writel"). * Flush cache for data passed from CPU to GMAC * Invalidate cache for data passed from GMAC to CPU
I tried to implement items above keeping as much code unchanged as possible. So logic of operation is kept as it is.
Signed-off-by: Alexey Brodkin abrodkin@synopsys.com
Cc: Joe Hershberger joe.hershberger@ni.com Cc: Vipin Kumar vipin.kumar@st.com Cc: Stefan Roese sr@denx.de Cc: Mischa Jonker mjonker@synopsys.com --- drivers/net/designware.c | 91 ++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 33 deletions(-)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 22155b4..04ae02c 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -30,26 +30,30 @@ static void tx_descs_init(struct eth_device *dev)
for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE]; - desc_p->dmamac_next = &desc_table_p[idx + 1]; + writel(&txbuffs[idx * CONFIG_ETH_BUFSIZE], + &desc_p->dmamac_addr); + writel(&desc_table_p[idx + 1], &desc_p->dmamac_next);
#if defined(CONFIG_DW_ALTDESCRIPTOR) - desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST | - DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS | \ - DESC_TXSTS_TXCHECKINSCTRL | \ - DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS); - - desc_p->txrx_status |= DESC_TXSTS_TXCHAIN; - desc_p->dmamac_cntl = 0; - desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA); + writel(readl(&desc_p->txrx_status) & ~(DESC_TXSTS_TXINT | + DESC_TXSTS_TXLAST | DESC_TXSTS_TXFIRST | + DESC_TXSTS_TXCRCDIS | DESC_TXSTS_TXCHECKINSCTRL | + DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS), + &desc_p->txrx_status); + + writel(readl(&desc_p->txrx_status) | DESC_TXSTS_TXCHAIN, + &desc_p->txrx_status); + writel(0, &desc_p->dmamac_cntl); + writel(readl(&desc_p->txrx_status) & ~(DESC_TXSTS_MSK | + DESC_TXSTS_OWNBYDMA), &desc_p->txrx_status); #else - desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN; - desc_p->txrx_status = 0; + writel(DESC_TXCTRL_TXCHAIN, &desc_p->dmamac_cntl); + writel(0, &desc_p->txrx_status); #endif }
/* Correcting the last pointer of the chain */ - desc_p->dmamac_next = &desc_table_p[0]; + writel(&desc_table_p[0], &desc_p->dmamac_next);
writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr); } @@ -63,20 +67,30 @@ static void rx_descs_init(struct eth_device *dev) struct dmamacdescr *desc_p; u32 idx;
+ /* Before passing buffers to GMAC we need to make sure zeros + * written there right after "priv" structure allocation were + * flushed into RAM. + * Otherwise there's a chance to get some of them flushed in RAM when + * GMAC is already pushing data to RAM via DMA. This way incoming from + * GMAC data will be corrupted. */ + flush_dcache_range((unsigned int)rxbuffs, (unsigned int)rxbuffs + + RX_TOTAL_BUFSIZE); + for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE]; - desc_p->dmamac_next = &desc_table_p[idx + 1];
- desc_p->dmamac_cntl = - (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | \ - DESC_RXCTRL_RXCHAIN; + writel(&rxbuffs[idx * CONFIG_ETH_BUFSIZE], + &desc_p->dmamac_addr); + writel(&desc_table_p[idx + 1], &desc_p->dmamac_next); + + writel((MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | + DESC_RXCTRL_RXCHAIN, &desc_p->dmamac_cntl);
- desc_p->txrx_status = DESC_RXSTS_OWNBYDMA; + writel(DESC_RXSTS_OWNBYDMA, &desc_p->txrx_status); }
/* Correcting the last pointer of the chain */ - desc_p->dmamac_next = &desc_table_p[0]; + writel(&desc_table_p[0], &desc_p->dmamac_next);
writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr); } @@ -198,26 +212,32 @@ static int dw_eth_send(struct eth_device *dev, void *packet, int length) struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num];
/* Check if the descriptor is owned by CPU */ - if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) { + if (readl(&desc_p->txrx_status) & DESC_TXSTS_OWNBYDMA) { printf("CPU not owner of tx frame\n"); return -1; }
memcpy((void *)desc_p->dmamac_addr, packet, length);
-#if defined(CONFIG_DW_ALTDESCRIPTOR) - desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST; - desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) & \ - DESC_TXCTRL_SIZE1MASK; + flush_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + length);
- desc_p->txrx_status &= ~(DESC_TXSTS_MSK); - desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA; +#if defined(CONFIG_DW_ALTDESCRIPTOR) + writel(readl(&desc_p->txrx_status) | DESC_TXSTS_TXFIRST | + DESC_TXSTS_TXLAST, &desc_p->txrx_status); + writel(readl(&desc_p->dmamac_cntl) | (length << DESC_TXCTRL_SIZE1SHFT) & + DESC_TXCTRL_SIZE1MASK, &desc_p->dmamac_cntl); + + writel(readl(&desc_p->txrx_status) & ~DESC_TXSTS_MSK, + &desc_p->txrx_status); + writel(readl(&desc_p->txrx_status) | DESC_TXSTS_OWNBYDMA, + &desc_p->txrx_status); #else - desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & \ - DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | \ - DESC_TXCTRL_TXFIRST; + writel(readl(&desc_p->dmamac_cntl) | + ((length << DESC_TXCTRL_SIZE1SHFT) & DESC_TXCTRL_SIZE1MASK) | + DESC_TXCTRL_TXLAST | DESC_TXCTRL_TXFIRST, &desc_p->dmamac_cntl);
- desc_p->txrx_status = DESC_TXSTS_OWNBYDMA; + writel(DESC_TXSTS_OWNBYDMA, &desc_p->txrx_status); #endif
/* Test the wrap-around condition. */ @@ -238,7 +258,7 @@ static int dw_eth_recv(struct eth_device *dev) u32 desc_num = priv->rx_currdescnum; struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
- u32 status = desc_p->txrx_status; + u32 status = readl(&desc_p->txrx_status); int length = 0;
/* Check if the owner is the CPU */ @@ -247,13 +267,18 @@ static int dw_eth_recv(struct eth_device *dev) length = (status & DESC_RXSTS_FRMLENMSK) >> \ DESC_RXSTS_FRMLENSHFT;
+ invalidate_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + + length); + NetReceive(desc_p->dmamac_addr, length);
/* * Make the current descriptor valid again and go to * the next one */ - desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA; + writel(readl(&desc_p->txrx_status) | DESC_RXSTS_OWNBYDMA, + &desc_p->txrx_status);
/* Test the wrap-around condition. */ if (++desc_num >= CONFIG_RX_DESCR_NUM)

Hi Alexey,
- Implement all accesses to shared structures between CPU and GMAC via
uncached reads/writes ("readl"/"writel").
I don't know how ARC exactly implements this for u-boot, but AFAIK, readl/writel are meant for 'strongly ordered' I/O writes, not necessarily uncached. The uncached part of it us usually achieved by mapping it into an uncached area, but this is not always possible without using the MMU. So you may need to allocate descriptors on cache-line boundaries and do manually flushing/invalidating.
Mischa
participants (2)
-
Alexey Brodkin
-
Mischa Jonker