Commit 70c14ff0e9f5e1f5456587b827620e636ba70a09

Authored by Dan Williams
Committed by Russell King
1 parent 7dea1b2006

[ARM] 4495/1: iop: combined watchdog timer driver for iop3xx and iop13xx

In order for this driver to be shared across the iop architectures the
iop3xx and iop13xx header files are modified to present a common interface
for the iop_wdt driver.

Details:
* iop13xx supports disabling the timer while iop3xx does not.  This requires
  a few 'compatibility' definitions in include/asm-arm/hardware/iop3xx.h to
  preclude adding #ifdef CONFIG_ARCH_IOP13XX blocks to the driver code.
* The heartbeat interval is derived from the internal bus clock rate, so this
  this patch also exports the tick rate to the iop_wdt driver.

Cc: Curt Bruns <curt.e.bruns@intel.com>
Cc: Peter Milne <peter.milne@d-tacq.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Wim Van Sebroeck <wim@iguana.be>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 7 changed files with 365 additions and 32 deletions Side-by-side Diff

arch/arm/plat-iop/time.c
... ... @@ -78,6 +78,13 @@
78 78 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
79 79 };
80 80  
  81 +static unsigned long iop_tick_rate;
  82 +unsigned long get_iop_tick_rate(void)
  83 +{
  84 + return iop_tick_rate;
  85 +}
  86 +EXPORT_SYMBOL(get_iop_tick_rate);
  87 +
81 88 void __init iop_init_time(unsigned long tick_rate)
82 89 {
83 90 u32 timer_ctl;
... ... @@ -85,6 +92,7 @@
85 92 ticks_per_jiffy = (tick_rate + HZ/2) / HZ;
86 93 ticks_per_usec = tick_rate / 1000000;
87 94 next_jiffy_time = 0xffffffff;
  95 + iop_tick_rate = tick_rate;
88 96  
89 97 timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED |
90 98 IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1;
drivers/char/watchdog/Kconfig
... ... @@ -187,6 +187,22 @@
187 187  
188 188 Say N if you are unsure.
189 189  
  190 +config IOP_WATCHDOG
  191 + tristate "IOP Watchdog"
  192 + depends on WATCHDOG && PLAT_IOP
  193 + select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
  194 + help
  195 + Say Y here if to include support for the watchdog timer
  196 + in the Intel IOP3XX & IOP13XX I/O Processors. This driver can
  197 + be built as a module by choosing M. The module will
  198 + be called iop_wdt.
  199 +
  200 + Note: The IOP13XX watchdog does an Internal Bus Reset which will
  201 + affect both cores and the peripherals of the IOP. The ATU-X
  202 + and/or ATUe configuration registers will remain intact, but if
  203 + operating as an Root Complex and/or Central Resource, the PCI-X
  204 + and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER.
  205 +
190 206 # AVR32 Architecture
191 207  
192 208 config AT32AP700X_WDT
drivers/char/watchdog/Makefile
... ... @@ -35,6 +35,7 @@
35 35 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
36 36 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
37 37 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
  38 +obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
38 39  
39 40 # AVR32 Architecture
40 41 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
drivers/char/watchdog/iop_wdt.c
  1 +/*
  2 + * drivers/char/watchdog/iop_wdt.c
  3 + *
  4 + * WDT driver for Intel I/O Processors
  5 + * Copyright (C) 2005, Intel Corporation.
  6 + *
  7 + * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify it
  10 + * under the terms and conditions of the GNU General Public License,
  11 + * version 2, as published by the Free Software Foundation.
  12 + *
  13 + * This program is distributed in the hope it will be useful, but WITHOUT
  14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  16 + * more details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License along with
  19 + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  20 + * Place - Suite 330, Boston, MA 02111-1307 USA.
  21 + *
  22 + * Curt E Bruns <curt.e.bruns@intel.com>
  23 + * Peter Milne <peter.milne@d-tacq.com>
  24 + * Dan Williams <dan.j.williams@intel.com>
  25 + */
  26 +
  27 +#include <linux/module.h>
  28 +#include <linux/kernel.h>
  29 +#include <linux/fs.h>
  30 +#include <linux/init.h>
  31 +#include <linux/device.h>
  32 +#include <linux/miscdevice.h>
  33 +#include <linux/watchdog.h>
  34 +#include <linux/uaccess.h>
  35 +#include <asm/hardware.h>
  36 +
  37 +static int nowayout = WATCHDOG_NOWAYOUT;
  38 +static unsigned long wdt_status;
  39 +static unsigned long boot_status;
  40 +
  41 +#define WDT_IN_USE 0
  42 +#define WDT_OK_TO_CLOSE 1
  43 +#define WDT_ENABLED 2
  44 +
  45 +static unsigned long iop_watchdog_timeout(void)
  46 +{
  47 + return (0xffffffffUL / get_iop_tick_rate());
  48 +}
  49 +
  50 +/**
  51 + * wdt_supports_disable - determine if we are accessing a iop13xx watchdog
  52 + * or iop3xx by whether it has a disable command
  53 + */
  54 +static int wdt_supports_disable(void)
  55 +{
  56 + int can_disable;
  57 +
  58 + if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM)
  59 + can_disable = 1;
  60 + else
  61 + can_disable = 0;
  62 +
  63 + return can_disable;
  64 +}
  65 +
  66 +static void wdt_enable(void)
  67 +{
  68 + /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
  69 + * Takes approx. 10.7s to timeout
  70 + */
  71 + write_wdtcr(IOP_WDTCR_EN_ARM);
  72 + write_wdtcr(IOP_WDTCR_EN);
  73 +}
  74 +
  75 +/* returns 0 if the timer was successfully disabled */
  76 +static int wdt_disable(void)
  77 +{
  78 + /* Stop Counting */
  79 + if (wdt_supports_disable()) {
  80 + write_wdtcr(IOP_WDTCR_DIS_ARM);
  81 + write_wdtcr(IOP_WDTCR_DIS);
  82 + clear_bit(WDT_ENABLED, &wdt_status);
  83 + printk(KERN_INFO "WATCHDOG: Disabled\n");
  84 + return 0;
  85 + } else
  86 + return 1;
  87 +}
  88 +
  89 +static int iop_wdt_open(struct inode *inode, struct file *file)
  90 +{
  91 + if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  92 + return -EBUSY;
  93 +
  94 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  95 +
  96 + wdt_enable();
  97 +
  98 + set_bit(WDT_ENABLED, &wdt_status);
  99 +
  100 + return nonseekable_open(inode, file);
  101 +}
  102 +
  103 +static ssize_t
  104 +iop_wdt_write(struct file *file, const char *data, size_t len,
  105 + loff_t *ppos)
  106 +{
  107 + if (len) {
  108 + if (!nowayout) {
  109 + size_t i;
  110 +
  111 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  112 +
  113 + for (i = 0; i != len; i++) {
  114 + char c;
  115 +
  116 + if (get_user(c, data + i))
  117 + return -EFAULT;
  118 + if (c == 'V')
  119 + set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  120 + }
  121 + }
  122 + wdt_enable();
  123 + }
  124 +
  125 + return len;
  126 +}
  127 +
  128 +static struct watchdog_info ident = {
  129 + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  130 + .identity = "iop watchdog",
  131 +};
  132 +
  133 +static int
  134 +iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  135 + unsigned long arg)
  136 +{
  137 + int options;
  138 + int ret = -ENOTTY;
  139 +
  140 + switch (cmd) {
  141 + case WDIOC_GETSUPPORT:
  142 + if (copy_to_user
  143 + ((struct watchdog_info *)arg, &ident, sizeof ident))
  144 + ret = -EFAULT;
  145 + else
  146 + ret = 0;
  147 + break;
  148 +
  149 + case WDIOC_GETSTATUS:
  150 + ret = put_user(0, (int *)arg);
  151 + break;
  152 +
  153 + case WDIOC_GETBOOTSTATUS:
  154 + ret = put_user(boot_status, (int *)arg);
  155 + break;
  156 +
  157 + case WDIOC_GETTIMEOUT:
  158 + ret = put_user(iop_watchdog_timeout(), (int *)arg);
  159 + break;
  160 +
  161 + case WDIOC_KEEPALIVE:
  162 + wdt_enable();
  163 + ret = 0;
  164 + break;
  165 +
  166 + case WDIOC_SETOPTIONS:
  167 + if (get_user(options, (int *)arg))
  168 + return -EFAULT;
  169 +
  170 + if (options & WDIOS_DISABLECARD) {
  171 + if (!nowayout) {
  172 + if (wdt_disable() == 0) {
  173 + set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  174 + ret = 0;
  175 + } else
  176 + ret = -ENXIO;
  177 + } else
  178 + ret = 0;
  179 + }
  180 +
  181 + if (options & WDIOS_ENABLECARD) {
  182 + wdt_enable();
  183 + ret = 0;
  184 + }
  185 + break;
  186 + }
  187 +
  188 + return ret;
  189 +}
  190 +
  191 +static int iop_wdt_release(struct inode *inode, struct file *file)
  192 +{
  193 + int state = 1;
  194 + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
  195 + if (test_bit(WDT_ENABLED, &wdt_status))
  196 + state = wdt_disable();
  197 +
  198 + /* if the timer is not disbaled reload and notify that we are still
  199 + * going down
  200 + */
  201 + if (state != 0) {
  202 + wdt_enable();
  203 + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
  204 + "reset in %lu seconds\n", iop_watchdog_timeout());
  205 + }
  206 +
  207 + clear_bit(WDT_IN_USE, &wdt_status);
  208 + clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  209 +
  210 + return 0;
  211 +}
  212 +
  213 +static const struct file_operations iop_wdt_fops = {
  214 + .owner = THIS_MODULE,
  215 + .llseek = no_llseek,
  216 + .write = iop_wdt_write,
  217 + .ioctl = iop_wdt_ioctl,
  218 + .open = iop_wdt_open,
  219 + .release = iop_wdt_release,
  220 +};
  221 +
  222 +static struct miscdevice iop_wdt_miscdev = {
  223 + .minor = WATCHDOG_MINOR,
  224 + .name = "watchdog",
  225 + .fops = &iop_wdt_fops,
  226 +};
  227 +
  228 +static int __init iop_wdt_init(void)
  229 +{
  230 + int ret;
  231 +
  232 + ret = misc_register(&iop_wdt_miscdev);
  233 + if (ret == 0)
  234 + printk("iop watchdog timer: timeout %lu sec\n",
  235 + iop_watchdog_timeout());
  236 +
  237 + /* check if the reset was caused by the watchdog timer */
  238 + boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;
  239 +
  240 + /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
  241 + * NOTE: An IB Reset will Reset both cores in the IOP342
  242 + */
  243 + write_wdtsr(IOP13XX_WDTCR_IB_RESET);
  244 +
  245 + return ret;
  246 +}
  247 +
  248 +static void __exit iop_wdt_exit(void)
  249 +{
  250 + misc_deregister(&iop_wdt_miscdev);
  251 +}
  252 +
  253 +module_init(iop_wdt_init);
  254 +module_exit(iop_wdt_exit);
  255 +
  256 +module_param(nowayout, int, 0);
  257 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  258 +
  259 +MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
  260 +MODULE_DESCRIPTION("iop watchdog timer driver");
  261 +MODULE_LICENSE("GPL");
  262 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
include/asm-arm/arch-iop13xx/iop13xx.h
... ... @@ -19,6 +19,39 @@
19 19 return id;
20 20 }
21 21  
  22 +/* WDTCR CP6 R7 Page 9 */
  23 +static inline u32 read_wdtcr(void)
  24 +{
  25 + u32 val;
  26 + asm volatile("mrc p6, 0, %0, c7, c9, 0":"=r" (val));
  27 + return val;
  28 +}
  29 +static inline void write_wdtcr(u32 val)
  30 +{
  31 + asm volatile("mcr p6, 0, %0, c7, c9, 0"::"r" (val));
  32 +}
  33 +
  34 +/* WDTSR CP6 R8 Page 9 */
  35 +static inline u32 read_wdtsr(void)
  36 +{
  37 + u32 val;
  38 + asm volatile("mrc p6, 0, %0, c8, c9, 0":"=r" (val));
  39 + return val;
  40 +}
  41 +static inline void write_wdtsr(u32 val)
  42 +{
  43 + asm volatile("mcr p6, 0, %0, c8, c9, 0"::"r" (val));
  44 +}
  45 +
  46 +/* RCSR - Reset Cause Status Register */
  47 +static inline u32 read_rcsr(void)
  48 +{
  49 + u32 val;
  50 + asm volatile("mrc p6, 0, %0, c0, c1, 0":"=r" (val));
  51 + return val;
  52 +}
  53 +
  54 +extern unsigned long get_iop_tick_rate(void);
22 55 #endif
23 56  
24 57 /*
... ... @@ -480,5 +513,15 @@
480 513 #define IOP13XX_PBI_LR1 IOP13XX_PBI_OFFSET(0x14)
481 514  
482 515 #define IOP13XX_PROCESSOR_FREQ IOP13XX_REG_ADDR32(0x2180)
  516 +
  517 +/* Watchdog timer definitions */
  518 +#define IOP_WDTCR_EN_ARM 0x1e1e1e1e
  519 +#define IOP_WDTCR_EN 0xe1e1e1e1
  520 +#define IOP_WDTCR_DIS_ARM 0x1f1f1f1f
  521 +#define IOP_WDTCR_DIS 0xf1f1f1f1
  522 +#define IOP_RCSR_WDT (1 << 5) /* reset caused by watchdog timer */
  523 +#define IOP13XX_WDTSR_WRITE_EN (1 << 31) /* used to speed up reset requests */
  524 +#define IOP13XX_WDTCR_IB_RESET (1 << 0)
  525 +
483 526 #endif /* _IOP13XX_HW_H_ */
include/asm-arm/arch-iop13xx/system.h
... ... @@ -13,43 +13,13 @@
13 13 cpu_do_idle();
14 14 }
15 15  
16   -/* WDTCR CP6 R7 Page 9 */
17   -static inline u32 read_wdtcr(void)
18   -{
19   - u32 val;
20   - asm volatile("mrc p6, 0, %0, c7, c9, 0":"=r" (val));
21   - return val;
22   -}
23   -static inline void write_wdtcr(u32 val)
24   -{
25   - asm volatile("mcr p6, 0, %0, c7, c9, 0"::"r" (val));
26   -}
27   -
28   -/* WDTSR CP6 R8 Page 9 */
29   -static inline u32 read_wdtsr(void)
30   -{
31   - u32 val;
32   - asm volatile("mrc p6, 0, %0, c8, c9, 0":"=r" (val));
33   - return val;
34   -}
35   -static inline void write_wdtsr(u32 val)
36   -{
37   - asm volatile("mcr p6, 0, %0, c8, c9, 0"::"r" (val));
38   -}
39   -
40   -#define IOP13XX_WDTCR_EN_ARM 0x1e1e1e1e
41   -#define IOP13XX_WDTCR_EN 0xe1e1e1e1
42   -#define IOP13XX_WDTCR_DIS_ARM 0x1f1f1f1f
43   -#define IOP13XX_WDTCR_DIS 0xf1f1f1f1
44   -#define IOP13XX_WDTSR_WRITE_EN (1 << 31)
45   -#define IOP13XX_WDTCR_IB_RESET (1 << 0)
46 16 static inline void arch_reset(char mode)
47 17 {
48 18 /*
49 19 * Reset the internal bus (warning both cores are reset)
50 20 */
51   - write_wdtcr(IOP13XX_WDTCR_EN_ARM);
52   - write_wdtcr(IOP13XX_WDTCR_EN);
  21 + write_wdtcr(IOP_WDTCR_EN_ARM);
  22 + write_wdtcr(IOP_WDTCR_EN);
53 23 write_wdtsr(IOP13XX_WDTSR_WRITE_EN | IOP13XX_WDTCR_IB_RESET);
54 24 write_wdtcr(0x1000);
55 25  
include/asm-arm/hardware/iop3xx.h
... ... @@ -194,6 +194,13 @@
194 194 #define IOP_TMR_PRIVILEGED 0x08
195 195 #define IOP_TMR_RATIO_1_1 0x00
196 196  
  197 +/* Watchdog timer definitions */
  198 +#define IOP_WDTCR_EN_ARM 0x1e1e1e1e
  199 +#define IOP_WDTCR_EN 0xe1e1e1e1
  200 +/* iop3xx does not support stopping the watchdog, so we just re-arm */
  201 +#define IOP_WDTCR_DIS_ARM (IOP_WDTCR_EN_ARM)
  202 +#define IOP_WDTCR_DIS (IOP_WDTCR_EN)
  203 +
197 204 /* Application accelerator unit */
198 205 #define IOP3XX_AAU_PHYS_BASE (IOP3XX_PERIPHERAL_PHYS_BASE + 0x800)
199 206 #define IOP3XX_AAU_UPPER_PA (IOP3XX_AAU_PHYS_BASE + 0xa7)
... ... @@ -272,6 +279,32 @@
272 279 static inline void write_tisr(u32 val)
273 280 {
274 281 asm volatile("mcr p6, 0, %0, c6, c1, 0" : : "r" (val));
  282 +}
  283 +
  284 +static inline u32 read_wdtcr(void)
  285 +{
  286 + u32 val;
  287 + asm volatile("mrc p6, 0, %0, c7, c1, 0":"=r" (val));
  288 + return val;
  289 +}
  290 +static inline void write_wdtcr(u32 val)
  291 +{
  292 + asm volatile("mcr p6, 0, %0, c7, c1, 0"::"r" (val));
  293 +}
  294 +
  295 +extern unsigned long get_iop_tick_rate(void);
  296 +
  297 +/* only iop13xx has these registers, we define these to present a
  298 + * common register interface for the iop_wdt driver.
  299 + */
  300 +#define IOP_RCSR_WDT (0)
  301 +static inline u32 read_rcsr(void)
  302 +{
  303 + return 0;
  304 +}
  305 +static inline void write_wdtsr(u32 val)
  306 +{
  307 + do { } while (0);
275 308 }
276 309  
277 310 extern struct platform_device iop3xx_dma_0_channel;