]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/cadence/macb.c
net/macb: merge at91_ether driver into macb driver
[karo-tx-linux.git] / drivers / net / ethernet / cadence / macb.c
index 2bb3c1c06e605c7165755bfbbeb20dada80b2945..a4c5462c071a968713f36c0861bc1b8d78d54dae 100644 (file)
@@ -102,7 +102,7 @@ static void *macb_rx_buffer(struct macb *bp, unsigned int index)
        return bp->rx_buffers + bp->rx_buffer_size * macb_rx_ring_wrap(index);
 }
 
-void macb_set_hwaddr(struct macb *bp)
+static void macb_set_hwaddr(struct macb *bp)
 {
        u32 bottom;
        u16 top;
@@ -120,9 +120,8 @@ void macb_set_hwaddr(struct macb *bp)
        macb_or_gem_writel(bp, SA4B, 0);
        macb_or_gem_writel(bp, SA4T, 0);
 }
-EXPORT_SYMBOL_GPL(macb_set_hwaddr);
 
-void macb_get_hwaddr(struct macb *bp)
+static void macb_get_hwaddr(struct macb *bp)
 {
        struct macb_platform_data *pdata;
        u32 bottom;
@@ -162,7 +161,6 @@ void macb_get_hwaddr(struct macb *bp)
        netdev_info(bp->dev, "invalid hw address, using random\n");
        eth_hw_addr_random(bp->dev);
 }
-EXPORT_SYMBOL_GPL(macb_get_hwaddr);
 
 static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
@@ -359,7 +357,7 @@ static int macb_mii_probe(struct net_device *dev)
        return 0;
 }
 
-int macb_mii_init(struct macb *bp)
+static int macb_mii_init(struct macb *bp)
 {
        struct macb_platform_data *pdata;
        struct device_node *np;
@@ -440,7 +438,6 @@ err_out_free_mdiobus:
 err_out:
        return err;
 }
-EXPORT_SYMBOL_GPL(macb_mii_init);
 
 static void macb_update_stats(struct macb *bp)
 {
@@ -1743,7 +1740,7 @@ static void macb_sethashtable(struct net_device *dev)
 /*
  * Enable/Disable promiscuous and multicast modes.
  */
-void macb_set_rx_mode(struct net_device *dev)
+static void macb_set_rx_mode(struct net_device *dev)
 {
        unsigned long cfg;
        struct macb *bp = netdev_priv(dev);
@@ -1784,7 +1781,6 @@ void macb_set_rx_mode(struct net_device *dev)
 
        macb_writel(bp, NCFGR, cfg);
 }
-EXPORT_SYMBOL_GPL(macb_set_rx_mode);
 
 static int macb_open(struct net_device *dev)
 {
@@ -1937,7 +1933,7 @@ static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p)
        }
 }
 
-struct net_device_stats *macb_get_stats(struct net_device *dev)
+static struct net_device_stats *macb_get_stats(struct net_device *dev)
 {
        struct macb *bp = netdev_priv(dev);
        struct net_device_stats *nstat = &bp->stats;
@@ -1983,7 +1979,6 @@ struct net_device_stats *macb_get_stats(struct net_device *dev)
 
        return nstat;
 }
-EXPORT_SYMBOL_GPL(macb_get_stats);
 
 static int macb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
@@ -2045,7 +2040,7 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        }
 }
 
-const struct ethtool_ops macb_ethtool_ops = {
+static const struct ethtool_ops macb_ethtool_ops = {
        .get_settings           = macb_get_settings,
        .set_settings           = macb_set_settings,
        .get_regs_len           = macb_get_regs_len,
@@ -2053,7 +2048,6 @@ const struct ethtool_ops macb_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_ts_info            = ethtool_op_get_ts_info,
 };
-EXPORT_SYMBOL_GPL(macb_ethtool_ops);
 
 static const struct ethtool_ops gem_ethtool_ops = {
        .get_settings           = macb_get_settings,
@@ -2067,7 +2061,7 @@ static const struct ethtool_ops gem_ethtool_ops = {
        .get_sset_count         = gem_get_sset_count,
 };
 
-int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct macb *bp = netdev_priv(dev);
        struct phy_device *phydev = bp->phy_dev;
@@ -2080,7 +2074,6 @@ int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 
        return phy_mii_ioctl(phydev, rq, cmd);
 }
-EXPORT_SYMBOL_GPL(macb_ioctl);
 
 static int macb_set_features(struct net_device *netdev,
                             netdev_features_t features)
@@ -2132,39 +2125,6 @@ static const struct net_device_ops macb_netdev_ops = {
        .ndo_set_features       = macb_set_features,
 };
 
-#if defined(CONFIG_OF)
-static struct macb_config at91sam9260_config = {
-       .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII,
-};
-
-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", .data = &at91sam9260_config },
-       { .compatible = "cdns,macb" },
-       { .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
@@ -2172,22 +2132,6 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids);
 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;
@@ -2230,92 +2174,57 @@ static void macb_probe_queues(void __iomem *mem,
                        (*num_queues)++;
 }
 
-static int macb_probe(struct platform_device *pdev)
+static int macb_init(struct platform_device *pdev)
 {
-       struct macb_platform_data *pdata;
-       struct resource *regs;
-       struct net_device *dev;
-       struct macb *bp;
-       struct macb_queue *queue;
-       struct phy_device *phydev;
-       u32 config;
-       int err = -ENXIO;
-       const char *mac;
-       void __iomem *mem;
+       struct net_device *dev = platform_get_drvdata(pdev);
        unsigned int hw_q, queue_mask, q, num_queues;
-       struct clk *pclk, *hclk, *tx_clk;
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!regs) {
-               dev_err(&pdev->dev, "no mmio resource defined\n");
-               goto err_out;
-       }
+       struct macb *bp = netdev_priv(dev);
+       struct macb_queue *queue;
+       int err;
+       u32 val;
 
-       pclk = devm_clk_get(&pdev->dev, "pclk");
-       if (IS_ERR(pclk)) {
-               err = PTR_ERR(pclk);
+       bp->pclk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(bp->pclk)) {
+               err = PTR_ERR(bp->pclk);
                dev_err(&pdev->dev, "failed to get macb_clk (%u)\n", err);
-               goto err_out;
+               return err;
        }
 
-       hclk = devm_clk_get(&pdev->dev, "hclk");
-       if (IS_ERR(hclk)) {
-               err = PTR_ERR(hclk);
+       bp->hclk = devm_clk_get(&pdev->dev, "hclk");
+       if (IS_ERR(bp->hclk)) {
+               err = PTR_ERR(bp->hclk);
                dev_err(&pdev->dev, "failed to get hclk (%u)\n", err);
-               goto err_out;
+               return err;
        }
 
-       tx_clk = devm_clk_get(&pdev->dev, "tx_clk");
-       if (IS_ERR(tx_clk))
-               tx_clk = NULL;
+       bp->tx_clk = devm_clk_get(&pdev->dev, "tx_clk");
+       if (IS_ERR(bp->tx_clk))
+               bp->tx_clk = NULL;
 
-       err = clk_prepare_enable(pclk);
+       err = clk_prepare_enable(bp->pclk);
        if (err) {
                dev_err(&pdev->dev, "failed to enable pclk (%u)\n", err);
-               goto err_out;
+               return err;
        }
 
-       err = clk_prepare_enable(hclk);
+       err = clk_prepare_enable(bp->hclk);
        if (err) {
                dev_err(&pdev->dev, "failed to enable hclk (%u)\n", err);
-               goto err_out_disable_pclk;
+               goto err_disable_pclk;
        }
 
-       err = clk_prepare_enable(tx_clk);
+       err = clk_prepare_enable(bp->tx_clk);
        if (err) {
                dev_err(&pdev->dev, "failed to enable tx_clk (%u)\n", err);
-               goto err_out_disable_hclk;
+               goto err_disable_hclk;
        }
 
-       err = -ENOMEM;
-       mem = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
-       if (!mem) {
-               dev_err(&pdev->dev, "failed to map registers, aborting.\n");
-               goto err_out_disable_clocks;
-       }
-
-       macb_probe_queues(mem, &queue_mask, &num_queues);
-       dev = alloc_etherdev_mq(sizeof(*bp), num_queues);
-       if (!dev)
-               goto err_out_disable_clocks;
-
-       SET_NETDEV_DEV(dev, &pdev->dev);
-
-       bp = netdev_priv(dev);
-       bp->pdev = pdev;
-       bp->dev = dev;
-       bp->regs = mem;
-       bp->num_queues = num_queues;
-       bp->pclk = pclk;
-       bp->hclk = hclk;
-       bp->tx_clk = tx_clk;
-
-       spin_lock_init(&bp->lock);
-
        /* set the queue register mapping once for all: queue0 has a special
         * register mapping but we don't want to test the queue index then
         * compute the corresponding register offset at run time.
         */
+       macb_probe_queues(bp->regs, &queue_mask, &num_queues);
+
        for (hw_q = 0, q = 0; hw_q < MACB_MAX_QUEUES; ++hw_q) {
                if (!(queue_mask & (1 << hw_q)))
                        continue;
@@ -2349,22 +2258,16 @@ static int macb_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev,
                                "Unable to request IRQ %d (error %d)\n",
                                queue->irq, err);
-                       goto err_out_free_netdev;
+                       goto err_disable_tx_clk;
                }
 
                INIT_WORK(&queue->tx_error_task, macb_tx_error_task);
                q++;
        }
-       dev->irq = bp->queues[0].irq;
 
        dev->netdev_ops = &macb_netdev_ops;
        netif_napi_add(dev, &bp->napi, macb_poll, 64);
 
-       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;
@@ -2391,18 +2294,439 @@ static int macb_probe(struct platform_device *pdev)
                dev->hw_features &= ~NETIF_F_SG;
        dev->features = dev->hw_features;
 
+       val = 0;
+       if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
+               val = GEM_BIT(RGMII);
+       else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
+                (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+               val = MACB_BIT(RMII);
+       else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+               val = MACB_BIT(MII);
+
+       if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
+               val |= MACB_BIT(CLKEN);
+
+       macb_or_gem_writel(bp, USRIO, val);
+
+       /* setup capacities */
+       macb_configure_caps(bp);
+
        /* Set MII management clock divider */
-       config = macb_mdc_clk_div(bp);
-       config |= macb_dbw(bp);
-       macb_writel(bp, NCFGR, config);
+       val = macb_mdc_clk_div(bp);
+       val |= macb_dbw(bp);
+       macb_writel(bp, NCFGR, val);
+
+       return 0;
+
+err_disable_tx_clk:
+       clk_disable_unprepare(bp->tx_clk);
+
+err_disable_hclk:
+       clk_disable_unprepare(bp->hclk);
+
+err_disable_pclk:
+       clk_disable_unprepare(bp->pclk);
+
+       return err;
+}
+
+#if defined(CONFIG_OF)
+/* 1518 rounded up */
+#define AT91ETHER_MAX_RBUFF_SZ 0x600
+/* max number of receive buffers */
+#define AT91ETHER_MAX_RX_DESCR 9
+
+/* Initialize and start the Receiver and Transmit subsystems */
+static int at91ether_start(struct net_device *dev)
+{
+       struct macb *lp = netdev_priv(dev);
+       dma_addr_t addr;
+       u32 ctl;
+       int i;
+
+       lp->rx_ring = dma_alloc_coherent(&lp->pdev->dev,
+                                        (AT91ETHER_MAX_RX_DESCR *
+                                         sizeof(struct macb_dma_desc)),
+                                        &lp->rx_ring_dma, GFP_KERNEL);
+       if (!lp->rx_ring)
+               return -ENOMEM;
+
+       lp->rx_buffers = dma_alloc_coherent(&lp->pdev->dev,
+                                           AT91ETHER_MAX_RX_DESCR *
+                                           AT91ETHER_MAX_RBUFF_SZ,
+                                           &lp->rx_buffers_dma, GFP_KERNEL);
+       if (!lp->rx_buffers) {
+               dma_free_coherent(&lp->pdev->dev,
+                                 AT91ETHER_MAX_RX_DESCR *
+                                 sizeof(struct macb_dma_desc),
+                                 lp->rx_ring, lp->rx_ring_dma);
+               lp->rx_ring = NULL;
+               return -ENOMEM;
+       }
+
+       addr = lp->rx_buffers_dma;
+       for (i = 0; i < AT91ETHER_MAX_RX_DESCR; i++) {
+               lp->rx_ring[i].addr = addr;
+               lp->rx_ring[i].ctrl = 0;
+               addr += AT91ETHER_MAX_RBUFF_SZ;
+       }
+
+       /* Set the Wrap bit on the last descriptor */
+       lp->rx_ring[AT91ETHER_MAX_RX_DESCR - 1].addr |= MACB_BIT(RX_WRAP);
+
+       /* Reset buffer index */
+       lp->rx_tail = 0;
+
+       /* Program address of descriptor list in Rx Buffer Queue register */
+       macb_writel(lp, RBQP, lp->rx_ring_dma);
+
+       /* Enable Receive and Transmit */
+       ctl = macb_readl(lp, NCR);
+       macb_writel(lp, NCR, ctl | MACB_BIT(RE) | MACB_BIT(TE));
+
+       return 0;
+}
+
+/* Open the ethernet interface */
+static int at91ether_open(struct net_device *dev)
+{
+       struct macb *lp = netdev_priv(dev);
+       u32 ctl;
+       int ret;
+
+       /* Clear internal statistics */
+       ctl = macb_readl(lp, NCR);
+       macb_writel(lp, NCR, ctl | MACB_BIT(CLRSTAT));
+
+       macb_set_hwaddr(lp);
+
+       ret = at91ether_start(dev);
+       if (ret)
+               return ret;
+
+       /* Enable MAC interrupts */
+       macb_writel(lp, IER, MACB_BIT(RCOMP)    |
+                            MACB_BIT(RXUBR)    |
+                            MACB_BIT(ISR_TUND) |
+                            MACB_BIT(ISR_RLE)  |
+                            MACB_BIT(TCOMP)    |
+                            MACB_BIT(ISR_ROVR) |
+                            MACB_BIT(HRESP));
+
+       /* schedule a link state check */
+       phy_start(lp->phy_dev);
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+/* Close the interface */
+static int at91ether_close(struct net_device *dev)
+{
+       struct macb *lp = netdev_priv(dev);
+       u32 ctl;
+
+       /* Disable Receiver and Transmitter */
+       ctl = macb_readl(lp, NCR);
+       macb_writel(lp, NCR, ctl & ~(MACB_BIT(TE) | MACB_BIT(RE)));
+
+       /* Disable MAC interrupts */
+       macb_writel(lp, IDR, MACB_BIT(RCOMP)    |
+                            MACB_BIT(RXUBR)    |
+                            MACB_BIT(ISR_TUND) |
+                            MACB_BIT(ISR_RLE)  |
+                            MACB_BIT(TCOMP)    |
+                            MACB_BIT(ISR_ROVR) |
+                            MACB_BIT(HRESP));
+
+       netif_stop_queue(dev);
+
+       dma_free_coherent(&lp->pdev->dev,
+                         AT91ETHER_MAX_RX_DESCR *
+                         sizeof(struct macb_dma_desc),
+                         lp->rx_ring, lp->rx_ring_dma);
+       lp->rx_ring = NULL;
+
+       dma_free_coherent(&lp->pdev->dev,
+                         AT91ETHER_MAX_RX_DESCR * AT91ETHER_MAX_RBUFF_SZ,
+                         lp->rx_buffers, lp->rx_buffers_dma);
+       lp->rx_buffers = NULL;
+
+       return 0;
+}
+
+/* Transmit packet */
+static int at91ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct macb *lp = netdev_priv(dev);
+
+       if (macb_readl(lp, TSR) & MACB_BIT(RM9200_BNQ)) {
+               netif_stop_queue(dev);
+
+               /* Store packet information (to free when Tx completed) */
+               lp->skb = skb;
+               lp->skb_length = skb->len;
+               lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len,
+                                                       DMA_TO_DEVICE);
+
+               /* Set address of the data in the Transmit Address register */
+               macb_writel(lp, TAR, lp->skb_physaddr);
+               /* Set length of the packet in the Transmit Control register */
+               macb_writel(lp, TCR, skb->len);
 
-       mac = of_get_mac_address(pdev->dev.of_node);
+       } else {
+               netdev_err(dev, "%s called, but device is busy!\n", __func__);
+               return NETDEV_TX_BUSY;
+       }
+
+       return NETDEV_TX_OK;
+}
+
+/* Extract received frame from buffer descriptors and sent to upper layers.
+ * (Called from interrupt context)
+ */
+static void at91ether_rx(struct net_device *dev)
+{
+       struct macb *lp = netdev_priv(dev);
+       unsigned char *p_recv;
+       struct sk_buff *skb;
+       unsigned int pktlen;
+
+       while (lp->rx_ring[lp->rx_tail].addr & MACB_BIT(RX_USED)) {
+               p_recv = lp->rx_buffers + lp->rx_tail * AT91ETHER_MAX_RBUFF_SZ;
+               pktlen = MACB_BF(RX_FRMLEN, lp->rx_ring[lp->rx_tail].ctrl);
+               skb = netdev_alloc_skb(dev, pktlen + 2);
+               if (skb) {
+                       skb_reserve(skb, 2);
+                       memcpy(skb_put(skb, pktlen), p_recv, pktlen);
+
+                       skb->protocol = eth_type_trans(skb, dev);
+                       lp->stats.rx_packets++;
+                       lp->stats.rx_bytes += pktlen;
+                       netif_rx(skb);
+               } else {
+                       lp->stats.rx_dropped++;
+               }
+
+               if (lp->rx_ring[lp->rx_tail].ctrl & MACB_BIT(RX_MHASH_MATCH))
+                       lp->stats.multicast++;
+
+               /* reset ownership bit */
+               lp->rx_ring[lp->rx_tail].addr &= ~MACB_BIT(RX_USED);
+
+               /* wrap after last buffer */
+               if (lp->rx_tail == AT91ETHER_MAX_RX_DESCR - 1)
+                       lp->rx_tail = 0;
+               else
+                       lp->rx_tail++;
+       }
+}
+
+/* MAC interrupt handler */
+static irqreturn_t at91ether_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct macb *lp = netdev_priv(dev);
+       u32 intstatus, ctl;
+
+       /* MAC Interrupt Status register indicates what interrupts are pending.
+        * It is automatically cleared once read.
+        */
+       intstatus = macb_readl(lp, ISR);
+
+       /* Receive complete */
+       if (intstatus & MACB_BIT(RCOMP))
+               at91ether_rx(dev);
+
+       /* Transmit complete */
+       if (intstatus & MACB_BIT(TCOMP)) {
+               /* The TCOM bit is set even if the transmission failed */
+               if (intstatus & (MACB_BIT(ISR_TUND) | MACB_BIT(ISR_RLE)))
+                       lp->stats.tx_errors++;
+
+               if (lp->skb) {
+                       dev_kfree_skb_irq(lp->skb);
+                       lp->skb = NULL;
+                       dma_unmap_single(NULL, lp->skb_physaddr,
+                                        lp->skb_length, DMA_TO_DEVICE);
+                       lp->stats.tx_packets++;
+                       lp->stats.tx_bytes += lp->skb_length;
+               }
+               netif_wake_queue(dev);
+       }
+
+       /* Work-around for EMAC Errata section 41.3.1 */
+       if (intstatus & MACB_BIT(RXUBR)) {
+               ctl = macb_readl(lp, NCR);
+               macb_writel(lp, NCR, ctl & ~MACB_BIT(RE));
+               macb_writel(lp, NCR, ctl | MACB_BIT(RE));
+       }
+
+       if (intstatus & MACB_BIT(ISR_ROVR))
+               netdev_err(dev, "ROVR error\n");
+
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void at91ether_poll_controller(struct net_device *dev)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       at91ether_interrupt(dev->irq, dev);
+       local_irq_restore(flags);
+}
+#endif
+
+static const struct net_device_ops at91ether_netdev_ops = {
+       .ndo_open               = at91ether_open,
+       .ndo_stop               = at91ether_close,
+       .ndo_start_xmit         = at91ether_start_xmit,
+       .ndo_get_stats          = macb_get_stats,
+       .ndo_set_rx_mode        = macb_set_rx_mode,
+       .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_init(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct macb *bp = netdev_priv(dev);
+       int err;
+       u32 reg;
+
+       bp->pclk = devm_clk_get(&pdev->dev, "ether_clk");
+       if (IS_ERR(bp->pclk))
+               return PTR_ERR(bp->pclk);
+
+       err = clk_prepare_enable(bp->pclk);
+       if (err) {
+               dev_err(&pdev->dev, "failed to enable pclk (%u)\n", err);
+               return err;
+       }
+
+       dev->netdev_ops = &at91ether_netdev_ops;
+       dev->ethtool_ops = &macb_ethtool_ops;
+
+       err = devm_request_irq(&pdev->dev, dev->irq, at91ether_interrupt,
+                              0, dev->name, dev);
+       if (err)
+               goto err_disable_clk;
+
+       macb_writel(bp, NCR, 0);
+
+       reg = MACB_BF(CLK, MACB_CLK_DIV32) | MACB_BIT(BIG);
+       if (bp->phy_interface == PHY_INTERFACE_MODE_RMII)
+               reg |= MACB_BIT(RM9200_RMII);
+
+       macb_writel(bp, NCFGR, reg);
+
+       return 0;
+
+err_disable_clk:
+       clk_disable_unprepare(bp->pclk);
+
+       return err;
+}
+
+static struct macb_config at91sam9260_config = {
+       .caps = MACB_CAPS_USRIO_HAS_CLKEN | MACB_CAPS_USRIO_DEFAULT_IS_MII,
+       .init = macb_init,
+};
+
+static struct macb_config pc302gem_config = {
+       .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+       .dma_burst_length = 16,
+       .init = macb_init,
+};
+
+static struct macb_config sama5d3_config = {
+       .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
+       .dma_burst_length = 16,
+       .init = macb_init,
+};
+
+static struct macb_config sama5d4_config = {
+       .caps = 0,
+       .dma_burst_length = 4,
+       .init = macb_init,
+};
+
+static struct macb_config emac_config = {
+       .init = at91ether_init,
+};
+
+static const struct of_device_id macb_dt_ids[] = {
+       { .compatible = "cdns,at32ap7000-macb" },
+       { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
+       { .compatible = "cdns,macb" },
+       { .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 },
+       { .compatible = "cdns,at91rm9200-emac", .data = &emac_config },
+       { .compatible = "cdns,emac", .data = &emac_config },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, macb_dt_ids);
+#endif /* CONFIG_OF */
+
+static int macb_probe(struct platform_device *pdev)
+{
+       int (*init)(struct platform_device *) = macb_init;
+       struct device_node *np = pdev->dev.of_node;
+       const struct macb_config *macb_config = NULL;
+       unsigned int queue_mask, num_queues;
+       struct macb_platform_data *pdata;
+       struct phy_device *phydev;
+       struct net_device *dev;
+       struct resource *regs;
+       void __iomem *mem;
+       const char *mac;
+       struct macb *bp;
+       int err;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mem = devm_ioremap_resource(&pdev->dev, regs);
+       if (IS_ERR(mem))
+               return PTR_ERR(mem);
+
+       macb_probe_queues(mem, &queue_mask, &num_queues);
+       dev = alloc_etherdev_mq(sizeof(*bp), num_queues);
+       if (!dev)
+               return -ENOMEM;
+
+       dev->base_addr = regs->start;
+
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       bp = netdev_priv(dev);
+       bp->pdev = pdev;
+       bp->dev = dev;
+       bp->regs = mem;
+       bp->num_queues = num_queues;
+       spin_lock_init(&bp->lock);
+
+       platform_set_drvdata(pdev, dev);
+
+       dev->irq = platform_get_irq(pdev, 0);
+       if (dev->irq < 0)
+               return dev->irq;
+
+       mac = of_get_mac_address(np);
        if (mac)
                memcpy(bp->dev->dev_addr, mac, ETH_ALEN);
        else
                macb_get_hwaddr(bp);
 
-       err = of_get_phy_mode(pdev->dev.of_node);
+       err = of_get_phy_mode(np);
        if (err < 0) {
                pdata = dev_get_platdata(&pdev->dev);
                if (pdata && pdata->is_rmii)
@@ -2413,32 +2737,35 @@ static int macb_probe(struct platform_device *pdev)
                bp->phy_interface = err;
        }
 
-       config = 0;
-       if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
-               config = GEM_BIT(RGMII);
-       else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
-                (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
-               config = MACB_BIT(RMII);
-       else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
-               config = MACB_BIT(MII);
+       if (np) {
+               const struct of_device_id *match;
 
-       if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
-               config |= MACB_BIT(CLKEN);
+               match = of_match_node(macb_dt_ids, np);
+               if (match)
+                       macb_config = match->data;
+       }
+
+       if (macb_config) {
+               bp->caps = macb_config->caps;
+               bp->dma_burst_length = macb_config->dma_burst_length;
+               init = macb_config->init;
+       }
 
-       macb_or_gem_writel(bp, USRIO, config);
+       /* 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_free_netdev;
+               goto err_disable_clocks;
        }
 
        err = macb_mii_init(bp);
        if (err)
                goto err_out_unregister_netdev;
 
-       platform_set_drvdata(pdev, dev);
-
        netif_carrier_off(dev);
 
        netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
@@ -2453,15 +2780,15 @@ static int macb_probe(struct platform_device *pdev)
 
 err_out_unregister_netdev:
        unregister_netdev(dev);
+
+err_disable_clocks:
+       clk_disable_unprepare(bp->tx_clk);
+       clk_disable_unprepare(bp->hclk);
+       clk_disable_unprepare(bp->pclk);
+
 err_out_free_netdev:
        free_netdev(dev);
-err_out_disable_clocks:
-       clk_disable_unprepare(tx_clk);
-err_out_disable_hclk:
-       clk_disable_unprepare(hclk);
-err_out_disable_pclk:
-       clk_disable_unprepare(pclk);
-err_out:
+
        return err;
 }