#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */
#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */
-#define MAX_PKT_SIZE 1518
-#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */
+#define MAX_PKT_SIZE 1536
+#define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */
struct ftgmac100_descs {
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
dma_addr_t descs_dma_addr;
/* Rx ring */
- struct page *rx_pages[RX_QUEUE_ENTRIES];
+ struct sk_buff *rx_skbs[RX_QUEUE_ENTRIES];
unsigned int rx_pointer;
u32 rxdes0_edorr_mask;
return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY);
}
-static void ftgmac100_rxdes_set_dma_own(const struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- /* clear status bits */
- rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask);
-}
-
#define RXDES0_ANY_ERROR ( \
FTGMAC100_RXDES0_RX_ERR | \
FTGMAC100_RXDES0_CRC_ERR | \
FTGMAC100_RXDES1_IP_CHKSUM_ERR));
}
-static inline struct page **ftgmac100_rxdes_page_slot(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- return &priv->rx_pages[rxdes - priv->descs->rxdes];
-}
-
-/*
- * rxdes2 is not used by hardware. We use it to keep track of page.
- * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu().
- */
-static void ftgmac100_rxdes_set_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes,
- struct page *page)
-{
- *ftgmac100_rxdes_page_slot(priv, rxdes) = page;
-}
-
-static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes)
-{
- return *ftgmac100_rxdes_page_slot(priv, rxdes);
-}
-
-static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
- struct ftgmac100_rxdes *rxdes, gfp_t gfp)
+static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry,
+ struct ftgmac100_rxdes *rxdes, gfp_t gfp)
{
struct net_device *netdev = priv->netdev;
- struct page *page;
+ struct sk_buff *skb;
dma_addr_t map;
int err;
- page = alloc_page(gfp);
- if (!page) {
+ skb = netdev_alloc_skb_ip_align(netdev, RX_BUF_SIZE);
+ if (unlikely(!skb)) {
if (net_ratelimit())
- netdev_err(netdev, "failed to allocate rx page\n");
+ netdev_warn(netdev, "failed to allocate rx skb\n");
err = -ENOMEM;
map = priv->rx_scratch_dma;
+ } else {
+ map = dma_map_single(priv->dev, skb->data, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ if (net_ratelimit())
+ netdev_err(netdev, "failed to map rx page\n");
+ dev_kfree_skb_any(skb);
+ map = priv->rx_scratch_dma;
+ skb = NULL;
+ err = -ENOMEM;
+ }
}
- map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(priv->dev, map))) {
- if (net_ratelimit())
- netdev_err(netdev, "failed to map rx page\n");
- __free_page(page);
- err = -ENOMEM;
- map = priv->rx_scratch_dma;
- page = NULL;
- }
+ /* Store skb */
+ priv->rx_skbs[entry] = skb;
- ftgmac100_rxdes_set_page(priv, rxdes, page);
+ /* Store DMA address into RX desc */
ftgmac100_rxdes_set_dma_addr(rxdes, map);
- ftgmac100_rxdes_set_dma_own(priv, rxdes);
+
+ /* Ensure the above is ordered vs clearing the OWN bit */
+ dma_wmb();
+
+ /* Clean rxdes0 (which resets own bit) */
+ rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask);
+
return 0;
}
struct net_device *netdev = priv->netdev;
struct ftgmac100_rxdes *rxdes;
struct sk_buff *skb;
- struct page *page;
unsigned int pointer, size;
dma_addr_t map;
goto drop;
}
- /* If the packet had no buffer (failed to allocate earlier)
+ /* If the packet had no skb (failed to allocate earlier)
* then try to allocate one and skip
*/
- page = ftgmac100_rxdes_get_page(priv, rxdes);
- if (!page) {
- ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
- goto drop;
- }
-
- /* start processing */
- skb = netdev_alloc_skb_ip_align(netdev, 128);
- if (unlikely(!skb)) {
- if (net_ratelimit())
- netdev_err(netdev, "rx skb alloc failed\n");
+ skb = priv->rx_skbs[pointer];
+ if (!unlikely(skb)) {
+ ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
goto drop;
}
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
- map = ftgmac100_rxdes_get_dma_addr(rxdes);
-
- dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
-
+ /* Grab received size annd transfer to skb */
size = ftgmac100_rxdes_data_length(rxdes);
- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size);
+ skb_put(skb, size);
- skb->len += size;
- skb->data_len += size;
- skb->truesize += PAGE_SIZE;
+ /* Tear down DMA mapping, do necessary cache management */
+ map = ftgmac100_rxdes_get_dma_addr(rxdes);
+#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU)
+ /* When we don't have an iommu, we can save cycles by not
+ * invalidating the cache for the part of the packet that
+ * wasn't received.
+ */
+ dma_unmap_single(priv->dev, map, size, DMA_FROM_DEVICE);
+#else
+ dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+#endif
- ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC);
+ /* Resplenish rx ring */
+ ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
priv->rx_pointer = ftgmac100_next_rx_pointer(pointer);
- /* Small frames are copied into linear part of skb to free one page */
- if (skb->len <= 128) {
- skb->truesize -= PAGE_SIZE;
- __pskb_pull_tail(skb, skb->len);
- } else {
- /* We pull the minimum amount into linear part */
- __pskb_pull_tail(skb, ETH_HLEN);
- }
skb->protocol = eth_type_trans(skb, netdev);
netdev->stats.rx_packets++;
- netdev->stats.rx_bytes += skb->len;
+ netdev->stats.rx_bytes += size;
/* push packet to protocol stack */
if (skb->ip_summed == CHECKSUM_NONE)
/* Free all RX buffers */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
- struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
+ struct sk_buff *skb = priv->rx_skbs[i];
dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes);
- if (!page)
+ if (!skb)
continue;
- dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
- __free_page(page);
+ priv->rx_skbs[i] = NULL;
+ dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
}
/* Free all TX buffers */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
- if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
+ if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL))
return -ENOMEM;
}
return 0;