From 022e20b2a4d7e596b8cbbb37151d092f90b53201 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 27 Aug 2007 10:37:12 +0000 Subject: [PATCH] Add a watchdog driver for rdc321x (needs testing) and try to detect the rdc cpu SVN-Revision: 8505 --- target/linux/rdc-2.6/config/default | 6 + .../rdc-2.6/files/arch/i386/kernel/cpu/rdc.c | 24 ++ .../rdc-2.6/files/arch/i386/mach-rdc/Makefile | 5 + .../files/arch/i386/mach-rdc/platform.c | 15 +- .../rdc-2.6/files/arch/i386/mach-rdc/wdt.c | 275 ++++++++++++++++++ .../include/asm-i386/mach-rdc/rdc321x_defs.h | 6 + .../linux/rdc-2.6/patches/000-rdc_fixes.patch | 9 - .../patches/005-rdc_x86_cpu_type.patch | 22 ++ 8 files changed, 351 insertions(+), 11 deletions(-) create mode 100644 target/linux/rdc-2.6/files/arch/i386/kernel/cpu/rdc.c create mode 100644 target/linux/rdc-2.6/files/arch/i386/mach-rdc/Makefile create mode 100644 target/linux/rdc-2.6/files/arch/i386/mach-rdc/wdt.c create mode 100644 target/linux/rdc-2.6/patches/005-rdc_x86_cpu_type.patch diff --git a/target/linux/rdc-2.6/config/default b/target/linux/rdc-2.6/config/default index b3681d0f11..51c8e6e90d 100644 --- a/target/linux/rdc-2.6/config/default +++ b/target/linux/rdc-2.6/config/default @@ -23,6 +23,7 @@ CONFIG_BITREVERSE=y CONFIG_BLK_DEV_IDE=m CONFIG_BLK_DEV_IDEDISK=m # CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_BROADCOM_PHY is not set CONFIG_CLOCKSOURCE_WATCHDOG=y # CONFIG_COMPAT_VDSO is not set # CONFIG_CPU5_WDT is not set @@ -49,6 +50,7 @@ CONFIG_EARLY_PRINTK=y # CONFIG_EDAC is not set # CONFIG_EDD is not set # CONFIG_EUROTECH_WDT is not set +# CONFIG_FIXED_PHY is not set CONFIG_FS_POSIX_ACL=y CONFIG_GENERIC_BUG=y CONFIG_GENERIC_CLOCKEVENTS=y @@ -82,6 +84,7 @@ CONFIG_I2C_BOARDINFO=y # CONFIG_IB700_WDT is not set # CONFIG_IBMASR is not set # CONFIG_IBM_ASM is not set +CONFIG_ICPLUS_PHY=y CONFIG_IDE=m # CONFIG_IDEPCI_PCIBUS_ORDER is not set # CONFIG_IDE_ARM is not set @@ -217,9 +220,11 @@ CONFIG_PCI_GOANY=y # CONFIG_PCI_GODIRECT is not set # CONFIG_PCI_GOMMCONFIG is not set CONFIG_PCMCIA=m +CONFIG_PHYLIB=y CONFIG_PHYSICAL_ALIGN=0x100000 CONFIG_PHYSICAL_START=0x100000 # CONFIG_PNPACPI is not set +# CONFIG_QSEMI_PHY is not set CONFIG_QUICKLIST=y CONFIG_R6040=m # CONFIG_R6040_NAPI is not set @@ -239,6 +244,7 @@ CONFIG_SEMAPHORE_SLEEPERS=y # CONFIG_SERIAL_8250_EXTENDED is not set # CONFIG_SMP is not set # CONFIG_SMSC37B787_WDT is not set +# CONFIG_SMSC_PHY is not set CONFIG_SOFT_WATCHDOG=m # CONFIG_SONYPI is not set # CONFIG_SPARSEMEM_STATIC is not set diff --git a/target/linux/rdc-2.6/files/arch/i386/kernel/cpu/rdc.c b/target/linux/rdc-2.6/files/arch/i386/kernel/cpu/rdc.c new file mode 100644 index 0000000000..f4b9083bfc --- /dev/null +++ b/target/linux/rdc-2.6/files/arch/i386/kernel/cpu/rdc.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +#include "cpu.h" + +static struct cpu_dev rdc_cpu_dev __cpuinitdata = { + .c_vendor = "RDC", + .c_models = { + { .vendor = X86_VENDOR_RDC, .family = 4, .model_names = + { + [0] = "R861x(-G)", + } + }, + }, +}; + +int __init rdc_init_cpu(void) +{ + cpu_devs[X86_VENDOR_RDC] = &rdc_cpu_dev; + return 0; +} diff --git a/target/linux/rdc-2.6/files/arch/i386/mach-rdc/Makefile b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/Makefile new file mode 100644 index 0000000000..5961bc7910 --- /dev/null +++ b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the RDC321x specific parts of the kernel +# +obj-$(CONFIG_X86_RDC) := gpio.o platform.o wdt.o + diff --git a/target/linux/rdc-2.6/files/arch/i386/mach-rdc/platform.c b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/platform.c index 96a9c9d37f..6809dcdd3f 100644 --- a/target/linux/rdc-2.6/files/arch/i386/mach-rdc/platform.c +++ b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/platform.c @@ -31,6 +31,8 @@ #include +#define PFX "rdc321x: " + /* FIXME : Flash */ static struct resource rdc_flash_resource[] = { [0] = { @@ -54,17 +56,26 @@ static struct platform_device rdc321x_leds = { .num_resources = 0, }; +static struct platform_device rdc321x_wdt = { + .name = "rdc321x-wdt", + .id = -1, + .num_resources = 0, +}; + static int __init rdc_board_setup(void) { int err; err = platform_device_register(&rdc_flash_device); if (err) - printk(KERN_ALERT "rdc321x: failed to register flash\n"); + printk(KERN_ALERT PFX "failed to register flash\n"); err = platform_device_register(&rdc321x_leds); if (err) - printk(KERN_ALERT "rdc321x: failed to register LEDS\n"); + printk(KERN_ALERT PFX "failed to register LEDS\n"); + + err = platform_device_register(&rdc321x_wdt); + printk(KERN_ALERT PFX "failed to register watchdog\n"); return err; } diff --git a/target/linux/rdc-2.6/files/arch/i386/mach-rdc/wdt.c b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/wdt.c new file mode 100644 index 0000000000..4e6f12b381 --- /dev/null +++ b/target/linux/rdc-2.6/files/arch/i386/mach-rdc/wdt.c @@ -0,0 +1,275 @@ +/* + * RDC321x watchdog driver + * + * Copyright (C) 2007 Florian Fainelli + * + * This driver is highly inspired from the rdc321x_wdt driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define RDC_WDT_MASK 0x80000000 /* Mask */ +#define RDC_WDT_EN 0x00800000 /* Enable bit */ +#define RDC_WDT_WTI 0x00200000 /* Generate a CPU reset/NMI/WDT irq when WDT timeout is reached */ +#define RDC_WDT_RST 0x00100000 /* Reset bit */ +#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */ +#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */ +#define RDC_WDT_CNT 0x00000001 /* WDT count */ + +#define RDC_CLS_TMR 0x80003844 /* Clear timer */ + +#define RDC_WDT_INTERVAL (HZ/10+1) + +int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int ticks = 1000; + +/* some device data */ + +static struct { + struct completion stop; + volatile int running; + struct timer_list timer; + volatile int queue; + int default_ticks; + unsigned long inuse; +} rdc321x_wdt_device; + +/* generic helper functions */ + +static void rdc321x_wdt_trigger(unsigned long unused) +{ + if( rdc321x_wdt_device.running ) + ticks--; + + /* keep watchdog alive */ + outl(RDC_WDT_EN|inl(RDC3210_CFGREG_DATA), RDC3210_CFGREG_DATA); + + /* requeue?? */ + if (rdc321x_wdt_device.queue && ticks) + mod_timer(&rdc321x_wdt_device.timer, jiffies + RDC_WDT_INTERVAL); + else { + /* ticks doesn't matter anyway */ + complete(&rdc321x_wdt_device.stop); + } + +} + +static void rdc321x_wdt_reset(void) +{ + ticks = rdc321x_wdt_device.default_ticks; +} + +static void rdc321x_wdt_start(void) +{ + if (!rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 1; + + /* Clear the timer */ + outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR); + + /* Enable watchdog and set the timeout to 81.92 us */ + outl(RDC_WDT_EN|RDC_WDT_CNT, RDC3210_CFGREG_DATA); + + mod_timer(&rdc321x_wdt_device.timer, jiffies + RDC_WDT_INTERVAL); + } + + /* if process dies, counter is not decremented */ + rdc321x_wdt_device.running++; +} + +static int rdc321x_wdt_stop(void) +{ + if (rdc321x_wdt_device.running) + rdc321x_wdt_device.running = 0; + + ticks = rdc321x_wdt_device.default_ticks; + + return -EIO; +} + +/* filesystem operations */ + +static int rdc321x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &rdc321x_wdt_device.inuse)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + +static int rdc321x_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &rdc321x_wdt_device.inuse); + return 0; +} + +static int rdc321x_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + unsigned int value; + static struct watchdog_info ident = + { + .options = WDIOF_CARDRESET, + .identity = "RDC321x WDT", + }; + + switch(cmd) { + case WDIOC_KEEPALIVE: + rdc321x_wdt_reset(); + break; + case WDIOC_GETSTATUS: + /* Read the value from the DATA register */ + value = inl(RDC3210_CFGREG_DATA); + if ( copy_to_user(argp, &value, sizeof(int)) ) + return -EFAULT; + break; + case WDIOC_GETSUPPORT: + if ( copy_to_user(argp, &ident, sizeof(ident)) ) + return -EFAULT; + break; + case WDIOC_SETOPTIONS: + if ( copy_from_user(&value, argp, sizeof(int)) ) + return -EFAULT; + switch(value) { + case WDIOS_ENABLECARD: + rdc321x_wdt_start(); + break; + case WDIOS_DISABLECARD: + return rdc321x_wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + +static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if ( !count ) + return -EIO; + + rdc321x_wdt_reset(); + + return count; +} + +static const struct file_operations rdc321x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = rdc321x_wdt_ioctl, + .open = rdc321x_wdt_open, + .write = rdc321x_wdt_write, + .release = rdc321x_wdt_release, +}; + +static struct miscdevice rdc321x_wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &rdc321x_wdt_fops, +}; + +static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) +{ + int err; + + if ( (err = misc_register(&rdc321x_wdt_misc)) < 0 ) { + printk(KERN_ERR PFX "misc_register failed\n"); + goto no_misc; + } + + /* Reset the watchdog */ + outl(RDC_WDT_RST, RDC3210_CFGREG_DATA); + + init_completion(&rdc321x_wdt_device.stop); + rdc321x_wdt_device.queue = 0; + + clear_bit(0, &rdc321x_wdt_device.inuse); + + setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); + + rdc321x_wdt_device.default_ticks = ticks; + + printk(KERN_INFO PFX "init success\n"); + + return 0; + +no_misc: + return err; +} + +static int rdc321x_wdt_remove(struct platform_device *pdev) +{ + if (rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 0; + wait_for_completion(&rdc321x_wdt_device.stop); + } + + misc_deregister(&rdc321x_wdt_misc); + + return 0; +} + +static struct platform_driver rdc321x_wdt_driver = { + .probe = rdc321x_wdt_probe, + .remove = rdc321x_wdt_remove, + .driver = { + .owner = THIS_MODULE, + .name = "rdc321x_wdt", + }, +}; + +static int __init rdc321x_wdt_init(void) +{ + return platform_driver_register(&rdc321x_wdt_driver); +} + +static void __exit rdc321x_wdt_exit(void) +{ + platform_driver_unregister(&rdc321x_wdt_driver); +} + +module_init(rdc321x_wdt_init); +module_exit(rdc321x_wdt_exit); + +MODULE_AUTHOR("Florian Fainelli "); +MODULE_DESCRIPTION("RDC321x watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/target/linux/rdc-2.6/files/include/asm-i386/mach-rdc/rdc321x_defs.h b/target/linux/rdc-2.6/files/include/asm-i386/mach-rdc/rdc321x_defs.h index e69de29bb2..838ba8f64f 100644 --- a/target/linux/rdc-2.6/files/include/asm-i386/mach-rdc/rdc321x_defs.h +++ b/target/linux/rdc-2.6/files/include/asm-i386/mach-rdc/rdc321x_defs.h @@ -0,0 +1,6 @@ +#define PFX "rdc321x: " + +/* General purpose configuration and data registers */ +#define RDC3210_CFGREG_ADDR 0x0CF8 +#define RDC3210_CFGREG_DATA 0x0CFC +#define RDC_MAX_GPIO 0x3A diff --git a/target/linux/rdc-2.6/patches/000-rdc_fixes.patch b/target/linux/rdc-2.6/patches/000-rdc_fixes.patch index b8cf2a9517..306fac79a6 100644 --- a/target/linux/rdc-2.6/patches/000-rdc_fixes.patch +++ b/target/linux/rdc-2.6/patches/000-rdc_fixes.patch @@ -30,15 +30,6 @@ diff -urN linux-2.6.19/arch/i386/Makefile linux-2.6.19.new/arch/i386/Makefile # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default -diff -urN linux-2.6.19/arch/i386/mach-rdc/Makefile linux-2.6.19.new/arch/i386/mach-rdc/Makefile ---- linux-2.6.19/arch/i386/mach-rdc/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.19.new/arch/i386/mach-rdc/Makefile 2006-12-17 17:13:33.000000000 +0100 -@@ -0,0 +1,5 @@ -+# -+# Makefile for the linux kernel. -+# -+ -+obj-$(CONFIG_X86_RDC) := gpio.o platform.o --- timex.h 2007-08-07 10:44:03.816112250 -0700 +++ linux/include/asm-i386/timex.h 2007-08-07 10:48:47.905866750 -0700 @@ -11,6 +11,8 @@ diff --git a/target/linux/rdc-2.6/patches/005-rdc_x86_cpu_type.patch b/target/linux/rdc-2.6/patches/005-rdc_x86_cpu_type.patch new file mode 100644 index 0000000000..185ec13f7b --- /dev/null +++ b/target/linux/rdc-2.6/patches/005-rdc_x86_cpu_type.patch @@ -0,0 +1,22 @@ +diff -urN linux-2.6.22.1/arch/i386/kernel/cpu/Makefile linux-2.6.22.1.new/arch/i386/kernel/cpu/Makefile +--- linux-2.6.22.1/arch/i386/kernel/cpu/Makefile 2007-08-11 22:16:24.000000000 +0200 ++++ linux-2.6.22.1.new/arch/i386/kernel/cpu/Makefile 2007-08-13 16:37:32.000000000 +0200 +@@ -12,6 +12,7 @@ + obj-y += rise.o + obj-y += nexgen.o + obj-y += umc.o ++obj-$(CONFIG_X86_RDC) += rdc.o + + obj-$(CONFIG_X86_MCE) += mcheck/ + +diff -urN linux-2.6.22.1/arch/i386/kernel/cpu/Makefile linux-2.6.22.1.new/arch/i386/kernel/cpu/Makefile +--- linux-2.6.22.1/include/asm-i386/processor.h 2007-08-13 16:42:25.000000000 +0200 ++++ linux-2.6.22.1.new/include/asm-i386/processor.h 2007-08-13 16:51:19.000000000 +0200 +@@ -92,6 +92,7 @@ + #define X86_VENDOR_TRANSMETA 7 + #define X86_VENDOR_NSC 8 + #define X86_VENDOR_NUM 9 ++#define X86_VENDOR_RDC 10 + #define X86_VENDOR_UNKNOWN 0xff + + /* -- 2.30.2