summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <b29396@freescale.com>2013-11-06 19:10:28 +0800
committerDong Aisheng <b29396@freescale.com>2013-11-08 10:50:30 +0800
commit9d94df08f12ae78908432bc20f346e57331397d8 (patch)
tree0c9a82bf0d7a036575f8c6ae8743d0ad2cf024a5
parent5249f7396f3ba4dd25f7a5bec50a57c40ad59023 (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.txt2
-rw-r--r--drivers/net/can/flexcan.c84
2 files changed, 80 insertions, 6 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:
Optional properties:
- clock-frequency : The oscillator frequency driving the flexcan device
+- gpr: phandle to general purpose register node. The remote wakeup control
+ bits is stored here.
Below are gpios for tranceiver:
- trx_en_gpio : enable gpio
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 83327a7e6ed8..f489a62b033a 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -33,11 +33,14 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/regmap.h>
#define DRV_NAME "flexcan"
@@ -123,7 +126,8 @@
(FLEXCAN_ESR_ERR_BUS | FLEXCAN_ESR_ERR_STATE)
#define FLEXCAN_ESR_ALL_INT \
(FLEXCAN_ESR_TWRN_INT | FLEXCAN_ESR_RWRN_INT | \
- FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT)
+ FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT | \
+ FLEXCAN_ESR_WAK_INT)
/* FLEXCAN interrupt flag register (IFLAG) bits */
#define FLEXCAN_TX_BUF_ID 8
@@ -212,6 +216,8 @@ struct flexcan_priv {
struct clk *clk_per;
struct flexcan_platform_data *pdata;
const struct flexcan_devtype_data *devtype_data;
+ struct regmap *gpr;
+ int id;
};
static struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -259,6 +265,32 @@ static inline void flexcan_write(u32 val, void __iomem *addr)
}
#endif
+static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
+{
+ int val;
+
+ /* enable stop request */
+ if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) {
+ val = priv->id ? IMX6Q_GPR13_CAN2_STOP_REQ :
+ IMX6Q_GPR13_CAN1_STOP_REQ;
+ regmap_update_bits(priv->gpr, IOMUXC_GPR13,
+ val, val);
+ }
+}
+
+static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
+{
+ int val;
+
+ /* remove stop request */
+ if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) {
+ val = priv->id ? IMX6Q_GPR13_CAN2_STOP_REQ :
+ IMX6Q_GPR13_CAN1_STOP_REQ;
+ regmap_update_bits(priv->gpr, IOMUXC_GPR13,
+ val, 0);
+ }
+}
+
/*
* Swtich transceiver on or off
*/
@@ -623,6 +655,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
if (reg_esr & FLEXCAN_ESR_ALL_INT)
flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, &regs->esr);
+ if (reg_esr & FLEXCAN_ESR_WAK_INT)
+ flexcan_exit_stop_mode(priv);
+
/*
* schedule NAPI in case of:
* - rx IRQ
@@ -742,12 +777,13 @@ static int flexcan_chip_start(struct net_device *dev)
* enable warning int
* choose format C
* disable local echo
- *
+ * enable self wakeup
*/
reg_mcr = flexcan_read(&regs->mcr);
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
- FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS;
+ FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
+ FLEXCAN_MCR_WAK_MSK | FLEXCAN_MCR_SLF_WAK;
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
flexcan_write(reg_mcr, &regs->mcr);
@@ -1009,6 +1045,7 @@ static int flexcan_probe(struct platform_device *pdev)
resource_size_t mem_size;
int err, irq;
u32 clock_freq = 0;
+ int wakeup = 1;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
@@ -1104,6 +1141,23 @@ static int flexcan_probe(struct platform_device *pdev)
devm_can_led_init(dev);
+ if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) {
+ priv->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "gpr");
+ if (IS_ERR(priv->gpr)) {
+ wakeup = 0;
+ dev_dbg(&pdev->dev, "can not get grp\n");
+ }
+
+ priv->id = of_alias_get_id(pdev->dev.of_node, "flexcan");
+ if (priv->id < 0) {
+ wakeup = 0;
+ dev_dbg(&pdev->dev, "can not get alias id\n");
+ }
+ }
+
+ device_set_wakeup_capable(&pdev->dev, wakeup);
+
dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%d)\n",
priv->base, dev->irq);
@@ -1145,11 +1199,21 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
struct net_device *dev = platform_get_drvdata(pdev);
struct flexcan_priv *priv = netdev_priv(dev);
- flexcan_chip_disable(priv);
-
if (netif_running(dev)) {
netif_stop_queue(dev);
netif_device_detach(dev);
+ /*
+ * if wakeup is enabled, enter stop mode
+ * else enter disabled mode.
+ */
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(dev->irq);
+ flexcan_enter_stop_mode(priv);
+ } else {
+ flexcan_chip_disable(priv);
+ }
+ } else {
+ flexcan_chip_disable(priv);
}
priv->can.state = CAN_STATE_SLEEPING;
@@ -1165,8 +1229,16 @@ static int flexcan_resume(struct platform_device *pdev)
if (netif_running(dev)) {
netif_device_attach(dev);
netif_start_queue(dev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(dev->irq);
+ flexcan_exit_stop_mode(priv);
+ } else {
+ flexcan_chip_enable(priv);
+ }
+ } else {
+ flexcan_chip_enable(priv);
}
- flexcan_chip_enable(priv);
return 0;
}