diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/dpaa/dpaa_eth.c')
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 340 |
1 files changed, 288 insertions, 52 deletions
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 00c4beb760c3..a0b556cc4e06 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -50,10 +50,15 @@ #include <linux/highmem.h> #include <linux/percpu.h> #include <linux/dma-mapping.h> +#include <linux/iommu.h> #include <linux/sort.h> #include <linux/phy_fixed.h> #include <soc/fsl/bman.h> #include <soc/fsl/qman.h> +#if !defined(CONFIG_PPC) && defined(CONFIG_SOC_BUS) +#include <linux/sys_soc.h> /* soc_device_match */ +#endif + #include "fman.h" #include "fman_port.h" #include "mac.h" @@ -73,6 +78,10 @@ static u16 tx_timeout = 1000; module_param(tx_timeout, ushort, 0444); MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); +#ifndef CONFIG_PPC +bool dpaa_errata_a010022; +#endif + #define FM_FD_STAT_RX_ERRORS \ (FM_FD_ERR_DMA | FM_FD_ERR_PHYSICAL | \ FM_FD_ERR_SIZE | FM_FD_ERR_CLS_DISCARD | \ @@ -1495,7 +1504,19 @@ static int dpaa_bp_add_8_bufs(const struct dpaa_bp *dpaa_bp) u8 i; for (i = 0; i < 8; i++) { +#ifndef CONFIG_PPC + if (dpaa_errata_a010022) { + struct page *page = alloc_page(GFP_KERNEL); + + if (unlikely(!page)) + goto release_previous_buffs; + new_buf = page_address(page); + } else { + new_buf = netdev_alloc_frag(dpaa_bp->raw_size); + } +#else new_buf = netdev_alloc_frag(dpaa_bp->raw_size); +#endif if (unlikely(!new_buf)) { dev_err(dev, "netdev_alloc_frag() failed, size %zu\n", dpaa_bp->raw_size); @@ -1595,6 +1616,17 @@ static int dpaa_eth_refill_bpools(struct dpaa_priv *priv) return 0; } +static phys_addr_t dpaa_iova_to_phys(struct device *dev, dma_addr_t addr) +{ + struct iommu_domain *domain; + + domain = iommu_get_domain_for_dev(dev); + if (domain) + return iommu_iova_to_phys(domain, addr); + else + return addr; +} + /* Cleanup function for outgoing frame descriptors that were built on Tx path, * either contiguous frames or scatter/gather ones. * Skb freeing is not handled here. @@ -1619,19 +1651,22 @@ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, int nr_frags, i; u64 ns; - skbh = (struct sk_buff **)phys_to_virt(addr); + skbh = (struct sk_buff **)phys_to_virt(dpaa_iova_to_phys(dev, addr)); skb = *skbh; if (unlikely(qm_fd_get_format(fd) == qm_fd_sg)) { nr_frags = skb_shinfo(skb)->nr_frags; - dma_unmap_single(dev, addr, - qm_fd_get_offset(fd) + DPAA_SGT_SIZE, - dma_dir); /* The sgt buffer has been allocated with netdev_alloc_frag(), * it's from lowmem. */ - sgt = phys_to_virt(addr + qm_fd_get_offset(fd)); + sgt = phys_to_virt(dpaa_iova_to_phys(dev, + addr + + qm_fd_get_offset(fd))); + + dma_unmap_single(dev, addr, + qm_fd_get_offset(fd) + DPAA_SGT_SIZE, + dma_dir); /* sgt[0] is from lowmem, was dma_map_single()-ed */ dma_unmap_single(dev, qm_sg_addr(&sgt[0]), @@ -1644,6 +1679,13 @@ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, dma_unmap_page(dev, qm_sg_addr(&sgt[i]), qm_sg_entry_get_len(&sgt[i]), dma_dir); } +#ifndef CONFIG_PPC + if (dpaa_errata_a010022) + put_page(virt_to_page(sgt)); + else +#endif + /* Free the page frag that we allocated on Tx */ + skb_free_frag(skbh); } else { dma_unmap_single(dev, addr, skb_tail_pointer(skb) - (u8 *)skbh, dma_dir); @@ -1692,25 +1734,21 @@ static u8 rx_csum_offload(const struct dpaa_priv *priv, const struct qm_fd *fd) * accommodate the shared info area of the skb. */ static struct sk_buff *contig_fd_to_skb(const struct dpaa_priv *priv, - const struct qm_fd *fd) + const struct qm_fd *fd, + struct dpaa_bp *dpaa_bp, + void *vaddr) { ssize_t fd_off = qm_fd_get_offset(fd); - dma_addr_t addr = qm_fd_addr(fd); - struct dpaa_bp *dpaa_bp; struct sk_buff *skb; - void *vaddr; - vaddr = phys_to_virt(addr); WARN_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); - dpaa_bp = dpaa_bpid2pool(fd->bpid); - if (!dpaa_bp) - goto free_buffer; - skb = build_skb(vaddr, dpaa_bp->size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); - if (WARN_ONCE(!skb, "Build skb failure on Rx\n")) - goto free_buffer; + if (WARN_ONCE(!skb, "Build skb failure on Rx\n")) { + skb_free_frag(vaddr); + return NULL; + } WARN_ON(fd_off != priv->rx_headroom); skb_reserve(skb, fd_off); skb_put(skb, qm_fd_get_length(fd)); @@ -1718,10 +1756,6 @@ static struct sk_buff *contig_fd_to_skb(const struct dpaa_priv *priv, skb->ip_summed = rx_csum_offload(priv, fd); return skb; - -free_buffer: - skb_free_frag(vaddr); - return NULL; } /* Build an skb with the data of the first S/G entry in the linear portion and @@ -1730,14 +1764,14 @@ free_buffer: * The page fragment holding the S/G Table is recycled here. */ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, - const struct qm_fd *fd) + const struct qm_fd *fd, + struct dpaa_bp *dpaa_bp, + void *vaddr) { ssize_t fd_off = qm_fd_get_offset(fd); - dma_addr_t addr = qm_fd_addr(fd); const struct qm_sg_entry *sgt; struct page *page, *head_page; - struct dpaa_bp *dpaa_bp; - void *vaddr, *sg_vaddr; + void *sg_vaddr; int frag_off, frag_len; struct sk_buff *skb; dma_addr_t sg_addr; @@ -1746,7 +1780,6 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, int *count_ptr; int i; - vaddr = phys_to_virt(addr); WARN_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES)); /* Iterate through the SGT entries and add data buffers to the skb */ @@ -1757,14 +1790,18 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, WARN_ON(qm_sg_entry_is_ext(&sgt[i])); sg_addr = qm_sg_addr(&sgt[i]); - sg_vaddr = phys_to_virt(sg_addr); - WARN_ON(!IS_ALIGNED((unsigned long)sg_vaddr, - SMP_CACHE_BYTES)); /* We may use multiple Rx pools */ dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); - if (!dpaa_bp) + if (!dpaa_bp) { + pr_info("%s: fail to get dpaa_bp for sg bpid %d\n", + __func__, sgt[i].bpid); goto free_buffers; + } + sg_vaddr = phys_to_virt(dpaa_iova_to_phys(dpaa_bp->dev, + sg_addr)); + WARN_ON(!IS_ALIGNED((unsigned long)sg_vaddr, + SMP_CACHE_BYTES)); count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); dma_unmap_single(dpaa_bp->dev, sg_addr, dpaa_bp->size, @@ -1836,10 +1873,11 @@ free_buffers: /* free all the SG entries */ for (i = 0; i < DPAA_SGT_MAX_ENTRIES ; i++) { sg_addr = qm_sg_addr(&sgt[i]); - sg_vaddr = phys_to_virt(sg_addr); - skb_free_frag(sg_vaddr); dpaa_bp = dpaa_bpid2pool(sgt[i].bpid); if (dpaa_bp) { + sg_addr = dpaa_iova_to_phys(dpaa_bp->dev, sg_addr); + sg_vaddr = phys_to_virt(sg_addr); + skb_free_frag(sg_vaddr); count_ptr = this_cpu_ptr(dpaa_bp->percpu_count); (*count_ptr)--; } @@ -1922,14 +1960,26 @@ static int skb_to_sg_fd(struct dpaa_priv *priv, size_t frag_len; void *sgt_buf; - /* get a page frag to store the SGTable */ - sz = SKB_DATA_ALIGN(priv->tx_headroom + DPAA_SGT_SIZE); - sgt_buf = netdev_alloc_frag(sz); - if (unlikely(!sgt_buf)) { - netdev_err(net_dev, "netdev_alloc_frag() failed for size %d\n", - sz); - return -ENOMEM; +#ifndef CONFIG_PPC + if (unlikely(dpaa_errata_a010022)) { + struct page *page = alloc_page(GFP_ATOMIC); + if (unlikely(!page)) + return -ENOMEM; + sgt_buf = page_address(page); + } else { +#endif + /* get a page frag to store the SGTable */ + sz = SKB_DATA_ALIGN(priv->tx_headroom + DPAA_SGT_SIZE); + sgt_buf = netdev_alloc_frag(sz); + if (unlikely(!sgt_buf)) { + netdev_err(net_dev, + "netdev_alloc_frag() failed for size %d\n", + sz); + return -ENOMEM; + } +#ifndef CONFIG_PPC } +#endif /* Enable L3/L4 hardware checksum computation. * @@ -2049,6 +2099,131 @@ static inline int dpaa_xmit(struct dpaa_priv *priv, return 0; } +#ifndef CONFIG_PPC +/* On LS1043A SoC there is a known erratum ERR010022 that results in split DMA + * transfers in the FMan under certain conditions. This, combined with a fixed + * size FIFO of ongoing DMA transfers that may overflow when a split occurs, + * results in the FMan stalling DMA transfers under high traffic. To avoid the + * problem, one needs to prevent the DMA transfer splits to occur by preparing + * the buffers + */ + +#define DPAA_A010022_HEADROOM 256 +#define CROSS_4K_BOUND(start, size) \ + (((start) + (size)) > (((start) + 0x1000) & ~0xFFF)) + +static bool dpaa_errata_a010022_has_dma_issue(struct sk_buff *skb, + struct dpaa_priv *priv) +{ + int nr_frags, i = 0; + skb_frag_t *frag; + + /* Transfers that do not start at 16B aligned addresses will be split; + * Transfers that cross a 4K page boundary will also be split + */ + + /* Check if the frame data is aligned to 16 bytes */ + if ((uintptr_t)skb->data % DPAA_FD_DATA_ALIGNMENT) + return true; + + /* Check if the headroom crosses a boundary */ + if (CROSS_4K_BOUND((uintptr_t)skb->head, skb_headroom(skb))) + return true; + + /* Check if the non-paged data crosses a boundary */ + if (CROSS_4K_BOUND((uintptr_t)skb->data, skb_headlen(skb))) + return true; + + nr_frags = skb_shinfo(skb)->nr_frags; + + while (i < nr_frags) { + frag = &skb_shinfo(skb)->frags[i]; + + /* Check if a paged fragment crosses a boundary from its + * offset to its end. + */ + if (CROSS_4K_BOUND(skb_frag_off(frag), skb_frag_size(frag))) + return true; + + i++; + } + + return false; +} + +static struct sk_buff *dpaa_errata_a010022_prevent(struct sk_buff *skb, + struct dpaa_priv *priv) +{ + int trans_offset = skb_transport_offset(skb); + int net_offset = skb_network_offset(skb); + int nsize, npage_order, headroom; + struct sk_buff *nskb = NULL; + struct page *npage; + void *npage_addr; + + if (!dpaa_errata_a010022_has_dma_issue(skb, priv)) + return skb; + + /* For the new skb we only need the old one's data (both non-paged and + * paged). We can skip the old tailroom. + * + * The headroom also needs to fit our private info (64 bytes) but we + * reserve 256 bytes instead in order to guarantee that the data is + * aligned to 256. + */ + headroom = DPAA_A010022_HEADROOM; + nsize = ALIGN(headroom + skb->len, SMP_CACHE_BYTES) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + /* Reserve enough memory to accommodate Jumbo frames */ + npage_order = (nsize - 1) / PAGE_SIZE; + npage = alloc_pages(GFP_ATOMIC | __GFP_COMP, npage_order); + if (unlikely(!npage)) { + WARN_ONCE(1, "Memory allocation failure\n"); + return NULL; + } + npage_addr = page_address(npage); + + nskb = build_skb(npage_addr, nsize); + if (unlikely(!nskb)) + goto err; + + /* Code borrowed and adapted from skb_copy() */ + skb_reserve(nskb, headroom); + skb_put(nskb, skb->len); + if (skb_copy_bits(skb, 0, nskb->data, skb->len)) { + WARN_ONCE(1, "skb parsing failure\n"); + goto err; + } + skb_copy_header(nskb, skb); + + /* Copy relevant timestamp info from the old skb to the new */ + if (priv->tx_tstamp) { + skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags; + skb_shinfo(nskb)->hwtstamps = skb_shinfo(skb)->hwtstamps; + skb_shinfo(nskb)->tskey = skb_shinfo(skb)->tskey; + if (skb->sk) + skb_set_owner_w(nskb, skb->sk); + } + + /* We move the headroom when we align it so we have to reset the + * network and transport header offsets relative to the new data + * pointer. The checksum offload relies on these offsets. + */ + skb_set_network_header(nskb, net_offset); + skb_set_transport_header(nskb, trans_offset); + + dev_kfree_skb(skb); + return nskb; + +err: + if (nskb) + dev_kfree_skb(nskb); + put_page(npage); + return NULL; +} +#endif + static netdev_tx_t dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev) { @@ -2095,6 +2270,15 @@ dpaa_start_xmit(struct sk_buff *skb, struct net_device *net_dev) nonlinear = skb_is_nonlinear(skb); } +#ifndef CONFIG_PPC + if (unlikely(dpaa_errata_a010022)) { + skb = dpaa_errata_a010022_prevent(skb, priv); + if (!skb) + goto enomem; + nonlinear = skb_is_nonlinear(skb); + } +#endif + if (nonlinear) { /* Just create a S/G fd based on the skb */ err = skb_to_sg_fd(priv, skb, &fd); @@ -2313,12 +2497,12 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, if (!dpaa_bp) return qman_cb_dqrr_consume; - dma_unmap_single(dpaa_bp->dev, addr, dpaa_bp->size, DMA_FROM_DEVICE); - /* prefetch the first 64 bytes of the frame or the SGT start */ - vaddr = phys_to_virt(addr); + vaddr = phys_to_virt(dpaa_iova_to_phys(dpaa_bp->dev, addr)); prefetch(vaddr + qm_fd_get_offset(fd)); + dma_unmap_single(dpaa_bp->dev, addr, dpaa_bp->size, DMA_FROM_DEVICE); + /* The only FD types that we may receive are contig and S/G */ WARN_ON((fd_format != qm_fd_contig) && (fd_format != qm_fd_sg)); @@ -2329,9 +2513,9 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, (*count_ptr)--; if (likely(fd_format == qm_fd_contig)) - skb = contig_fd_to_skb(priv, fd); + skb = contig_fd_to_skb(priv, fd, dpaa_bp, vaddr); else - skb = sg_fd_to_skb(priv, fd); + skb = sg_fd_to_skb(priv, fd, dpaa_bp, vaddr); if (!skb) return qman_cb_dqrr_consume; @@ -2784,8 +2968,46 @@ static int dpaa_eth_probe(struct platform_device *pdev) int err = 0, i, channel; struct device *dev; + err = bman_is_probed(); + if (!err) + return -EPROBE_DEFER; + if (err < 0) { + dev_err(&pdev->dev, "failing probe due to bman probe error\n"); + return -ENODEV; + } + err = qman_is_probed(); + if (!err) + return -EPROBE_DEFER; + if (err < 0) { + dev_err(&pdev->dev, "failing probe due to qman probe error\n"); + return -ENODEV; + } + err = bman_portals_probed(); + if (!err) + return -EPROBE_DEFER; + if (err < 0) { + dev_err(&pdev->dev, + "failing probe due to bman portals probe error\n"); + return -ENODEV; + } + err = qman_portals_probed(); + if (!err) + return -EPROBE_DEFER; + if (err < 0) { + dev_err(&pdev->dev, + "failing probe due to qman portals probe error\n"); + return -ENODEV; + } + + mac_dev = dpaa_mac_dev_get(pdev); + if (IS_ERR(mac_dev)) { + dev_err(&pdev->dev, "dpaa_mac_dev_get() failed\n"); + err = PTR_ERR(mac_dev); + goto probe_err; + } + /* device used for DMA mapping */ - dev = pdev->dev.parent; + dev = fman_port_get_device(mac_dev->port[RX]); err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(40)); if (err) { dev_err(dev, "dma_coerce_mask_and_coherent() failed\n"); @@ -2810,13 +3032,6 @@ static int dpaa_eth_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug, DPAA_MSG_DEFAULT); - mac_dev = dpaa_mac_dev_get(pdev); - if (IS_ERR(mac_dev)) { - dev_err(dev, "dpaa_mac_dev_get() failed\n"); - err = PTR_ERR(mac_dev); - goto free_netdev; - } - /* If fsl_fm_max_frm is set to a higher value than the all-common 1500, * we choose conservatively and let the user explicitly set a higher * MTU via ifconfig. Otherwise, the user may end up with different MTUs @@ -2952,9 +3167,9 @@ delete_egress_cgr: qman_release_cgrid(priv->cgr_data.cgr.cgrid); free_dpaa_bps: dpaa_bps_free(priv); -free_netdev: dev_set_drvdata(dev, NULL); free_netdev(net_dev); +probe_err: return err; } @@ -2992,6 +3207,23 @@ static int dpaa_remove(struct platform_device *pdev) return err; } +#ifndef CONFIG_PPC +static bool __init soc_has_errata_a010022(void) +{ +#ifdef CONFIG_SOC_BUS + const struct soc_device_attribute soc_msi_matches[] = { + { .family = "QorIQ LS1043A", + .data = NULL }, + { }, + }; + + if (!soc_device_match(soc_msi_matches)) + return false; +#endif + return true; /* cannot identify SoC or errata applies */ +} +#endif + static const struct platform_device_id dpaa_devtype[] = { { .name = "dpaa-ethernet", @@ -3016,6 +3248,10 @@ static int __init dpaa_load(void) pr_debug("FSL DPAA Ethernet driver\n"); +#ifndef CONFIG_PPC + /* Detect if the current SoC requires the DMA transfer alignment workaround */ + dpaa_errata_a010022 = soc_has_errata_a010022(); +#endif /* initialize dpaa_eth mirror values */ dpaa_rx_extra_headroom = fman_get_rx_extra_headroom(); dpaa_max_frm = fman_get_max_frm(); |