mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 14:13:16 +00:00
194 lines
5.9 KiB
Diff
194 lines
5.9 KiB
Diff
|
From 140de383d9d26e8be300b7ba86d56b47898cd8c9 Mon Sep 17 00:00:00 2001
|
||
|
From: Joakim Zhang <qiangqing.zhang@nxp.com>
|
||
|
Date: Tue, 30 Jul 2019 18:01:11 +0800
|
||
|
Subject: [PATCH] can: flexcan: add CAN wakeup function for i.MX8
|
||
|
|
||
|
The System Controller Firmware (SCFW) is a low-level system function
|
||
|
which runs on a dedicated Cortex-M core to provide power, clock, and
|
||
|
resource management. It exists on some i.MX8 processors. e.g. i.MX8QM
|
||
|
(QM, QP), and i.MX8QX (QXP, DX).
|
||
|
|
||
|
SCU driver manages the IPC interface between host CPU and the
|
||
|
SCU firmware running on M4.
|
||
|
|
||
|
For i.MX8, stop mode request is controlled by System Controller Unit(SCU)
|
||
|
firmware.
|
||
|
|
||
|
Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
|
||
|
---
|
||
|
drivers/net/can/flexcan.c | 99 ++++++++++++++++++++++++++++++++++++++++-------
|
||
|
1 file changed, 86 insertions(+), 13 deletions(-)
|
||
|
|
||
|
--- a/drivers/net/can/flexcan.c
|
||
|
+++ b/drivers/net/can/flexcan.c
|
||
|
@@ -29,6 +29,11 @@
|
||
|
#include <linux/pinctrl/consumer.h>
|
||
|
#include <linux/regmap.h>
|
||
|
|
||
|
+#ifdef CONFIG_IMX_SCU_SOC
|
||
|
+#include <linux/firmware/imx/sci.h>
|
||
|
+#include <dt-bindings/firmware/imx/rsrc.h>
|
||
|
+#endif
|
||
|
+
|
||
|
#define DRV_NAME "flexcan"
|
||
|
|
||
|
/* 8 for RX fifo and 2 error handling */
|
||
|
@@ -223,6 +228,7 @@
|
||
|
#define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN BIT(7) /* default to BE register access */
|
||
|
#define FLEXCAN_QUIRK_SETUP_STOP_MODE BIT(8) /* Setup stop mode to support wakeup */
|
||
|
#define FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD BIT(9) /* Use timestamp then support can fd mode */
|
||
|
+#define FLEXCAN_QUIRK_USE_SCFW BIT(10) /* Use System Controller Firmware */
|
||
|
|
||
|
/* Structure of the message buffer */
|
||
|
struct flexcan_mb {
|
||
|
@@ -323,6 +329,11 @@ struct flexcan_priv {
|
||
|
struct regulator *reg_xceiver;
|
||
|
struct flexcan_stop_mode stm;
|
||
|
|
||
|
+#ifdef CONFIG_IMX_SCU_SOC
|
||
|
+ /* IPC handle when enable stop mode by System Controller firmware(scfw) */
|
||
|
+ struct imx_sc_ipc *sc_ipc_handle;
|
||
|
+#endif
|
||
|
+
|
||
|
/* Read and Write APIs */
|
||
|
u32 (*read)(void __iomem *addr);
|
||
|
void (*write)(u32 val, void __iomem *addr);
|
||
|
@@ -352,7 +363,8 @@ static const struct flexcan_devtype_data
|
||
|
static struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
|
||
|
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
||
|
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
|
||
|
- FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD,
|
||
|
+ FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD | FLEXCAN_QUIRK_SETUP_STOP_MODE |
|
||
|
+ FLEXCAN_QUIRK_USE_SCFW,
|
||
|
};
|
||
|
|
||
|
static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
|
||
|
@@ -504,6 +516,32 @@ static void flexcan_enable_wakeup_irq(st
|
||
|
priv->write(reg_mcr, ®s->mcr);
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_IMX_SCU_SOC
|
||
|
+static void flexcan_stop_mode_enable_scfw(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 = IMX_SC_R_CAN_0;
|
||
|
+ else if (idx == 1)
|
||
|
+ rsrc_id = IMX_SC_R_CAN_1;
|
||
|
+ else
|
||
|
+ rsrc_id = IMX_SC_R_CAN_2;
|
||
|
+
|
||
|
+ val = enabled ? 1 : 0;
|
||
|
+ /* stop mode request */
|
||
|
+ imx_sc_misc_set_control(priv->sc_ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val);
|
||
|
+}
|
||
|
+#else
|
||
|
+static int flexcan_stop_mode_enable_scfw(struct flexcan_priv *priv, bool enabled)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
||
|
{
|
||
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||
|
@@ -513,9 +551,12 @@ static inline int flexcan_enter_stop_mod
|
||
|
reg_mcr |= FLEXCAN_MCR_SLF_WAK;
|
||
|
priv->write(reg_mcr, ®s->mcr);
|
||
|
|
||
|
- /* enable stop request */
|
||
|
- regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||
|
- 1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||
|
+ /* enable stop request */
|
||
|
+ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SCFW)
|
||
|
+ flexcan_stop_mode_enable_scfw(priv, true);
|
||
|
+ else
|
||
|
+ regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||
|
+ 1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||
|
|
||
|
return flexcan_low_power_enter_ack(priv);
|
||
|
}
|
||
|
@@ -526,8 +567,11 @@ static inline int flexcan_exit_stop_mode
|
||
|
u32 reg_mcr;
|
||
|
|
||
|
/* remove stop request */
|
||
|
- regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||
|
- 1 << priv->stm.req_bit, 0);
|
||
|
+ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SCFW)
|
||
|
+ flexcan_stop_mode_enable_scfw(priv, false);
|
||
|
+ else
|
||
|
+ regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||
|
+ 1 << priv->stm.req_bit, 0);
|
||
|
|
||
|
|
||
|
reg_mcr = priv->read(®s->mcr);
|
||
|
@@ -1745,11 +1789,6 @@ static int flexcan_setup_stop_mode(struc
|
||
|
gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit,
|
||
|
priv->stm.ack_gpr, priv->stm.ack_bit);
|
||
|
|
||
|
- device_set_wakeup_capable(&pdev->dev, true);
|
||
|
-
|
||
|
- if (of_property_read_bool(np, "wakeup-source"))
|
||
|
- device_set_wakeup_enable(&pdev->dev, true);
|
||
|
-
|
||
|
return 0;
|
||
|
|
||
|
out_put_node:
|
||
|
@@ -1757,6 +1796,30 @@ out_put_node:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_IMX_SCU_SOC
|
||
|
+static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct net_device *dev = platform_get_drvdata(pdev);
|
||
|
+ struct flexcan_priv *priv;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ priv = netdev_priv(dev);
|
||
|
+
|
||
|
+ ret = imx_scu_get_handle(&(priv->sc_ipc_handle));
|
||
|
+ if (ret < 0) {
|
||
|
+ dev_err(&pdev->dev, "get ipc handle used by SCU failed\n");
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+#else
|
||
|
+static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
static const struct of_device_id flexcan_of_match[] = {
|
||
|
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
|
||
|
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
|
||
|
@@ -1899,9 +1962,19 @@ static int flexcan_probe(struct platform
|
||
|
devm_can_led_init(dev);
|
||
|
|
||
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE) {
|
||
|
- err = flexcan_setup_stop_mode(pdev);
|
||
|
- if (err)
|
||
|
+ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SCFW)
|
||
|
+ err = flexcan_setup_stop_mode_scfw(pdev);
|
||
|
+ else
|
||
|
+ err = flexcan_setup_stop_mode(pdev);
|
||
|
+
|
||
|
+ if (err) {
|
||
|
dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
|
||
|
+ } else {
|
||
|
+ device_set_wakeup_capable(&pdev->dev, true);
|
||
|
+
|
||
|
+ if (of_property_read_bool(pdev->dev.of_node, "wakeup-source"))
|
||
|
+ device_set_wakeup_enable(&pdev->dev, true);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
return 0;
|