^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * sunxi Watchdog Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) 2013 Carlo Caione
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * 2012 Henrik Nordstrom
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Based on xen_wdt.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * (c) Copyright 2010 Novell, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define WDT_MAX_TIMEOUT 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define WDT_MIN_TIMEOUT 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define WDT_TIMEOUT_MASK 0x0F
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define WDT_MODE_EN (1 << 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define DRV_NAME "sunxi-wdt"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define DRV_VERSION "1.0"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) static unsigned int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * This structure stores the register offsets for different variants
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * of Allwinner's watchdog hardware.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) struct sunxi_wdt_reg {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) u8 wdt_ctrl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) u8 wdt_cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) u8 wdt_mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) u8 wdt_timeout_shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) u8 wdt_reset_mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) u8 wdt_reset_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) struct sunxi_wdt_dev {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) struct watchdog_device wdt_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) void __iomem *wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) const struct sunxi_wdt_reg *wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * wdt_timeout_map maps the watchdog timer interval value in seconds to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * [timeout seconds] = register value
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static const int wdt_timeout_map[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) [1] = 0x1, /* 1s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) [2] = 0x2, /* 2s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) [3] = 0x3, /* 3s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) [4] = 0x4, /* 4s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) [5] = 0x5, /* 5s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) [6] = 0x6, /* 6s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) [8] = 0x7, /* 8s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) [10] = 0x8, /* 10s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) [12] = 0x9, /* 12s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) [14] = 0xA, /* 14s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) [16] = 0xB, /* 16s */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) unsigned long action, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) void __iomem *wdt_base = sunxi_wdt->wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) u32 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* Set system reset function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) val = readl(wdt_base + regs->wdt_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) val &= ~(regs->wdt_reset_mask);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) val |= regs->wdt_reset_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) writel(val, wdt_base + regs->wdt_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) /* Set lowest timeout and enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) val = readl(wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) val |= WDT_MODE_EN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) writel(val, wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) * Restart the watchdog. The default (and lowest) interval
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * value for the watchdog is 0.5s.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) mdelay(5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) val = readl(wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) val |= WDT_MODE_EN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) writel(val, wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static int sunxi_wdt_ping(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) void __iomem *wdt_base = sunxi_wdt->wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) unsigned int timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) void __iomem *wdt_base = sunxi_wdt->wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (wdt_timeout_map[timeout] == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) timeout++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) sunxi_wdt->wdt_dev.timeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) reg = readl(wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) writel(reg, wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) sunxi_wdt_ping(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) void __iomem *wdt_base = sunxi_wdt->wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) writel(0, wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) void __iomem *wdt_base = sunxi_wdt->wdt_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) sunxi_wdt->wdt_dev.timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) /* Set system reset function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) reg = readl(wdt_base + regs->wdt_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) reg &= ~(regs->wdt_reset_mask);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) reg |= regs->wdt_reset_val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) writel(reg, wdt_base + regs->wdt_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) /* Enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) reg = readl(wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) reg |= WDT_MODE_EN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) writel(reg, wdt_base + regs->wdt_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) static const struct watchdog_info sunxi_wdt_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) .identity = DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) .options = WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) static const struct watchdog_ops sunxi_wdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) .start = sunxi_wdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) .stop = sunxi_wdt_stop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) .ping = sunxi_wdt_ping,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) .set_timeout = sunxi_wdt_set_timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) .restart = sunxi_wdt_restart,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) static const struct sunxi_wdt_reg sun4i_wdt_reg = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) .wdt_ctrl = 0x00,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) .wdt_cfg = 0x04,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) .wdt_mode = 0x04,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) .wdt_timeout_shift = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) .wdt_reset_mask = 0x02,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) .wdt_reset_val = 0x02,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static const struct sunxi_wdt_reg sun6i_wdt_reg = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) .wdt_ctrl = 0x10,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) .wdt_cfg = 0x14,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) .wdt_mode = 0x18,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) .wdt_timeout_shift = 4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) .wdt_reset_mask = 0x03,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) .wdt_reset_val = 0x01,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) static const struct of_device_id sunxi_wdt_dt_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) { /* sentinel */ }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) static int sunxi_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) struct sunxi_wdt_dev *sunxi_wdt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) if (!sunxi_wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) sunxi_wdt->wdt_regs = of_device_get_match_data(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if (!sunxi_wdt->wdt_regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) sunxi_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) if (IS_ERR(sunxi_wdt->wdt_base))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) return PTR_ERR(sunxi_wdt->wdt_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) sunxi_wdt->wdt_dev.info = &sunxi_wdt_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) sunxi_wdt->wdt_dev.ops = &sunxi_wdt_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) sunxi_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) sunxi_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) sunxi_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) sunxi_wdt->wdt_dev.parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) watchdog_set_restart_priority(&sunxi_wdt->wdt_dev, 128);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) sunxi_wdt_stop(&sunxi_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) err = devm_watchdog_register_device(dev, &sunxi_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (unlikely(err))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) sunxi_wdt->wdt_dev.timeout, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static struct platform_driver sunxi_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) .probe = sunxi_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) .name = DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) .of_match_table = sunxi_wdt_dt_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) module_platform_driver(sunxi_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) module_param(timeout, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) MODULE_AUTHOR("Henrik Nordstrom <henrik@henriknordstrom.net>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) MODULE_DESCRIPTION("sunxi WatchDog Timer Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) MODULE_VERSION(DRV_VERSION);