| 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 = { |
| @@ -503,6 +515,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; |
| @@ -512,9 +550,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); |
| } |
| @@ -525,8 +566,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); |
| @@ -1782,11 +1826,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: |
| @@ -1794,6 +1833,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, }, |
| @@ -1936,9 +1999,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; |