Skip to content

Commit

Permalink
can: flexcan: fix transition from and to low power mode in chip_{en,d…
Browse files Browse the repository at this point in the history
…is}able

In flexcan_chip_enable() and flexcan_chip_disable() fixed delays are used.
Experiments have shown that the transition from and to low power mode may take
several microseconds.

This patch adds a while loop which polls the Low Power Mode ACK bit (LPM_ACK)
that indicates a successfull mode change. If the function runs into a timeout a
error value is returned.

Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
  • Loading branch information
Marc Kleine-Budde committed Mar 3, 2014
1 parent 7e9e148 commit 9b00b30
Showing 1 changed file with 38 additions and 12 deletions.
50 changes: 38 additions & 12 deletions drivers/net/can/flexcan.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@

#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)

#define FLEXCAN_TIMEOUT_US (50)

/*
* FLEXCAN hardware feature flags
*
Expand Down Expand Up @@ -269,26 +271,42 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
(reg_esr & FLEXCAN_ESR_ERR_BUS);
}

static inline void flexcan_chip_enable(struct flexcan_priv *priv)
static int flexcan_chip_enable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->base;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;

reg = flexcan_read(&regs->mcr);
reg &= ~FLEXCAN_MCR_MDIS;
flexcan_write(reg, &regs->mcr);

udelay(10);
while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
usleep_range(10, 20);

if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK)
return -ETIMEDOUT;

return 0;
}

static inline void flexcan_chip_disable(struct flexcan_priv *priv)
static int flexcan_chip_disable(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->base;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;

reg = flexcan_read(&regs->mcr);
reg |= FLEXCAN_MCR_MDIS;
flexcan_write(reg, &regs->mcr);

while (timeout-- && !(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
usleep_range(10, 20);

if (!(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
return -ETIMEDOUT;

return 0;
}

static int flexcan_get_berr_counter(const struct net_device *dev,
Expand Down Expand Up @@ -709,7 +727,9 @@ static int flexcan_chip_start(struct net_device *dev)
u32 reg_mcr, reg_ctrl;

/* enable module */
flexcan_chip_enable(priv);
err = flexcan_chip_enable(priv);
if (err)
return err;

/* soft reset */
flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
Expand Down Expand Up @@ -949,12 +969,16 @@ static int register_flexcandev(struct net_device *dev)
goto out_disable_ipg;

/* select "bus clock", chip must be disabled */
flexcan_chip_disable(priv);
err = flexcan_chip_disable(priv);
if (err)
goto out_disable_per;
reg = flexcan_read(&regs->ctrl);
reg |= FLEXCAN_CTRL_CLK_SRC;
flexcan_write(reg, &regs->ctrl);

flexcan_chip_enable(priv);
err = flexcan_chip_enable(priv);
if (err)
goto out_chip_disable;

/* set freeze, halt and activate FIFO, restrict register access */
reg = flexcan_read(&regs->mcr);
Expand All @@ -971,14 +995,15 @@ static int register_flexcandev(struct net_device *dev)
if (!(reg & FLEXCAN_MCR_FEN)) {
netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
err = -ENODEV;
goto out_disable_per;
goto out_chip_disable;
}

err = register_candev(dev);

out_disable_per:
/* disable core and turn off clocks */
out_chip_disable:
flexcan_chip_disable(priv);
out_disable_per:
clk_disable_unprepare(priv->clk_per);
out_disable_ipg:
clk_disable_unprepare(priv->clk_ipg);
Expand Down Expand Up @@ -1121,8 +1146,11 @@ static int flexcan_suspend(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
int err;

flexcan_chip_disable(priv);
err = flexcan_chip_disable(priv);
if (err)
return err;

if (netif_running(dev)) {
netif_stop_queue(dev);
Expand All @@ -1143,9 +1171,7 @@ static int flexcan_resume(struct device *device)
netif_device_attach(dev);
netif_start_queue(dev);
}
flexcan_chip_enable(priv);

return 0;
return flexcan_chip_enable(priv);
}
#endif /* CONFIG_PM_SLEEP */

Expand Down

0 comments on commit 9b00b30

Please sign in to comment.