#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
+#include <linux/pm_runtime.h>
+#include <linux/crc32.h>
+#include <linux/inetdevice.h>
#include "macb.h"
#define MACB_RX_BUFFER_SIZE 128
#define GEM_MTU_MIN_SIZE ETH_MIN_MTU
#define MACB_NETIF_LSO NETIF_F_TSO
-#define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0)
-#define MACB_WOL_ENABLED (0x1 << 1)
-
/* Graceful stop timeouts in us. We should allow up to
* 1 frame time (10 Mbits/s, full-duplex, ignoring collisions)
*/
#define MACB_HALT_TIMEOUT 1230
+#define MACB_PM_TIMEOUT 100 /* ms */
/* DMA buffer descriptor might be different size
* depends on hardware configuration:
top = cpu_to_le16(*((u16 *)(bp->dev->dev_addr + 4)));
macb_or_gem_writel(bp, SA1T, top);
+ gem_writel(bp, RXPTPUNI, bottom);
+ gem_writel(bp, TXPTPUNI, bottom);
+
/* Clear unused address register sets */
macb_or_gem_writel(bp, SA2B, 0);
macb_or_gem_writel(bp, SA2T, 0);
{
struct macb *bp = bus->priv;
int value;
+ int err;
+ ulong timeout;
+
+ err = pm_runtime_get_sync(&bp->pdev->dev);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ /* wait for end of transfer */
+ do {
+ if (MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ break;
+
+ cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (time_after_eq(jiffies, timeout)) {
+ netdev_err(bp->dev, "wait for end of transfer timed out\n");
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
+ return -ETIMEDOUT;
+ }
macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF)
| MACB_BF(RW, MACB_MAN_READ)
| MACB_BF(REGA, regnum)
| MACB_BF(CODE, MACB_MAN_CODE)));
+ timeout = jiffies + msecs_to_jiffies(1000);
/* wait for end of transfer */
- while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ do {
+ if (MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ break;
+
cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (time_after_eq(jiffies, timeout)) {
+ netdev_err(bp->dev, "wait for end of transfer timed out\n");
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
+ return -ETIMEDOUT;
+ }
value = MACB_BFEXT(DATA, macb_readl(bp, MAN));
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
return value;
}
u16 value)
{
struct macb *bp = bus->priv;
+ int err;
+ ulong timeout;
+
+ err = pm_runtime_get_sync(&bp->pdev->dev);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ /* wait for end of transfer */
+ do {
+ if (MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ break;
+
+ cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+
+ if (time_after_eq(jiffies, timeout)) {
+ netdev_err(bp->dev, "wait for end of transfer timed out\n");
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
+ return -ETIMEDOUT;
+ }
macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF)
| MACB_BF(RW, MACB_MAN_WRITE)
| MACB_BF(CODE, MACB_MAN_CODE)
| MACB_BF(DATA, value)));
+ timeout = jiffies + msecs_to_jiffies(1000);
/* wait for end of transfer */
- while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ do {
+ if (MACB_BFEXT(IDLE, macb_readl(bp, NSR)))
+ break;
+
cpu_relax();
+ } while (!time_after_eq(jiffies, timeout));
+ if (time_after_eq(jiffies, timeout)) {
+ netdev_err(bp->dev, "wait for end of transfer timed out\n");
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
+ return -ETIMEDOUT;
+ }
+
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
return 0;
}
/**
- * macb_set_tx_clk() - Set a clock to a new frequency
- * @clk Pointer to the clock to change
- * @rate New frequency in Hz
- * @dev Pointer to the struct net_device
+ * macb_set_tx_clk - Set a clock to a new frequency
+ * @clk: Pointer to the clock to change
+ * @speed: New frequency in Hz
+ * @dev: Pointer to the struct net_device
*/
static void macb_set_tx_clk(struct clk *clk, int speed, struct net_device *dev)
{
static void macb_handle_link_change(struct net_device *dev)
{
struct macb *bp = netdev_priv(dev);
- struct phy_device *phydev = dev->phydev;
+ struct phy_device *phydev = bp->phy_dev;
unsigned long flags;
int status_change = 0;
}
pdata = dev_get_platdata(&bp->pdev->dev);
- if (pdata) {
- if (gpio_is_valid(pdata->phy_irq_pin)) {
- ret = devm_gpio_request(&bp->pdev->dev,
- pdata->phy_irq_pin, "phy int");
- if (!ret) {
- phy_irq = gpio_to_irq(pdata->phy_irq_pin);
- phydev->irq = (phy_irq < 0) ? PHY_POLL : phy_irq;
- }
- } else {
- phydev->irq = PHY_POLL;
+ if (pdata && gpio_is_valid(pdata->phy_irq_pin)) {
+ ret = devm_gpio_request(&bp->pdev->dev,
+ pdata->phy_irq_pin, "phy int");
+ if (!ret) {
+ phy_irq = gpio_to_irq(pdata->phy_irq_pin);
+ phydev->irq = (phy_irq < 0) ?
+ PHY_POLL : phy_irq;
}
}
bp->link = 0;
bp->speed = 0;
bp->duplex = -1;
+ bp->phy_dev = phydev;
return 0;
}
static int macb_mii_init(struct macb *bp)
{
struct macb_platform_data *pdata;
- struct device_node *np;
+ struct device_node *np, *mdio_np;
int err = -ENXIO, i;
/* Enable management port */
snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
bp->pdev->name, bp->pdev->id);
bp->mii_bus->priv = bp;
- bp->mii_bus->parent = &bp->pdev->dev;
+ bp->mii_bus->parent = &bp->dev->dev;
pdata = dev_get_platdata(&bp->pdev->dev);
dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
np = bp->pdev->dev.of_node;
- if (np) {
- if (of_phy_is_fixed_link(np)) {
- if (of_phy_register_fixed_link(np) < 0) {
- dev_err(&bp->pdev->dev,
- "broken fixed-link specification\n");
- goto err_out_unregister_bus;
- }
- bp->phy_node = of_node_get(np);
-
- err = mdiobus_register(bp->mii_bus);
- } else {
- /* try dt phy registration */
- err = of_mdiobus_register(bp->mii_bus, np);
-
- /* fallback to standard phy registration if no phy were
- * found during dt phy registration
- */
- if (!err && !phy_find_first(bp->mii_bus)) {
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *phydev;
-
- phydev = mdiobus_scan(bp->mii_bus, i);
- if (IS_ERR(phydev) &&
- PTR_ERR(phydev) != -ENODEV) {
- err = PTR_ERR(phydev);
- break;
- }
+ mdio_np = of_get_child_by_name(np, "mdio");
+ if (mdio_np) {
+ of_node_put(mdio_np);
+ err = of_mdiobus_register(bp->mii_bus, mdio_np);
+ if (err)
+ goto err_out_unregister_bus;
+ } else if (np) {
+ /* try dt phy registration */
+ err = of_mdiobus_register(bp->mii_bus, np);
+
+ /* fallback to standard phy registration if no phy were
+ * found during dt phy registration
+ */
+ if (!err && !phy_find_first(bp->mii_bus)) {
+ for (i = 0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = mdiobus_scan(bp->mii_bus, i);
+ if (IS_ERR(phydev) &&
+ PTR_ERR(phydev) != -ENODEV) {
+ err = PTR_ERR(phydev);
+ break;
}
-
- if (err)
- goto err_out_unregister_bus;
}
+
+ if (err)
+ goto err_out_unregister_bus;
}
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (pdata)
bp->mii_bus->phy_mask = pdata->phy_mask;
*/
}
+
+static int macb_validate_hw_csum(struct sk_buff *skb)
+{
+ u32 pkt_csum = *((u32 *)&skb->data[skb->len - ETH_FCS_LEN]);
+ u32 csum = ~crc32_le(~0, skb_mac_header(skb),
+ skb->len + ETH_HLEN - ETH_FCS_LEN);
+
+ return (pkt_csum != csum);
+}
+
static int gem_rx(struct macb *bp, int budget)
{
unsigned int len;
bool rxused;
entry = macb_rx_ring_wrap(bp, bp->rx_tail);
+
desc = macb_rx_desc(bp, entry);
/* Make hw descriptor updates visible to CPU */
bp->rx_buffer_size, DMA_FROM_DEVICE);
skb->protocol = eth_type_trans(skb, bp->dev);
+
+ /* Validate MAC fcs if RX checsum offload disabled */
+ if (!(bp->dev->features & NETIF_F_RXCSUM)) {
+ if (macb_validate_hw_csum(skb)) {
+ netdev_err(bp->dev, "incorrect FCS\n");
+ bp->dev->stats.rx_dropped++;
+ break;
+ }
+ }
+
skb_checksum_none_assert(skb);
if (bp->dev->features & NETIF_F_RXCSUM &&
!(bp->dev->flags & IFF_PROMISC) &&
break;
}
+ /* Validate MAC fcs if RX checsum offload disabled */
+ if (!(bp->dev->features & NETIF_F_RXCSUM)) {
+ if (macb_validate_hw_csum(skb)) {
+ netdev_err(bp->dev, "incorrect FCS\n");
+ bp->dev->stats.rx_dropped++;
+
+ /* Make descriptor updates visible to hardware */
+ wmb();
+
+ return 1;
+ }
+ }
+
/* Make descriptor updates visible to hardware */
wmb();
return work_done;
}
+static void macb_hresp_error_task(unsigned long data)
+{
+ struct macb *bp = (struct macb *)data;
+ struct net_device *dev = bp->dev;
+ struct macb_queue *queue = bp->queues;
+ unsigned int q;
+ u32 ctrl;
+
+ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+ queue_writel(queue, IDR, MACB_RX_INT_FLAGS |
+ MACB_TX_INT_FLAGS |
+ MACB_BIT(HRESP));
+ }
+ ctrl = macb_readl(bp, NCR);
+ ctrl &= ~(MACB_BIT(RE) | MACB_BIT(TE));
+ macb_writel(bp, NCR, ctrl);
+
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+
+ bp->macbgem_ops.mog_init_rings(bp);
+
+ macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma));
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
+ macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma));
+#endif
+ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+ queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
+ queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma));
+#endif
+ /* We only use the first queue at the moment. Remaining
+ * queues must be tied-off before we enable the receiver.
+ *
+ * See the documentation for receive_q1_ptr for more info.
+ */
+ if (q)
+ queue_writel(queue, RBQP,
+ lower_32_bits(bp->rx_ring_tieoff_dma));
+
+ /* Enable interrupts */
+ queue_writel(queue, IER,
+ MACB_RX_INT_FLAGS |
+ MACB_TX_INT_FLAGS |
+ MACB_BIT(HRESP));
+ }
+
+ ctrl |= MACB_BIT(RE) | MACB_BIT(TE);
+ macb_writel(bp, NCR, ctrl);
+
+ netif_carrier_on(dev);
+ netif_tx_start_all_queues(dev);
+}
+
static irqreturn_t macb_interrupt(int irq, void *dev_id)
{
struct macb_queue *queue = dev_id;
spin_lock(&bp->lock);
while (status) {
+ if (status & MACB_BIT(WOL)) {
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ queue_writel(queue, ISR, MACB_BIT(WOL));
+ break;
+ }
+
/* close possible race with dev_close */
if (unlikely(!netif_running(dev))) {
queue_writel(queue, IDR, -1);
}
if (status & MACB_BIT(HRESP)) {
- /* TODO: Reset the hardware, and maybe move the
- * netdev_err to a lower-priority context as well
- * (work queue?)
- */
+ tasklet_schedule(&bp->hresp_err_tasklet);
netdev_err(dev, "DMA bus error: HRESP not OK\n");
if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
}
}
- netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%zu]\n",
+ netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%Zu]\n",
bp->dev->mtu, bp->rx_buffer_size);
}
bp->rx_ring = NULL;
}
+ if (bp->rx_ring_tieoff) {
+ dma_free_coherent(&bp->pdev->dev, macb_dma_desc_get_size(bp),
+ bp->rx_ring_tieoff, bp->rx_ring_tieoff_dma);
+ bp->rx_ring_tieoff = NULL;
+ }
+
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
kfree(queue->tx_skb);
queue->tx_skb = NULL;
&bp->rx_ring_dma, GFP_KERNEL);
if (!bp->rx_ring)
goto out_err;
+
+ /* If we have more than one queue, allocate a tie off descriptor
+ * that will be used to disable unused RX queues.
+ */
+ if (bp->num_queues > 1) {
+ bp->rx_ring_tieoff = dma_alloc_coherent(&bp->pdev->dev,
+ macb_dma_desc_get_size(bp),
+ &bp->rx_ring_tieoff_dma,
+ GFP_KERNEL);
+ if (!bp->rx_ring_tieoff)
+ goto out_err;
+ }
+
netdev_dbg(bp->dev,
"Allocated RX ring of %d bytes at %08lx (mapped %p)\n",
size, (unsigned long)bp->rx_ring_dma, bp->rx_ring);
return -ENOMEM;
}
+static void macb_init_tieoff(struct macb *bp)
+{
+ struct macb_dma_desc *d = bp->rx_ring_tieoff;
+
+ if (bp->num_queues > 1) {
+ /* Setup a wrapping descriptor with no free slots
+ * (WRAP and USED) to tie off/disable unused RX queues.
+ */
+ macb_set_addr(bp, d, MACB_BIT(RX_WRAP) | MACB_BIT(RX_USED));
+ d->ctrl = 0;
+ }
+}
+
static void gem_init_rings(struct macb *bp)
{
struct macb_queue *queue;
bp->rx_prepared_head = 0;
gem_rx_refill(bp);
+ macb_init_tieoff(bp);
}
static void macb_init_rings(struct macb *bp)
bp->queues[0].tx_head = 0;
bp->queues[0].tx_tail = 0;
desc->ctrl |= MACB_BIT(TX_WRAP);
+
+ macb_init_tieoff(bp);
}
static void macb_reset_hw(struct macb *bp)
macb_writel(bp, TSR, -1);
macb_writel(bp, RSR, -1);
+ /* Disable RX partial store and forward and reset watermark value */
+ if (bp->caps & MACB_CAPS_PARTIAL_STORE_FORWARD)
+ gem_writel(bp, PBUFRXCUT, 0xFFF);
+
/* Disable all interrupts */
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
queue_writel(queue, IDR, -1);
config |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
config |= MACB_BF(RBOF, NET_IP_ALIGN); /* Make eth data aligned */
config |= MACB_BIT(PAE); /* PAuse Enable */
- config |= MACB_BIT(DRFCS); /* Discard Rx FCS */
+
+ /* Do not discard Rx FCS if RX checsum offload disabled */
+ if (bp->dev->features & NETIF_F_RXCSUM)
+ config |= MACB_BIT(DRFCS); /* Discard Rx FCS */
+
if (bp->caps & MACB_CAPS_JUMBO)
config |= MACB_BIT(JFRAME); /* Enable jumbo frames */
else
if ((bp->caps & MACB_CAPS_JUMBO) && bp->jumbo_max_len)
gem_writel(bp, JML, bp->jumbo_max_len);
bp->speed = SPEED_10;
- bp->duplex = DUPLEX_HALF;
+ if (bp->caps & MACB_CAPS_PARTIAL_STORE_FORWARD)
+ bp->duplex = DUPLEX_FULL;
+ else
+ bp->duplex = DUPLEX_HALF;
bp->rx_frm_len_mask = MACB_RX_FRMLEN_MASK;
if (bp->caps & MACB_CAPS_JUMBO)
bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK;
macb_configure_dma(bp);
+ /* Enable RX partial store and forward and set watermark */
+ if (bp->caps & MACB_CAPS_PARTIAL_STORE_FORWARD) {
+ gem_writel(bp, PBUFRXCUT,
+ (gem_readl(bp, PBUFRXCUT) &
+ GEM_BF(WTRMRK, bp->rx_watermark)) |
+ GEM_BIT(ENCUTTHRU));
+ }
+
/* Initialize TX and RX buffers */
macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma));
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue_writel(queue, TBQPH, upper_32_bits(queue->tx_ring_dma));
#endif
+ /* We only use the first queue at the moment. Remaining
+ * queues must be tied-off before we enable the receiver.
+ *
+ * See the documentation for receive_q1_ptr for more info.
+ */
+ if (q)
+ queue_writel(queue, RBQP,
+ lower_32_bits(bp->rx_ring_tieoff_dma));
/* Enable interrupts */
queue_writel(queue, IER,
MACB_BIT(HRESP));
}
+ if ((bp->phy_interface == PHY_INTERFACE_MODE_SGMII) &&
+ (bp->caps & MACB_CAPS_PCS))
+ gem_writel(bp, PCSCNTRL,
+ gem_readl(bp, PCSCNTRL) | GEM_BIT(PCSAUTONEG));
+
/* Enable TX and RX */
- macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE));
+ macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE) |
+ MACB_BIT(PTPUNI));
}
/* The hash address register is 64 bits long and takes up two
netdev_dbg(bp->dev, "open\n");
+ err = pm_runtime_get_sync(&bp->pdev->dev);
+ if (err < 0)
+ return err;
+
/* carrier starts down */
netif_carrier_off(dev);
/* if the phy is not yet register, retry later*/
- if (!dev->phydev)
+ if (!bp->phy_dev)
return -EAGAIN;
/* RX buffers initialization */
macb_init_hw(bp);
/* schedule a link state check */
- phy_start(dev->phydev);
+ phy_start(bp->phy_dev);
netif_tx_start_all_queues(dev);
netif_tx_stop_all_queues(dev);
napi_disable(&bp->napi);
- if (dev->phydev)
- phy_stop(dev->phydev);
+ if (bp->phy_dev)
+ phy_stop(bp->phy_dev);
spin_lock_irqsave(&bp->lock, flags);
macb_reset_hw(bp);
if (bp->ptp_info)
bp->ptp_info->ptp_remove(dev);
+ pm_runtime_put(&bp->pdev->dev);
+
return 0;
}
regs_buff[13] = gem_readl(bp, DMACFG);
}
-static void macb_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
-{
- struct macb *bp = netdev_priv(netdev);
-
- wol->supported = 0;
- wol->wolopts = 0;
-
- if (bp->wol & MACB_WOL_HAS_MAGIC_PACKET) {
- wol->supported = WAKE_MAGIC;
-
- if (bp->wol & MACB_WOL_ENABLED)
- wol->wolopts |= WAKE_MAGIC;
- }
-}
-
-static int macb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
-{
- struct macb *bp = netdev_priv(netdev);
-
- if (!(bp->wol & MACB_WOL_HAS_MAGIC_PACKET) ||
- (wol->wolopts & ~WAKE_MAGIC))
- return -EOPNOTSUPP;
-
- if (wol->wolopts & WAKE_MAGIC)
- bp->wol |= MACB_WOL_ENABLED;
- else
- bp->wol &= ~MACB_WOL_ENABLED;
-
- device_set_wakeup_enable(&bp->pdev->dev, bp->wol & MACB_WOL_ENABLED);
-
- return 0;
-}
static void macb_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
.get_regs = macb_get_regs,
.get_link = ethtool_op_get_link,
.get_ts_info = ethtool_op_get_ts_info,
- .get_wol = macb_get_wol,
- .set_wol = macb_set_wol,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_ringparam = macb_get_ringparam,
const struct macb_config *dt_conf)
{
u32 dcfg;
+ int retval;
if (dt_conf)
bp->caps = dt_conf->caps;
+ /* By default we set to partial store and forward mode for zynqmp.
+ * Disable if not set in devicetree.
+ */
+ if (bp->caps & MACB_CAPS_PARTIAL_STORE_FORWARD) {
+ retval = of_property_read_u16(bp->pdev->dev.of_node,
+ "rx-watermark",
+ &bp->rx_watermark);
+
+ /* Disable partial store and forward in case of error or
+ * invalid watermark value
+ */
+ if (retval || bp->rx_watermark > 0xFFF) {
+ dev_info(&bp->pdev->dev,
+ "Not enabling partial store and forward\n");
+ bp->caps &= ~MACB_CAPS_PARTIAL_STORE_FORWARD;
+ }
+ }
+
if (hw_is_gem(bp->regs, bp->native_io)) {
bp->caps |= MACB_CAPS_MACB_IS_GEM;
dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps);
}
+#if defined(CONFIG_OF)
+static void macb_reset_phy(struct platform_device *pdev)
+{
+ int err, phy_reset, msec = 1;
+ bool active_low;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np)
+ return;
+
+ of_property_read_u32(np, "phy-reset-duration", &msec);
+ active_low = of_property_read_bool(np, "phy-reset-active-low");
+
+ phy_reset = of_get_named_gpio(np, "phy-reset-gpio", 0);
+ if (!gpio_is_valid(phy_reset))
+ return;
+
+ err = devm_gpio_request_one(&pdev->dev, phy_reset,
+ active_low ? GPIOF_OUT_INIT_LOW :
+ GPIOF_OUT_INIT_HIGH, "phy-reset");
+ if (err) {
+ dev_err(&pdev->dev, "failed to get phy-reset-gpio: %d\n", err);
+ return;
+ }
+ msleep(msec);
+ gpio_set_value(phy_reset, active_low);
+}
+#else /* CONFIG_OF */
+static void macb_reset_phy(struct platform_device *pdev)
+{
+}
+#endif /* CONFIG_OF */
+
static void macb_probe_queues(void __iomem *mem,
bool native_io,
unsigned int *queue_mask,
static int macb_clk_init(struct platform_device *pdev, struct clk **pclk,
struct clk **hclk, struct clk **tx_clk,
- struct clk **rx_clk)
+ struct clk **rx_clk, struct clk **tsu_clk)
{
- struct macb_platform_data *pdata;
int err;
- pdata = dev_get_platdata(&pdev->dev);
- if (pdata) {
- *pclk = pdata->pclk;
- *hclk = pdata->hclk;
- } else {
- *pclk = devm_clk_get(&pdev->dev, "pclk");
- *hclk = devm_clk_get(&pdev->dev, "hclk");
- }
-
+ *pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(*pclk)) {
err = PTR_ERR(*pclk);
dev_err(&pdev->dev, "failed to get macb_clk (%u)\n", err);
return err;
}
+ *hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(*hclk)) {
err = PTR_ERR(*hclk);
dev_err(&pdev->dev, "failed to get hclk (%u)\n", err);
if (IS_ERR(*rx_clk))
*rx_clk = NULL;
+ *tsu_clk = devm_clk_get(&pdev->dev, "tsu_clk");
+ if (IS_ERR(*tsu_clk))
+ *tsu_clk = NULL;
+
err = clk_prepare_enable(*pclk);
if (err) {
dev_err(&pdev->dev, "failed to enable pclk (%u)\n", err);
goto err_disable_txclk;
}
+ err = clk_prepare_enable(*tsu_clk);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable tsu_clk (%u)\n", err);
+ goto err_disable_rxclk;
+ }
+
return 0;
+err_disable_rxclk:
+ clk_disable_unprepare(*rx_clk);
+
err_disable_txclk:
clk_disable_unprepare(*tx_clk);
if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue->TBQPH = GEM_TBQPH(hw_q - 1);
#endif
+ queue->RBQP = GEM_RBQP(hw_q - 1);
} else {
/* queue0 uses legacy registers */
queue->ISR = MACB_ISR;
if (bp->hw_dma_cap & HW_DMA_CAP_64B)
queue->TBQPH = MACB_TBQPH;
#endif
+ queue->RBQP = MACB_RBQP;
}
/* get irq: here we use the linux queue index, not the hardware
/* 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_PARTIAL_STORE_FORWARD)
+ dev->hw_features &= ~NETIF_F_RXCSUM;
if (bp->caps & MACB_CAPS_SG_DISABLED)
dev->hw_features &= ~NETIF_F_SG;
dev->features = dev->hw_features;
val |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
macb_writel(bp, NCFGR, val);
+ if ((bp->phy_interface == PHY_INTERFACE_MODE_SGMII) &&
+ (bp->caps & MACB_CAPS_PCS))
+ gem_writel(bp, PCSCNTRL,
+ gem_readl(bp, PCSCNTRL) | GEM_BIT(PCSAUTONEG));
+
return 0;
}
MACB_BIT(HRESP));
/* schedule a link state check */
- phy_start(dev->phydev);
+ phy_start(lp->phy_dev);
netif_start_queue(dev);
skb = netdev_alloc_skb(dev, pktlen + 2);
if (skb) {
skb_reserve(skb, 2);
- skb_put_data(skb, p_recv, pktlen);
+ memcpy(skb_put(skb, pktlen), p_recv, pktlen);
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_packets++;
.ndo_set_mac_address = eth_mac_addr,
.ndo_do_ioctl = macb_ioctl,
.ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = at91ether_poll_controller,
#endif
static int at91ether_clk_init(struct platform_device *pdev, struct clk **pclk,
struct clk **hclk, struct clk **tx_clk,
- struct clk **rx_clk)
+ struct clk **rx_clk, struct clk **tsu_clk)
{
int err;
*hclk = NULL;
*tx_clk = NULL;
*rx_clk = NULL;
+ *tsu_clk = NULL;
*pclk = devm_clk_get(&pdev->dev, "ether_clk");
if (IS_ERR(*pclk))
static const struct macb_config sama5d3_config = {
.caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE
- | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO,
+ | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
- .jumbo_max_len = 10240,
};
static const struct macb_config sama5d4_config = {
};
static const struct macb_config zynqmp_config = {
- .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
- MACB_CAPS_JUMBO |
- MACB_CAPS_GEM_HAS_PTP,
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO |
+ MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_PCS |
+ MACB_CAPS_PARTIAL_STORE_FORWARD | MACB_CAPS_WOL,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
.init = macb_init,
MODULE_DEVICE_TABLE(of, macb_dt_ids);
#endif /* CONFIG_OF */
-static const struct macb_config default_gem_config = {
- .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
- MACB_CAPS_JUMBO |
- MACB_CAPS_GEM_HAS_PTP,
- .dma_burst_length = 16,
- .clk_init = macb_clk_init,
- .init = macb_init,
- .jumbo_max_len = 10240,
-};
-
static int macb_probe(struct platform_device *pdev)
{
- const struct macb_config *macb_config = &default_gem_config;
int (*clk_init)(struct platform_device *, struct clk **,
- struct clk **, struct clk **, struct clk **)
- = macb_config->clk_init;
- int (*init)(struct platform_device *) = macb_config->init;
+ struct clk **, struct clk **, struct clk **,
+ struct clk **) = macb_clk_init;
+ int (*init)(struct platform_device *) = macb_init;
struct device_node *np = pdev->dev.of_node;
struct device_node *phy_node;
+ const struct macb_config *macb_config = NULL;
struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
+ struct clk *tsu_clk = NULL;
unsigned int queue_mask, num_queues;
struct macb_platform_data *pdata;
bool native_io;
}
}
- err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk);
+ err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
if (err)
return err;
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MACB_PM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
native_io = hw_is_native_io(mem);
macb_probe_queues(mem, native_io, &queue_mask, &num_queues);
bp->hclk = hclk;
bp->tx_clk = tx_clk;
bp->rx_clk = rx_clk;
+ bp->tsu_clk = tsu_clk;
+ if (tsu_clk)
+ bp->tsu_rate = clk_get_rate(tsu_clk);
+
if (macb_config)
bp->jumbo_max_len = macb_config->jumbo_max_len;
- bp->wol = 0;
- if (of_get_property(np, "magic-packet", NULL))
- bp->wol |= MACB_WOL_HAS_MAGIC_PACKET;
- device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET);
-
spin_lock_init(&bp->lock);
/* setup capabilities */
macb_get_hwaddr(bp);
/* Power up the PHY if there is a GPIO reset */
- phy_node = of_get_next_available_child(np, NULL);
- if (phy_node) {
+ phy_node = of_parse_phandle(np, "phy-handle", 0);
+ if (!phy_node && of_phy_is_fixed_link(np)) {
+ err = of_phy_register_fixed_link(np);
+ if (err < 0) {
+ dev_err(&pdev->dev, "broken fixed-link specification");
+ goto failed_phy;
+ }
+ phy_node = of_node_get(np);
+ bp->phy_node = phy_node;
+ } else {
int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
-
if (gpio_is_valid(gpio)) {
bp->reset_gpio = gpio_to_desc(gpio);
gpiod_direction_output(bp->reset_gpio, 1);
}
}
- of_node_put(phy_node);
err = of_get_phy_mode(np);
if (err < 0) {
bp->phy_interface = err;
}
+ macb_reset_phy(pdev);
+
/* IP specific init */
err = init(pdev);
if (err)
goto err_out_free_netdev;
+ err = register_netdev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
+ goto err_out_unregister_netdev;
+ }
+
err = macb_mii_init(bp);
if (err)
- goto err_out_free_netdev;
-
- phydev = dev->phydev;
+ goto err_out_unregister_netdev;
netif_carrier_off(dev);
- err = register_netdev(dev);
- if (err) {
- dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
- goto err_out_unregister_mdio;
- }
+ tasklet_init(&bp->hresp_err_tasklet, macb_hresp_error_task,
+ (unsigned long)bp);
- phy_attached_info(phydev);
+ if (bp->caps & MACB_CAPS_WOL)
+ device_set_wakeup_capable(&bp->dev->dev, 1);
netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
dev->base_addr, dev->irq, dev->dev_addr);
- return 0;
+ phydev = bp->phy_dev;
+ phy_attached_info(phydev);
+ pm_runtime_mark_last_busy(&bp->pdev->dev);
+ pm_runtime_put_autosuspend(&bp->pdev->dev);
-err_out_unregister_mdio:
- phy_disconnect(dev->phydev);
- mdiobus_unregister(bp->mii_bus);
- mdiobus_free(bp->mii_bus);
+ return 0;
- /* Shutdown the PHY if there is a GPIO reset */
- if (bp->reset_gpio)
- gpiod_set_value(bp->reset_gpio, 0);
+err_out_unregister_netdev:
+ unregister_netdev(dev);
err_out_free_netdev:
free_netdev(dev);
+failed_phy:
+ of_node_put(phy_node);
+
err_disable_clocks:
clk_disable_unprepare(tx_clk);
clk_disable_unprepare(hclk);
clk_disable_unprepare(pclk);
clk_disable_unprepare(rx_clk);
+ clk_disable_unprepare(tsu_clk);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
return err;
}
if (dev) {
bp = netdev_priv(dev);
- if (dev->phydev)
- phy_disconnect(dev->phydev);
+ if (bp->phy_dev)
+ phy_disconnect(bp->phy_dev);
mdiobus_unregister(bp->mii_bus);
dev->phydev = NULL;
mdiobus_free(bp->mii_bus);
gpiod_set_value(bp->reset_gpio, 0);
unregister_netdev(dev);
- clk_disable_unprepare(bp->tx_clk);
- clk_disable_unprepare(bp->hclk);
- clk_disable_unprepare(bp->pclk);
- clk_disable_unprepare(bp->rx_clk);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ if (!pm_runtime_suspended(&pdev->dev)) {
+ clk_disable_unprepare(bp->tx_clk);
+ clk_disable_unprepare(bp->hclk);
+ clk_disable_unprepare(bp->pclk);
+ clk_disable_unprepare(bp->rx_clk);
+ clk_disable_unprepare(bp->tsu_clk);
+ pm_runtime_set_suspended(&pdev->dev);
+ }
of_node_put(bp->phy_node);
free_netdev(dev);
}
struct platform_device *pdev = to_platform_device(dev);
struct net_device *netdev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(netdev);
+ struct macb_queue *queue = bp->queues;
+ unsigned long flags;
+ unsigned int q;
+ u32 ctrl, arpipmask;
- netif_carrier_off(netdev);
- netif_device_detach(netdev);
+ if (!netif_running(netdev))
+ return 0;
- if (bp->wol & MACB_WOL_ENABLED) {
+ if (device_may_wakeup(&bp->dev->dev)) {
+ spin_lock_irqsave(&bp->lock, flags);
+ ctrl = macb_readl(bp, NCR);
+ ctrl &= ~(MACB_BIT(TE) | MACB_BIT(RE));
+ macb_writel(bp, NCR, ctrl);
+ /* Tie off RXQ0 as well */
+ macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_tieoff_dma));
+ ctrl = macb_readl(bp, NCR);
+ ctrl |= MACB_BIT(RE);
+ macb_writel(bp, NCR, ctrl);
+ gem_writel(bp, NCFGR, gem_readl(bp, NCFGR) & ~MACB_BIT(NBC));
+ macb_writel(bp, TSR, -1);
+ macb_writel(bp, RSR, -1);
+ macb_readl(bp, ISR);
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ macb_writel(bp, ISR, -1);
+
+ /* Enable WOL (Q0 only) and disable all other interrupts */
macb_writel(bp, IER, MACB_BIT(WOL));
- macb_writel(bp, WOL, MACB_BIT(MAG));
+ for (q = 1, queue = bp->queues; q < bp->num_queues;
+ ++q, ++queue) {
+ queue_writel(queue, IDR, MACB_RX_INT_FLAGS |
+ MACB_TX_INT_FLAGS |
+ MACB_BIT(HRESP));
+ }
+
+ arpipmask = cpu_to_be32p(&bp->dev->ip_ptr->ifa_list->ifa_local)
+ & 0xFFFF;
+ gem_writel(bp, WOL, MACB_BIT(ARP) | arpipmask);
+ spin_unlock_irqrestore(&bp->lock, flags);
enable_irq_wake(bp->queues[0].irq);
+ netif_device_detach(netdev);
+ napi_disable(&bp->napi);
} else {
- clk_disable_unprepare(bp->tx_clk);
- clk_disable_unprepare(bp->hclk);
- clk_disable_unprepare(bp->pclk);
- clk_disable_unprepare(bp->rx_clk);
+ netif_device_detach(netdev);
+ napi_disable(&bp->napi);
+ phy_stop(bp->phy_dev);
+ phy_suspend(bp->phy_dev);
+ spin_lock_irqsave(&bp->lock, flags);
+ macb_reset_hw(bp);
+ spin_unlock_irqrestore(&bp->lock, flags);
}
+ netif_carrier_off(netdev);
+ if (bp->ptp_info)
+ bp->ptp_info->ptp_remove(netdev);
+ pm_runtime_force_suspend(dev);
+
return 0;
}
struct platform_device *pdev = to_platform_device(dev);
struct net_device *netdev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(netdev);
+ unsigned long flags;
+
+ if (!netif_running(netdev))
+ return 0;
+
+ pm_runtime_force_resume(dev);
- if (bp->wol & MACB_WOL_ENABLED) {
+ if (device_may_wakeup(&bp->dev->dev)) {
+ spin_lock_irqsave(&bp->lock, flags);
macb_writel(bp, IDR, MACB_BIT(WOL));
- macb_writel(bp, WOL, 0);
+ gem_writel(bp, WOL, 0);
+ /* Clear Q0 ISR as WOL was enabled on Q0 */
+ if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
+ macb_writel(bp, ISR, -1);
disable_irq_wake(bp->queues[0].irq);
+ spin_unlock_irqrestore(&bp->lock, flags);
+ macb_writel(bp, NCR, MACB_BIT(MPE));
+ napi_enable(&bp->napi);
+ netif_carrier_on(netdev);
} else {
+ macb_writel(bp, NCR, MACB_BIT(MPE));
+ napi_enable(&bp->napi);
+ netif_carrier_on(netdev);
+ phy_resume(bp->phy_dev);
+ phy_start(bp->phy_dev);
+ }
+
+ bp->macbgem_ops.mog_init_rings(bp);
+ macb_init_hw(bp);
+ macb_set_rx_mode(netdev);
+ netif_device_attach(netdev);
+ if (bp->ptp_info)
+ bp->ptp_info->ptp_init(netdev);
+
+ return 0;
+}
+
+static int __maybe_unused macb_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct macb *bp = netdev_priv(netdev);
+
+ if (!(device_may_wakeup(&bp->dev->dev))) {
+ clk_disable_unprepare(bp->tx_clk);
+ clk_disable_unprepare(bp->hclk);
+ clk_disable_unprepare(bp->pclk);
+ clk_disable_unprepare(bp->rx_clk);
+ }
+ clk_disable_unprepare(bp->tsu_clk);
+
+ return 0;
+}
+
+static int __maybe_unused macb_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct macb *bp = netdev_priv(netdev);
+
+ if (!(device_may_wakeup(&bp->dev->dev))) {
clk_prepare_enable(bp->pclk);
clk_prepare_enable(bp->hclk);
clk_prepare_enable(bp->tx_clk);
clk_prepare_enable(bp->rx_clk);
}
-
- netif_device_attach(netdev);
+ clk_prepare_enable(bp->tsu_clk);
return 0;
}
-static SIMPLE_DEV_PM_OPS(macb_pm_ops, macb_suspend, macb_resume);
+static const struct dev_pm_ops macb_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(macb_suspend, macb_resume)
+ SET_RUNTIME_PM_OPS(macb_runtime_suspend, macb_runtime_resume, NULL)
+};
static struct platform_driver macb_driver = {
.probe = macb_probe,