diff options
author | Dong Aisheng <b29396@freescale.com> | 2013-11-06 06:10:28 -0500 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:47:05 -0400 |
commit | 1252db8f4bab1122a8daf28af6208549f650dae0 (patch) | |
tree | 74aa4c78b5641c59114c3ed459215818a9f660e5 | |
parent | 38f373d10d3d14972a73ad30a4ecaa83f8152c64 (diff) |
ENGR00286724-8 can: flexcan: add self wakeup support
If wakeup is enabled, enter stop mode, else enter disabled mode.
Self wake can only work on stop mode.
For imx6q, the stop request has to be mannually assert on
IOMUX GPR13[28:29] register, we use syscon to control that bit.
Signed-off-by: Dong Aisheng <b29396@freescale.com>
-rw-r--r-- | Documentation/devicetree/bindings/net/can/fsl-flexcan.txt | 2 | ||||
-rw-r--r-- | drivers/net/can/flexcan.c | 82 |
2 files changed, 79 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt index e57c610c3a78..487e8a63eff1 100644 --- a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt +++ b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt | |||
@@ -15,6 +15,8 @@ Required properties: | |||
15 | Optional properties: | 15 | Optional properties: |
16 | 16 | ||
17 | - clock-frequency : The oscillator frequency driving the flexcan device | 17 | - clock-frequency : The oscillator frequency driving the flexcan device |
18 | - gpr: phandle to general purpose register node. The remote wakeup control | ||
19 | bits is stored here. | ||
18 | 20 | ||
19 | Below are gpios for tranceiver: | 21 | Below are gpios for tranceiver: |
20 | - trx_en_gpio : enable gpio | 22 | - trx_en_gpio : enable gpio |
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 152558de92f7..e99e4d04ee43 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c | |||
@@ -33,11 +33,14 @@ | |||
33 | #include <linux/io.h> | 33 | #include <linux/io.h> |
34 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
35 | #include <linux/list.h> | 35 | #include <linux/list.h> |
36 | #include <linux/mfd/syscon.h> | ||
37 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | ||
36 | #include <linux/module.h> | 38 | #include <linux/module.h> |
37 | #include <linux/of.h> | 39 | #include <linux/of.h> |
38 | #include <linux/of_device.h> | 40 | #include <linux/of_device.h> |
39 | #include <linux/platform_device.h> | 41 | #include <linux/platform_device.h> |
40 | #include <linux/pinctrl/consumer.h> | 42 | #include <linux/pinctrl/consumer.h> |
43 | #include <linux/regmap.h> | ||
41 | 44 | ||
42 | #define DRV_NAME "flexcan" | 45 | #define DRV_NAME "flexcan" |
43 | 46 | ||
@@ -123,7 +126,8 @@ | |||
123 | (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) | 126 | (FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE) |
124 | #define FLEXCAN_ESR_ALL_INT \ | 127 | #define FLEXCAN_ESR_ALL_INT \ |
125 | (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ | 128 | (FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \ |
126 | FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) | 129 | FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT | \ |
130 | FLEXCAN_ESR_WAK_INT) | ||
127 | 131 | ||
128 | /* FLEXCAN interrupt flag register (IFLAG) bits */ | 132 | /* FLEXCAN interrupt flag register (IFLAG) bits */ |
129 | #define FLEXCAN_TX_BUF_ID 8 | 133 | #define FLEXCAN_TX_BUF_ID 8 |
@@ -212,6 +216,8 @@ struct flexcan_priv { | |||
212 | struct clk *clk_per; | 216 | struct clk *clk_per; |
213 | struct flexcan_platform_data *pdata; | 217 | struct flexcan_platform_data *pdata; |
214 | const struct flexcan_devtype_data *devtype_data; | 218 | const struct flexcan_devtype_data *devtype_data; |
219 | struct regmap *gpr; | ||
220 | int id; | ||
215 | }; | 221 | }; |
216 | 222 | ||
217 | static struct flexcan_devtype_data fsl_p1010_devtype_data = { | 223 | static struct flexcan_devtype_data fsl_p1010_devtype_data = { |
@@ -259,6 +265,32 @@ static inline void flexcan_write(u32 val, void __iomem *addr) | |||
259 | } | 265 | } |
260 | #endif | 266 | #endif |
261 | 267 | ||
268 | static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv) | ||
269 | { | ||
270 | int val; | ||
271 | |||
272 | /* enable stop request */ | ||
273 | if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) { | ||
274 | val = priv->id ? IMX6Q_GPR13_CAN2_STOP_REQ : | ||
275 | IMX6Q_GPR13_CAN1_STOP_REQ; | ||
276 | regmap_update_bits(priv->gpr, IOMUXC_GPR13, | ||
277 | val, val); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv) | ||
282 | { | ||
283 | int val; | ||
284 | |||
285 | /* remove stop request */ | ||
286 | if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) { | ||
287 | val = priv->id ? IMX6Q_GPR13_CAN2_STOP_REQ : | ||
288 | IMX6Q_GPR13_CAN1_STOP_REQ; | ||
289 | regmap_update_bits(priv->gpr, IOMUXC_GPR13, | ||
290 | val, 0); | ||
291 | } | ||
292 | } | ||
293 | |||
262 | /* | 294 | /* |
263 | * Swtich transceiver on or off | 295 | * Swtich transceiver on or off |
264 | */ | 296 | */ |
@@ -623,6 +655,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) | |||
623 | if (reg_esr & FLEXCAN_ESR_ALL_INT) | 655 | if (reg_esr & FLEXCAN_ESR_ALL_INT) |
624 | flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); | 656 | flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); |
625 | 657 | ||
658 | if (reg_esr & FLEXCAN_ESR_WAK_INT) | ||
659 | flexcan_exit_stop_mode(priv); | ||
660 | |||
626 | /* | 661 | /* |
627 | * schedule NAPI in case of: | 662 | * schedule NAPI in case of: |
628 | * - rx IRQ | 663 | * - rx IRQ |
@@ -741,13 +776,14 @@ static int flexcan_chip_start(struct net_device *dev) | |||
741 | * enable warning int | 776 | * enable warning int |
742 | * choose format C | 777 | * choose format C |
743 | * disable local echo | 778 | * disable local echo |
744 | * | 779 | * enable self wakeup |
745 | */ | 780 | */ |
746 | reg_mcr = flexcan_read(®s->mcr); | 781 | reg_mcr = flexcan_read(®s->mcr); |
747 | reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); | 782 | reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); |
748 | reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | | 783 | reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | |
749 | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | | 784 | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | |
750 | FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | | 785 | FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | |
786 | FLEXCAN_MCR_WAK_MSK | FLEXCAN_MCR_SLF_WAK | | ||
751 | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); | 787 | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); |
752 | netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); | 788 | netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); |
753 | flexcan_write(reg_mcr, ®s->mcr); | 789 | flexcan_write(reg_mcr, ®s->mcr); |
@@ -1003,6 +1039,7 @@ static int flexcan_probe(struct platform_device *pdev) | |||
1003 | resource_size_t mem_size; | 1039 | resource_size_t mem_size; |
1004 | int err, irq; | 1040 | int err, irq; |
1005 | u32 clock_freq = 0; | 1041 | u32 clock_freq = 0; |
1042 | int wakeup = 1; | ||
1006 | 1043 | ||
1007 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | 1044 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); |
1008 | if (IS_ERR(pinctrl)) | 1045 | if (IS_ERR(pinctrl)) |
@@ -1098,6 +1135,23 @@ static int flexcan_probe(struct platform_device *pdev) | |||
1098 | 1135 | ||
1099 | devm_can_led_init(dev); | 1136 | devm_can_led_init(dev); |
1100 | 1137 | ||
1138 | if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) { | ||
1139 | priv->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | ||
1140 | "gpr"); | ||
1141 | if (IS_ERR(priv->gpr)) { | ||
1142 | wakeup = 0; | ||
1143 | dev_dbg(&pdev->dev, "can not get grp\n"); | ||
1144 | } | ||
1145 | |||
1146 | priv->id = of_alias_get_id(pdev->dev.of_node, "flexcan"); | ||
1147 | if (priv->id < 0) { | ||
1148 | wakeup = 0; | ||
1149 | dev_dbg(&pdev->dev, "can not get alias id\n"); | ||
1150 | } | ||
1151 | } | ||
1152 | |||
1153 | device_set_wakeup_capable(&pdev->dev, wakeup); | ||
1154 | |||
1101 | dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", | 1155 | dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n", |
1102 | priv->base, dev->irq); | 1156 | priv->base, dev->irq); |
1103 | 1157 | ||
@@ -1139,11 +1193,21 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) | |||
1139 | struct net_device *dev = platform_get_drvdata(pdev); | 1193 | struct net_device *dev = platform_get_drvdata(pdev); |
1140 | struct flexcan_priv *priv = netdev_priv(dev); | 1194 | struct flexcan_priv *priv = netdev_priv(dev); |
1141 | 1195 | ||
1142 | flexcan_chip_disable(priv); | ||
1143 | |||
1144 | if (netif_running(dev)) { | 1196 | if (netif_running(dev)) { |
1145 | netif_stop_queue(dev); | 1197 | netif_stop_queue(dev); |
1146 | netif_device_detach(dev); | 1198 | netif_device_detach(dev); |
1199 | /* | ||
1200 | * if wakeup is enabled, enter stop mode | ||
1201 | * else enter disabled mode. | ||
1202 | */ | ||
1203 | if (device_may_wakeup(&pdev->dev)) { | ||
1204 | enable_irq_wake(dev->irq); | ||
1205 | flexcan_enter_stop_mode(priv); | ||
1206 | } else { | ||
1207 | flexcan_chip_disable(priv); | ||
1208 | } | ||
1209 | } else { | ||
1210 | flexcan_chip_disable(priv); | ||
1147 | } | 1211 | } |
1148 | priv->can.state = CAN_STATE_SLEEPING; | 1212 | priv->can.state = CAN_STATE_SLEEPING; |
1149 | 1213 | ||
@@ -1159,8 +1223,16 @@ static int flexcan_resume(struct platform_device *pdev) | |||
1159 | if (netif_running(dev)) { | 1223 | if (netif_running(dev)) { |
1160 | netif_device_attach(dev); | 1224 | netif_device_attach(dev); |
1161 | netif_start_queue(dev); | 1225 | netif_start_queue(dev); |
1226 | |||
1227 | if (device_may_wakeup(&pdev->dev)) { | ||
1228 | disable_irq_wake(dev->irq); | ||
1229 | flexcan_exit_stop_mode(priv); | ||
1230 | } else { | ||
1231 | flexcan_chip_enable(priv); | ||
1232 | } | ||
1233 | } else { | ||
1234 | flexcan_chip_enable(priv); | ||
1162 | } | 1235 | } |
1163 | flexcan_chip_enable(priv); | ||
1164 | 1236 | ||
1165 | return 0; | 1237 | return 0; |
1166 | } | 1238 | } |