of_xilinx_wdt.c revision 4c7fbbc4a57a35ed109f58f52eff1a04660789e9
1/* 2 * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt 3 * 4 * (C) Copyright 2013 - 2014 Xilinx, Inc. 5 * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 */ 12 13#include <linux/err.h> 14#include <linux/module.h> 15#include <linux/types.h> 16#include <linux/kernel.h> 17#include <linux/ioport.h> 18#include <linux/watchdog.h> 19#include <linux/io.h> 20#include <linux/of.h> 21#include <linux/of_device.h> 22#include <linux/of_address.h> 23 24/* Register offsets for the Wdt device */ 25#define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ 26#define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */ 27#define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */ 28 29/* Control/Status Register Masks */ 30#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */ 31#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */ 32#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ 33 34/* Control/Status Register 0/1 bits */ 35#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ 36 37/* SelfTest constants */ 38#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 39#define XWT_TIMER_FAILED 0xFFFFFFFF 40 41#define WATCHDOG_NAME "Xilinx Watchdog" 42 43struct xwdt_device { 44 void __iomem *base; 45 u32 wdt_interval; 46 spinlock_t spinlock; 47 struct watchdog_device xilinx_wdt_wdd; 48}; 49 50static int xilinx_wdt_start(struct watchdog_device *wdd) 51{ 52 u32 control_status_reg; 53 struct xwdt_device *xdev = watchdog_get_drvdata(wdd); 54 55 spin_lock(&xdev->spinlock); 56 57 /* Clean previous status and enable the watchdog timer */ 58 control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); 59 control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 60 61 iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), 62 xdev->base + XWT_TWCSR0_OFFSET); 63 64 iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET); 65 66 spin_unlock(&xdev->spinlock); 67 68 return 0; 69} 70 71static int xilinx_wdt_stop(struct watchdog_device *wdd) 72{ 73 u32 control_status_reg; 74 struct xwdt_device *xdev = watchdog_get_drvdata(wdd); 75 76 spin_lock(&xdev->spinlock); 77 78 control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); 79 80 iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), 81 xdev->base + XWT_TWCSR0_OFFSET); 82 83 iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET); 84 85 spin_unlock(&xdev->spinlock); 86 pr_info("Stopped!\n"); 87 88 return 0; 89} 90 91static int xilinx_wdt_keepalive(struct watchdog_device *wdd) 92{ 93 u32 control_status_reg; 94 struct xwdt_device *xdev = watchdog_get_drvdata(wdd); 95 96 spin_lock(&xdev->spinlock); 97 98 control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); 99 control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); 100 iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET); 101 102 spin_unlock(&xdev->spinlock); 103 104 return 0; 105} 106 107static const struct watchdog_info xilinx_wdt_ident = { 108 .options = WDIOF_MAGICCLOSE | 109 WDIOF_KEEPALIVEPING, 110 .firmware_version = 1, 111 .identity = WATCHDOG_NAME, 112}; 113 114static const struct watchdog_ops xilinx_wdt_ops = { 115 .owner = THIS_MODULE, 116 .start = xilinx_wdt_start, 117 .stop = xilinx_wdt_stop, 118 .ping = xilinx_wdt_keepalive, 119}; 120 121static u32 xwdt_selftest(struct xwdt_device *xdev) 122{ 123 int i; 124 u32 timer_value1; 125 u32 timer_value2; 126 127 spin_lock(&xdev->spinlock); 128 129 timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET); 130 timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); 131 132 for (i = 0; 133 ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && 134 (timer_value2 == timer_value1)); i++) { 135 timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); 136 } 137 138 spin_unlock(&xdev->spinlock); 139 140 if (timer_value2 != timer_value1) 141 return ~XWT_TIMER_FAILED; 142 else 143 return XWT_TIMER_FAILED; 144} 145 146static int xwdt_probe(struct platform_device *pdev) 147{ 148 int rc; 149 u32 *tmptr; 150 u32 *pfreq; 151 struct resource *res; 152 struct xwdt_device *xdev; 153 bool no_timeout = false; 154 struct watchdog_device *xilinx_wdt_wdd; 155 156 xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); 157 if (!xdev) 158 return -ENOMEM; 159 160 xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd; 161 xilinx_wdt_wdd->info = &xilinx_wdt_ident; 162 xilinx_wdt_wdd->ops = &xilinx_wdt_ops; 163 xilinx_wdt_wdd->parent = &pdev->dev; 164 165 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 166 xdev->base = devm_ioremap_resource(&pdev->dev, res); 167 if (IS_ERR(xdev->base)) 168 return PTR_ERR(xdev->base); 169 170 pfreq = (u32 *)of_get_property(pdev->dev.of_node, 171 "clock-frequency", NULL); 172 173 if (pfreq == NULL) { 174 dev_warn(&pdev->dev, 175 "The watchdog clock frequency cannot be obtained\n"); 176 no_timeout = true; 177 } 178 179 tmptr = (u32 *)of_get_property(pdev->dev.of_node, 180 "xlnx,wdt-interval", NULL); 181 if (tmptr == NULL) { 182 dev_warn(&pdev->dev, 183 "Parameter \"xlnx,wdt-interval\" not found\n"); 184 no_timeout = true; 185 } else { 186 xdev->wdt_interval = *tmptr; 187 } 188 189 tmptr = (u32 *)of_get_property(pdev->dev.of_node, 190 "xlnx,wdt-enable-once", NULL); 191 if (tmptr == NULL) { 192 dev_warn(&pdev->dev, 193 "Parameter \"xlnx,wdt-enable-once\" not found\n"); 194 watchdog_set_nowayout(xilinx_wdt_wdd, true); 195 } 196 197/* 198 * Twice of the 2^wdt_interval / freq because the first wdt overflow is 199 * ignored (interrupt), reset is only generated at second wdt overflow 200 */ 201 if (!no_timeout) 202 xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) / 203 *pfreq); 204 205 spin_lock_init(&xdev->spinlock); 206 watchdog_set_drvdata(xilinx_wdt_wdd, xdev); 207 208 rc = xwdt_selftest(xdev); 209 if (rc == XWT_TIMER_FAILED) { 210 dev_err(&pdev->dev, "SelfTest routine error\n"); 211 return rc; 212 } 213 214 rc = watchdog_register_device(xilinx_wdt_wdd); 215 if (rc) { 216 dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); 217 return rc; 218 } 219 220 dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", 221 xdev->base, xilinx_wdt_wdd->timeout); 222 223 platform_set_drvdata(pdev, xdev); 224 225 return 0; 226} 227 228static int xwdt_remove(struct platform_device *pdev) 229{ 230 struct xwdt_device *xdev = platform_get_drvdata(pdev); 231 232 watchdog_unregister_device(&xdev->xilinx_wdt_wdd); 233 234 return 0; 235} 236 237/* Match table for of_platform binding */ 238static struct of_device_id xwdt_of_match[] = { 239 { .compatible = "xlnx,xps-timebase-wdt-1.00.a", }, 240 { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, 241 {}, 242}; 243MODULE_DEVICE_TABLE(of, xwdt_of_match); 244 245static struct platform_driver xwdt_driver = { 246 .probe = xwdt_probe, 247 .remove = xwdt_remove, 248 .driver = { 249 .owner = THIS_MODULE, 250 .name = WATCHDOG_NAME, 251 .of_match_table = xwdt_of_match, 252 }, 253}; 254 255module_platform_driver(xwdt_driver); 256 257MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); 258MODULE_DESCRIPTION("Xilinx Watchdog driver"); 259MODULE_LICENSE("GPL v2"); 260