net: macb: Probe MDIO bus before registering netdev
[deliverable/linux.git] / drivers / net / ethernet / cadence / macb.c
index 6619178ed77b6f3fc395cdc5839eeae0fd4c8663..a63551d0a18a4b63a5ea385a46fabef39b0d36a8 100644 (file)
@@ -441,7 +441,7 @@ static int macb_mii_init(struct macb *bp)
        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->dev->dev;
+       bp->mii_bus->parent = &bp->pdev->dev;
        pdata = dev_get_platdata(&bp->pdev->dev);
 
        dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
@@ -458,7 +458,8 @@ static int macb_mii_init(struct macb *bp)
                                struct phy_device *phydev;
 
                                phydev = mdiobus_scan(bp->mii_bus, i);
-                               if (IS_ERR(phydev)) {
+                               if (IS_ERR(phydev) &&
+                                   PTR_ERR(phydev) != -ENODEV) {
                                        err = PTR_ERR(phydev);
                                        break;
                                }
@@ -917,7 +918,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
                unsigned int frag_len = bp->rx_buffer_size;
 
                if (offset + frag_len > len) {
-                       BUG_ON(frag != last_frag);
+                       if (unlikely(frag != last_frag)) {
+                               dev_kfree_skb_any(skb);
+                               return -1;
+                       }
                        frag_len = len - offset;
                }
                skb_copy_to_linear_data_offset(skb, offset,
@@ -945,8 +949,23 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
        return 0;
 }
 
+static inline void macb_init_rx_ring(struct macb *bp)
+{
+       dma_addr_t addr;
+       int i;
+
+       addr = bp->rx_buffers_dma;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               bp->rx_ring[i].addr = addr;
+               bp->rx_ring[i].ctrl = 0;
+               addr += bp->rx_buffer_size;
+       }
+       bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP);
+}
+
 static int macb_rx(struct macb *bp, int budget)
 {
+       bool reset_rx_queue = false;
        int received = 0;
        unsigned int tail;
        int first_frag = -1;
@@ -972,10 +991,18 @@ static int macb_rx(struct macb *bp, int budget)
 
                if (ctrl & MACB_BIT(RX_EOF)) {
                        int dropped;
-                       BUG_ON(first_frag == -1);
+
+                       if (unlikely(first_frag == -1)) {
+                               reset_rx_queue = true;
+                               continue;
+                       }
 
                        dropped = macb_rx_frame(bp, first_frag, tail);
                        first_frag = -1;
+                       if (unlikely(dropped < 0)) {
+                               reset_rx_queue = true;
+                               continue;
+                       }
                        if (!dropped) {
                                received++;
                                budget--;
@@ -983,6 +1010,26 @@ static int macb_rx(struct macb *bp, int budget)
                }
        }
 
+       if (unlikely(reset_rx_queue)) {
+               unsigned long flags;
+               u32 ctrl;
+
+               netdev_err(bp->dev, "RX queue corruption: reset it\n");
+
+               spin_lock_irqsave(&bp->lock, flags);
+
+               ctrl = macb_readl(bp, NCR);
+               macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE));
+
+               macb_init_rx_ring(bp);
+               macb_writel(bp, RBQP, bp->rx_ring_dma);
+
+               macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
+
+               spin_unlock_irqrestore(&bp->lock, flags);
+               return received;
+       }
+
        if (first_frag != -1)
                bp->rx_tail = first_frag;
        else
@@ -1100,7 +1147,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
                        macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
 
                        if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE)
-                               macb_writel(bp, ISR, MACB_BIT(RXUBR));
+                               queue_writel(queue, ISR, MACB_BIT(RXUBR));
                }
 
                if (status & MACB_BIT(ISR_ROVR)) {
@@ -1523,15 +1570,8 @@ static void gem_init_rings(struct macb *bp)
 static void macb_init_rings(struct macb *bp)
 {
        int i;
-       dma_addr_t addr;
 
-       addr = bp->rx_buffers_dma;
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               bp->rx_ring[i].addr = addr;
-               bp->rx_ring[i].ctrl = 0;
-               addr += bp->rx_buffer_size;
-       }
-       bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP);
+       macb_init_rx_ring(bp);
 
        for (i = 0; i < TX_RING_SIZE; i++) {
                bp->queues[0].tx_ring[i].addr = 0;
@@ -2957,9 +2997,10 @@ static int macb_probe(struct platform_device *pdev)
        phy_node =  of_get_next_available_child(np, NULL);
        if (phy_node) {
                int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
-               if (gpio_is_valid(gpio))
+               if (gpio_is_valid(gpio)) {
                        bp->reset_gpio = gpio_to_desc(gpio);
-               gpiod_direction_output(bp->reset_gpio, 1);
+                       gpiod_direction_output(bp->reset_gpio, 1);
+               }
        }
        of_node_put(phy_node);
 
@@ -2979,29 +3020,36 @@ static int macb_probe(struct platform_device *pdev)
        if (err)
                goto err_out_free_netdev;
 
+       err = macb_mii_init(bp);
+       if (err)
+               goto err_out_free_netdev;
+
+       phydev = bp->phy_dev;
+
+       netif_carrier_off(dev);
+
        err = register_netdev(dev);
        if (err) {
                dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
-               goto err_out_unregister_netdev;
+               goto err_out_unregister_mdio;
        }
 
-       err = macb_mii_init(bp);
-       if (err)
-               goto err_out_unregister_netdev;
-
-       netif_carrier_off(dev);
+       phy_attached_info(phydev);
 
        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);
 
-       phydev = bp->phy_dev;
-       phy_attached_info(phydev);
-
        return 0;
 
-err_out_unregister_netdev:
-       unregister_netdev(dev);
+err_out_unregister_mdio:
+       phy_disconnect(bp->phy_dev);
+       mdiobus_unregister(bp->mii_bus);
+       mdiobus_free(bp->mii_bus);
+
+       /* Shutdown the PHY if there is a GPIO reset */
+       if (bp->reset_gpio)
+               gpiod_set_value(bp->reset_gpio, 0);
 
 err_out_free_netdev:
        free_netdev(dev);
@@ -3029,7 +3077,8 @@ static int macb_remove(struct platform_device *pdev)
                mdiobus_free(bp->mii_bus);
 
                /* Shutdown the PHY if there is a GPIO reset */
-               gpiod_set_value(bp->reset_gpio, 0);
+               if (bp->reset_gpio)
+                       gpiod_set_value(bp->reset_gpio, 0);
 
                unregister_netdev(dev);
                clk_disable_unprepare(bp->tx_clk);
This page took 0.026326 seconds and 5 git commands to generate.