]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ENGR00286724-8 can: flexcan: add self wakeup support
authorDong Aisheng <b29396@freescale.com>
Wed, 6 Nov 2013 11:10:28 +0000 (19:10 +0800)
committerNitin Garg <nitin.garg@freescale.com>
Fri, 16 Jan 2015 03:17:29 +0000 (21:17 -0600)
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>
(cherry picked from commit 7f8ef8eeb2bd93d75eb4c970bcaabcfd499d348d)

Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
drivers/net/can/flexcan.c

index 4ec97cdee02622f82450bcedba5a995904bf1f64..d5b8d9f1e723213219722bf403f05d237e81bab4 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
 
 - clock-frequency : The oscillator frequency driving the flexcan device
 - xceiver-supply: Regulator that powers the CAN transceiver
+- gpr: phandle to general purpose register node. The remote wakeup control
+       bits is stored here.
 - trx_en_gpio : enable gpio
 - trx_stby_gpio : standby gpio
 - trx_nerr_gpio : NERR gpio
index 855ef8b32a3a5bb37bd184f674534f66175f9c10..b604a861386e5b2381e7cbab356419e3af22fb02 100644 (file)
 #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/regulator/consumer.h>
+#include <linux/regmap.h>
 
 #define DRV_NAME                       "flexcan"
 
        (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
@@ -215,6 +219,8 @@ struct flexcan_priv {
        struct flexcan_platform_data *pdata;
        const struct flexcan_devtype_data *devtype_data;
        struct regulator *reg_xceiver;
+       struct regmap *gpr;
+       int id;
 };
 
 static struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -265,6 +271,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);
+       }
+}
+
 static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
 {
        if (priv->pdata && priv->pdata->transceiver_switch) {
@@ -715,6 +747,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
@@ -828,13 +863,14 @@ 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_MAXMB(0xff);
        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_WAK_MSK | FLEXCAN_MCR_SLF_WAK |
                FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
        netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
        flexcan_write(reg_mcr, &regs->mcr);
@@ -1111,6 +1147,7 @@ static int flexcan_probe(struct platform_device *pdev)
        void __iomem *base;
        int err, irq;
        u32 clock_freq = 0;
+       int wakeup = 1;
 
        if (pdev->dev.of_node)
                of_property_read_u32(pdev->dev.of_node,
@@ -1190,6 +1227,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);
 
@@ -1226,6 +1280,18 @@ static int flexcan_suspend(struct device *device)
        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(device)) {
+                       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;
 
@@ -1241,8 +1307,18 @@ static int flexcan_resume(struct device *device)
        if (netif_running(dev)) {
                netif_device_attach(dev);
                netif_start_queue(dev);
+
+               if (device_may_wakeup(device)) {
+                       disable_irq_wake(dev->irq);
+                       flexcan_exit_stop_mode(priv);
+               } else {
+                       flexcan_chip_enable(priv);
+               }
+       } else {
+               flexcan_chip_enable(priv);
        }
-       return flexcan_chip_enable(priv);
+
+       return 0;
 }
 #endif /* CONFIG_PM_SLEEP */