Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: sirf: add watchdog driver of CSR SiRFprimaII and SiRFatlasVI

On CSR SiRFprimaII and SiRFatlasVI, the 6th timer can act as a watchdog
timer when the Watchdog mode is enabled.

watchdog occur when TIMER watchdog counter matches the value software
pre-set, when this event occurs, the effect is the same as the system
software reset.

Signed-off-by: Xianglong Du <Xianglong.Du@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Cc: Romain Izard <romain.izard.pro@gmail.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Xianglong Du and committed by
Wim Van Sebroeck
f0fcbdbf 85eee819

+249
+14
Documentation/devicetree/bindings/watchdog/sirfsoc_wdt.txt
··· 1 + SiRFSoC Timer and Watchdog Timer(WDT) Controller 2 + 3 + Required properties: 4 + - compatible: "sirf,prima2-tick" 5 + - reg: Address range of tick timer/WDT register set 6 + - interrupts: interrupt number to the cpu 7 + 8 + Example: 9 + 10 + timer@b0020000 { 11 + compatible = "sirf,prima2-tick"; 12 + reg = <0xb0020000 0x1000>; 13 + interrupts = <0>; 14 + };
+1
arch/arm/configs/prima2_defconfig
··· 39 39 CONFIG_SPI_SIRF=y 40 40 CONFIG_SPI_SPIDEV=y 41 41 # CONFIG_HWMON is not set 42 + CONFIG_WATCHDOG=y 42 43 CONFIG_USB_GADGET=y 43 44 CONFIG_USB_MASS_STORAGE=m 44 45 CONFIG_MMC=y
+9
drivers/watchdog/Kconfig
··· 402 402 To compile this driver as a module, choose M here: the 403 403 module will be called moxart_wdt. 404 404 405 + config SIRFSOC_WATCHDOG 406 + tristate "SiRFSOC watchdog" 407 + depends on ARCH_SIRF 408 + select WATCHDOG_CORE 409 + default y 410 + help 411 + Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When 412 + the watchdog triggers the system will be reset. 413 + 405 414 # AVR32 Architecture 406 415 407 416 config AT32AP700X_WDT
+1
drivers/watchdog/Makefile
··· 56 56 obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o 57 57 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o 58 58 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o 59 + obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o 59 60 60 61 # AVR32 Architecture 61 62 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+224
drivers/watchdog/sirfsoc_wdt.c
··· 1 + /* 2 + * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI 3 + * 4 + * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. 5 + * 6 + * Licensed under GPLv2 or later. 7 + */ 8 + 9 + #include <linux/module.h> 10 + #include <linux/watchdog.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/moduleparam.h> 13 + #include <linux/of.h> 14 + #include <linux/io.h> 15 + #include <linux/uaccess.h> 16 + 17 + #define SIRFSOC_TIMER_COUNTER_LO 0x0000 18 + #define SIRFSOC_TIMER_MATCH_0 0x0008 19 + #define SIRFSOC_TIMER_INT_EN 0x0024 20 + #define SIRFSOC_TIMER_WATCHDOG_EN 0x0028 21 + #define SIRFSOC_TIMER_LATCH 0x0030 22 + #define SIRFSOC_TIMER_LATCHED_LO 0x0034 23 + 24 + #define SIRFSOC_TIMER_WDT_INDEX 5 25 + 26 + #define SIRFSOC_WDT_MIN_TIMEOUT 30 /* 30 secs */ 27 + #define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */ 28 + #define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */ 29 + 30 + static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT; 31 + static bool nowayout = WATCHDOG_NOWAYOUT; 32 + 33 + module_param(timeout, uint, 0); 34 + module_param(nowayout, bool, 0); 35 + 36 + MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); 37 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 38 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 39 + 40 + static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd) 41 + { 42 + u32 counter, match; 43 + void __iomem *wdt_base; 44 + int time_left; 45 + 46 + wdt_base = watchdog_get_drvdata(wdd); 47 + counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO); 48 + match = readl(wdt_base + 49 + SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); 50 + 51 + time_left = match - counter; 52 + 53 + return time_left / CLOCK_TICK_RATE; 54 + } 55 + 56 + static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) 57 + { 58 + u32 counter, timeout_ticks; 59 + void __iomem *wdt_base; 60 + 61 + timeout_ticks = wdd->timeout * CLOCK_TICK_RATE; 62 + wdt_base = watchdog_get_drvdata(wdd); 63 + 64 + /* Enable the latch before reading the LATCH_LO register */ 65 + writel(1, wdt_base + SIRFSOC_TIMER_LATCH); 66 + 67 + /* Set the TO value */ 68 + counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO); 69 + 70 + counter += timeout_ticks; 71 + 72 + writel(counter, wdt_base + 73 + SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); 74 + 75 + return 0; 76 + } 77 + 78 + static int sirfsoc_wdt_enable(struct watchdog_device *wdd) 79 + { 80 + void __iomem *wdt_base = watchdog_get_drvdata(wdd); 81 + sirfsoc_wdt_updatetimeout(wdd); 82 + 83 + /* 84 + * NOTE: If interrupt is not enabled 85 + * then WD-Reset doesn't get generated at all. 86 + */ 87 + writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) 88 + | (1 << SIRFSOC_TIMER_WDT_INDEX), 89 + wdt_base + SIRFSOC_TIMER_INT_EN); 90 + writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); 91 + 92 + return 0; 93 + } 94 + 95 + static int sirfsoc_wdt_disable(struct watchdog_device *wdd) 96 + { 97 + void __iomem *wdt_base = watchdog_get_drvdata(wdd); 98 + 99 + writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); 100 + writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) 101 + & (~(1 << SIRFSOC_TIMER_WDT_INDEX)), 102 + wdt_base + SIRFSOC_TIMER_INT_EN); 103 + 104 + return 0; 105 + } 106 + 107 + static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) 108 + { 109 + wdd->timeout = to; 110 + sirfsoc_wdt_updatetimeout(wdd); 111 + 112 + return 0; 113 + } 114 + 115 + #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 116 + 117 + static const struct watchdog_info sirfsoc_wdt_ident = { 118 + .options = OPTIONS, 119 + .firmware_version = 0, 120 + .identity = "SiRFSOC Watchdog", 121 + }; 122 + 123 + static struct watchdog_ops sirfsoc_wdt_ops = { 124 + .owner = THIS_MODULE, 125 + .start = sirfsoc_wdt_enable, 126 + .stop = sirfsoc_wdt_disable, 127 + .get_timeleft = sirfsoc_wdt_gettimeleft, 128 + .ping = sirfsoc_wdt_updatetimeout, 129 + .set_timeout = sirfsoc_wdt_settimeout, 130 + }; 131 + 132 + static struct watchdog_device sirfsoc_wdd = { 133 + .info = &sirfsoc_wdt_ident, 134 + .ops = &sirfsoc_wdt_ops, 135 + .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT, 136 + .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT, 137 + .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT, 138 + }; 139 + 140 + static int sirfsoc_wdt_probe(struct platform_device *pdev) 141 + { 142 + struct resource *res; 143 + int ret; 144 + void __iomem *base; 145 + 146 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 147 + base = devm_ioremap_resource(&pdev->dev, res); 148 + if (IS_ERR(base)) 149 + return PTR_ERR(base); 150 + 151 + watchdog_set_drvdata(&sirfsoc_wdd, base); 152 + 153 + watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev); 154 + watchdog_set_nowayout(&sirfsoc_wdd, nowayout); 155 + 156 + ret = watchdog_register_device(&sirfsoc_wdd); 157 + if (ret) 158 + return ret; 159 + 160 + platform_set_drvdata(pdev, &sirfsoc_wdd); 161 + 162 + return 0; 163 + } 164 + 165 + static void sirfsoc_wdt_shutdown(struct platform_device *pdev) 166 + { 167 + struct watchdog_device *wdd = platform_get_drvdata(pdev); 168 + 169 + sirfsoc_wdt_disable(wdd); 170 + } 171 + 172 + static int sirfsoc_wdt_remove(struct platform_device *pdev) 173 + { 174 + sirfsoc_wdt_shutdown(pdev); 175 + return 0; 176 + } 177 + 178 + #ifdef CONFIG_PM_SLEEP 179 + static int sirfsoc_wdt_suspend(struct device *dev) 180 + { 181 + return 0; 182 + } 183 + 184 + static int sirfsoc_wdt_resume(struct device *dev) 185 + { 186 + struct watchdog_device *wdd = dev_get_drvdata(dev); 187 + 188 + /* 189 + * NOTE: Since timer controller registers settings are saved 190 + * and restored back by the timer-prima2.c, so we need not 191 + * update WD settings except refreshing timeout. 192 + */ 193 + sirfsoc_wdt_updatetimeout(wdd); 194 + 195 + return 0; 196 + } 197 + #endif 198 + 199 + static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops, 200 + sirfsoc_wdt_suspend, sirfsoc_wdt_resume); 201 + 202 + static const struct of_device_id sirfsoc_wdt_of_match[] = { 203 + { .compatible = "sirf,prima2-tick"}, 204 + {}, 205 + }; 206 + MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match); 207 + 208 + static struct platform_driver sirfsoc_wdt_driver = { 209 + .driver = { 210 + .name = "sirfsoc-wdt", 211 + .owner = THIS_MODULE, 212 + .pm = &sirfsoc_wdt_pm_ops, 213 + .of_match_table = of_match_ptr(sirfsoc_wdt_of_match), 214 + }, 215 + .probe = sirfsoc_wdt_probe, 216 + .remove = sirfsoc_wdt_remove, 217 + .shutdown = sirfsoc_wdt_shutdown, 218 + }; 219 + module_platform_driver(sirfsoc_wdt_driver); 220 + 221 + MODULE_DESCRIPTION("SiRF SoC watchdog driver"); 222 + MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>"); 223 + MODULE_LICENSE("GPL v2"); 224 + MODULE_ALIAS("platform:sirfsoc-wdt");