linux/drivers/watchdog/stm32_iwdg.c
<<
>>
Prefs
   1/*
   2 * Driver for STM32 Independent Watchdog
   3 *
   4 * Copyright (C) Yannick Fertre 2017
   5 * Author: Yannick Fertre <yannick.fertre@st.com>
   6 *
   7 * This driver is based on tegra_wdt.c
   8 *
   9 * License terms:  GNU General Public License (GPL), version 2
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/delay.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/interrupt.h>
  17#include <linux/io.h>
  18#include <linux/iopoll.h>
  19#include <linux/of.h>
  20#include <linux/platform_device.h>
  21#include <linux/watchdog.h>
  22
  23/* IWDG registers */
  24#define IWDG_KR         0x00 /* Key register */
  25#define IWDG_PR         0x04 /* Prescaler Register */
  26#define IWDG_RLR        0x08 /* ReLoad Register */
  27#define IWDG_SR         0x0C /* Status Register */
  28#define IWDG_WINR       0x10 /* Windows Register */
  29
  30/* IWDG_KR register bit mask */
  31#define KR_KEY_RELOAD   0xAAAA /* reload counter enable */
  32#define KR_KEY_ENABLE   0xCCCC /* peripheral enable */
  33#define KR_KEY_EWA      0x5555 /* write access enable */
  34#define KR_KEY_DWA      0x0000 /* write access disable */
  35
  36/* IWDG_PR register bit values */
  37#define PR_4            0x00 /* prescaler set to 4 */
  38#define PR_8            0x01 /* prescaler set to 8 */
  39#define PR_16           0x02 /* prescaler set to 16 */
  40#define PR_32           0x03 /* prescaler set to 32 */
  41#define PR_64           0x04 /* prescaler set to 64 */
  42#define PR_128          0x05 /* prescaler set to 128 */
  43#define PR_256          0x06 /* prescaler set to 256 */
  44
  45/* IWDG_RLR register values */
  46#define RLR_MIN         0x07C /* min value supported by reload register */
  47#define RLR_MAX         0xFFF /* max value supported by reload register */
  48
  49/* IWDG_SR register bit mask */
  50#define FLAG_PVU        BIT(0) /* Watchdog prescaler value update */
  51#define FLAG_RVU        BIT(1) /* Watchdog counter reload value update */
  52
  53/* set timeout to 100000 us */
  54#define TIMEOUT_US      100000
  55#define SLEEP_US        1000
  56
  57struct stm32_iwdg {
  58        struct watchdog_device  wdd;
  59        void __iomem            *regs;
  60        struct clk              *clk;
  61        unsigned int            rate;
  62};
  63
  64static inline u32 reg_read(void __iomem *base, u32 reg)
  65{
  66        return readl_relaxed(base + reg);
  67}
  68
  69static inline void reg_write(void __iomem *base, u32 reg, u32 val)
  70{
  71        writel_relaxed(val, base + reg);
  72}
  73
  74static int stm32_iwdg_start(struct watchdog_device *wdd)
  75{
  76        struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
  77        u32 val = FLAG_PVU | FLAG_RVU;
  78        u32 reload;
  79        int ret;
  80
  81        dev_dbg(wdd->parent, "%s\n", __func__);
  82
  83        /* prescaler fixed to 256 */
  84        reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1,
  85                         RLR_MIN, RLR_MAX);
  86
  87        /* enable write access */
  88        reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
  89
  90        /* set prescaler & reload registers */
  91        reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */
  92        reg_write(wdt->regs, IWDG_RLR, reload);
  93        reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
  94
  95        /* wait for the registers to be updated (max 100ms) */
  96        ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val,
  97                                         !(val & (FLAG_PVU | FLAG_RVU)),
  98                                         SLEEP_US, TIMEOUT_US);
  99        if (ret) {
 100                dev_err(wdd->parent,
 101                        "Fail to set prescaler or reload registers\n");
 102                return ret;
 103        }
 104
 105        /* reload watchdog */
 106        reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
 107
 108        return 0;
 109}
 110
 111static int stm32_iwdg_ping(struct watchdog_device *wdd)
 112{
 113        struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
 114
 115        dev_dbg(wdd->parent, "%s\n", __func__);
 116
 117        /* reload watchdog */
 118        reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
 119
 120        return 0;
 121}
 122
 123static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
 124                                  unsigned int timeout)
 125{
 126        dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout);
 127
 128        wdd->timeout = timeout;
 129
 130        if (watchdog_active(wdd))
 131                return stm32_iwdg_start(wdd);
 132
 133        return 0;
 134}
 135
 136static const struct watchdog_info stm32_iwdg_info = {
 137        .options        = WDIOF_SETTIMEOUT |
 138                          WDIOF_MAGICCLOSE |
 139                          WDIOF_KEEPALIVEPING,
 140        .identity       = "STM32 Independent Watchdog",
 141};
 142
 143static const struct watchdog_ops stm32_iwdg_ops = {
 144        .owner          = THIS_MODULE,
 145        .start          = stm32_iwdg_start,
 146        .ping           = stm32_iwdg_ping,
 147        .set_timeout    = stm32_iwdg_set_timeout,
 148};
 149
 150static int stm32_iwdg_probe(struct platform_device *pdev)
 151{
 152        struct watchdog_device *wdd;
 153        struct stm32_iwdg *wdt;
 154        struct resource *res;
 155        void __iomem *regs;
 156        struct clk *clk;
 157        int ret;
 158
 159        /* This is the timer base. */
 160        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 161        regs = devm_ioremap_resource(&pdev->dev, res);
 162        if (IS_ERR(regs)) {
 163                dev_err(&pdev->dev, "Could not get resource\n");
 164                return PTR_ERR(regs);
 165        }
 166
 167        clk = devm_clk_get(&pdev->dev, NULL);
 168        if (IS_ERR(clk)) {
 169                dev_err(&pdev->dev, "Unable to get clock\n");
 170                return PTR_ERR(clk);
 171        }
 172
 173        ret = clk_prepare_enable(clk);
 174        if (ret) {
 175                dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk);
 176                return ret;
 177        }
 178
 179        /*
 180         * Allocate our watchdog driver data, which has the
 181         * struct watchdog_device nested within it.
 182         */
 183        wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
 184        if (!wdt) {
 185                ret = -ENOMEM;
 186                goto err;
 187        }
 188
 189        /* Initialize struct stm32_iwdg. */
 190        wdt->regs = regs;
 191        wdt->clk = clk;
 192        wdt->rate = clk_get_rate(clk);
 193
 194        /* Initialize struct watchdog_device. */
 195        wdd = &wdt->wdd;
 196        wdd->info = &stm32_iwdg_info;
 197        wdd->ops = &stm32_iwdg_ops;
 198        wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate;
 199        wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate;
 200        wdd->parent = &pdev->dev;
 201
 202        watchdog_set_drvdata(wdd, wdt);
 203        watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
 204
 205        ret = watchdog_init_timeout(wdd, 0, &pdev->dev);
 206        if (ret)
 207                dev_warn(&pdev->dev,
 208                         "unable to set timeout value, using default\n");
 209
 210        ret = watchdog_register_device(wdd);
 211        if (ret) {
 212                dev_err(&pdev->dev, "failed to register watchdog device\n");
 213                goto err;
 214        }
 215
 216        platform_set_drvdata(pdev, wdt);
 217
 218        return 0;
 219err:
 220        clk_disable_unprepare(clk);
 221
 222        return ret;
 223}
 224
 225static int stm32_iwdg_remove(struct platform_device *pdev)
 226{
 227        struct stm32_iwdg *wdt = platform_get_drvdata(pdev);
 228
 229        watchdog_unregister_device(&wdt->wdd);
 230        clk_disable_unprepare(wdt->clk);
 231
 232        return 0;
 233}
 234
 235static const struct of_device_id stm32_iwdg_of_match[] = {
 236        { .compatible = "st,stm32-iwdg" },
 237        { /* end node */ }
 238};
 239MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
 240
 241static struct platform_driver stm32_iwdg_driver = {
 242        .probe          = stm32_iwdg_probe,
 243        .remove         = stm32_iwdg_remove,
 244        .driver = {
 245                .name   = "iwdg",
 246                .of_match_table = stm32_iwdg_of_match,
 247        },
 248};
 249module_platform_driver(stm32_iwdg_driver);
 250
 251MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
 252MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver");
 253MODULE_LICENSE("GPL v2");
 254