Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apritzel/bc792c4dbbd8789f5f18aef538e8c440 to your computer and use it in GitHub Desktop.
Save apritzel/bc792c4dbbd8789f5f18aef538e8c440 to your computer and use it in GitHub Desktop.
From fbf6111be9ee89afb1a0c47363693dbab96b7d50 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Wed, 28 Jul 2016 21:52:51 +0100
Subject: [PATCH] net: sun8i-emac: fix endianness issues
The network MAC in recent Allwinner SoCs uses an internal DMA controller,
which gets its information from DMA descriptors in normal memory.
The device expects the values in there to be in little-endian format.
Since we use normal memory accesses to fill those DMA descriptors, we
must wrap write accesses to these descriptors with cpu_to_le32() calls
to make sure they land in memory in the expected byte order.
This allows the MAC to be used with big-endian kernels.
Tested on Pine64 with both big-endian and little-endian kernels.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/net/ethernet/allwinner/sun8i-emac.c | 120 ++++++++++++++++------------
1 file changed, 68 insertions(+), 52 deletions(-)
diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c
index fc0c1dd..e90d5b6 100644
--- a/drivers/net/ethernet/allwinner/sun8i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun8i-emac.c
@@ -60,10 +60,12 @@
#define SUN8I_EMAC_RX_DESC_LIST 0x34
#define SUN8I_EMAC_TX_DESC_LIST 0x20
+#define LE32_BIT(x) (cpu_to_le32(BIT(x)))
+
#define SUN8I_EMAC_RX_DO_CRC BIT(27)
#define SUN8I_EMAC_RX_STRIP_FCS BIT(28)
-#define SUN8I_COULD_BE_USED_BY_DMA BIT(31)
+#define SUN8I_COULD_BE_USED_BY_DMA LE32_BIT(31)
/* Used in RX_CTL1*/
#define RX_DMA_EN BIT(30)
@@ -91,15 +93,15 @@
#define TX_INT BIT(0)
/* Bits used in frame RX status */
-#define DSC_RX_FIRST BIT(9)
-#define DSC_RX_LAST BIT(8)
+#define DSC_RX_FIRST LE32_BIT(9)
+#define DSC_RX_LAST LE32_BIT(8)
/* Bits used in frame TX ctl */
-#define SUN8I_EMAC_MAGIC_TX_BIT BIT(24)
-#define SUN8I_EMAC_TX_DO_CRC (BIT(27) | BIT(28))
-#define DSC_TX_FIRST BIT(29)
-#define DSC_TX_LAST BIT(30)
-#define SUN8I_EMAC_WANT_INT BIT(31)
+#define SUN8I_EMAC_MAGIC_TX_BIT LE32_BIT(24)
+#define SUN8I_EMAC_TX_DO_CRC (LE32_BIT(27) | LE32_BIT(28))
+#define DSC_TX_FIRST LE32_BIT(29)
+#define DSC_TX_LAST LE32_BIT(30)
+#define SUN8I_EMAC_WANT_INT LE32_BIT(31)
enum emac_variant {
NONE_EMAC,/* for be sure that variant is non-0 if set */
@@ -181,7 +183,7 @@ struct sun8i_emac_stats {
#endif
/* MAGIC value for knowing if a descriptor is available or not */
-#define DCLEAN (BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9))
+#define DCLEAN (cpu_to_le32(BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9)))
/* struct dma_desc - Structure of DMA descriptor used by the hardware
* @status: Status of the frame written by HW, so RO for the
@@ -191,10 +193,10 @@ struct sun8i_emac_stats {
* @next: physical address of next dma_desc
*/
struct dma_desc {
- u32 status;
- u32 ctl;
- u32 buf_addr;
- u32 next;
+ __le32 status;
+ __le32 ctl;
+ __le32 buf_addr;
+ __le32 next;
};
/* Describe how data from skb are DMA mapped (used in txinfo map member) */
@@ -299,7 +301,8 @@ static int sun8i_emac_rx_skb(struct net_device *ndev, int i)
dev_kfree_skb(skb);
return -EFAULT;
}
- ddesc->ctl |= DESC_BUF_MAX;
+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr);
+ ddesc->ctl |= cpu_to_le32(DESC_BUF_MAX);
wmb();/* SUN8I_COULD_BE_USED_BY_DMA must be the last value written */
ddesc->status = SUN8I_COULD_BE_USED_BY_DMA;
@@ -448,49 +451,49 @@ static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
* (not on ARP for example) so we dont raise rx_errors/discard frame
*/
/* the checksum or length of received frame's payload is wrong*/
- if (ddesc->status & BIT(0)) {
+ if (ddesc->status & LE32_BIT(0)) {
priv->estats.rx_payload_error++;
rxcsum_done = 0;
}
/* RX_CRC_ERR */
- if (ddesc->status & BIT(1)) {
+ if (ddesc->status & LE32_BIT(1)) {
priv->ndev->stats.rx_errors++;
priv->ndev->stats.rx_crc_errors++;
priv->estats.rx_crc_error++;
goto discard_frame;
}
/* RX_PHY_ERR */
- if ((ddesc->status & BIT(3))) {
+ if ((ddesc->status & LE32_BIT(3))) {
priv->ndev->stats.rx_errors++;
priv->estats.rx_phy_error++;
goto discard_frame;
}
/* RX_LENGTH_ERR */
- if ((ddesc->status & BIT(4))) {
+ if ((ddesc->status & LE32_BIT(4))) {
priv->ndev->stats.rx_errors++;
priv->ndev->stats.rx_length_errors++;
priv->estats.rx_length_error++;
goto discard_frame;
}
/* RX_COL_ERR */
- if ((ddesc->status & BIT(6))) {
+ if ((ddesc->status & LE32_BIT(6))) {
priv->ndev->stats.rx_errors++;
priv->estats.rx_col_error++;
goto discard_frame;
}
/* RX_HEADER_ERR */
- if ((ddesc->status & BIT(7))) {
+ if ((ddesc->status & LE32_BIT(7))) {
priv->estats.rx_header_error++;
rxcsum_done = 0;
}
/* RX_OVERFLOW_ERR */
- if ((ddesc->status & BIT(11))) {
+ if ((ddesc->status & LE32_BIT(11))) {
priv->ndev->stats.rx_over_errors++;
priv->estats.rx_overflow_error++;
goto discard_frame;
}
/* RX_NO_ENOUGTH_BUF_ERR */
- if ((ddesc->status & BIT(14))) {
+ if ((ddesc->status & LE32_BIT(14))) {
priv->ndev->stats.rx_errors++;
priv->estats.rx_buf_error++;
goto discard_frame;
@@ -503,7 +506,7 @@ static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
dev_warn_ratelimited(priv->dev, "BUG: Non-first frame received. This should not happen\n");
goto discard_frame;
}
- frame_len = (ddesc->status >> 16) & 0x3FFF;
+ frame_len = (le32_to_cpu(ddesc->status) >> 16) & 0x3FFF;
if (!(ndev->features & NETIF_F_RXFCS))
frame_len -= ETH_FCS_LEN;
@@ -511,11 +514,12 @@ static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
netif_dbg(priv, rx_status, priv->ndev,
"%s from %02d %pad len=%d status=%x st=%x\n",
- __func__, i, &ddesc, frame_len, ddesc->status, ddesc->ctl);
+ __func__, i, &ddesc, frame_len, le32_to_cpu(ddesc->status),
+ le32_to_cpu(ddesc->ctl));
skb_put(skb, frame_len);
- dma_unmap_single(priv->dev, ddesc->buf_addr, DESC_BUF_MAX,
+ dma_unmap_single(priv->dev, le32_to_cpu(ddesc->buf_addr), DESC_BUF_MAX,
DMA_FROM_DEVICE);
skb->protocol = eth_type_trans(skb, priv->ndev);
if (rxcsum_done) {
@@ -541,9 +545,10 @@ static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
return 0;
/* If the frame need to be dropped, we simply reuse the buffer */
discard_frame:
- ddesc->ctl = DESC_BUF_MAX;
+ ddesc->ctl = cpu_to_le32(DESC_BUF_MAX);
wmb();/* SUN8I_COULD_BE_USED_BY_DMA must be the last value written */
ddesc->status = SUN8I_COULD_BE_USED_BY_DMA;
+
return 0;
}
@@ -565,6 +570,8 @@ static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget)
spin_lock(&priv->tx_lock);
do {
+ u32 ddesc_status;
+
ddesc = priv->dd_tx + priv->tx_dirty;
if (ddesc->status & SUN8I_COULD_BE_USED_BY_DMA)
@@ -573,46 +580,49 @@ static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget)
if (ddesc->status == DCLEAN)
goto xmit_end;
- if (ddesc->status == 0 && !ddesc->ctl) {
+ ddesc_status = le32_to_cpu(ddesc->status);
+
+ if (ddesc_status == 0 && !ddesc->ctl) {
dev_err(priv->dev, "BUG: reached the void %d %d\n",
priv->tx_dirty, priv->tx_slot);
goto xmit_end;
}
/* TX_UNDERFLOW_ERR */
- if (ddesc->status & BIT(1))
+ if (ddesc_status & BIT(1))
priv->ndev->stats.tx_errors++;
/* TX_DEFER_ERR */
- if (ddesc->status & BIT(2))
+ if (ddesc_status & BIT(2))
priv->ndev->stats.tx_errors++;
/* BIT 6:3 numbers of collisions */
- if (ddesc->status & 0x78)
+ if (ddesc_status & 0x78)
priv->ndev->stats.collisions +=
- (ddesc->status & 0x78) >> 3;
+ (ddesc_status & 0x78) >> 3;
/* TX_COL_ERR_1 */
- if (ddesc->status & BIT(8))
+ if (ddesc_status & BIT(8))
priv->ndev->stats.tx_errors++;
/* TX_COL_ERR_0 */
- if (ddesc->status & BIT(9))
+ if (ddesc_status & BIT(9))
priv->ndev->stats.tx_errors++;
/* TX_CRS_ERR */
- if (ddesc->status & BIT(10))
+ if (ddesc_status & BIT(10))
priv->ndev->stats.tx_carrier_errors++;
/* TX_PAYLOAD_ERR */
- if (ddesc->status & BIT(12))
+ if (ddesc_status & BIT(12))
priv->ndev->stats.tx_errors++;
/* TX_LENGTH_ERR */
- if (ddesc->status & BIT(14))
+ if (ddesc_status & BIT(14))
priv->ndev->stats.tx_errors++;
/* TX_HEADER_ERR */
- if (ddesc->status & BIT(16))
+ if (ddesc_status & BIT(16))
priv->ndev->stats.tx_errors++;
- frame_len = ddesc->ctl & 0x3FFF;
+ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF;
if (priv->txl[priv->tx_dirty].map == MAP_SINGLE)
- dma_unmap_single(priv->dev, ddesc->buf_addr,
+ dma_unmap_single(priv->dev,
+ le32_to_cpu(ddesc->buf_addr),
frame_len, DMA_TO_DEVICE);
else
- dma_unmap_page(priv->dev, ddesc->buf_addr,
+ dma_unmap_page(priv->dev, le32_to_cpu(ddesc->buf_addr),
frame_len, DMA_TO_DEVICE);
/* we can free skb only on last frame */
if (priv->txl[priv->tx_dirty].skb && (ddesc->ctl & DSC_TX_LAST))
@@ -1146,13 +1156,13 @@ static int sun8i_emac_alloc_rings(struct net_device *ndev)
ddesc = priv->dd_rx;
for (i = 0; i < priv->nbdesc_rx; i++) {
sun8i_emac_rx_skb(ndev, i);
- ddesc->next = (u32)priv->dd_rx_phy + (i + 1)
- * sizeof(struct dma_desc);
+ ddesc->next = cpu_to_le32(priv->dd_rx_phy + (i + 1)
+ * sizeof(struct dma_desc));
ddesc++;
}
/* last descriptor point back to first one */
ddesc--;
- ddesc->next = (u32)priv->dd_rx_phy;
+ ddesc->next = cpu_to_le32(priv->dd_rx_phy);
/* allocate/init TX ring */
priv->dd_tx = dma_zalloc_coherent(priv->dev,
@@ -1167,13 +1177,14 @@ static int sun8i_emac_alloc_rings(struct net_device *ndev)
for (i = 0; i < priv->nbdesc_tx; i++) {
ddesc->status = DCLEAN;
ddesc->ctl = 0;
- ddesc->next = (u32)(priv->dd_tx_phy + (i + 1)
- * sizeof(struct dma_desc));
+ ddesc->next = cpu_to_le32((priv->dd_tx_phy + (i + 1)
+ * sizeof(struct dma_desc)));
ddesc++;
}
/* last descriptor point back to first one */
ddesc--;
- ddesc->next = (u32)priv->dd_tx_phy;
+ ddesc->next = cpu_to_le32(priv->dd_tx_phy);
+
i--;
priv->tx_slot = 0;
@@ -1313,14 +1324,16 @@ static void sun8i_emac_tx_clean(struct net_device *ndev)
for (i = 0; i < priv->nbdesc_tx; i++) {
if (priv->txl[i].skb) {
ddesc = priv->dd_tx + i;
- frame_len = ddesc->ctl & 0x3FFF;
+ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF;
switch (priv->txl[i].map) {
case MAP_SINGLE:
- dma_unmap_single(priv->dev, ddesc->buf_addr,
+ dma_unmap_single(priv->dev,
+ le32_to_cpu(ddesc->buf_addr),
frame_len, DMA_TO_DEVICE);
break;
case MAP_PAGE:
- dma_unmap_page(priv->dev, ddesc->buf_addr,
+ dma_unmap_page(priv->dev,
+ le32_to_cpu(ddesc->buf_addr),
frame_len, DMA_TO_DEVICE);
break;
default:
@@ -1350,7 +1363,8 @@ static void sun8i_emac_rx_clean(struct net_device *ndev)
for (i = 0; i < priv->nbdesc_rx; i++)
if (priv->rx_skb[i]) {
ddesc = priv->dd_rx + i;
- dma_unmap_single(priv->dev, ddesc->buf_addr,
+ dma_unmap_single(priv->dev,
+ le32_to_cpu(ddesc->buf_addr),
DESC_BUF_MAX, DMA_FROM_DEVICE);
dev_kfree_skb_any(priv->rx_skb[i]);
priv->rx_skb[i] = NULL;
@@ -1442,11 +1456,12 @@ static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev)
dev_err(priv->dev, "ERROR: Cannot map buffer for DMA\n");
goto xmit_error;
}
+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr);
priv->txl[i].map = MAP_SINGLE;
priv->txl[i].skb = skb;
tlen = len;
- ddesc->ctl = len;
+ ddesc->ctl = cpu_to_le32(len);
/* Undocumented bit that make it works
* Without it, packets never be sent on H3 SoC
*/
@@ -1461,7 +1476,7 @@ static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev)
priv->txl[i].skb = skb;
ddesc = priv->dd_tx + i;
fraglen = skb_frag_size(frag);
- ddesc->ctl = fraglen;
+ ddesc->ctl = cpu_to_le32(fraglen);
tlen += fraglen,
ddesc->ctl |= SUN8I_EMAC_MAGIC_TX_BIT;
if (do_csum)
@@ -1473,6 +1488,7 @@ static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev)
dev_err(priv->dev, "Cannot map buffer for DMA\n");
goto xmit_error;
}
+ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr);
priv->txl[i].map = MAP_PAGE;
ddesc->status = SUN8I_COULD_BE_USED_BY_DMA;
}
--
2.9.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment