uboot/drivers/watchdog/cdns_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cadence WDT driver - Used by Xilinx Zynq
   4 * Reference: Linux kernel Cadence watchdog driver.
   5 *
   6 * Author(s):   Shreenidhi Shedi <yesshedi@gmail.com>
   7 */
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <log.h>
  12#include <wdt.h>
  13#include <clk.h>
  14#include <div64.h>
  15#include <dm/device_compat.h>
  16#include <linux/err.h>
  17#include <linux/io.h>
  18
  19struct cdns_regs {
  20        u32 zmr;        /* WD Zero mode register, offset - 0x0 */
  21        u32 ccr;        /* Counter Control Register offset - 0x4 */
  22        u32 restart;    /* Restart key register, offset - 0x8 */
  23        u32 status;     /* Status Register, offset - 0xC */
  24};
  25
  26struct cdns_wdt_priv {
  27        bool rst;
  28        struct cdns_regs *regs;
  29};
  30
  31#define CDNS_WDT_DEFAULT_TIMEOUT        10
  32
  33/* Supports 1 - 516 sec */
  34#define CDNS_WDT_MIN_TIMEOUT            1
  35#define CDNS_WDT_MAX_TIMEOUT            516
  36
  37/* Restart key */
  38#define CDNS_WDT_RESTART_KEY            0x00001999
  39
  40/* Counter register access key */
  41#define CDNS_WDT_REGISTER_ACCESS_KEY    0x00920000
  42
  43/* Counter value divisor */
  44#define CDNS_WDT_COUNTER_VALUE_DIVISOR  0x1000
  45
  46/* Clock prescaler value and selection */
  47#define CDNS_WDT_PRESCALE_64            64
  48#define CDNS_WDT_PRESCALE_512           512
  49#define CDNS_WDT_PRESCALE_4096          4096
  50#define CDNS_WDT_PRESCALE_SELECT_64     1
  51#define CDNS_WDT_PRESCALE_SELECT_512    2
  52#define CDNS_WDT_PRESCALE_SELECT_4096   3
  53
  54/* Input clock frequency */
  55#define CDNS_WDT_CLK_75MHZ      75000000
  56
  57/* Counter maximum value */
  58#define CDNS_WDT_COUNTER_MAX    0xFFF
  59
  60/*********************    Register Map    **********************************/
  61
  62/*
  63 * Zero Mode Register - This register controls how the time out is indicated
  64 * and also contains the access code to allow writes to the register (0xABC).
  65 */
  66#define CDNS_WDT_ZMR_WDEN_MASK  0x00000001 /* Enable the WDT */
  67#define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */
  68#define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */
  69#define CDNS_WDT_ZMR_RSTLEN_16  0x00000030 /* Reset pulse of 16 pclk cycles */
  70#define CDNS_WDT_ZMR_ZKEY_VAL   0x00ABC000 /* Access key, 0xABC << 12 */
  71
  72/*
  73 * Counter Control register - This register controls how fast the timer runs
  74 * and the reset value and also contains the access code to allow writes to
  75 * the register.
  76 */
  77#define CDNS_WDT_CCR_CRV_MASK   0x00003FFC /* Counter reset value */
  78
  79/* Write access to Registers */
  80static inline void cdns_wdt_writereg(u32 *addr, u32 val)
  81{
  82        writel(val, addr);
  83}
  84
  85/**
  86 * cdns_wdt_reset - Reload the watchdog timer (i.e. pat the watchdog).
  87 *
  88 * @dev: Watchdog device
  89 *
  90 * Write the restart key value (0x00001999) to the restart register.
  91 *
  92 * Return: Always 0
  93 */
  94static int cdns_wdt_reset(struct udevice *dev)
  95{
  96        struct cdns_wdt_priv *priv = dev_get_priv(dev);
  97
  98        debug("%s\n", __func__);
  99
 100        cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
 101
 102        return 0;
 103}
 104
 105/**
 106 * cdns_wdt_start - Enable and start the watchdog.
 107 *
 108 * @dev: Watchdog device
 109 * @timeout: Timeout value
 110 * @flags: Driver flags
 111 *
 112 * The counter value is calculated according to the formula:
 113 *              count = (timeout * clock) / prescaler + 1.
 114 *
 115 * The calculated count is divided by 0x1000 to obtain the field value
 116 * to write to counter control register.
 117 *
 118 * Clears the contents of prescaler and counter reset value. Sets the
 119 * prescaler to 4096 and the calculated count and access key
 120 * to write to CCR Register.
 121 *
 122 * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit)
 123 * or Interrupt signal(IRQEN) with a specified cycles and the access
 124 * key to write to ZMR Register.
 125 *
 126 * Return: Upon success 0, failure -1.
 127 */
 128static int cdns_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
 129{
 130        ulong clk_f;
 131        u32 count, prescaler, ctrl_clksel, data = 0;
 132        struct clk clock;
 133        struct cdns_wdt_priv *priv = dev_get_priv(dev);
 134
 135        if (clk_get_by_index(dev, 0, &clock) < 0) {
 136                dev_err(dev, "failed to get clock\n");
 137                return -1;
 138        }
 139
 140        clk_f = clk_get_rate(&clock);
 141        if (IS_ERR_VALUE(clk_f)) {
 142                dev_err(dev, "failed to get rate\n");
 143                return -1;
 144        }
 145
 146        /* Calculate timeout in seconds and restrict to min and max value */
 147        do_div(timeout, 1000);
 148        timeout = max_t(u64, timeout, CDNS_WDT_MIN_TIMEOUT);
 149        timeout = min_t(u64, timeout, CDNS_WDT_MAX_TIMEOUT);
 150
 151        debug("%s: CLK_FREQ %ld, timeout %lld\n", __func__, clk_f, timeout);
 152
 153        if (clk_f <= CDNS_WDT_CLK_75MHZ) {
 154                prescaler = CDNS_WDT_PRESCALE_512;
 155                ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512;
 156        } else {
 157                prescaler = CDNS_WDT_PRESCALE_4096;
 158                ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096;
 159        }
 160
 161        /*
 162         * Counter value divisor to obtain the value of
 163         * counter reset to be written to control register.
 164         */
 165        count = (timeout * (clk_f / prescaler)) /
 166                CDNS_WDT_COUNTER_VALUE_DIVISOR + 1;
 167
 168        if (count > CDNS_WDT_COUNTER_MAX)
 169                count = CDNS_WDT_COUNTER_MAX;
 170
 171        cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL);
 172
 173        count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
 174
 175        /* Write counter access key first to be able write to register */
 176        data = count | CDNS_WDT_REGISTER_ACCESS_KEY | ctrl_clksel;
 177        cdns_wdt_writereg(&priv->regs->ccr, data);
 178
 179        data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 |
 180                CDNS_WDT_ZMR_ZKEY_VAL;
 181
 182        /* Reset on timeout if specified in device tree. */
 183        if (priv->rst) {
 184                data |= CDNS_WDT_ZMR_RSTEN_MASK;
 185                data &= ~CDNS_WDT_ZMR_IRQEN_MASK;
 186        } else {
 187                data &= ~CDNS_WDT_ZMR_RSTEN_MASK;
 188                data |= CDNS_WDT_ZMR_IRQEN_MASK;
 189        }
 190
 191        cdns_wdt_writereg(&priv->regs->zmr, data);
 192        cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
 193
 194        return 0;
 195}
 196
 197/**
 198 * cdns_wdt_stop - Stop the watchdog.
 199 *
 200 * @dev: Watchdog device
 201 *
 202 * Read the contents of the ZMR register, clear the WDEN bit in the register
 203 * and set the access key for successful write.
 204 *
 205 * Return: Always 0
 206 */
 207static int cdns_wdt_stop(struct udevice *dev)
 208{
 209        struct cdns_wdt_priv *priv = dev_get_priv(dev);
 210
 211        cdns_wdt_writereg(&priv->regs->zmr,
 212                          CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK));
 213
 214        return 0;
 215}
 216
 217/**
 218 * cdns_wdt_expire_now - Expire the watchdog.
 219 *
 220 * @dev: Watchdog device
 221 * @flags: Driver flags
 222 *
 223 * Access WDT and configure with minimal counter value to expire ASAP.
 224 * Expiration issues system reset. When DEBUG is enabled count should be
 225 * bigger to at least see debug message.
 226 *
 227 * Return: Always 0
 228 */
 229static int cdns_wdt_expire_now(struct udevice *dev, ulong flags)
 230{
 231        struct cdns_wdt_priv *priv = dev_get_priv(dev);
 232        u32 data, count = 0;
 233
 234#if defined(DEBUG)
 235        count = 0x40; /* Increase the value if you need more time */
 236        debug("%s: Expire wdt%u\n", __func__, dev_seq(dev));
 237#endif
 238
 239        cdns_wdt_writereg(&priv->regs->zmr, CDNS_WDT_ZMR_ZKEY_VAL);
 240
 241        count = (count << 2) & CDNS_WDT_CCR_CRV_MASK;
 242
 243        /* Write counter access key first to be able write to register */
 244        data = count | CDNS_WDT_REGISTER_ACCESS_KEY;
 245        cdns_wdt_writereg(&priv->regs->ccr, data);
 246
 247        data = CDNS_WDT_ZMR_WDEN_MASK |  CDNS_WDT_ZMR_RSTEN_MASK |
 248                CDNS_WDT_ZMR_ZKEY_VAL;
 249
 250        cdns_wdt_writereg(&priv->regs->zmr, data);
 251        cdns_wdt_writereg(&priv->regs->restart, CDNS_WDT_RESTART_KEY);
 252
 253        return 0;
 254}
 255
 256/**
 257 * cdns_wdt_probe - Probe call for the device.
 258 *
 259 * @dev: Handle to the udevice structure.
 260 *
 261 * Return: Always 0.
 262 */
 263static int cdns_wdt_probe(struct udevice *dev)
 264{
 265        debug("%s: Probing wdt%u\n", __func__, dev_seq(dev));
 266
 267        return 0;
 268}
 269
 270static int cdns_wdt_of_to_plat(struct udevice *dev)
 271{
 272        struct cdns_wdt_priv *priv = dev_get_priv(dev);
 273
 274        priv->regs = (struct cdns_regs *)dev_read_addr(dev);
 275        if (IS_ERR(priv->regs))
 276                return PTR_ERR(priv->regs);
 277
 278        priv->rst = dev_read_bool(dev, "reset-on-timeout");
 279
 280        debug("%s: reset %d\n", __func__, priv->rst);
 281
 282        return 0;
 283}
 284
 285static const struct wdt_ops cdns_wdt_ops = {
 286        .start = cdns_wdt_start,
 287        .reset = cdns_wdt_reset,
 288        .stop = cdns_wdt_stop,
 289        .expire_now = cdns_wdt_expire_now,
 290};
 291
 292static const struct udevice_id cdns_wdt_ids[] = {
 293        { .compatible = "cdns,wdt-r1p2" },
 294        {}
 295};
 296
 297U_BOOT_DRIVER(cdns_wdt) = {
 298        .name = "cdns_wdt",
 299        .id = UCLASS_WDT,
 300        .of_match = cdns_wdt_ids,
 301        .probe = cdns_wdt_probe,
 302        .priv_auto      = sizeof(struct cdns_wdt_priv),
 303        .of_to_plat = cdns_wdt_of_to_plat,
 304        .ops = &cdns_wdt_ops,
 305};
 306