Skip to content

Commit

Permalink
[ARM] 4495/1: iop: combined watchdog timer driver for iop3xx and iop13xx
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Dan Williams authored and Russell King committed Jul 20, 2007
1 parent 7dea1b2 commit 70c14ff
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 32 deletions.
8 changes: 8 additions & 0 deletions arch/arm/plat-iop/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,21 @@ static struct irqaction iop_timer_irq = {
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
};

static unsigned long iop_tick_rate;
unsigned long get_iop_tick_rate(void)
{
return iop_tick_rate;
}
EXPORT_SYMBOL(get_iop_tick_rate);

void __init iop_init_time(unsigned long tick_rate)
{
u32 timer_ctl;

ticks_per_jiffy = (tick_rate + HZ/2) / HZ;
ticks_per_usec = tick_rate / 1000000;
next_jiffy_time = 0xffffffff;
iop_tick_rate = tick_rate;

timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED |
IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1;
Expand Down
16 changes: 16 additions & 0 deletions drivers/char/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,22 @@ config PNX4008_WATCHDOG

Say N if you are unsure.

config IOP_WATCHDOG
tristate "IOP Watchdog"
depends on WATCHDOG && PLAT_IOP
select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X)
help
Say Y here if to include support for the watchdog timer
in the Intel IOP3XX & IOP13XX I/O Processors. This driver can
be built as a module by choosing M. The module will
be called iop_wdt.

Note: The IOP13XX watchdog does an Internal Bus Reset which will
affect both cores and the peripherals of the IOP. The ATU-X
and/or ATUe configuration registers will remain intact, but if
operating as an Root Complex and/or Central Resource, the PCI-X
and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER.

# AVR32 Architecture

config AT32AP700X_WDT
Expand Down
1 change: 1 addition & 0 deletions drivers/char/watchdog/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o

# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
Expand Down
262 changes: 262 additions & 0 deletions drivers/char/watchdog/iop_wdt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
* drivers/char/watchdog/iop_wdt.c
*
* WDT driver for Intel I/O Processors
* Copyright (C) 2005, Intel Corporation.
*
* Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Curt E Bruns <curt.e.bruns@intel.com>
* Peter Milne <peter.milne@d-tacq.com>
* Dan Williams <dan.j.williams@intel.com>
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <asm/hardware.h>

static int nowayout = WATCHDOG_NOWAYOUT;
static unsigned long wdt_status;
static unsigned long boot_status;

#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
#define WDT_ENABLED 2

static unsigned long iop_watchdog_timeout(void)
{
return (0xffffffffUL / get_iop_tick_rate());
}

/**
* wdt_supports_disable - determine if we are accessing a iop13xx watchdog
* or iop3xx by whether it has a disable command
*/
static int wdt_supports_disable(void)
{
int can_disable;

if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM)
can_disable = 1;
else
can_disable = 0;

return can_disable;
}

static void wdt_enable(void)
{
/* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
* Takes approx. 10.7s to timeout
*/
write_wdtcr(IOP_WDTCR_EN_ARM);
write_wdtcr(IOP_WDTCR_EN);
}

/* returns 0 if the timer was successfully disabled */
static int wdt_disable(void)
{
/* Stop Counting */
if (wdt_supports_disable()) {
write_wdtcr(IOP_WDTCR_DIS_ARM);
write_wdtcr(IOP_WDTCR_DIS);
clear_bit(WDT_ENABLED, &wdt_status);
printk(KERN_INFO "WATCHDOG: Disabled\n");
return 0;
} else
return 1;
}

static int iop_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;

clear_bit(WDT_OK_TO_CLOSE, &wdt_status);

wdt_enable();

set_bit(WDT_ENABLED, &wdt_status);

return nonseekable_open(inode, file);
}

static ssize_t
iop_wdt_write(struct file *file, const char *data, size_t len,
loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;

clear_bit(WDT_OK_TO_CLOSE, &wdt_status);

for (i = 0; i != len; i++) {
char c;

if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
wdt_enable();
}

return len;
}

static struct watchdog_info ident = {
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = "iop watchdog",
};

static int
iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int options;
int ret = -ENOTTY;

switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user
((struct watchdog_info *)arg, &ident, sizeof ident))
ret = -EFAULT;
else
ret = 0;
break;

case WDIOC_GETSTATUS:
ret = put_user(0, (int *)arg);
break;

case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, (int *)arg);
break;

case WDIOC_GETTIMEOUT:
ret = put_user(iop_watchdog_timeout(), (int *)arg);
break;

case WDIOC_KEEPALIVE:
wdt_enable();
ret = 0;
break;

case WDIOC_SETOPTIONS:
if (get_user(options, (int *)arg))
return -EFAULT;

if (options & WDIOS_DISABLECARD) {
if (!nowayout) {
if (wdt_disable() == 0) {
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
ret = 0;
} else
ret = -ENXIO;
} else
ret = 0;
}

if (options & WDIOS_ENABLECARD) {
wdt_enable();
ret = 0;
}
break;
}

return ret;
}

static int iop_wdt_release(struct inode *inode, struct file *file)
{
int state = 1;
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
if (test_bit(WDT_ENABLED, &wdt_status))
state = wdt_disable();

/* if the timer is not disbaled reload and notify that we are still
* going down
*/
if (state != 0) {
wdt_enable();
printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
"reset in %lu seconds\n", iop_watchdog_timeout());
}

clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);

return 0;
}

static const struct file_operations iop_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = iop_wdt_write,
.ioctl = iop_wdt_ioctl,
.open = iop_wdt_open,
.release = iop_wdt_release,
};

static struct miscdevice iop_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &iop_wdt_fops,
};

static int __init iop_wdt_init(void)
{
int ret;

ret = misc_register(&iop_wdt_miscdev);
if (ret == 0)
printk("iop watchdog timer: timeout %lu sec\n",
iop_watchdog_timeout());

/* check if the reset was caused by the watchdog timer */
boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;

/* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
* NOTE: An IB Reset will Reset both cores in the IOP342
*/
write_wdtsr(IOP13XX_WDTCR_IB_RESET);

return ret;
}

static void __exit iop_wdt_exit(void)
{
misc_deregister(&iop_wdt_miscdev);
}

module_init(iop_wdt_init);
module_exit(iop_wdt_exit);

module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");

MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
MODULE_DESCRIPTION("iop watchdog timer driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
43 changes: 43 additions & 0 deletions include/asm-arm/arch-iop13xx/iop13xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,39 @@ static inline int iop13xx_cpu_id(void)
return id;
}

/* WDTCR CP6 R7 Page 9 */
static inline u32 read_wdtcr(void)
{
u32 val;
asm volatile("mrc p6, 0, %0, c7, c9, 0":"=r" (val));
return val;
}
static inline void write_wdtcr(u32 val)
{
asm volatile("mcr p6, 0, %0, c7, c9, 0"::"r" (val));
}

/* WDTSR CP6 R8 Page 9 */
static inline u32 read_wdtsr(void)
{
u32 val;
asm volatile("mrc p6, 0, %0, c8, c9, 0":"=r" (val));
return val;
}
static inline void write_wdtsr(u32 val)
{
asm volatile("mcr p6, 0, %0, c8, c9, 0"::"r" (val));
}

/* RCSR - Reset Cause Status Register */
static inline u32 read_rcsr(void)
{
u32 val;
asm volatile("mrc p6, 0, %0, c0, c1, 0":"=r" (val));
return val;
}

extern unsigned long get_iop_tick_rate(void);
#endif

/*
Expand Down Expand Up @@ -480,4 +513,14 @@ static inline int iop13xx_cpu_id(void)
#define IOP13XX_PBI_LR1 IOP13XX_PBI_OFFSET(0x14)

#define IOP13XX_PROCESSOR_FREQ IOP13XX_REG_ADDR32(0x2180)

/* Watchdog timer definitions */
#define IOP_WDTCR_EN_ARM 0x1e1e1e1e
#define IOP_WDTCR_EN 0xe1e1e1e1
#define IOP_WDTCR_DIS_ARM 0x1f1f1f1f
#define IOP_WDTCR_DIS 0xf1f1f1f1
#define IOP_RCSR_WDT (1 << 5) /* reset caused by watchdog timer */
#define IOP13XX_WDTSR_WRITE_EN (1 << 31) /* used to speed up reset requests */
#define IOP13XX_WDTCR_IB_RESET (1 << 0)

#endif /* _IOP13XX_HW_H_ */
Loading

0 comments on commit 70c14ff

Please sign in to comment.