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

watchdog: sama5d4_wdt: addition of sam9x60 compatible watchdog

Add support for SAM9X60 WDT into sama5d4_wdt.
This means that this driver gets a flag inside the data struct
that represents the sam9x60 support.
This flag differentiates between the two hardware blocks, and is set
according to the compatible of the driver instantiation.

Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com>
Reviewed-by-off-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/1574067012-18559-3-git-send-email-eugen.hristev@microchip.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Eugen Hristev and committed by
Wim Van Sebroeck
bb44aa09 d5226fa6

+105 -25
+21
drivers/watchdog/at91sam9_wdt.h
··· 24 24 #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ 25 25 #define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */ 26 26 #define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) 27 + #define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */ 28 + #define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */ 27 29 #define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ 30 + #define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */ 28 31 #define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ 29 32 #define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ 30 33 #define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ ··· 39 36 #define AT91_WDT_SR 0x08 /* Watchdog Status Register */ 40 37 #define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */ 41 38 #define AT91_WDT_WDERR BIT(1) /* Watchdog Error */ 39 + 40 + /* Watchdog Timer Value Register */ 41 + #define AT91_SAM9X60_VR 0x08 42 + 43 + /* Watchdog Window Level Register */ 44 + #define AT91_SAM9X60_WLR 0x0c 45 + /* Watchdog Period Value */ 46 + #define AT91_SAM9X60_COUNTER (0xfffUL << 0) 47 + #define AT91_SAM9X60_SET_COUNTER(x) ((x) & AT91_SAM9X60_COUNTER) 48 + 49 + /* Interrupt Enable Register */ 50 + #define AT91_SAM9X60_IER 0x14 51 + /* Period Interrupt Enable */ 52 + #define AT91_SAM9X60_PERINT BIT(0) 53 + /* Interrupt Disable Register */ 54 + #define AT91_SAM9X60_IDR 0x18 55 + /* Interrupt Status Register */ 56 + #define AT91_SAM9X60_ISR 0x1c 42 57 43 58 #endif
+84 -25
drivers/watchdog/sama5d4_wdt.c
··· 2 2 /* 3 3 * Driver for Atmel SAMA5D4 Watchdog Timer 4 4 * 5 - * Copyright (C) 2015 Atmel Corporation 5 + * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries 6 6 */ 7 7 8 8 #include <linux/delay.h> ··· 11 11 #include <linux/kernel.h> 12 12 #include <linux/module.h> 13 13 #include <linux/of.h> 14 + #include <linux/of_device.h> 14 15 #include <linux/of_irq.h> 15 16 #include <linux/platform_device.h> 16 17 #include <linux/reboot.h> ··· 30 29 struct watchdog_device wdd; 31 30 void __iomem *reg_base; 32 31 u32 mr; 32 + u32 ir; 33 33 unsigned long last_ping; 34 + bool need_irq; 35 + bool sam9x60_support; 34 36 }; 35 37 36 38 static int wdt_timeout; ··· 82 78 { 83 79 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 84 80 85 - wdt->mr &= ~AT91_WDT_WDDIS; 81 + if (wdt->sam9x60_support) { 82 + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); 83 + wdt->mr &= ~AT91_SAM9X60_WDDIS; 84 + } else { 85 + wdt->mr &= ~AT91_WDT_WDDIS; 86 + } 86 87 wdt_write(wdt, AT91_WDT_MR, wdt->mr); 87 88 88 89 return 0; ··· 97 88 { 98 89 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 99 90 100 - wdt->mr |= AT91_WDT_WDDIS; 91 + if (wdt->sam9x60_support) { 92 + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); 93 + wdt->mr |= AT91_SAM9X60_WDDIS; 94 + } else { 95 + wdt->mr |= AT91_WDT_WDDIS; 96 + } 101 97 wdt_write(wdt, AT91_WDT_MR, wdt->mr); 102 98 103 99 return 0; ··· 122 108 { 123 109 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 124 110 u32 value = WDT_SEC2TICKS(timeout); 111 + 112 + if (wdt->sam9x60_support) { 113 + wdt_write(wdt, AT91_SAM9X60_WLR, 114 + AT91_SAM9X60_SET_COUNTER(value)); 115 + 116 + wdd->timeout = timeout; 117 + return 0; 118 + } 125 119 126 120 wdt->mr &= ~AT91_WDT_WDV; 127 121 wdt->mr |= AT91_WDT_SET_WDV(value); ··· 165 143 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) 166 144 { 167 145 struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); 146 + u32 reg; 168 147 169 - if (wdt_read(wdt, AT91_WDT_SR)) { 148 + if (wdt->sam9x60_support) 149 + reg = wdt_read(wdt, AT91_SAM9X60_ISR); 150 + else 151 + reg = wdt_read(wdt, AT91_WDT_SR); 152 + 153 + if (reg) { 170 154 pr_crit("Atmel Watchdog Software Reset\n"); 171 155 emergency_restart(); 172 156 pr_crit("Reboot didn't succeed\n"); ··· 185 157 { 186 158 const char *tmp; 187 159 188 - wdt->mr = AT91_WDT_WDDIS; 160 + if (wdt->sam9x60_support) 161 + wdt->mr = AT91_SAM9X60_WDDIS; 162 + else 163 + wdt->mr = AT91_WDT_WDDIS; 189 164 190 165 if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && 191 166 !strcmp(tmp, "software")) 192 - wdt->mr |= AT91_WDT_WDFIEN; 193 - else 194 - wdt->mr |= AT91_WDT_WDRSTEN; 167 + wdt->need_irq = true; 195 168 196 169 if (of_property_read_bool(np, "atmel,idle-halt")) 197 170 wdt->mr |= AT91_WDT_WDIDLEHLT; ··· 205 176 206 177 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) 207 178 { 208 - u32 reg; 179 + u32 reg, val; 180 + 181 + val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT); 209 182 /* 210 183 * When booting and resuming, the bootloader may have changed the 211 184 * watchdog configuration. 212 185 * If the watchdog is already running, we can safely update it. 213 186 * Else, we have to disable it properly. 214 187 */ 215 - if (wdt_enabled) { 216 - wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); 217 - } else { 188 + if (!wdt_enabled) { 218 189 reg = wdt_read(wdt, AT91_WDT_MR); 219 - if (!(reg & AT91_WDT_WDDIS)) 190 + if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) 191 + wdt_write_nosleep(wdt, AT91_WDT_MR, 192 + reg | AT91_SAM9X60_WDDIS); 193 + else if (!wdt->sam9x60_support && 194 + (!(reg & AT91_WDT_WDDIS))) 220 195 wdt_write_nosleep(wdt, AT91_WDT_MR, 221 196 reg | AT91_WDT_WDDIS); 222 197 } 198 + 199 + if (wdt->sam9x60_support) { 200 + if (wdt->need_irq) 201 + wdt->ir = AT91_SAM9X60_PERINT; 202 + else 203 + wdt->mr |= AT91_SAM9X60_PERIODRST; 204 + 205 + wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir); 206 + wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val)); 207 + } else { 208 + wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); 209 + wdt->mr |= AT91_WDT_SET_WDV(val); 210 + 211 + if (wdt->need_irq) 212 + wdt->mr |= AT91_WDT_WDFIEN; 213 + else 214 + wdt->mr |= AT91_WDT_WDRSTEN; 215 + } 216 + 217 + wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); 218 + 223 219 return 0; 224 220 } 225 221 ··· 255 201 struct sama5d4_wdt *wdt; 256 202 void __iomem *regs; 257 203 u32 irq = 0; 258 - u32 timeout; 259 204 int ret; 260 205 261 206 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); ··· 268 215 wdd->min_timeout = MIN_WDT_TIMEOUT; 269 216 wdd->max_timeout = MAX_WDT_TIMEOUT; 270 217 wdt->last_ping = jiffies; 218 + wdt->sam9x60_support = of_device_is_compatible(dev->of_node, 219 + "microchip,sam9x60-wdt"); 271 220 272 221 watchdog_set_drvdata(wdd, wdt); 273 222 ··· 279 224 280 225 wdt->reg_base = regs; 281 226 282 - irq = irq_of_parse_and_map(dev->of_node, 0); 283 - if (!irq) 284 - dev_warn(dev, "failed to get IRQ from DT\n"); 285 - 286 227 ret = of_sama5d4_wdt_init(dev->of_node, wdt); 287 228 if (ret) 288 229 return ret; 289 230 290 - if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { 231 + if (wdt->need_irq) { 232 + irq = irq_of_parse_and_map(dev->of_node, 0); 233 + if (!irq) { 234 + dev_warn(dev, "failed to get IRQ from DT\n"); 235 + wdt->need_irq = false; 236 + } 237 + } 238 + 239 + if (wdt->need_irq) { 291 240 ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler, 292 241 IRQF_SHARED | IRQF_IRQPOLL | 293 242 IRQF_NO_SUSPEND, pdev->name, pdev); ··· 302 243 } 303 244 304 245 watchdog_init_timeout(wdd, wdt_timeout, dev); 305 - 306 - timeout = WDT_SEC2TICKS(wdd->timeout); 307 - 308 - wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); 309 - wdt->mr |= AT91_WDT_SET_WDV(timeout); 310 246 311 247 ret = sama5d4_wdt_init(wdt); 312 248 if (ret) ··· 323 269 } 324 270 325 271 static const struct of_device_id sama5d4_wdt_of_match[] = { 326 - { .compatible = "atmel,sama5d4-wdt", }, 272 + { 273 + .compatible = "atmel,sama5d4-wdt", 274 + }, 275 + { 276 + .compatible = "microchip,sam9x60-wdt", 277 + }, 327 278 { } 328 279 }; 329 280 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);