From 966a76829933c9efae7b8456525bf8cd33166343 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 6 Nov 2013 19:10:28 +0800 Subject: [PATCH] MLK-10131 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 (cherry picked from commit 7f8ef8eeb2bd93d75eb4c970bcaabcfd499d348d) (cherry picked from commit 496fef522e515488147cce3adcc7f101bb532805) --- .../bindings/net/can/fsl-flexcan.txt | 5 + drivers/net/can/flexcan.c | 98 ++++++++++++++++--- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt index 56d6cc336e1c..e988579c320b 100644 --- a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt +++ b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt @@ -17,6 +17,11 @@ Optional properties: - clock-frequency : The oscillator frequency driving the flexcan device - xceiver-supply: Regulator that powers the CAN transceiver +- gpr: phandle to general purpose register node. The remote wakeup control + bits is stored here. +- trx_en_gpio : enable gpio +- trx_stby_gpio : standby gpio +- trx_nerr_gpio : NERR gpio Example: diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 7836cf670395..50e71b8b8207 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -29,11 +29,16 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include #include +#include #define DRV_NAME "flexcan" @@ -140,7 +145,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 */ @@ -261,6 +267,8 @@ struct flexcan_priv { struct flexcan_platform_data *pdata; const struct flexcan_devtype_data *devtype_data; struct regulator *reg_xceiver; + struct regmap *gpr; + int id; }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { @@ -316,6 +324,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->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) { + 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->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) { + val = priv->id ? IMX6Q_GPR13_CAN2_STOP_REQ : + IMX6Q_GPR13_CAN1_STOP_REQ; + regmap_update_bits(priv->gpr, IOMUXC_GPR13, + val, 0); + } +} + static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv) { if (priv->pdata && priv->pdata->transceiver_switch) { @@ -738,6 +772,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 * - state change IRQ @@ -852,12 +889,15 @@ static int flexcan_chip_start(struct net_device *dev) * disable local echo * choose format C * set max mailbox number + * enable self wakeup */ reg_mcr = flexcan_read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | - FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | - FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); + FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | + FLEXCAN_MCR_WAK_MSK | FLEXCAN_MCR_SLF_WAK | + FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); flexcan_write(reg_mcr, ®s->mcr); @@ -1170,6 +1210,7 @@ static int flexcan_probe(struct platform_device *pdev) struct flexcan_regs __iomem *regs; int err, irq; u32 clock_freq = 0; + int wakeup = 1; reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) @@ -1251,6 +1292,23 @@ static int flexcan_probe(struct platform_device *pdev) devm_can_led_init(dev); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG) { + 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->regs, dev->irq); @@ -1277,35 +1335,51 @@ static int __maybe_unused flexcan_suspend(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); - int err; + int err = 0; if (netif_running(dev)) { - err = flexcan_chip_disable(priv); - if (err) - return err; netif_stop_queue(dev); netif_device_detach(dev); + /* + * if wakeup is enabled, enter stop mode + * else enter disabled mode. + */ + if (device_may_wakeup(device)) { + enable_irq_wake(dev->irq); + flexcan_enter_stop_mode(priv); + } else { + err = flexcan_chip_disable(priv); + } + } else { + flexcan_chip_disable(priv); } priv->can.state = CAN_STATE_SLEEPING; - return 0; + return err; } static int __maybe_unused flexcan_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); - int err; + int err = 0; priv->can.state = CAN_STATE_ERROR_ACTIVE; if (netif_running(dev)) { netif_device_attach(dev); netif_start_queue(dev); + + if (device_may_wakeup(device)) { + disable_irq_wake(dev->irq); + flexcan_exit_stop_mode(priv); + } else { + err = flexcan_chip_enable(priv); + } + } else { err = flexcan_chip_enable(priv); - if (err) - return err; } - return 0; + + return err; } static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); -- 2.17.1