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