aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Riesch <christian.riesch@omicron.at>2014-03-24 08:46:27 -0400
committerDavid S. Miller <davem@davemloft.net>2014-03-24 15:32:03 -0400
commitcd11cf505318ff24e42f35145f9cdf8596fa1958 (patch)
tree06ca0a0ed579d0e8aaab3a7b0f2921f3c9c20855
parent33b7107f59a61236d94ecd6b45e20283cd5abcc8 (diff)
net: davinci_emac: Fix rollback of emac_dev_open()
If an error occurs during the initialization in emac_dev_open() (the driver's ndo_open function), interrupts, DMA descriptors etc. must be freed. The current rollback code is buggy in several ways. 1) Freeing the interrupts. The current code will not free all interrupts that were requested by the driver. Furthermore, the code tries to do a platform_get_resource(priv->pdev, IORESOURCE_IRQ, -1) in its last iteration. This patch fixes these bugs. 2) Wrong order of err: and rollback: labels. If the setup of the PHY in the code fails, the interrupts that have been requested before are not freed: request irq if requesting irqs fails, goto rollback setup phy if phy setup fails, goto err return 0 rollback: free irqs err: This patch brings the code into the correct order. 3) The code calls napi_enable() and emac_int_enable(), but does not undo both in case of an error. This patch adds calls of emac_int_disable() and napi_disable() to the rollback code. 4) RX DMA descriptors are not freed in case of an error: Right before requesting the irqs, the function creates DMA descriptors for the RX channel. These RX descriptors are never freed when we jump to either rollback or err. This patch adds code for freeing the DMA descriptors in the case of an initialization error. This required a modification of cpdma_ctrl_stop() in davinci_cpdma.c: We must be able to call this function to free the DMA descriptors while the DMA channels are in IDLE state (before cpdma_ctlr_start() was called). Tested on a custom board with the Texas Instruments AM1808. Signed-off-by: Christian Riesch <christian.riesch@omicron.at> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c4
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c44
2 files changed, 31 insertions, 17 deletions
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 364d0c7952c0..88ef27067bf2 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -355,7 +355,7 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr)
355 int i; 355 int i;
356 356
357 spin_lock_irqsave(&ctlr->lock, flags); 357 spin_lock_irqsave(&ctlr->lock, flags);
358 if (ctlr->state != CPDMA_STATE_ACTIVE) { 358 if (ctlr->state == CPDMA_STATE_TEARDOWN) {
359 spin_unlock_irqrestore(&ctlr->lock, flags); 359 spin_unlock_irqrestore(&ctlr->lock, flags);
360 return -EINVAL; 360 return -EINVAL;
361 } 361 }
@@ -891,7 +891,7 @@ int cpdma_chan_stop(struct cpdma_chan *chan)
891 unsigned timeout; 891 unsigned timeout;
892 892
893 spin_lock_irqsave(&chan->lock, flags); 893 spin_lock_irqsave(&chan->lock, flags);
894 if (chan->state != CPDMA_STATE_ACTIVE) { 894 if (chan->state == CPDMA_STATE_TEARDOWN) {
895 spin_unlock_irqrestore(&chan->lock, flags); 895 spin_unlock_irqrestore(&chan->lock, flags);
896 return -EINVAL; 896 return -EINVAL;
897 } 897 }
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index 251430450005..8f0e69ce07ca 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -1533,8 +1533,8 @@ static int emac_dev_open(struct net_device *ndev)
1533 u32 cnt; 1533 u32 cnt;
1534 struct resource *res; 1534 struct resource *res;
1535 int q, m, ret; 1535 int q, m, ret;
1536 int res_num = 0, irq_num = 0;
1536 int i = 0; 1537 int i = 0;
1537 int k = 0;
1538 struct emac_priv *priv = netdev_priv(ndev); 1538 struct emac_priv *priv = netdev_priv(ndev);
1539 1539
1540 pm_runtime_get(&priv->pdev->dev); 1540 pm_runtime_get(&priv->pdev->dev);
@@ -1564,14 +1564,24 @@ static int emac_dev_open(struct net_device *ndev)
1564 } 1564 }
1565 1565
1566 /* Request IRQ */ 1566 /* Request IRQ */
1567 while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ,
1568 res_num))) {
1569 for (irq_num = res->start; irq_num <= res->end; irq_num++) {
1570 dev_err(emac_dev, "Request IRQ %d\n", irq_num);
1571 if (request_irq(irq_num, emac_irq, 0, ndev->name,
1572 ndev)) {
1573 dev_err(emac_dev,
1574 "DaVinci EMAC: request_irq() failed\n");
1575 ret = -EBUSY;
1567 1576
1568 while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
1569 for (i = res->start; i <= res->end; i++) {
1570 if (request_irq(i, emac_irq, 0, ndev->name, ndev))
1571 goto rollback; 1577 goto rollback;
1578 }
1572 } 1579 }
1573 k++; 1580 res_num++;
1574 } 1581 }
1582 /* prepare counters for rollback in case of an error */
1583 res_num--;
1584 irq_num--;
1575 1585
1576 /* Start/Enable EMAC hardware */ 1586 /* Start/Enable EMAC hardware */
1577 emac_hw_enable(priv); 1587 emac_hw_enable(priv);
@@ -1638,19 +1648,23 @@ static int emac_dev_open(struct net_device *ndev)
1638 1648
1639 return 0; 1649 return 0;
1640 1650
1641rollback: 1651err:
1642 1652 emac_int_disable(priv);
1643 dev_err(emac_dev, "DaVinci EMAC: request_irq() failed"); 1653 napi_disable(&priv->napi);
1644 1654
1645 for (q = k; k >= 0; k--) { 1655rollback:
1646 for (m = i; m >= res->start; m--) 1656 for (q = res_num; q >= 0; q--) {
1657 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, q);
1658 /* at the first iteration, irq_num is already set to the
1659 * right value
1660 */
1661 if (q != res_num)
1662 irq_num = res->end;
1663
1664 for (m = irq_num; m >= res->start; m--)
1647 free_irq(m, ndev); 1665 free_irq(m, ndev);
1648 res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k-1);
1649 m = res->end;
1650 } 1666 }
1651 1667 cpdma_ctlr_stop(priv->dma);
1652 ret = -EBUSY;
1653err:
1654 pm_runtime_put(&priv->pdev->dev); 1668 pm_runtime_put(&priv->pdev->dev);
1655 return ret; 1669 return ret;
1656} 1670}