Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torvalds/linux.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 20:38:14 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-06 20:38:14 +0400
commitae045e2455429c418a418a3376301a9e5753a0a8 (patch)
treeb445bdeecd3f38aa0d0a29c9585cee49e4ccb0f1 /drivers/net/ethernet/cadence/macb.c
parentf4f142ed4ef835709c7e6d12eaca10d190bcebed (diff)
parentd247b6ab3ce6dd43665780865ec5fa145d9ab6bd (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: 1) Steady transitioning of the BPF instructure to a generic spot so all kernel subsystems can make use of it, from Alexei Starovoitov. 2) SFC driver supports busy polling, from Alexandre Rames. 3) Take advantage of hash table in UDP multicast delivery, from David Held. 4) Lighten locking, in particular by getting rid of the LRU lists, in inet frag handling. From Florian Westphal. 5) Add support for various RFC6458 control messages in SCTP, from Geir Ola Vaagland. 6) Allow to filter bridge forwarding database dumps by device, from Jamal Hadi Salim. 7) virtio-net also now supports busy polling, from Jason Wang. 8) Some low level optimization tweaks in pktgen from Jesper Dangaard Brouer. 9) Add support for ipv6 address generation modes, so that userland can have some input into the process. From Jiri Pirko. 10) Consolidate common TCP connection request code in ipv4 and ipv6, from Octavian Purdila. 11) New ARP packet logger in netfilter, from Pablo Neira Ayuso. 12) Generic resizable RCU hash table, with intial users in netlink and nftables. From Thomas Graf. 13) Maintain a name assignment type so that userspace can see where a network device name came from (enumerated by kernel, assigned explicitly by userspace, etc.) From Tom Gundersen. 14) Automatic flow label generation on transmit in ipv6, from Tom Herbert. 15) New packet timestamping facilities from Willem de Bruijn, meant to assist in measuring latencies going into/out-of the packet scheduler, latency from TCP data transmission to ACK, etc" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1536 commits) cxgb4 : Disable recursive mailbox commands when enabling vi net: reduce USB network driver config options. tg3: Modify tg3_tso_bug() to handle multiple TX rings amd-xgbe: Perform phy connect/disconnect at dev open/stop amd-xgbe: Use dma_set_mask_and_coherent to set DMA mask net: sun4i-emac: fix memory leak on bad packet sctp: fix possible seqlock seadlock in sctp_packet_transmit() Revert "net: phy: Set the driver when registering an MDIO bus device" cxgb4vf: Turn off SGE RX/TX Callback Timers and interrupts in PCI shutdown routine team: Simplify return path of team_newlink bridge: Update outdated comment on promiscuous mode net-timestamp: ACK timestamp for bytestreams net-timestamp: TCP timestamping net-timestamp: SCHED timestamp on entering packet scheduler net-timestamp: add key to disambiguate concurrent datagrams net-timestamp: move timestamp flags out of sk_flags net-timestamp: extend SCM_TIMESTAMPING ancillary data struct cxgb4i : Move stray CPL definitions to cxgb4 driver tcp: reduce spurious retransmits due to transient SACK reneging qlcnic: Initialize dcbnl_ops before register_netdev ...
Diffstat (limited to 'drivers/net/ethernet/cadence/macb.c')
-rw-r--r--drivers/net/ethernet/cadence/macb.c419
1 files changed, 349 insertions, 70 deletions
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index e9daa072ebb4..ca5d7798b265 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -52,6 +52,9 @@
| MACB_BIT(TXERR))
#define MACB_TX_INT_FLAGS (MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP))
+#define MACB_MAX_TX_LEN ((unsigned int)((1 << MACB_TX_FRMLEN_SIZE) - 1))
+#define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1))
+
/*
* Graceful stop timeouts in us. We should allow up to
* 1 frame time (10 Mbits/s, full-duplex, ignoring collisions)
@@ -264,7 +267,8 @@ static void macb_handle_link_change(struct net_device *dev)
reg |= MACB_BIT(FD);
if (phydev->speed == SPEED_100)
reg |= MACB_BIT(SPD);
- if (phydev->speed == SPEED_1000)
+ if (phydev->speed == SPEED_1000 &&
+ bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE)
reg |= GEM_BIT(GBE);
macb_or_gem_writel(bp, NCFGR, reg);
@@ -337,7 +341,7 @@ static int macb_mii_probe(struct net_device *dev)
}
/* mask with MAC supported features */
- if (macb_is_gem(bp))
+ if (macb_is_gem(bp) && bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE)
phydev->supported &= PHY_GBIT_FEATURES;
else
phydev->supported &= PHY_BASIC_FEATURES;
@@ -467,6 +471,24 @@ static int macb_halt_tx(struct macb *bp)
return -ETIMEDOUT;
}
+static void macb_tx_unmap(struct macb *bp, struct macb_tx_skb *tx_skb)
+{
+ if (tx_skb->mapping) {
+ if (tx_skb->mapped_as_page)
+ dma_unmap_page(&bp->pdev->dev, tx_skb->mapping,
+ tx_skb->size, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(&bp->pdev->dev, tx_skb->mapping,
+ tx_skb->size, DMA_TO_DEVICE);
+ tx_skb->mapping = 0;
+ }
+
+ if (tx_skb->skb) {
+ dev_kfree_skb_any(tx_skb->skb);
+ tx_skb->skb = NULL;
+ }
+}
+
static void macb_tx_error_task(struct work_struct *work)
{
struct macb *bp = container_of(work, struct macb, tx_error_task);
@@ -504,10 +526,23 @@ static void macb_tx_error_task(struct work_struct *work)
skb = tx_skb->skb;
if (ctrl & MACB_BIT(TX_USED)) {
- netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n",
- macb_tx_ring_wrap(tail), skb->data);
- bp->stats.tx_packets++;
- bp->stats.tx_bytes += skb->len;
+ /* skb is set for the last buffer of the frame */
+ while (!skb) {
+ macb_tx_unmap(bp, tx_skb);
+ tail++;
+ tx_skb = macb_tx_skb(bp, tail);
+ skb = tx_skb->skb;
+ }
+
+ /* ctrl still refers to the first buffer descriptor
+ * since it's the only one written back by the hardware
+ */
+ if (!(ctrl & MACB_BIT(TX_BUF_EXHAUSTED))) {
+ netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n",
+ macb_tx_ring_wrap(tail), skb->data);
+ bp->stats.tx_packets++;
+ bp->stats.tx_bytes += skb->len;
+ }
} else {
/*
* "Buffers exhausted mid-frame" errors may only happen
@@ -521,10 +556,7 @@ static void macb_tx_error_task(struct work_struct *work)
desc->ctrl = ctrl | MACB_BIT(TX_USED);
}
- dma_unmap_single(&bp->pdev->dev, tx_skb->mapping, skb->len,
- DMA_TO_DEVICE);
- tx_skb->skb = NULL;
- dev_kfree_skb(skb);
+ macb_tx_unmap(bp, tx_skb);
}
/* Make descriptor updates visible to hardware */
@@ -572,20 +604,35 @@ static void macb_tx_interrupt(struct macb *bp)
ctrl = desc->ctrl;
+ /* TX_USED bit is only set by hardware on the very first buffer
+ * descriptor of the transmitted frame.
+ */
if (!(ctrl & MACB_BIT(TX_USED)))
break;
- tx_skb = macb_tx_skb(bp, tail);
- skb = tx_skb->skb;
+ /* Process all buffers of the current transmitted frame */
+ for (;; tail++) {
+ tx_skb = macb_tx_skb(bp, tail);
+ skb = tx_skb->skb;
+
+ /* First, update TX stats if needed */
+ if (skb) {
+ netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
+ macb_tx_ring_wrap(tail), skb->data);
+ bp->stats.tx_packets++;
+ bp->stats.tx_bytes += skb->len;
+ }
- netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
- macb_tx_ring_wrap(tail), skb->data);
- dma_unmap_single(&bp->pdev->dev, tx_skb->mapping, skb->len,
- DMA_TO_DEVICE);
- bp->stats.tx_packets++;
- bp->stats.tx_bytes += skb->len;
- tx_skb->skb = NULL;
- dev_kfree_skb_irq(skb);
+ /* Now we can safely release resources */
+ macb_tx_unmap(bp, tx_skb);
+
+ /* skb is set only for the last buffer of the frame.
+ * WARNING: at this point skb has been freed by
+ * macb_tx_unmap().
+ */
+ if (skb)
+ break;
+ }
}
bp->tx_tail = tail;
@@ -718,6 +765,10 @@ static int gem_rx(struct macb *bp, int budget)
skb->protocol = eth_type_trans(skb, bp->dev);
skb_checksum_none_assert(skb);
+ if (bp->dev->features & NETIF_F_RXCSUM &&
+ !(bp->dev->flags & IFF_PROMISC) &&
+ GEM_BFEXT(RX_CSUM, ctrl) & GEM_RX_CSUM_CHECKED_MASK)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
bp->stats.rx_packets++;
bp->stats.rx_bytes += skb->len;
@@ -1001,15 +1052,145 @@ static void macb_poll_controller(struct net_device *dev)
}
#endif
-static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static inline unsigned int macb_count_tx_descriptors(struct macb *bp,
+ unsigned int len)
+{
+ return (len + bp->max_tx_length - 1) / bp->max_tx_length;
+}
+
+static unsigned int macb_tx_map(struct macb *bp,
+ struct sk_buff *skb)
{
- struct macb *bp = netdev_priv(dev);
dma_addr_t mapping;
- unsigned int len, entry;
+ unsigned int len, entry, i, tx_head = bp->tx_head;
+ struct macb_tx_skb *tx_skb = NULL;
struct macb_dma_desc *desc;
- struct macb_tx_skb *tx_skb;
+ unsigned int offset, size, count = 0;
+ unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned int eof = 1;
u32 ctrl;
+
+ /* First, map non-paged data */
+ len = skb_headlen(skb);
+ offset = 0;
+ while (len) {
+ size = min(len, bp->max_tx_length);
+ entry = macb_tx_ring_wrap(tx_head);
+ tx_skb = &bp->tx_skb[entry];
+
+ mapping = dma_map_single(&bp->pdev->dev,
+ skb->data + offset,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(&bp->pdev->dev, mapping))
+ goto dma_error;
+
+ /* Save info to properly release resources */
+ tx_skb->skb = NULL;
+ tx_skb->mapping = mapping;
+ tx_skb->size = size;
+ tx_skb->mapped_as_page = false;
+
+ len -= size;
+ offset += size;
+ count++;
+ tx_head++;
+ }
+
+ /* Then, map paged data from fragments */
+ for (f = 0; f < nr_frags; f++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
+
+ len = skb_frag_size(frag);
+ offset = 0;
+ while (len) {
+ size = min(len, bp->max_tx_length);
+ entry = macb_tx_ring_wrap(tx_head);
+ tx_skb = &bp->tx_skb[entry];
+
+ mapping = skb_frag_dma_map(&bp->pdev->dev, frag,
+ offset, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(&bp->pdev->dev, mapping))
+ goto dma_error;
+
+ /* Save info to properly release resources */
+ tx_skb->skb = NULL;
+ tx_skb->mapping = mapping;
+ tx_skb->size = size;
+ tx_skb->mapped_as_page = true;
+
+ len -= size;
+ offset += size;
+ count++;
+ tx_head++;
+ }
+ }
+
+ /* Should never happen */
+ if (unlikely(tx_skb == NULL)) {
+ netdev_err(bp->dev, "BUG! empty skb!\n");
+ return 0;
+ }
+
+ /* This is the last buffer of the frame: save socket buffer */
+ tx_skb->skb = skb;
+
+ /* Update TX ring: update buffer descriptors in reverse order
+ * to avoid race condition
+ */
+
+ /* Set 'TX_USED' bit in buffer descriptor at tx_head position
+ * to set the end of TX queue
+ */
+ i = tx_head;
+ entry = macb_tx_ring_wrap(i);
+ ctrl = MACB_BIT(TX_USED);
+ desc = &bp->tx_ring[entry];
+ desc->ctrl = ctrl;
+
+ do {
+ i--;
+ entry = macb_tx_ring_wrap(i);
+ tx_skb = &bp->tx_skb[entry];
+ desc = &bp->tx_ring[entry];
+
+ ctrl = (u32)tx_skb->size;
+ if (eof) {
+ ctrl |= MACB_BIT(TX_LAST);
+ eof = 0;
+ }
+ if (unlikely(entry == (TX_RING_SIZE - 1)))
+ ctrl |= MACB_BIT(TX_WRAP);
+
+ /* Set TX buffer descriptor */
+ desc->addr = tx_skb->mapping;
+ /* desc->addr must be visible to hardware before clearing
+ * 'TX_USED' bit in desc->ctrl.
+ */
+ wmb();
+ desc->ctrl = ctrl;
+ } while (i != bp->tx_head);
+
+ bp->tx_head = tx_head;
+
+ return count;
+
+dma_error:
+ netdev_err(bp->dev, "TX DMA map failed\n");
+
+ for (i = bp->tx_head; i != tx_head; i++) {
+ tx_skb = macb_tx_skb(bp, i);
+
+ macb_tx_unmap(bp, tx_skb);
+ }
+
+ return 0;
+}
+
+static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct macb *bp = netdev_priv(dev);
unsigned long flags;
+ unsigned int count, nr_frags, frag_size, f;
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
netdev_vdbg(bp->dev,
@@ -1020,44 +1201,34 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb->data, 16, true);
#endif
- len = skb->len;
+ /* Count how many TX buffer descriptors are needed to send this
+ * socket buffer: skb fragments of jumbo frames may need to be
+ * splitted into many buffer descriptors.
+ */
+ count = macb_count_tx_descriptors(bp, skb_headlen(skb));
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ for (f = 0; f < nr_frags; f++) {
+ frag_size = skb_frag_size(&skb_shinfo(skb)->frags[f]);
+ count += macb_count_tx_descriptors(bp, frag_size);
+ }
+
spin_lock_irqsave(&bp->lock, flags);
/* This is a hard error, log it. */
- if (CIRC_SPACE(bp->tx_head, bp->tx_tail, TX_RING_SIZE) < 1) {
+ if (CIRC_SPACE(bp->tx_head, bp->tx_tail, TX_RING_SIZE) < count) {
netif_stop_queue(dev);
spin_unlock_irqrestore(&bp->lock, flags);
- netdev_err(bp->dev, "BUG! Tx Ring full when queue awake!\n");
netdev_dbg(bp->dev, "tx_head = %u, tx_tail = %u\n",
bp->tx_head, bp->tx_tail);
return NETDEV_TX_BUSY;
}
- entry = macb_tx_ring_wrap(bp->tx_head);
- netdev_vdbg(bp->dev, "Allocated ring entry %u\n", entry);
- mapping = dma_map_single(&bp->pdev->dev, skb->data,
- len, DMA_TO_DEVICE);
- if (dma_mapping_error(&bp->pdev->dev, mapping)) {
+ /* Map socket buffer for DMA transfer */
+ if (!macb_tx_map(bp, skb)) {
dev_kfree_skb_any(skb);
goto unlock;
}
- bp->tx_head++;
- tx_skb = &bp->tx_skb[entry];
- tx_skb->skb = skb;
- tx_skb->mapping = mapping;
- netdev_vdbg(bp->dev, "Mapped skb data %p to DMA addr %08lx\n",
- skb->data, (unsigned long)mapping);
-
- ctrl = MACB_BF(TX_FRMLEN, len);
- ctrl |= MACB_BIT(TX_LAST);
- if (entry == (TX_RING_SIZE - 1))
- ctrl |= MACB_BIT(TX_WRAP);
-
- desc = &bp->tx_ring[entry];
- desc->addr = mapping;
- desc->ctrl = ctrl;
-
/* Make newly initialized descriptor visible to hardware */
wmb();
@@ -1342,7 +1513,7 @@ static u32 macb_dbw(struct macb *bp)
/*
* Configure the receive DMA engine
* - use the correct receive buffer size
- * - set the possibility to use INCR16 bursts
+ * - set best burst length for DMA operations
* (if not supported by FIFO, it will fallback to default)
* - set both rx/tx packet buffers to full memory size
* These are configurable parameters for GEM.
@@ -1354,24 +1525,20 @@ static void macb_configure_dma(struct macb *bp)
if (macb_is_gem(bp)) {
dmacfg = gem_readl(bp, DMACFG) & ~GEM_BF(RXBS, -1L);
dmacfg |= GEM_BF(RXBS, bp->rx_buffer_size / RX_BUFFER_MULTIPLE);
- dmacfg |= GEM_BF(FBLDO, 16);
+ if (bp->dma_burst_length)
+ dmacfg = GEM_BFINS(FBLDO, bp->dma_burst_length, dmacfg);
dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L);
dmacfg &= ~GEM_BIT(ENDIA);
+ if (bp->dev->features & NETIF_F_HW_CSUM)
+ dmacfg |= GEM_BIT(TXCOEN);
+ else
+ dmacfg &= ~GEM_BIT(TXCOEN);
+ netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n",
+ dmacfg);
gem_writel(bp, DMACFG, dmacfg);
}
}
-/*
- * Configure peripheral capacities according to integration options used
- */
-static void macb_configure_caps(struct macb *bp)
-{
- if (macb_is_gem(bp)) {
- if (GEM_BFEXT(IRQCOR, gem_readl(bp, DCFG1)) == 0)
- bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE;
- }
-}
-
static void macb_init_hw(struct macb *bp)
{
u32 config;
@@ -1386,6 +1553,8 @@ static void macb_init_hw(struct macb *bp)
config |= MACB_BIT(BIG); /* Receive oversized frames */
if (bp->dev->flags & IFF_PROMISC)
config |= MACB_BIT(CAF); /* Copy All Frames */
+ else if (macb_is_gem(bp) && bp->dev->features & NETIF_F_RXCSUM)
+ config |= GEM_BIT(RXCOEN);
if (!(bp->dev->flags & IFF_BROADCAST))
config |= MACB_BIT(NBC); /* No BroadCast */
config |= macb_dbw(bp);
@@ -1394,7 +1563,6 @@ static void macb_init_hw(struct macb *bp)
bp->duplex = DUPLEX_HALF;
macb_configure_dma(bp);
- macb_configure_caps(bp);
/* Initialize TX and RX buffers */
macb_writel(bp, RBQP, bp->rx_ring_dma);
@@ -1500,13 +1668,22 @@ void macb_set_rx_mode(struct net_device *dev)
cfg = macb_readl(bp, NCFGR);
- if (dev->flags & IFF_PROMISC)
+ if (dev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
cfg |= MACB_BIT(CAF);
- else if (dev->flags & (~IFF_PROMISC))
- /* Disable promiscuous mode */
+
+ /* Disable RX checksum offload */
+ if (macb_is_gem(bp))
+ cfg &= ~GEM_BIT(RXCOEN);
+ } else {
+ /* Disable promiscuous mode */
cfg &= ~MACB_BIT(CAF);
+ /* Enable RX checksum offload only if requested */
+ if (macb_is_gem(bp) && dev->features & NETIF_F_RXCSUM)
+ cfg |= GEM_BIT(RXCOEN);
+ }
+
if (dev->flags & IFF_ALLMULTI) {
/* Enable all multicast mode */
macb_or_gem_writel(bp, HRB, -1);
@@ -1767,6 +1944,40 @@ int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
EXPORT_SYMBOL_GPL(macb_ioctl);
+static int macb_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct macb *bp = netdev_priv(netdev);
+ netdev_features_t changed = features ^ netdev->features;
+
+ /* TX checksum offload */
+ if ((changed & NETIF_F_HW_CSUM) && macb_is_gem(bp)) {
+ u32 dmacfg;
+
+ dmacfg = gem_readl(bp, DMACFG);
+ if (features & NETIF_F_HW_CSUM)
+ dmacfg |= GEM_BIT(TXCOEN);
+ else
+ dmacfg &= ~GEM_BIT(TXCOEN);
+ gem_writel(bp, DMACFG, dmacfg);
+ }
+
+ /* RX checksum offload */
+ if ((changed & NETIF_F_RXCSUM) && macb_is_gem(bp)) {
+ u32 netcfg;
+
+ netcfg = gem_readl(bp, NCFGR);
+ if (features & NETIF_F_RXCSUM &&
+ !(netdev->flags & IFF_PROMISC))
+ netcfg |= GEM_BIT(RXCOEN);
+ else
+ netcfg &= ~GEM_BIT(RXCOEN);
+ gem_writel(bp, NCFGR, netcfg);
+ }
+
+ return 0;
+}
+
static const struct net_device_ops macb_netdev_ops = {
.ndo_open = macb_open,
.ndo_stop = macb_close,
@@ -1780,20 +1991,77 @@ static const struct net_device_ops macb_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = macb_poll_controller,
#endif
+ .ndo_set_features = macb_set_features,
};
#if defined(CONFIG_OF)
+static struct macb_config pc302gem_config = {
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+ .dma_burst_length = 16,
+};
+
+static struct macb_config sama5d3_config = {
+ .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+ .dma_burst_length = 16,
+};
+
+static struct macb_config sama5d4_config = {
+ .caps = 0,
+ .dma_burst_length = 4,
+};
+
static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,at32ap7000-macb" },
{ .compatible = "cdns,at91sam9260-macb" },
{ .compatible = "cdns,macb" },
- { .compatible = "cdns,pc302-gem" },
- { .compatible = "cdns,gem" },
+ { .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
+ { .compatible = "cdns,gem", .data = &pc302gem_config },
+ { .compatible = "atmel,sama5d3-gem", .data = &sama5d3_config },
+ { .compatible = "atmel,sama5d4-gem", .data = &sama5d4_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, macb_dt_ids);
#endif
+/*
+ * Configure peripheral capacities according to device tree
+ * and integration options used
+ */
+static void macb_configure_caps(struct macb *bp)
+{
+ u32 dcfg;
+ const struct of_device_id *match;
+ const struct macb_config *config;
+
+ if (bp->pdev->dev.of_node) {
+ match = of_match_node(macb_dt_ids, bp->pdev->dev.of_node);
+ if (match && match->data) {
+ config = (const struct macb_config *)match->data;
+
+ bp->caps = config->caps;
+ /*
+ * As we have access to the matching node, configure
+ * DMA burst length as well
+ */
+ bp->dma_burst_length = config->dma_burst_length;
+ }
+ }
+
+ if (MACB_BFEXT(IDNUM, macb_readl(bp, MID)) == 0x2)
+ bp->caps |= MACB_CAPS_MACB_IS_GEM;
+
+ if (macb_is_gem(bp)) {
+ dcfg = gem_readl(bp, DCFG1);
+ if (GEM_BFEXT(IRQCOR, dcfg) == 0)
+ bp->caps |= MACB_CAPS_ISR_CLEAR_ON_WRITE;
+ dcfg = gem_readl(bp, DCFG2);
+ if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0)
+ bp->caps |= MACB_CAPS_FIFO_MODE;
+ }
+
+ netdev_dbg(bp->dev, "Cadence caps 0x%08x\n", bp->caps);
+}
+
static int __init macb_probe(struct platform_device *pdev)
{
struct macb_platform_data *pdata;
@@ -1828,9 +2096,6 @@ static int __init macb_probe(struct platform_device *pdev)
SET_NETDEV_DEV(dev, &pdev->dev);
- /* TODO: Actually, we have some interesting features... */
- dev->features |= 0;
-
bp = netdev_priv(dev);
bp->pdev = pdev;
bp->dev = dev;
@@ -1897,19 +2162,33 @@ static int __init macb_probe(struct platform_device *pdev)
dev->base_addr = regs->start;
+ /* setup capacities */
+ macb_configure_caps(bp);
+
/* setup appropriated routines according to adapter type */
if (macb_is_gem(bp)) {
+ bp->max_tx_length = GEM_MAX_TX_LEN;
bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers;
bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers;
bp->macbgem_ops.mog_init_rings = gem_init_rings;
bp->macbgem_ops.mog_rx = gem_rx;
} else {
+ bp->max_tx_length = MACB_MAX_TX_LEN;
bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers;
bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers;
bp->macbgem_ops.mog_init_rings = macb_init_rings;
bp->macbgem_ops.mog_rx = macb_rx;
}
+ /* Set features */
+ dev->hw_features = NETIF_F_SG;
+ /* Checksum offload is only available on gem with packet buffer */
+ if (macb_is_gem(bp) && !(bp->caps & MACB_CAPS_FIFO_MODE))
+ dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+ if (bp->caps & MACB_CAPS_SG_DISABLED)
+ dev->hw_features &= ~NETIF_F_SG;
+ dev->features = dev->hw_features;
+
/* Set MII management clock divider */
config = macb_mdc_clk_div(bp);
config |= macb_dbw(bp);