aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Kleine-Budde <mkl@pengutronix.de>2014-02-28 09:30:18 -0500
committerMarc Kleine-Budde <mkl@pengutronix.de>2014-03-03 06:41:27 -0500
commit9b00b300e7bce032c467c36ca47fe2a776887fc2 (patch)
treed66a93171f6a33c6e7000f731d7b58f368b5bcd2
parent7e9e148af01ef388efb6e2490805970be4622792 (diff)
can: flexcan: fix transition from and to low power mode in chip_{en,dis}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>
-rw-r--r--drivers/net/can/flexcan.c50
1 files changed, 38 insertions, 12 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 30af702a07ad..5af60ab23e6f 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -144,6 +144,8 @@
144 144
145#define FLEXCAN_MB_CODE_MASK (0xf0ffffff) 145#define FLEXCAN_MB_CODE_MASK (0xf0ffffff)
146 146
147#define FLEXCAN_TIMEOUT_US (50)
148
147/* 149/*
148 * FLEXCAN hardware feature flags 150 * FLEXCAN hardware feature flags
149 * 151 *
@@ -269,26 +271,42 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
269 (reg_esr & FLEXCAN_ESR_ERR_BUS); 271 (reg_esr & FLEXCAN_ESR_ERR_BUS);
270} 272}
271 273
272static inline void flexcan_chip_enable(struct flexcan_priv *priv) 274static int flexcan_chip_enable(struct flexcan_priv *priv)
273{ 275{
274 struct flexcan_regs __iomem *regs = priv->base; 276 struct flexcan_regs __iomem *regs = priv->base;
277 unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
275 u32 reg; 278 u32 reg;
276 279
277 reg = flexcan_read(&regs->mcr); 280 reg = flexcan_read(&regs->mcr);
278 reg &= ~FLEXCAN_MCR_MDIS; 281 reg &= ~FLEXCAN_MCR_MDIS;
279 flexcan_write(reg, &regs->mcr); 282 flexcan_write(reg, &regs->mcr);
280 283
281 udelay(10); 284 while (timeout-- && (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
285 usleep_range(10, 20);
286
287 if (flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK)
288 return -ETIMEDOUT;
289
290 return 0;
282} 291}
283 292
284static inline void flexcan_chip_disable(struct flexcan_priv *priv) 293static int flexcan_chip_disable(struct flexcan_priv *priv)
285{ 294{
286 struct flexcan_regs __iomem *regs = priv->base; 295 struct flexcan_regs __iomem *regs = priv->base;
296 unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
287 u32 reg; 297 u32 reg;
288 298
289 reg = flexcan_read(&regs->mcr); 299 reg = flexcan_read(&regs->mcr);
290 reg |= FLEXCAN_MCR_MDIS; 300 reg |= FLEXCAN_MCR_MDIS;
291 flexcan_write(reg, &regs->mcr); 301 flexcan_write(reg, &regs->mcr);
302
303 while (timeout-- && !(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
304 usleep_range(10, 20);
305
306 if (!(flexcan_read(&regs->mcr) & FLEXCAN_MCR_LPM_ACK))
307 return -ETIMEDOUT;
308
309 return 0;
292} 310}
293 311
294static int flexcan_get_berr_counter(const struct net_device *dev, 312static int flexcan_get_berr_counter(const struct net_device *dev,
@@ -709,7 +727,9 @@ static int flexcan_chip_start(struct net_device *dev)
709 u32 reg_mcr, reg_ctrl; 727 u32 reg_mcr, reg_ctrl;
710 728
711 /* enable module */ 729 /* enable module */
712 flexcan_chip_enable(priv); 730 err = flexcan_chip_enable(priv);
731 if (err)
732 return err;
713 733
714 /* soft reset */ 734 /* soft reset */
715 flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr); 735 flexcan_write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
@@ -949,12 +969,16 @@ static int register_flexcandev(struct net_device *dev)
949 goto out_disable_ipg; 969 goto out_disable_ipg;
950 970
951 /* select "bus clock", chip must be disabled */ 971 /* select "bus clock", chip must be disabled */
952 flexcan_chip_disable(priv); 972 err = flexcan_chip_disable(priv);
973 if (err)
974 goto out_disable_per;
953 reg = flexcan_read(&regs->ctrl); 975 reg = flexcan_read(&regs->ctrl);
954 reg |= FLEXCAN_CTRL_CLK_SRC; 976 reg |= FLEXCAN_CTRL_CLK_SRC;
955 flexcan_write(reg, &regs->ctrl); 977 flexcan_write(reg, &regs->ctrl);
956 978
957 flexcan_chip_enable(priv); 979 err = flexcan_chip_enable(priv);
980 if (err)
981 goto out_chip_disable;
958 982
959 /* set freeze, halt and activate FIFO, restrict register access */ 983 /* set freeze, halt and activate FIFO, restrict register access */
960 reg = flexcan_read(&regs->mcr); 984 reg = flexcan_read(&regs->mcr);
@@ -971,14 +995,15 @@ static int register_flexcandev(struct net_device *dev)
971 if (!(reg & FLEXCAN_MCR_FEN)) { 995 if (!(reg & FLEXCAN_MCR_FEN)) {
972 netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); 996 netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
973 err = -ENODEV; 997 err = -ENODEV;
974 goto out_disable_per; 998 goto out_chip_disable;
975 } 999 }
976 1000
977 err = register_candev(dev); 1001 err = register_candev(dev);
978 1002
979 out_disable_per:
980 /* disable core and turn off clocks */ 1003 /* disable core and turn off clocks */
1004 out_chip_disable:
981 flexcan_chip_disable(priv); 1005 flexcan_chip_disable(priv);
1006 out_disable_per:
982 clk_disable_unprepare(priv->clk_per); 1007 clk_disable_unprepare(priv->clk_per);
983 out_disable_ipg: 1008 out_disable_ipg:
984 clk_disable_unprepare(priv->clk_ipg); 1009 clk_disable_unprepare(priv->clk_ipg);
@@ -1121,8 +1146,11 @@ static int flexcan_suspend(struct device *device)
1121{ 1146{
1122 struct net_device *dev = dev_get_drvdata(device); 1147 struct net_device *dev = dev_get_drvdata(device);
1123 struct flexcan_priv *priv = netdev_priv(dev); 1148 struct flexcan_priv *priv = netdev_priv(dev);
1149 int err;
1124 1150
1125 flexcan_chip_disable(priv); 1151 err = flexcan_chip_disable(priv);
1152 if (err)
1153 return err;
1126 1154
1127 if (netif_running(dev)) { 1155 if (netif_running(dev)) {
1128 netif_stop_queue(dev); 1156 netif_stop_queue(dev);
@@ -1143,9 +1171,7 @@ static int flexcan_resume(struct device *device)
1143 netif_device_attach(dev); 1171 netif_device_attach(dev);
1144 netif_start_queue(dev); 1172 netif_start_queue(dev);
1145 } 1173 }
1146 flexcan_chip_enable(priv); 1174 return flexcan_chip_enable(priv);
1147
1148 return 0;
1149} 1175}
1150#endif /* CONFIG_PM_SLEEP */ 1176#endif /* CONFIG_PM_SLEEP */
1151 1177