uboot/drivers/watchdog/ast_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2017 Google, Inc
   4 */
   5
   6#include <common.h>
   7#include <dm.h>
   8#include <errno.h>
   9#include <log.h>
  10#include <wdt.h>
  11#include <asm/io.h>
  12#include <asm/arch/wdt.h>
  13#include <linux/err.h>
  14
  15#define WDT_AST2500     2500
  16#define WDT_AST2400     2400
  17
  18struct ast_wdt_priv {
  19        struct ast_wdt *regs;
  20};
  21
  22static int ast_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
  23{
  24        struct ast_wdt_priv *priv = dev_get_priv(dev);
  25        ulong driver_data = dev_get_driver_data(dev);
  26        u32 reset_mode = ast_reset_mode_from_flags(flags);
  27
  28        /* 32 bits at 1MHz is 4294967ms */
  29        timeout = min_t(u64, timeout, 4294967);
  30
  31        /* WDT counts in ticks of 1MHz clock. 1ms / 1e3 * 1e6 */
  32        timeout *= 1000;
  33
  34        clrsetbits_le32(&priv->regs->ctrl,
  35                        WDT_CTRL_RESET_MASK << WDT_CTRL_RESET_MODE_SHIFT,
  36                        reset_mode << WDT_CTRL_RESET_MODE_SHIFT);
  37
  38        if (driver_data >= WDT_AST2500 && reset_mode == WDT_CTRL_RESET_SOC)
  39                writel(ast_reset_mask_from_flags(flags),
  40                       &priv->regs->reset_mask);
  41
  42        writel((u32) timeout, &priv->regs->counter_reload_val);
  43        writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
  44        /*
  45         * Setting CLK1MHZ bit is just for compatibility with ast2400 part.
  46         * On ast2500 watchdog timer clock is fixed at 1MHz and the bit is
  47         * read-only
  48         */
  49        setbits_le32(&priv->regs->ctrl,
  50                     WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
  51
  52        return 0;
  53}
  54
  55static int ast_wdt_stop(struct udevice *dev)
  56{
  57        struct ast_wdt_priv *priv = dev_get_priv(dev);
  58
  59        clrbits_le32(&priv->regs->ctrl, WDT_CTRL_EN);
  60
  61        writel(WDT_RESET_DEFAULT, &priv->regs->reset_mask);
  62        return 0;
  63}
  64
  65static int ast_wdt_reset(struct udevice *dev)
  66{
  67        struct ast_wdt_priv *priv = dev_get_priv(dev);
  68
  69        writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
  70
  71        return 0;
  72}
  73
  74static int ast_wdt_expire_now(struct udevice *dev, ulong flags)
  75{
  76        struct ast_wdt_priv *priv = dev_get_priv(dev);
  77        int ret;
  78
  79        ret = ast_wdt_start(dev, 1, flags);
  80        if (ret)
  81                return ret;
  82
  83        while (readl(&priv->regs->ctrl) & WDT_CTRL_EN)
  84                ;
  85
  86        return ast_wdt_stop(dev);
  87}
  88
  89static int ast_wdt_of_to_plat(struct udevice *dev)
  90{
  91        struct ast_wdt_priv *priv = dev_get_priv(dev);
  92
  93        priv->regs = dev_read_addr_ptr(dev);
  94        if (!priv->regs)
  95                return -EINVAL;
  96
  97        return 0;
  98}
  99
 100static const struct wdt_ops ast_wdt_ops = {
 101        .start = ast_wdt_start,
 102        .reset = ast_wdt_reset,
 103        .stop = ast_wdt_stop,
 104        .expire_now = ast_wdt_expire_now,
 105};
 106
 107static const struct udevice_id ast_wdt_ids[] = {
 108        { .compatible = "aspeed,wdt", .data = WDT_AST2500 },
 109        { .compatible = "aspeed,ast2500-wdt", .data = WDT_AST2500 },
 110        { .compatible = "aspeed,ast2400-wdt", .data = WDT_AST2400 },
 111        {}
 112};
 113
 114static int ast_wdt_probe(struct udevice *dev)
 115{
 116        debug("%s() wdt%u\n", __func__, dev_seq(dev));
 117        ast_wdt_stop(dev);
 118
 119        return 0;
 120}
 121
 122U_BOOT_DRIVER(ast_wdt) = {
 123        .name = "ast_wdt",
 124        .id = UCLASS_WDT,
 125        .of_match = ast_wdt_ids,
 126        .probe = ast_wdt_probe,
 127        .priv_auto      = sizeof(struct ast_wdt_priv),
 128        .of_to_plat = ast_wdt_of_to_plat,
 129        .ops = &ast_wdt_ops,
 130};
 131