uboot/drivers/watchdog/orion_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/watchdog/orion_wdt.c
   3 *
   4 * Watchdog driver for Orion/Kirkwood processors
   5 *
   6 * Authors:     Tomas Hlavacek <tmshlvck@gmail.com>
   7 *              Sylver Bruneau <sylver.bruneau@googlemail.com>
   8 *              Marek Behun <marek.behun@nic.cz>
   9 *
  10 * This file is licensed under  the terms of the GNU General Public
  11 * License version 2. This program is licensed "as is" without any
  12 * warranty of any kind, whether express or implied.
  13 */
  14
  15#include <common.h>
  16#include <dm.h>
  17#include <clk.h>
  18#include <log.h>
  19#include <wdt.h>
  20#include <linux/bitops.h>
  21#include <linux/kernel.h>
  22#include <asm/io.h>
  23#include <asm/arch/cpu.h>
  24#include <asm/arch/soc.h>
  25
  26DECLARE_GLOBAL_DATA_PTR;
  27
  28struct orion_wdt_priv {
  29        void __iomem *reg;
  30        int wdt_counter_offset;
  31        void __iomem *rstout;
  32        void __iomem *rstout_mask;
  33        u32 timeout;
  34        unsigned long clk_rate;
  35        struct clk clk;
  36};
  37
  38#define RSTOUT_ENABLE_BIT               BIT(8)
  39#define RSTOUT_MASK_BIT                 BIT(10)
  40#define WDT_ENABLE_BIT                  BIT(8)
  41
  42#define TIMER_CTRL                      0x0000
  43#define TIMER_A370_STATUS               0x04
  44
  45#define WDT_AXP_FIXED_ENABLE_BIT        BIT(10)
  46#define WDT_A370_EXPIRED                BIT(31)
  47
  48static int orion_wdt_reset(struct udevice *dev)
  49{
  50        struct orion_wdt_priv *priv = dev_get_priv(dev);
  51
  52        /* Reload watchdog duration */
  53        writel(priv->clk_rate * priv->timeout,
  54               priv->reg + priv->wdt_counter_offset);
  55
  56        return 0;
  57}
  58
  59static int orion_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
  60{
  61        struct orion_wdt_priv *priv = dev_get_priv(dev);
  62        u32 reg;
  63
  64        priv->timeout = DIV_ROUND_UP(timeout_ms, 1000);
  65
  66        /* Enable the fixed watchdog clock input */
  67        reg = readl(priv->reg + TIMER_CTRL);
  68        reg |= WDT_AXP_FIXED_ENABLE_BIT;
  69        writel(reg, priv->reg + TIMER_CTRL);
  70
  71        /* Set watchdog duration */
  72        writel(priv->clk_rate * priv->timeout,
  73               priv->reg + priv->wdt_counter_offset);
  74
  75        /* Clear the watchdog expiration bit */
  76        reg = readl(priv->reg + TIMER_A370_STATUS);
  77        reg &= ~WDT_A370_EXPIRED;
  78        writel(reg, priv->reg + TIMER_A370_STATUS);
  79
  80        /* Enable watchdog timer */
  81        reg = readl(priv->reg + TIMER_CTRL);
  82        reg |= WDT_ENABLE_BIT;
  83        writel(reg, priv->reg + TIMER_CTRL);
  84
  85        /* Enable reset on watchdog */
  86        reg = readl(priv->rstout);
  87        reg |= RSTOUT_ENABLE_BIT;
  88        writel(reg, priv->rstout);
  89
  90        reg = readl(priv->rstout_mask);
  91        reg &= ~RSTOUT_MASK_BIT;
  92        writel(reg, priv->rstout_mask);
  93
  94        return 0;
  95}
  96
  97static int orion_wdt_stop(struct udevice *dev)
  98{
  99        struct orion_wdt_priv *priv = dev_get_priv(dev);
 100        u32 reg;
 101
 102        /* Disable reset on watchdog */
 103        reg = readl(priv->rstout_mask);
 104        reg |= RSTOUT_MASK_BIT;
 105        writel(reg, priv->rstout_mask);
 106
 107        reg = readl(priv->rstout);
 108        reg &= ~RSTOUT_ENABLE_BIT;
 109        writel(reg, priv->rstout);
 110
 111        /* Disable watchdog timer */
 112        reg = readl(priv->reg + TIMER_CTRL);
 113        reg &= ~WDT_ENABLE_BIT;
 114        writel(reg, priv->reg + TIMER_CTRL);
 115
 116        return 0;
 117}
 118
 119static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
 120                                        void __iomem **reg, int *offset)
 121{
 122        fdt_addr_t addr;
 123        fdt_size_t off;
 124
 125        addr = devfdt_get_addr_size_index(dev, index, &off);
 126        if (addr == FDT_ADDR_T_NONE)
 127                return false;
 128
 129        *reg = (void __iomem *) addr;
 130        if (offset)
 131                *offset = off;
 132
 133        return true;
 134}
 135
 136static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
 137{
 138        struct orion_wdt_priv *priv = dev_get_priv(dev);
 139
 140        if (!save_reg_from_ofdata(dev, 0, &priv->reg,
 141                                  &priv->wdt_counter_offset))
 142                goto err;
 143
 144        if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
 145                goto err;
 146
 147        if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
 148                goto err;
 149
 150        return 0;
 151err:
 152        debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
 153        return -ENXIO;
 154}
 155
 156static int orion_wdt_probe(struct udevice *dev)
 157{
 158        struct orion_wdt_priv *priv = dev_get_priv(dev);
 159        int ret;
 160
 161        debug("%s: Probing wdt%u\n", __func__, dev->seq);
 162        orion_wdt_stop(dev);
 163
 164        ret = clk_get_by_name(dev, "fixed", &priv->clk);
 165        if (!ret)
 166                priv->clk_rate = clk_get_rate(&priv->clk);
 167        else
 168                priv->clk_rate = 25000000;
 169
 170        return 0;
 171}
 172
 173static const struct wdt_ops orion_wdt_ops = {
 174        .start = orion_wdt_start,
 175        .reset = orion_wdt_reset,
 176        .stop = orion_wdt_stop,
 177};
 178
 179static const struct udevice_id orion_wdt_ids[] = {
 180        { .compatible = "marvell,armada-380-wdt" },
 181        {}
 182};
 183
 184U_BOOT_DRIVER(orion_wdt) = {
 185        .name = "orion_wdt",
 186        .id = UCLASS_WDT,
 187        .of_match = orion_wdt_ids,
 188        .probe = orion_wdt_probe,
 189        .priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
 190        .ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
 191        .ops = &orion_wdt_ops,
 192};
 193