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