diff options
author | Dong Aisheng <b29396@freescale.com> | 2013-11-06 19:10:28 +0800 |
---|---|---|
committer | Dong Aisheng <b29396@freescale.com> | 2013-11-08 10:50:30 +0800 |
commit | 9d94df08f12ae78908432bc20f346e57331397d8 (patch) | |
tree | 0c9a82bf0d7a036575f8c6ae8743d0ad2cf024a5 /drivers/net | |
parent | 5249f7396f3ba4dd25f7a5bec50a57c40ad59023 (diff) |
ENGR00286724-8 can: flexcan: add self wakeup support
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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/can/flexcan.c | 84 |
1 files changed, 78 insertions, 6 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 83327a7e6ed8..f489a62b033a 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -33,11 +33,14 @@ #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/pinctrl/consumer.h> +#include <linux/regmap.h> #define DRV_NAME "flexcan" @@ -123,7 +126,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 */ #define FLEXCAN_TX_BUF_ID 8 @@ -212,6 +216,8 @@ struct flexcan_priv { struct clk *clk_per; struct flexcan_platform_data *pdata; const struct flexcan_devtype_data *devtype_data; + struct regmap *gpr; + int id; }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { @@ -259,6 +265,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); + } +} + /* * Swtich transceiver on or off */ @@ -623,6 +655,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, ®s->esr); + if (reg_esr & FLEXCAN_ESR_WAK_INT) + flexcan_exit_stop_mode(priv); + /* * schedule NAPI in case of: * - rx IRQ @@ -742,12 +777,13 @@ 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(®s->mcr); 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_IDAM_C | FLEXCAN_MCR_SRX_DIS | + FLEXCAN_MCR_WAK_MSK | FLEXCAN_MCR_SLF_WAK; netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); flexcan_write(reg_mcr, ®s->mcr); @@ -1009,6 +1045,7 @@ static int flexcan_probe(struct platform_device *pdev) resource_size_t mem_size; int err, irq; u32 clock_freq = 0; + int wakeup = 1; pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) @@ -1104,6 +1141,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); @@ -1145,11 +1199,21 @@ static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) struct net_device *dev = platform_get_drvdata(pdev); struct flexcan_priv *priv = netdev_priv(dev); - flexcan_chip_disable(priv); - 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(&pdev->dev)) { + 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; @@ -1165,8 +1229,16 @@ static int flexcan_resume(struct platform_device *pdev) if (netif_running(dev)) { netif_device_attach(dev); netif_start_queue(dev); + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(dev->irq); + flexcan_exit_stop_mode(priv); + } else { + flexcan_chip_enable(priv); + } + } else { + flexcan_chip_enable(priv); } - flexcan_chip_enable(priv); return 0; } |