uboot/drivers/watchdog/mt7620_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2020 MediaTek Inc.
   4 *
   5 * Author:  Weijie Gao <weijie.gao@mediatek.com>
   6 *
   7 * Watchdog timer for MT7620 and earlier SoCs
   8 */
   9
  10#include <div64.h>
  11#include <dm.h>
  12#include <reset.h>
  13#include <wdt.h>
  14#include <linux/bitops.h>
  15#include <linux/io.h>
  16
  17struct mt7620_wdt {
  18        void __iomem *regs;
  19        u64 timeout;
  20};
  21
  22#define TIMER_FREQ                      40000000
  23#define TIMER_MASK                      0xffff
  24#define TIMER_PRESCALE                  65536
  25
  26#define TIMER_LOAD                      0x00
  27#define TIMER_CTL                       0x08
  28
  29#define TIMER_ENABLE                    BIT(7)
  30#define TIMER_MODE_SHIFT                4
  31#define   TIMER_MODE_WDT                3
  32#define TIMER_PRESCALE_SHIFT            0
  33#define   TIMER_PRESCALE_65536          15
  34
  35static void mt7620_wdt_ping(struct mt7620_wdt *priv)
  36{
  37        u64 val;
  38
  39        val = (TIMER_FREQ / TIMER_PRESCALE) * priv->timeout;
  40        do_div(val, 1000);
  41
  42        if (val > TIMER_MASK)
  43                val = TIMER_MASK;
  44
  45        writel(val, priv->regs + TIMER_LOAD);
  46}
  47
  48static int mt7620_wdt_start(struct udevice *dev, u64 ms, ulong flags)
  49{
  50        struct mt7620_wdt *priv = dev_get_priv(dev);
  51
  52        priv->timeout = ms;
  53        mt7620_wdt_ping(priv);
  54
  55        writel(TIMER_ENABLE | (TIMER_MODE_WDT << TIMER_MODE_SHIFT) |
  56               (TIMER_PRESCALE_65536 << TIMER_PRESCALE_SHIFT),
  57               priv->regs + TIMER_CTL);
  58
  59        return 0;
  60}
  61
  62static int mt7620_wdt_stop(struct udevice *dev)
  63{
  64        struct mt7620_wdt *priv = dev_get_priv(dev);
  65
  66        mt7620_wdt_ping(priv);
  67
  68        clrbits_32(priv->regs + TIMER_CTL, TIMER_ENABLE);
  69
  70        return 0;
  71}
  72
  73static int mt7620_wdt_reset(struct udevice *dev)
  74{
  75        struct mt7620_wdt *priv = dev_get_priv(dev);
  76
  77        mt7620_wdt_ping(priv);
  78
  79        return 0;
  80}
  81
  82static int mt7620_wdt_expire_now(struct udevice *dev, ulong flags)
  83{
  84        struct mt7620_wdt *priv = dev_get_priv(dev);
  85
  86        mt7620_wdt_start(dev, 1, flags);
  87
  88        /*
  89         * 0 will disable the timer directly, a positive number must be used
  90         * instead. Since the timer is a countdown timer, 1 (tick) is used.
  91         *
  92         * For a timer with input clock = 40MHz, 1 timer tick is short
  93         * enough to trigger a timeout immediately.
  94         *
  95         * Restore prescale to 1, and load timer with 1 to trigger timeout.
  96         */
  97        writel(TIMER_ENABLE | (TIMER_MODE_WDT << TIMER_MODE_SHIFT),
  98               priv->regs + TIMER_CTL);
  99        writel(1, priv->regs + TIMER_LOAD);
 100
 101        return 0;
 102}
 103
 104static int mt7620_wdt_probe(struct udevice *dev)
 105{
 106        struct mt7620_wdt *priv = dev_get_priv(dev);
 107        struct reset_ctl reset_wdt;
 108        int ret;
 109
 110        ret = reset_get_by_index(dev, 0, &reset_wdt);
 111        if (!ret)
 112                reset_deassert(&reset_wdt);
 113
 114        priv->regs = dev_remap_addr(dev);
 115        if (!priv->regs)
 116                return -EINVAL;
 117
 118        mt7620_wdt_stop(dev);
 119
 120        return 0;
 121}
 122
 123static const struct wdt_ops mt7620_wdt_ops = {
 124        .start = mt7620_wdt_start,
 125        .reset = mt7620_wdt_reset,
 126        .stop = mt7620_wdt_stop,
 127        .expire_now = mt7620_wdt_expire_now,
 128};
 129
 130static const struct udevice_id mt7620_wdt_ids[] = {
 131        { .compatible = "mediatek,mt7620-wdt" },
 132        {}
 133};
 134
 135U_BOOT_DRIVER(mt7620_wdt) = {
 136        .name = "mt7620_wdt",
 137        .id = UCLASS_WDT,
 138        .of_match = mt7620_wdt_ids,
 139        .probe = mt7620_wdt_probe,
 140        .priv_auto = sizeof(struct mt7620_wdt),
 141        .ops = &mt7620_wdt_ops,
 142};
 143