[PATCH V5] can: flexcan: add self wakeup support

2018-11-22 Thread Joakim Zhang
From: Aisheng Dong 

If wakeup is enabled, enter stop mode, else enter disabled mode. Self wake
can only work on stop mode.

Starting from IMX6, the flexcan stop mode control bits is SoC specific,
move it out of IP driver and parse it from devicetree.

Signed-off-by: Aisheng Dong 
Signed-off-by: Joakim Zhang 
---
ChangeLog:
V1->V2:
*add a vendor prefix in property (stop-mode -> fsl,stop-mode).
V2->V3:
*add FLEXCAN_QUIRK_SETUP_STOP_MODE quirk.
*rename function.
*fix system can't be wakeuped during suspend.
V3->V4:
*normalize the code following Aisheng Dong's comments.
V4->V5:
*move enable/disable self wakeup feature into
 enter/exit_stop_mode() function.
---
 drivers/net/can/flexcan.c | 172 --
 1 file changed, 163 insertions(+), 9 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 8e972ef08637..e6dc51d4d1ee 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -19,11 +19,14 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #define DRV_NAME   "flexcan"
 
@@ -131,7 +134,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 */
 /* Errata ERR005829 step7: Reserve first valid MB */
@@ -190,6 +194,7 @@
 #define FLEXCAN_QUIRK_USE_OFF_TIMESTAMPBIT(5) /* Use timestamp based 
offloading */
 #define FLEXCAN_QUIRK_BROKEN_PERR_STATEBIT(6) /* No interrupt for 
error passive */
 #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 */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
@@ -254,6 +259,14 @@ struct flexcan_devtype_data {
u32 quirks; /* quirks needed for different IP cores */
 };
 
+struct flexcan_stop_mode {
+   struct regmap *gpr;
+   u8 req_gpr;
+   u8 req_bit;
+   u8 ack_gpr;
+   u8 ack_bit;
+};
+
 struct flexcan_priv {
struct can_priv can;
struct can_rx_offload offload;
@@ -270,6 +283,7 @@ struct flexcan_priv {
struct clk *clk_per;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
+   struct flexcan_stop_mode stm;
 
/* Read and Write APIs */
u32 (*read)(void __iomem *addr);
@@ -293,7 +307,8 @@ static const struct flexcan_devtype_data 
fsl_imx28_devtype_data = {
 
 static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
-   FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | 
FLEXCAN_QUIRK_BROKEN_PERR_STATE,
+   FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | 
FLEXCAN_QUIRK_BROKEN_PERR_STATE |
+   FLEXCAN_QUIRK_SETUP_STOP_MODE,
 };
 
 static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
@@ -353,6 +368,49 @@ static inline void flexcan_write_le(u32 val, void __iomem 
*addr)
iowrite32(val, addr);
 }
 
+static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   reg_mcr = priv->read(>mcr);
+
+   if (enable)
+   reg_mcr |= FLEXCAN_MCR_WAK_MSK;
+   else
+   reg_mcr &= ~FLEXCAN_MCR_WAK_MSK;
+
+   priv->write(reg_mcr, >mcr);
+}
+
+static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   reg_mcr = priv->read(>mcr);
+   reg_mcr |= FLEXCAN_MCR_SLF_WAK;
+   priv->write(reg_mcr, >mcr);
+
+   /* enable stop request */
+   regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+  1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+}
+
+static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   /* remove stop request */
+   regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+  1 << priv->stm.req_bit, 0);
+
+   reg_mcr = priv->read(>mcr);
+   reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
+   priv->write(reg_mcr, >mcr);
+}
+
 static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
 {
struct flexcan_regs __iomem *regs = priv->regs;
@@ -1244,6 +1302,57 @@ static void unregister_flexcandev(struct net_device *dev)
unregister_candev(dev);
 }
 
+static int flexcan_setup_stop_mode(struct platform_device *pdev)
+{
+   struct net_device *dev = platform_get_drvdata(pdev);
+ 

[PATCH V5] can: flexcan: add self wakeup support

2018-11-22 Thread Joakim Zhang
From: Aisheng Dong 

If wakeup is enabled, enter stop mode, else enter disabled mode. Self wake
can only work on stop mode.

Starting from IMX6, the flexcan stop mode control bits is SoC specific,
move it out of IP driver and parse it from devicetree.

Signed-off-by: Aisheng Dong 
Signed-off-by: Joakim Zhang 
---
ChangeLog:
V1->V2:
*add a vendor prefix in property (stop-mode -> fsl,stop-mode).
V2->V3:
*add FLEXCAN_QUIRK_SETUP_STOP_MODE quirk.
*rename function.
*fix system can't be wakeuped during suspend.
V3->V4:
*normalize the code following Aisheng Dong's comments.
V4->V5:
*move enable/disable self wakeup feature into
 enter/exit_stop_mode() function.
---
 drivers/net/can/flexcan.c | 172 --
 1 file changed, 163 insertions(+), 9 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 8e972ef08637..e6dc51d4d1ee 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -19,11 +19,14 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #define DRV_NAME   "flexcan"
 
@@ -131,7 +134,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 */
 /* Errata ERR005829 step7: Reserve first valid MB */
@@ -190,6 +194,7 @@
 #define FLEXCAN_QUIRK_USE_OFF_TIMESTAMPBIT(5) /* Use timestamp based 
offloading */
 #define FLEXCAN_QUIRK_BROKEN_PERR_STATEBIT(6) /* No interrupt for 
error passive */
 #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 */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
@@ -254,6 +259,14 @@ struct flexcan_devtype_data {
u32 quirks; /* quirks needed for different IP cores */
 };
 
+struct flexcan_stop_mode {
+   struct regmap *gpr;
+   u8 req_gpr;
+   u8 req_bit;
+   u8 ack_gpr;
+   u8 ack_bit;
+};
+
 struct flexcan_priv {
struct can_priv can;
struct can_rx_offload offload;
@@ -270,6 +283,7 @@ struct flexcan_priv {
struct clk *clk_per;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
+   struct flexcan_stop_mode stm;
 
/* Read and Write APIs */
u32 (*read)(void __iomem *addr);
@@ -293,7 +307,8 @@ static const struct flexcan_devtype_data 
fsl_imx28_devtype_data = {
 
 static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
-   FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | 
FLEXCAN_QUIRK_BROKEN_PERR_STATE,
+   FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | 
FLEXCAN_QUIRK_BROKEN_PERR_STATE |
+   FLEXCAN_QUIRK_SETUP_STOP_MODE,
 };
 
 static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
@@ -353,6 +368,49 @@ static inline void flexcan_write_le(u32 val, void __iomem 
*addr)
iowrite32(val, addr);
 }
 
+static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   reg_mcr = priv->read(>mcr);
+
+   if (enable)
+   reg_mcr |= FLEXCAN_MCR_WAK_MSK;
+   else
+   reg_mcr &= ~FLEXCAN_MCR_WAK_MSK;
+
+   priv->write(reg_mcr, >mcr);
+}
+
+static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   reg_mcr = priv->read(>mcr);
+   reg_mcr |= FLEXCAN_MCR_SLF_WAK;
+   priv->write(reg_mcr, >mcr);
+
+   /* enable stop request */
+   regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+  1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+}
+
+static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv)
+{
+   struct flexcan_regs __iomem *regs = priv->regs;
+   u32 reg_mcr;
+
+   /* remove stop request */
+   regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+  1 << priv->stm.req_bit, 0);
+
+   reg_mcr = priv->read(>mcr);
+   reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
+   priv->write(reg_mcr, >mcr);
+}
+
 static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
 {
struct flexcan_regs __iomem *regs = priv->regs;
@@ -1244,6 +1302,57 @@ static void unregister_flexcandev(struct net_device *dev)
unregister_candev(dev);
 }
 
+static int flexcan_setup_stop_mode(struct platform_device *pdev)
+{
+   struct net_device *dev = platform_get_drvdata(pdev);
+