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