uboot/drivers/watchdog/sunxi_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Derived from linux/drivers/watchdog/sunxi_wdt.c:
   4 *      Copyright (C) 2013 Carlo Caione
   5 *      Copyright (C) 2012 Henrik Nordstrom
   6 */
   7
   8#include <dm.h>
   9#include <wdt.h>
  10#include <asm/io.h>
  11#include <linux/delay.h>
  12
  13#define MSEC_PER_SEC            1000
  14
  15#define WDT_MAX_TIMEOUT         16
  16#define WDT_TIMEOUT_MASK        0xf
  17
  18#define WDT_CTRL_RELOAD         ((1 << 0) | (0x0a57 << 1))
  19
  20#define WDT_MODE_EN             BIT(0)
  21
  22struct sunxi_wdt_reg {
  23        u8 wdt_ctrl;
  24        u8 wdt_cfg;
  25        u8 wdt_mode;
  26        u8 wdt_timeout_shift;
  27        u8 wdt_reset_mask;
  28        u8 wdt_reset_val;
  29        u32 wdt_key_val;
  30};
  31
  32struct sunxi_wdt_priv {
  33        void __iomem                    *base;
  34        const struct sunxi_wdt_reg      *regs;
  35};
  36
  37/* Map of timeout in seconds to register value */
  38static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = {
  39        [0]     = 0x0,
  40        [1]     = 0x1,
  41        [2]     = 0x2,
  42        [3]     = 0x3,
  43        [4]     = 0x4,
  44        [5]     = 0x5,
  45        [6]     = 0x6,
  46        [7]     = 0x7,
  47        [8]     = 0x7,
  48        [9]     = 0x8,
  49        [10]    = 0x8,
  50        [11]    = 0x9,
  51        [12]    = 0x9,
  52        [13]    = 0xa,
  53        [14]    = 0xa,
  54        [15]    = 0xb,
  55        [16]    = 0xb,
  56};
  57
  58static int sunxi_wdt_reset(struct udevice *dev)
  59{
  60        struct sunxi_wdt_priv *priv = dev_get_priv(dev);
  61        const struct sunxi_wdt_reg *regs = priv->regs;
  62        void __iomem *base = priv->base;
  63
  64        writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl);
  65
  66        return 0;
  67}
  68
  69static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
  70{
  71        struct sunxi_wdt_priv *priv = dev_get_priv(dev);
  72        const struct sunxi_wdt_reg *regs = priv->regs;
  73        void __iomem *base = priv->base;
  74        u32 val;
  75
  76        timeout /= MSEC_PER_SEC;
  77        if (timeout > WDT_MAX_TIMEOUT)
  78                timeout = WDT_MAX_TIMEOUT;
  79
  80        /* Set system reset function */
  81        val = readl(base + regs->wdt_cfg);
  82        val &= ~regs->wdt_reset_mask;
  83        val |= regs->wdt_reset_val;
  84        val |= regs->wdt_key_val;
  85        writel(val, base + regs->wdt_cfg);
  86
  87        /* Set timeout and enable watchdog */
  88        val = readl(base + regs->wdt_mode);
  89        val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
  90        val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
  91        val |= WDT_MODE_EN;
  92        val |= regs->wdt_key_val;
  93        writel(val, base + regs->wdt_mode);
  94
  95        return sunxi_wdt_reset(dev);
  96}
  97
  98static int sunxi_wdt_stop(struct udevice *dev)
  99{
 100        struct sunxi_wdt_priv *priv = dev_get_priv(dev);
 101        const struct sunxi_wdt_reg *regs = priv->regs;
 102        void __iomem *base = priv->base;
 103
 104        writel(regs->wdt_key_val, base + regs->wdt_mode);
 105
 106        return 0;
 107}
 108
 109static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags)
 110{
 111        int ret;
 112
 113        ret = sunxi_wdt_start(dev, 0, flags);
 114        if (ret)
 115                return ret;
 116
 117        mdelay(500);
 118
 119        return 0;
 120}
 121
 122static const struct wdt_ops sunxi_wdt_ops = {
 123        .reset          = sunxi_wdt_reset,
 124        .start          = sunxi_wdt_start,
 125        .stop           = sunxi_wdt_stop,
 126        .expire_now     = sunxi_wdt_expire_now,
 127};
 128
 129static const struct sunxi_wdt_reg sun4i_wdt_reg = {
 130        .wdt_ctrl               = 0x00,
 131        .wdt_cfg                = 0x04,
 132        .wdt_mode               = 0x04,
 133        .wdt_timeout_shift      = 3,
 134        .wdt_reset_mask         = 0x2,
 135        .wdt_reset_val          = 0x2,
 136};
 137
 138static const struct sunxi_wdt_reg sun6i_wdt_reg = {
 139        .wdt_ctrl               = 0x10,
 140        .wdt_cfg                = 0x14,
 141        .wdt_mode               = 0x18,
 142        .wdt_timeout_shift      = 4,
 143        .wdt_reset_mask         = 0x3,
 144        .wdt_reset_val          = 0x1,
 145};
 146
 147static const struct sunxi_wdt_reg sun20i_wdt_reg = {
 148        .wdt_ctrl               = 0x10,
 149        .wdt_cfg                = 0x14,
 150        .wdt_mode               = 0x18,
 151        .wdt_timeout_shift      = 4,
 152        .wdt_reset_mask         = 0x03,
 153        .wdt_reset_val          = 0x01,
 154        .wdt_key_val            = 0x16aa0000,
 155};
 156
 157static const struct udevice_id sunxi_wdt_ids[] = {
 158        { .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
 159        { .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
 160        { .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
 161        { /* sentinel */ }
 162};
 163
 164static int sunxi_wdt_probe(struct udevice *dev)
 165{
 166        struct sunxi_wdt_priv *priv = dev_get_priv(dev);
 167
 168        priv->base = dev_remap_addr(dev);
 169        if (!priv->base)
 170                return -EINVAL;
 171
 172        priv->regs = (void *)dev_get_driver_data(dev);
 173        if (!priv->regs)
 174                return -EINVAL;
 175
 176        sunxi_wdt_stop(dev);
 177
 178        return 0;
 179}
 180
 181U_BOOT_DRIVER(sunxi_wdt) = {
 182        .name           = "sunxi_wdt",
 183        .id             = UCLASS_WDT,
 184        .of_match       = sunxi_wdt_ids,
 185        .probe          = sunxi_wdt_probe,
 186        .priv_auto      = sizeof(struct sunxi_wdt_priv),
 187        .ops            = &sunxi_wdt_ops,
 188};
 189