^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Imagination Technologies PowerDown Controller Watchdog Timer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (c) 2014 Imagination Technologies Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * 2012 Henrik Nordstrom
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Notes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * -----
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * The timeout value is rounded to the next power of two clock cycles.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * This is configured using the PDC_WDT_CONFIG register, according to this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * formula:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * timeout = 2^(delay + 1) clock cycles
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Where 'delay' is the value written in PDC_WDT_CONFIG register.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * Therefore, the hardware only allows to program watchdog timeouts, expressed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * as a power of two number of watchdog clock cycles. The current implementation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * guarantees that the actual watchdog timeout will be _at least_ the value
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * programmed in the imgpdg_wdt driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * The following table shows how the user-configured timeout relates
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * to the actual hardware timeout (watchdog clock @ 40000 Hz):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * input timeout | WD_DELAY | actual timeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * -----------------------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * 10 | 18 | 13 seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * 20 | 19 | 26 seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * 30 | 20 | 52 seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * 60 | 21 | 104 seconds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * Albeit coarse, this granularity would suffice most watchdog uses.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * If the platform allows it, the user should be able to change the watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * clock rate and achieve a finer timeout granularity.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include <linux/log2.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #include <linux/mod_devicetable.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) /* registers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define PDC_WDT_SOFT_RESET 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define PDC_WDT_CONFIG 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define PDC_WDT_CONFIG_ENABLE BIT(31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define PDC_WDT_CONFIG_DELAY_MASK 0x1f
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define PDC_WDT_TICKLE1 0x08
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #define PDC_WDT_TICKLE1_MAGIC 0xabcd1234
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #define PDC_WDT_TICKLE2 0x0c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #define PDC_WDT_TICKLE2_MAGIC 0x4321dcba
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #define PDC_WDT_TICKLE_STATUS_MASK 0x7
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) #define PDC_WDT_TICKLE_STATUS_SHIFT 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #define PDC_WDT_TICKLE_STATUS_HRESET 0x0 /* Hard reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #define PDC_WDT_TICKLE_STATUS_TIMEOUT 0x1 /* Timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) #define PDC_WDT_TICKLE_STATUS_TICKLE 0x2 /* Tickled incorrectly */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) #define PDC_WDT_TICKLE_STATUS_SRESET 0x3 /* Soft reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) #define PDC_WDT_TICKLE_STATUS_USER 0x4 /* User reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) /* Timeout values are in seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) #define PDC_WDT_MIN_TIMEOUT 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) #define PDC_WDT_DEF_TIMEOUT 64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) static int heartbeat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) module_param(heartbeat, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct pdc_wdt_dev {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct watchdog_device wdt_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct clk *wdt_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct clk *sys_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) void __iomem *base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) writel(PDC_WDT_TICKLE1_MAGIC, wdt->base + PDC_WDT_TICKLE1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) writel(PDC_WDT_TICKLE2_MAGIC, wdt->base + PDC_WDT_TICKLE2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) static int pdc_wdt_stop(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) val = readl(wdt->base + PDC_WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) val &= ~PDC_WDT_CONFIG_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) writel(val, wdt->base + PDC_WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) /* Must tickle to finish the stop */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) pdc_wdt_keepalive(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) writel(val, wdt->base + PDC_WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) unsigned int new_timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) wdt->wdt_dev.timeout = new_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) __pdc_wdt_set_timeout(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) /* Start the watchdog timer (delay should already be set) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) static int pdc_wdt_start(struct watchdog_device *wdt_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) __pdc_wdt_set_timeout(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) val = readl(wdt->base + PDC_WDT_CONFIG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) val |= PDC_WDT_CONFIG_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) writel(val, wdt->base + PDC_WDT_CONFIG);
^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 pdc_wdt_restart(struct watchdog_device *wdt_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) unsigned long action, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /* Assert SOFT_RESET */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) writel(0x1, wdt->base + PDC_WDT_SOFT_RESET);
^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 const struct watchdog_info pdc_wdt_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) .identity = "IMG PDC Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) .options = WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) static const struct watchdog_ops pdc_wdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) .start = pdc_wdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) .stop = pdc_wdt_stop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) .ping = pdc_wdt_keepalive,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) .set_timeout = pdc_wdt_set_timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) .restart = pdc_wdt_restart,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) static void pdc_clk_disable_unprepare(void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) clk_disable_unprepare(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) static int pdc_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) u64 div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) int ret, val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) unsigned long clk_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) struct pdc_wdt_dev *pdc_wdt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) pdc_wdt = devm_kzalloc(dev, sizeof(*pdc_wdt), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) if (!pdc_wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) pdc_wdt->base = devm_platform_ioremap_resource(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) if (IS_ERR(pdc_wdt->base))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) return PTR_ERR(pdc_wdt->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) pdc_wdt->sys_clk = devm_clk_get(dev, "sys");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (IS_ERR(pdc_wdt->sys_clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) dev_err(dev, "failed to get the sys clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) return PTR_ERR(pdc_wdt->sys_clk);
^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) pdc_wdt->wdt_clk = devm_clk_get(dev, "wdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) if (IS_ERR(pdc_wdt->wdt_clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) dev_err(dev, "failed to get the wdt clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) return PTR_ERR(pdc_wdt->wdt_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) ret = clk_prepare_enable(pdc_wdt->sys_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) dev_err(dev, "could not prepare or enable sys clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) pdc_wdt->sys_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) ret = clk_prepare_enable(pdc_wdt->wdt_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) dev_err(dev, "could not prepare or enable wdt clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) pdc_wdt->wdt_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) /* We use the clock rate to calculate the max timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) clk_rate = clk_get_rate(pdc_wdt->wdt_clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (clk_rate == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) dev_err(dev, "failed to get clock rate\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) dev_err(dev, "invalid clock rate\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) if (order_base_2(clk_rate) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) pdc_wdt->wdt_dev.info = &pdc_wdt_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) do_div(div, clk_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) pdc_wdt->wdt_dev.max_timeout = div;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) pdc_wdt->wdt_dev.parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) pdc_wdt_stop(&pdc_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /* Find what caused the last reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) val = readl(pdc_wdt->base + PDC_WDT_TICKLE1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) val = (val & PDC_WDT_TICKLE_STATUS_MASK) >> PDC_WDT_TICKLE_STATUS_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) switch (val) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) case PDC_WDT_TICKLE_STATUS_TICKLE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) case PDC_WDT_TICKLE_STATUS_TIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) dev_info(dev, "watchdog module last reset due to timeout\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) case PDC_WDT_TICKLE_STATUS_HRESET:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) dev_info(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) "watchdog module last reset due to hard reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) case PDC_WDT_TICKLE_STATUS_SRESET:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) dev_info(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) "watchdog module last reset due to soft reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) case PDC_WDT_TICKLE_STATUS_USER:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) dev_info(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) "watchdog module last reset due to user reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) dev_info(dev, "contains an illegal status code (%08x)\n", val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) watchdog_set_restart_priority(&pdc_wdt->wdt_dev, 128);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) platform_set_drvdata(pdev, pdc_wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) watchdog_stop_on_reboot(&pdc_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) watchdog_stop_on_unregister(&pdc_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) return devm_watchdog_register_device(dev, &pdc_wdt->wdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) static const struct of_device_id pdc_wdt_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) { .compatible = "img,pdc-wdt" },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) MODULE_DEVICE_TABLE(of, pdc_wdt_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) static struct platform_driver pdc_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) .name = "imgpdc-wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) .of_match_table = pdc_wdt_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) .probe = pdc_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) module_platform_driver(pdc_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) MODULE_AUTHOR("Jude Abraham <Jude.Abraham@imgtec.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) MODULE_AUTHOR("Naidu Tellapati <Naidu.Tellapati@imgtec.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) MODULE_DESCRIPTION("Imagination Technologies PDC Watchdog Timer Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) MODULE_LICENSE("GPL v2");