MLK-21410 can: flexcan: add CAN wakeup function for MX8
authorJoakim Zhang <qiangqing.zhang@nxp.com>
Mon, 8 Apr 2019 04:50:28 +0000 (12:50 +0800)
committerJoakim Zhang <qiangqing.zhang@nxp.com>
Thu, 18 Apr 2019 09:16:19 +0000 (17:16 +0800)
This patch is to add CAN wakeup function on MX8 platforms and update the
binding file fsl-flexcan.txt.

For MX8, the function "flexcan_irq()" should not call "flexcan_exit_stop_mode()"
due to firmware(SCU) cannot make SC IPC calls from an interrupt context.
If not exit stop mode in ISR, it will continuously enter wakeup ISR for the reason
that system will respond IRQ before call CAN system resume.
To fix the issue, we can exit stop mode during noirq resume stage.

For wakeup case, it should not set pinctrl to sleep state by
pinctrl_pm_select_sleep_state.

Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
drivers/net/can/flexcan.c

index f9acae5..2b5c829 100644 (file)
@@ -24,6 +24,8 @@ Optional properties:
   req_bit is the bit offset of CAN stop request.
   ack_gpr is the gpr register offset of CAN stop acknowledge.
   ack_bit is the bit offset of CAN stop acknowledge.
+  stop-mode = <&gpr req_gpr req_bit ack_gpr ack_bit>, this format is for i.MX6/7
+  series to set stop mode. For i.MX8 series, stop mode feature is supported by default.
 - trx_en_gpio : enable gpio
 - trx_stby_gpio : standby gpio
 - trx_nerr_gpio : NERR gpio
index b6c68a8..775e5bd 100644 (file)
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 
+#ifdef CONFIG_ARCH_MXC_ARM64
+#include <soc/imx8/sc/sci.h>
+#endif
+
 #define DRV_NAME                       "flexcan"
 
 /* 8 for RX fifo and 2 error handling */
@@ -371,6 +375,9 @@ struct flexcan_priv {
        struct regmap *gpr;
        int id;
        struct flexcan_stop_mode stm;
+#ifdef CONFIG_ARCH_MXC_ARM64
+       sc_ipc_t ipc_handle;
+#endif
 
        /* Selects the clock source to CAN Protocol Engine (PE), 1 by default*/
        u32 clk_src;
@@ -588,21 +595,50 @@ static void flexcan_wake_mask_disable(struct flexcan_priv *priv)
        priv->write(reg_mcr, &regs->mcr);
 }
 
+#ifdef CONFIG_ARCH_MXC_ARM64
+static void imx8_ipg_stop_enable(struct flexcan_priv *priv, bool enabled)
+{
+       struct device_node *np = priv->dev->of_node;
+       u32 rsrc_id, val;
+       int idx;
+
+       idx = of_alias_get_id(np, "can");
+       if (idx == 0)
+               rsrc_id = SC_R_CAN_0;
+       else if (idx == 1)
+               rsrc_id = SC_R_CAN_1;
+       else
+               rsrc_id = SC_R_CAN_2;
+
+       val = enabled ? 1 : 0;
+       sc_misc_set_control(priv->ipc_handle, rsrc_id, SC_C_IPG_STOP, val);
+}
+#else
+static void imx8_ipg_stop_enable(struct flexcan_priv *priv, bool enabled) {}
+#endif
+
 static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
 {
-       /* enable stop request */
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
-               regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
-                                  1 << priv->stm.req_bit,
-                                  1 << priv->stm.req_bit);
+       if (priv->stm.gpr) {
+               if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
+                       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                                          1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+       } else {
+               if (priv->devtype_data->quirks & FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD)
+                       imx8_ipg_stop_enable(priv, true);
+       }
 }
 
 static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
 {
-       /* remove stop request */
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
-               regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
-                                  1 << priv->stm.req_bit, 0);
+       if (priv->stm.gpr) {
+               if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
+                       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                                          1 << priv->stm.req_bit, 0);
+       } else {
+               if (priv->devtype_data->quirks & FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD)
+                       imx8_ipg_stop_enable(priv, false);
+       }
 }
 
 static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
@@ -1083,9 +1119,6 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                priv->write(reg_esr & FLEXCAN_ESR_ALL_INT, &regs->esr);
        }
 
-       if (reg_esr & FLEXCAN_ESR_WAK_INT)
-               flexcan_exit_stop_mode(priv);
-
        /* state change interrupt or broken error state quirk fix is enabled */
        if ((reg_esr & FLEXCAN_ESR_ERR_STATE) ||
            (priv->devtype_data->quirks & (FLEXCAN_QUIRK_BROKEN_WERR_STATE |
@@ -1587,6 +1620,34 @@ static void unregister_flexcandev(struct net_device *dev)
        unregister_candev(dev);
 }
 
+#ifdef CONFIG_ARCH_MXC_ARM64
+static int imx8_sc_ipc_fetch(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct flexcan_priv *priv;
+       sc_err_t sc_err = SC_ERR_NONE;
+       u32 mu_id;
+
+       priv = netdev_priv(dev);
+
+       sc_err = sc_ipc_getMuID(&mu_id);
+       if (sc_err != SC_ERR_NONE) {
+               netdev_err(dev, "FLEXCAN ipg stop: Get MU ID failed\n");
+               return sc_err;
+       }
+
+       sc_err = sc_ipc_open(&priv->ipc_handle, mu_id);
+       if (sc_err != SC_ERR_NONE) {
+               netdev_err(dev, "FLEXCAN ipg stop: Open MU channel failed\n");
+               return sc_err;
+       }
+
+       return sc_err;
+}
+#else
+static int imx8_sc_ipc_fetch(struct platform_device *pdev) { return 0; }
+#endif
+
 static int flexcan_of_parse_stop_mode(struct platform_device *pdev)
 {
        struct net_device *dev = platform_get_drvdata(pdev);
@@ -1826,7 +1887,13 @@ static int flexcan_probe(struct platform_device *pdev)
 
        devm_can_led_init(dev);
 
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) {
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD) {
+               err = imx8_sc_ipc_fetch(pdev);
+               if (err) {
+                       wakeup = 0;
+                       dev_dbg(&pdev->dev, "failed to fetch scu ipc\n");
+               }
+       } else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) {
                err = flexcan_of_parse_stop_mode(pdev);
                if (err) {
                        wakeup = 0;
@@ -1858,6 +1925,9 @@ static int flexcan_remove(struct platform_device *pdev)
        struct net_device *dev = platform_get_drvdata(pdev);
        struct flexcan_priv *priv = netdev_priv(dev);
 
+#ifdef CONFIG_ARCH_MXC_ARM64
+       sc_ipc_close(priv->ipc_handle);
+#endif
        unregister_flexcandev(dev);
        pm_runtime_disable(&pdev->dev);
        can_rx_offload_del(&priv->offload);
@@ -1884,12 +1954,12 @@ static int __maybe_unused flexcan_suspend(struct device *device)
                } else {
                        flexcan_chip_stop(dev);
                        ret = pm_runtime_force_suspend(device);
+
+                       pinctrl_pm_select_sleep_state(device);
                }
        }
        priv->can.state = CAN_STATE_SLEEPING;
 
-       pinctrl_pm_select_sleep_state(device);
-
        return ret;
 }