diff options
Diffstat (limited to 'drivers/watchdog/imx2_wdt.c')
| -rw-r--r-- | drivers/watchdog/imx2_wdt.c | 96 |
1 files changed, 84 insertions, 12 deletions
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index b84f80f7d342..4b3a192ee3e8 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/of_address.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/watchdog.h> @@ -35,6 +35,7 @@ #define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ +#define IMX2_WDT_WCR_WDW BIT(7) /* -> Watchdog disable for WAIT */ #define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */ #define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ #define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */ @@ -60,11 +61,19 @@ #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) +struct imx2_wdt_data { + bool wdw_supported; +}; + struct imx2_wdt_device { struct clk *clk; struct regmap *regmap; struct watchdog_device wdog; + const struct imx2_wdt_data *data; bool ext_reset; + bool clk_is_on; + bool no_ping; + bool sleep_wait; }; static bool nowayout = WATCHDOG_NOWAYOUT; @@ -127,6 +136,9 @@ static inline void imx2_wdt_setup(struct watchdog_device *wdog) /* Suspend timer in low power mode, write once-only */ val |= IMX2_WDT_WCR_WDZST; + /* Suspend timer in low power WAIT mode, write once-only */ + if (wdev->sleep_wait) + val |= IMX2_WDT_WCR_WDW; /* Strip the old watchdog Time-Out value */ val &= ~IMX2_WDT_WCR_WT; /* Generate internal chip-level reset if WDOG times out */ @@ -160,6 +172,9 @@ static int imx2_wdt_ping(struct watchdog_device *wdog) { struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + if (!wdev->clk_is_on) + return 0; + regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1); regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2); return 0; @@ -287,6 +302,8 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->parent = dev; + wdev->data = of_device_get_match_data(dev); + ret = platform_get_irq(pdev, 0); if (ret > 0) if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0, @@ -301,16 +318,34 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (ret) return ret; + wdev->clk_is_on = true; + regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; wdev->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); + + if (of_property_read_bool(dev->of_node, "fsl,suspend-in-wait")) { + if (!wdev->data->wdw_supported) { + dev_err(dev, "suspend-in-wait not supported\n"); + return -EINVAL; + } + wdev->sleep_wait = true; + } + + /* + * The i.MX7D doesn't support low power mode, so we need to ping the watchdog + * during suspend. Interaction with "fsl,suspend-in-wait" is unknown! + */ + wdev->no_ping = !of_device_is_compatible(dev->of_node, "fsl,imx7d-wdt"); platform_set_drvdata(pdev, wdog); watchdog_set_drvdata(wdog, wdev); watchdog_set_nowayout(wdog, nowayout); watchdog_set_restart_priority(wdog, 128); watchdog_init_timeout(wdog, timeout, dev); + if (wdev->no_ping) + watchdog_stop_ping_on_suspend(wdog); if (imx2_wdt_is_running(wdev)) { imx2_wdt_set_timeout(wdog, wdog->timeout); @@ -339,12 +374,12 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) */ imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); imx2_wdt_ping(wdog); - dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); + dev_crit(&pdev->dev, "Device shutdown.\n"); } } /* Disable watchdog if it is active or non-active but still running */ -static int __maybe_unused imx2_wdt_suspend(struct device *dev) +static int imx2_wdt_suspend(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -359,21 +394,30 @@ static int __maybe_unused imx2_wdt_suspend(struct device *dev) imx2_wdt_ping(wdog); } - clk_disable_unprepare(wdev->clk); + if (wdev->no_ping) { + clk_disable_unprepare(wdev->clk); + + wdev->clk_is_on = false; + } return 0; } /* Enable watchdog and configure it if necessary */ -static int __maybe_unused imx2_wdt_resume(struct device *dev) +static int imx2_wdt_resume(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); int ret; - ret = clk_prepare_enable(wdev->clk); - if (ret) - return ret; + if (wdev->no_ping) { + ret = clk_prepare_enable(wdev->clk); + + if (ret) + return ret; + + wdev->clk_is_on = true; + } if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { /* @@ -391,11 +435,39 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, - imx2_wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, + imx2_wdt_resume); + +static struct imx2_wdt_data imx_wdt = { + .wdw_supported = true, +}; + +static struct imx2_wdt_data imx_wdt_legacy = { + .wdw_supported = false, +}; static const struct of_device_id imx2_wdt_dt_ids[] = { - { .compatible = "fsl,imx21-wdt", }, + { .compatible = "fsl,imx21-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx25-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx27-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx31-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,imx35-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx50-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx51-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx53-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6q-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sl-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sll-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6sx-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx6ul-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx7d-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mm-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mn-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mp-wdt", .data = &imx_wdt }, + { .compatible = "fsl,imx8mq-wdt", .data = &imx_wdt }, + { .compatible = "fsl,ls1012a-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,ls1043a-wdt", .data = &imx_wdt_legacy }, + { .compatible = "fsl,vf610-wdt", .data = &imx_wdt }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); @@ -404,7 +476,7 @@ static struct platform_driver imx2_wdt_driver = { .shutdown = imx2_wdt_shutdown, .driver = { .name = DRIVER_NAME, - .pm = &imx2_wdt_pm_ops, + .pm = pm_sleep_ptr(&imx2_wdt_pm_ops), .of_match_table = imx2_wdt_dt_ids, }, }; |
