From f5f1c1ae5e2318ff051d029b386f4f5910d4f758 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 8 Mar 2012 17:51:47 +0530 Subject: [PATCH] ARM: OMAP: AM33XX: NET: cpsw: Add suspend resume support Need to reset all sub-components of CPGMAC to gate the clock Moved omap_dm_timer enable and configure to cpsw_ndo_open and disabled the timer in cpsw_ndo_close so that during suspend/resume timer will be disabled and enabled respectively Added timer omap_dm_timer_free in cpsw_ndo_remove to free the dm_timer while removing the module Added wait_for_clock_enable to ensure that CPGMAC clock is enabled before accessing the CPGMAC registers TODO: Currently driver doesnot support pm runtime, so hack for wait for cpgmac clock enable is done. Once PM runtime support is done then the hack will be removed Signed-off-by: Mugunthan V N --- drivers/net/ethernet/ti/cpsw.c | 49 ++++++++++++++++++++----- drivers/net/ethernet/ti/cpsw_ale.c | 1 + drivers/net/ethernet/ti/davinci_cpdma.c | 11 ++++++ drivers/net/ethernet/ti/davinci_mdio.c | 29 ++++++++++++++- 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 640ceb877ba1..3092588cb1be 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -140,6 +140,7 @@ struct cpsw_regs { u32 soft_reset; u32 stat_port_en; u32 ptype; + u32 soft_idle; }; struct cpsw_slave_regs { @@ -746,6 +747,16 @@ static int cpsw_ndo_open(struct net_device *ndev) cpsw_set_coalesce(ndev, &coal); } + /* Enable Timer for capturing cpsw rx interrupts */ + omap_dm_timer_set_int_enable(dmtimer_rx, OMAP_TIMER_INT_CAPTURE); + omap_dm_timer_set_capture(dmtimer_rx, 1, 0, 0); + omap_dm_timer_enable(dmtimer_rx); + + /* Enable Timer for capturing cpsw tx interrupts */ + omap_dm_timer_set_int_enable(dmtimer_tx, OMAP_TIMER_INT_CAPTURE); + omap_dm_timer_set_capture(dmtimer_tx, 1, 0, 0); + omap_dm_timer_enable(dmtimer_tx); + cpdma_ctlr_start(priv->dma); cpsw_intr_enable(priv); napi_enable(&priv->napi); @@ -770,6 +781,10 @@ static int cpsw_ndo_stop(struct net_device *ndev) msg(info, ifdown, "shutting down cpsw device\n"); cpsw_intr_disable(priv); cpdma_ctlr_int_ctrl(priv->dma, false); + + omap_dm_timer_set_int_enable(dmtimer_rx, 0); + omap_dm_timer_set_int_enable(dmtimer_tx, 0); + netif_stop_queue(priv->ndev); napi_disable(&priv->napi); netif_carrier_off(priv->ndev); @@ -1140,17 +1155,18 @@ static int __devinit cpsw_probe(struct platform_device *pdev) omap_ctrl_writel(CPSW_TIMER_MASK, CPSW_TIMER_CAP_REG); - /* Enable Timer for capturing cpsw rx interrupts */ dmtimer_rx = omap_dm_timer_request_specific(CPSW_RX_TIMER_REQ); - omap_dm_timer_set_int_enable(dmtimer_rx, OMAP_TIMER_INT_CAPTURE); - omap_dm_timer_set_capture(dmtimer_rx, 1, 0, 0); - omap_dm_timer_enable(dmtimer_rx); - - /* Enable Timer for capturing cpsw tx interrupts */ + if (!dmtimer_rx) { + dev_err(priv->dev, "Error getting Rx Timer resource\n"); + ret = -ENODEV; + goto clean_iomap_ret; + } dmtimer_tx = omap_dm_timer_request_specific(CPSW_TX_TIMER_REQ); - omap_dm_timer_set_int_enable(dmtimer_tx, OMAP_TIMER_INT_CAPTURE); - omap_dm_timer_set_capture(dmtimer_tx, 1, 0, 0); - omap_dm_timer_enable(dmtimer_tx); + if (!dmtimer_tx) { + dev_err(priv->dev, "Error getting Tx Timer resource\n"); + ret = -ENODEV; + goto clean_timer_rx_ret; + } memset(&dma_params, 0, sizeof(dma_params)); dma_params.dev = &pdev->dev; @@ -1196,7 +1212,7 @@ static int __devinit cpsw_probe(struct platform_device *pdev) if (!priv->dma) { dev_err(priv->dev, "error initializing dma\n"); ret = -ENOMEM; - goto clean_iomap_ret; + goto clean_timer_ret; } priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0), @@ -1275,6 +1291,10 @@ clean_dma_ret: cpdma_chan_destroy(priv->txch); cpdma_chan_destroy(priv->rxch); cpdma_ctlr_destroy(priv->dma); +clean_timer_ret: + omap_dm_timer_free(dmtimer_tx); +clean_timer_rx_ret: + omap_dm_timer_free(dmtimer_rx); clean_iomap_ret: iounmap(priv->regs); clean_cpsw_ss_iores_ret: @@ -1299,6 +1319,8 @@ static int __devexit cpsw_remove(struct platform_device *pdev) msg(notice, probe, "removing device\n"); platform_set_drvdata(pdev, NULL); + omap_dm_timer_free(dmtimer_rx); + omap_dm_timer_free(dmtimer_tx); free_irq(ndev->irq, priv); cpsw_ale_destroy(priv->ale); cpdma_chan_destroy(priv->txch); @@ -1320,9 +1342,16 @@ static int cpsw_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = platform_get_drvdata(pdev); + struct cpsw_priv *priv = netdev_priv(ndev); if (netif_running(ndev)) cpsw_ndo_stop(ndev); + + soft_reset("cpsw", &priv->regs->soft_reset); + soft_reset("sliver 0", &priv->slaves[0].sliver->soft_reset); + soft_reset("sliver 1", &priv->slaves[1].sliver->soft_reset); + soft_reset("cpsw_ss", &priv->ss_regs->soft_reset); + return 0; } diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 2ce6739084f3..9639c31f8755 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -662,6 +662,7 @@ EXPORT_SYMBOL_GPL(cpsw_ale_start); void cpsw_ale_stop(struct cpsw_ale *ale) { + cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0); del_timer_sync(&ale->timer); device_remove_file(ale->params.dev, &ale->ale_table_attr); device_remove_file(ale->params.dev, &ale->ale_control_attr); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 617252c7803b..306d93036c1e 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -351,6 +351,17 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr) ctlr->state = CPDMA_STATE_IDLE; + if (ctlr->params.has_soft_reset) { + unsigned long timeout = jiffies + HZ/10; + + dma_reg_write(ctlr, CPDMA_SOFTRESET, 1); + while (time_before(jiffies, timeout)) { + if (dma_reg_read(ctlr, CPDMA_SOFTRESET) == 0) + break; + } + WARN_ON(!time_before(jiffies, timeout)); + } + spin_unlock_irqrestore(&ctlr->lock, flags); return 0; } diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 7615040df756..213d204dbc51 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -48,6 +48,8 @@ #define DEF_OUT_FREQ 2200000 /* 2.2 MHz */ +#define CPGMAC_CLK_CTRL_REG 0x44E00014 + struct davinci_mdio_regs { u32 version; u32 control; @@ -402,6 +404,28 @@ static int __devexit davinci_mdio_remove(struct platform_device *pdev) return 0; } +static inline int wait_for_clock_enable(struct davinci_mdio_data *data) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT); + u32 __iomem *cpgmac_clk = ioremap(CPGMAC_CLK_CTRL_REG, 4); + u32 reg = 0; + + while (time_after(timeout, jiffies)) { + reg = readl(cpgmac_clk); + if ((reg & 0x70000) == 0) + goto iounmap_ret; + } + dev_err(data->dev, + "timed out waiting for CPGMAC clock enable, value = 0x%x\n", + reg); + iounmap(cpgmac_clk); + return -ETIMEDOUT; + +iounmap_ret: + iounmap(cpgmac_clk); + return 0; +} + static int davinci_mdio_suspend(struct device *dev) { struct davinci_mdio_data *data = dev_get_drvdata(dev); @@ -433,12 +457,15 @@ static int davinci_mdio_resume(struct device *dev) if (data->clk) clk_enable(data->clk); + /* Need to wait till Module is enabled */ + wait_for_clock_enable(data); + /* restart the scan state machine */ ctrl = __raw_readl(&data->regs->control); ctrl |= CONTROL_ENABLE; __raw_writel(ctrl, &data->regs->control); - data->suspended = false; + spin_unlock(&data->lock); return 0; -- 2.39.2