linux/drivers/watchdog/imx7ulp_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 NXP.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/io.h>
   8#include <linux/iopoll.h>
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/of.h>
  12#include <linux/platform_device.h>
  13#include <linux/reboot.h>
  14#include <linux/watchdog.h>
  15
  16#define WDOG_CS                 0x0
  17#define WDOG_CS_CMD32EN         BIT(13)
  18#define WDOG_CS_ULK             BIT(11)
  19#define WDOG_CS_RCS             BIT(10)
  20#define LPO_CLK                 0x1
  21#define LPO_CLK_SHIFT           8
  22#define WDOG_CS_CLK             (LPO_CLK << LPO_CLK_SHIFT)
  23#define WDOG_CS_EN              BIT(7)
  24#define WDOG_CS_UPDATE          BIT(5)
  25#define WDOG_CS_WAIT            BIT(1)
  26#define WDOG_CS_STOP            BIT(0)
  27
  28#define WDOG_CNT        0x4
  29#define WDOG_TOVAL      0x8
  30
  31#define REFRESH_SEQ0    0xA602
  32#define REFRESH_SEQ1    0xB480
  33#define REFRESH         ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
  34
  35#define UNLOCK_SEQ0     0xC520
  36#define UNLOCK_SEQ1     0xD928
  37#define UNLOCK          ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
  38
  39#define DEFAULT_TIMEOUT 60
  40#define MAX_TIMEOUT     128
  41#define WDOG_CLOCK_RATE 1000
  42#define WDOG_WAIT_TIMEOUT       20
  43
  44static bool nowayout = WATCHDOG_NOWAYOUT;
  45module_param(nowayout, bool, 0000);
  46MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  47                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  48
  49struct imx7ulp_wdt_device {
  50        struct watchdog_device wdd;
  51        void __iomem *base;
  52        struct clk *clk;
  53};
  54
  55static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
  56{
  57        u32 val = readl(base + WDOG_CS);
  58
  59        if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
  60                                                       val & mask, 0,
  61                                                       WDOG_WAIT_TIMEOUT))
  62                return -ETIMEDOUT;
  63
  64        return 0;
  65}
  66
  67static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
  68{
  69        struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
  70
  71        u32 val = readl(wdt->base + WDOG_CS);
  72        int ret;
  73
  74        local_irq_disable();
  75        writel(UNLOCK, wdt->base + WDOG_CNT);
  76        ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
  77        if (ret)
  78                goto enable_out;
  79        if (enable)
  80                writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
  81        else
  82                writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
  83        imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
  84
  85enable_out:
  86        local_irq_enable();
  87
  88        return ret;
  89}
  90
  91static bool imx7ulp_wdt_is_enabled(void __iomem *base)
  92{
  93        u32 val = readl(base + WDOG_CS);
  94
  95        return val & WDOG_CS_EN;
  96}
  97
  98static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
  99{
 100        struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 101
 102        writel(REFRESH, wdt->base + WDOG_CNT);
 103
 104        return 0;
 105}
 106
 107static int imx7ulp_wdt_start(struct watchdog_device *wdog)
 108{
 109        return imx7ulp_wdt_enable(wdog, true);
 110}
 111
 112static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
 113{
 114        return imx7ulp_wdt_enable(wdog, false);
 115}
 116
 117static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
 118                                   unsigned int timeout)
 119{
 120        struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 121        u32 val = WDOG_CLOCK_RATE * timeout;
 122        int ret;
 123
 124        local_irq_disable();
 125        writel(UNLOCK, wdt->base + WDOG_CNT);
 126        ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
 127        if (ret)
 128                goto timeout_out;
 129        writel(val, wdt->base + WDOG_TOVAL);
 130        imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
 131
 132        wdog->timeout = timeout;
 133
 134timeout_out:
 135        local_irq_enable();
 136
 137        return ret;
 138}
 139
 140static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
 141                               unsigned long action, void *data)
 142{
 143        struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 144        int ret;
 145
 146        ret = imx7ulp_wdt_enable(wdog, true);
 147        if (ret)
 148                return ret;
 149
 150        ret = imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
 151        if (ret)
 152                return ret;
 153
 154        /* wait for wdog to fire */
 155        while (true)
 156                ;
 157
 158        return NOTIFY_DONE;
 159}
 160
 161static const struct watchdog_ops imx7ulp_wdt_ops = {
 162        .owner = THIS_MODULE,
 163        .start = imx7ulp_wdt_start,
 164        .stop  = imx7ulp_wdt_stop,
 165        .ping  = imx7ulp_wdt_ping,
 166        .set_timeout = imx7ulp_wdt_set_timeout,
 167        .restart = imx7ulp_wdt_restart,
 168};
 169
 170static const struct watchdog_info imx7ulp_wdt_info = {
 171        .identity = "i.MX7ULP watchdog timer",
 172        .options  = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
 173                    WDIOF_MAGICCLOSE,
 174};
 175
 176static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
 177{
 178        u32 val;
 179        int ret;
 180
 181        local_irq_disable();
 182        /* unlock the wdog for reconfiguration */
 183        writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
 184        writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
 185        ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
 186        if (ret)
 187                goto init_out;
 188
 189        /* set an initial timeout value in TOVAL */
 190        writel(timeout, base + WDOG_TOVAL);
 191        /* enable 32bit command sequence and reconfigure */
 192        val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
 193              WDOG_CS_WAIT | WDOG_CS_STOP;
 194        writel(val, base + WDOG_CS);
 195        imx7ulp_wdt_wait(base, WDOG_CS_RCS);
 196
 197init_out:
 198        local_irq_enable();
 199
 200        return ret;
 201}
 202
 203static void imx7ulp_wdt_action(void *data)
 204{
 205        clk_disable_unprepare(data);
 206}
 207
 208static int imx7ulp_wdt_probe(struct platform_device *pdev)
 209{
 210        struct imx7ulp_wdt_device *imx7ulp_wdt;
 211        struct device *dev = &pdev->dev;
 212        struct watchdog_device *wdog;
 213        int ret;
 214
 215        imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
 216        if (!imx7ulp_wdt)
 217                return -ENOMEM;
 218
 219        platform_set_drvdata(pdev, imx7ulp_wdt);
 220
 221        imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
 222        if (IS_ERR(imx7ulp_wdt->base))
 223                return PTR_ERR(imx7ulp_wdt->base);
 224
 225        imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
 226        if (IS_ERR(imx7ulp_wdt->clk)) {
 227                dev_err(dev, "Failed to get watchdog clock\n");
 228                return PTR_ERR(imx7ulp_wdt->clk);
 229        }
 230
 231        ret = clk_prepare_enable(imx7ulp_wdt->clk);
 232        if (ret)
 233                return ret;
 234
 235        ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
 236        if (ret)
 237                return ret;
 238
 239        wdog = &imx7ulp_wdt->wdd;
 240        wdog->info = &imx7ulp_wdt_info;
 241        wdog->ops = &imx7ulp_wdt_ops;
 242        wdog->min_timeout = 1;
 243        wdog->max_timeout = MAX_TIMEOUT;
 244        wdog->parent = dev;
 245        wdog->timeout = DEFAULT_TIMEOUT;
 246
 247        watchdog_init_timeout(wdog, 0, dev);
 248        watchdog_stop_on_reboot(wdog);
 249        watchdog_stop_on_unregister(wdog);
 250        watchdog_set_drvdata(wdog, imx7ulp_wdt);
 251        ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
 252        if (ret)
 253                return ret;
 254
 255        return devm_watchdog_register_device(dev, wdog);
 256}
 257
 258static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
 259{
 260        struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
 261
 262        if (watchdog_active(&imx7ulp_wdt->wdd))
 263                imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
 264
 265        clk_disable_unprepare(imx7ulp_wdt->clk);
 266
 267        return 0;
 268}
 269
 270static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
 271{
 272        struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
 273        u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
 274        int ret;
 275
 276        ret = clk_prepare_enable(imx7ulp_wdt->clk);
 277        if (ret)
 278                return ret;
 279
 280        if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
 281                imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
 282
 283        if (watchdog_active(&imx7ulp_wdt->wdd))
 284                imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
 285
 286        return 0;
 287}
 288
 289static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
 290                         imx7ulp_wdt_resume);
 291
 292static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
 293        { .compatible = "fsl,imx7ulp-wdt", },
 294        { /* sentinel */ }
 295};
 296MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
 297
 298static struct platform_driver imx7ulp_wdt_driver = {
 299        .probe          = imx7ulp_wdt_probe,
 300        .driver         = {
 301                .name   = "imx7ulp-wdt",
 302                .pm     = &imx7ulp_wdt_pm_ops,
 303                .of_match_table = imx7ulp_wdt_dt_ids,
 304        },
 305};
 306module_platform_driver(imx7ulp_wdt_driver);
 307
 308MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
 309MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
 310MODULE_LICENSE("GPL v2");
 311