diff options
author | Marc Kleine-Budde <mkl@pengutronix.de> | 2014-02-28 09:30:18 -0500 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2014-03-03 06:41:27 -0500 |
commit | 9b00b300e7bce032c467c36ca47fe2a776887fc2 (patch) | |
tree | d66a93171f6a33c6e7000f731d7b58f368b5bcd2 | |
parent | 7e9e148af01ef388efb6e2490805970be4622792 (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.c | 50 |
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 | ||
272 | static inline void flexcan_chip_enable(struct flexcan_priv *priv) | 274 | static 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(®s->mcr); | 280 | reg = flexcan_read(®s->mcr); |
278 | reg &= ~FLEXCAN_MCR_MDIS; | 281 | reg &= ~FLEXCAN_MCR_MDIS; |
279 | flexcan_write(reg, ®s->mcr); | 282 | flexcan_write(reg, ®s->mcr); |
280 | 283 | ||
281 | udelay(10); | 284 | while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) |
285 | usleep_range(10, 20); | ||
286 | |||
287 | if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) | ||
288 | return -ETIMEDOUT; | ||
289 | |||
290 | return 0; | ||
282 | } | 291 | } |
283 | 292 | ||
284 | static inline void flexcan_chip_disable(struct flexcan_priv *priv) | 293 | static 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(®s->mcr); | 299 | reg = flexcan_read(®s->mcr); |
290 | reg |= FLEXCAN_MCR_MDIS; | 300 | reg |= FLEXCAN_MCR_MDIS; |
291 | flexcan_write(reg, ®s->mcr); | 301 | flexcan_write(reg, ®s->mcr); |
302 | |||
303 | while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) | ||
304 | usleep_range(10, 20); | ||
305 | |||
306 | if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) | ||
307 | return -ETIMEDOUT; | ||
308 | |||
309 | return 0; | ||
292 | } | 310 | } |
293 | 311 | ||
294 | static int flexcan_get_berr_counter(const struct net_device *dev, | 312 | static 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, ®s->mcr); | 735 | flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->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(®s->ctrl); | 975 | reg = flexcan_read(®s->ctrl); |
954 | reg |= FLEXCAN_CTRL_CLK_SRC; | 976 | reg |= FLEXCAN_CTRL_CLK_SRC; |
955 | flexcan_write(reg, ®s->ctrl); | 977 | flexcan_write(reg, ®s->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(®s->mcr); | 984 | reg = flexcan_read(®s->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 | ||