diff options
Diffstat (limited to 'drivers/sbus/char')
-rw-r--r-- | drivers/sbus/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/sbus/char/bpp.c | 34 | ||||
-rw-r--r-- | drivers/sbus/char/cpwatchdog.c | 838 | ||||
-rw-r--r-- | drivers/sbus/char/flash.c | 17 | ||||
-rw-r--r-- | drivers/sbus/char/jsflash.c | 2 | ||||
-rw-r--r-- | drivers/sbus/char/pcikbd.c | 19 | ||||
-rw-r--r-- | drivers/sbus/char/rtc.c | 50 | ||||
-rw-r--r-- | drivers/sbus/char/sunkbd.c | 11 | ||||
-rw-r--r-- | drivers/sbus/char/sunmouse.c | 11 | ||||
-rw-r--r-- | drivers/sbus/char/vfc_dev.c | 28 |
10 files changed, 942 insertions, 69 deletions
diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index b3a736db5..437f2d28c 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PCI) += su.o pcikbd.o obj-$(CONFIG_SAB82532) += sab82532.o obj-$(CONFIG_ENVCTRL) += envctrl.o obj-$(CONFIG_DISPLAY7SEG) += display7seg.o +obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwatchdog.o obj-$(CONFIG_OBP_FLASH) += flash.o obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index c96a141a5..2be051503 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -17,6 +17,7 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/smp_lock.h> +#include <linux/spinlock.h> #include <linux/timer.h> #include <linux/ioport.h> #include <linux/major.h> @@ -432,6 +433,7 @@ static int terminate(unsigned minor) return 0; } +static spinlock_t bpp_open_lock = SPIN_LOCK_UNLOCKED; /* * Allow only one process to open the device at a time. @@ -439,13 +441,25 @@ static int terminate(unsigned minor) static int bpp_open(struct inode *inode, struct file *f) { unsigned minor = MINOR(inode->i_rdev); - if (minor >= BPP_NO) return -ENODEV; - if (! instances[minor].present) return -ENODEV; - if (instances[minor].opened) return -EBUSY; - - instances[minor].opened = 1; + int ret; + + spin_lock(&bpp_open_lock); + ret = 0; + if (minor >= BPP_NO) { + ret = -ENODEV; + } else { + if (! instances[minor].present) { + ret = -ENODEV; + } else { + if (instances[minor].opened) + ret = -EBUSY; + else + instances[minor].opened = 1; + } + } + spin_unlock(&bpp_open_lock); - return 0; + return ret; } /* @@ -458,12 +472,14 @@ static int bpp_release(struct inode *inode, struct file *f) { unsigned minor = MINOR(inode->i_rdev); - lock_kernel(); + spin_lock(&bpp_open_lock); instances[minor].opened = 0; if (instances[minor].mode != COMPATIBILITY) - terminate(minor); - unlock_kernel(); + terminate(minor); + + spin_unlock(&bpp_open_lock); + return 0; } diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c new file mode 100644 index 000000000..c319b4d33 --- /dev/null +++ b/drivers/sbus/char/cpwatchdog.c @@ -0,0 +1,838 @@ +/* cpwatchdog.c - driver implementation for hardware watchdog + * timers found on Sun Microsystems CP1400 and CP1500 boards. + * + * This device supports both the generic Linux watchdog + * interface and Solaris-compatible ioctls as best it is + * able. + * + * NOTE: CP1400 systems appear to have a defective intr_mask + * register on the PLD, preventing the disabling of + * timer interrupts. We use a timer to periodically + * reset 'stopped' watchdogs on affected platforms. + * + * TODO: DevFS support (/dev/watchdogs/0 ... /dev/watchdogs/2) + * + * Copyright (c) 2000 Eric Brower (ebrower@usa.net) + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <asm/irq.h> +#include <asm/ebus.h> +#include <asm/oplib.h> +#include <asm/uaccess.h> + +#include <asm/watchdog.h> + +#define WD_OBPNAME "watchdog" +#define WD_BADMODEL "SUNW,501-5336" +#define WD_BTIMEOUT (jiffies + (HZ * 1000)) +#define WD_BLIMIT 0xFFFF + +#define WD0_DEVNAME "watchdog0" +#define WD1_DEVNAME "watchdog1" +#define WD2_DEVNAME "watchdog2" + +#define WD0_MINOR 212 +#define WD1_MINOR 213 +#define WD2_MINOR 214 + + +/* Internal driver definitions + */ +#define WD0_ID 0 /* Watchdog0 */ +#define WD1_ID 1 /* Watchdog1 */ +#define WD2_ID 2 /* Watchdog2 */ +#define WD_NUMDEVS 3 /* Device contains 3 timers */ + +#define WD_INTR_OFF 0 /* Interrupt disable value */ +#define WD_INTR_ON 1 /* Interrupt enable value */ + +#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */ +#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */ +#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */ + +/* Register value definitions + */ +#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */ +#define WD1_INTR_MASK 0x02 +#define WD2_INTR_MASK 0x04 + +#define WD_S_RUNNING 0x01 /* Watchdog device status running */ +#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */ + +/* Sun uses Altera PLD EPF8820ATC144-4 + * providing three hardware watchdogs: + * + * 1) RIC - sends an interrupt when triggered + * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU + * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board + * + *** Timer register block definition (struct wd_timer_regblk) + * + * dcntr and limit registers (halfword access): + * ------------------- + * | 15 | ...| 1 | 0 | + * ------------------- + * |- counter val -| + * ------------------- + * dcntr - Current 16-bit downcounter value. + * When downcounter reaches '0' watchdog expires. + * Reading this register resets downcounter with 'limit' value. + * limit - 16-bit countdown value in 1/10th second increments. + * Writing this register begins countdown with input value. + * Reading from this register does not affect counter. + * NOTES: After watchdog reset, dcntr and limit contain '1' + * + * status register (byte access): + * --------------------------- + * | 7 | ... | 2 | 1 | 0 | + * --------------+------------ + * |- UNUSED -| EXP | RUN | + * --------------------------- + * status- Bit 0 - Watchdog is running + * Bit 1 - Watchdog has expired + * + *** PLD register block definition (struct wd_pld_regblk) + * + * intr_mask register (byte access): + * --------------------------------- + * | 7 | ... | 3 | 2 | 1 | 0 | + * +-------------+------------------ + * |- UNUSED -| WD3 | WD2 | WD1 | + * --------------------------------- + * WD3 - 1 == Interrupt disabled for watchdog 3 + * WD2 - 1 == Interrupt disabled for watchdog 2 + * WD1 - 1 == Interrupt disabled for watchdog 1 + * + * pld_status register (byte access): + * UNKNOWN, MAGICAL MYSTERY REGISTER + * + */ +struct wd_timer_regblk { + volatile __u16 dcntr; /* down counter - hw */ + volatile __u16 dcntr_pad; + volatile __u16 limit; /* limit register - hw */ + volatile __u16 limit_pad; + volatile __u8 status; /* status register - b */ + volatile __u8 status_pad; + volatile __u16 status_pad2; + volatile __u32 pad32; /* yet more padding */ +}; + +struct wd_pld_regblk { + volatile __u8 intr_mask; /* interrupt mask - b */ + volatile __u8 intr_mask_pad; + volatile __u16 intr_mask_pad2; + volatile __u8 status; /* device status - b */ + volatile __u8 status_pad; + volatile __u16 status_pad2; +}; + +struct wd_regblk { + volatile struct wd_timer_regblk wd0_regs; + volatile struct wd_timer_regblk wd1_regs; + volatile struct wd_timer_regblk wd2_regs; + volatile struct wd_pld_regblk pld_regs; +}; + +/* Individual timer structure + */ +struct wd_timer { + __u16 timeout; + __u8 intr_mask; + unsigned char runstatus; + volatile struct wd_timer_regblk* regs; +}; + +/* Device structure + */ +struct wd_device { + int irq; + spinlock_t lock; + unsigned char isbaddoggie; /* defective PLD */ + unsigned char opt_enable; + unsigned char opt_reboot; + unsigned short opt_timeout; + unsigned char initialized; + struct wd_timer watchdog[WD_NUMDEVS]; + volatile struct wd_regblk* regs; +}; + +static struct wd_device wd_dev = { + 0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0, +}; + +struct timer_list wd_timer; + +static int wd0_timeout = 0; +static int wd1_timeout = 0; +static int wd2_timeout = 0; + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +MODULE_PARM (wd0_timeout, "i"); +MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); +MODULE_PARM (wd1_timeout, "i"); +MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); +MODULE_PARM (wd2_timeout, "i"); +MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); + +MODULE_AUTHOR + ("Eric Brower <ebrower@usa.net>"); +MODULE_DESCRIPTION + ("Hardware watchdog driver for Sun Microsystems CP1400/1500"); +MODULE_SUPPORTED_DEVICE + ("watchdog"); +#endif /* ifdef MODULE */ + +/* Forward declarations of internal methods + */ +void wd_dumpregs(void); +void wd_interrupt(int irq, void *dev_id, struct pt_regs *regs); +void wd_toggleintr(struct wd_timer* pTimer, int enable); +void wd_pingtimer(struct wd_timer* pTimer); +void wd_starttimer(struct wd_timer* pTimer); +void wd_resetbrokentimer(struct wd_timer* pTimer); +void wd_stoptimer(struct wd_timer* pTimer); +void wd_brokentimer(unsigned long data); +int wd_getstatus(struct wd_timer* pTimer); + +/* PLD expects words to be written in LSB format, + * so we must flip all words prior to writing them to regs + */ +inline unsigned short flip_word(unsigned short word) +{ + return ((word & 0xff) << 8) | ((word >> 8) & 0xff); +} + +#define wd_writew(val, addr) (writew(flip_word(val), addr)) +#define wd_readw(addr) (flip_word(readw(addr))) +#define wd_writeb(val, addr) (writeb(val, addr)) +#define wd_readb(addr) (readb(addr)) + + +/* CP1400s seem to have broken PLD implementations-- + * the interrupt_mask register cannot be written, so + * no timer interrupts can be masked within the PLD. + */ +static inline int wd_isbroken(void) +{ + /* we could test this by read/write/read/restore + * on the interrupt mask register only if OBP + * 'watchdog-enable?' == FALSE, but it seems + * ubiquitous on CP1400s + */ + char val[32]; + prom_getproperty(prom_root_node, "model", val, sizeof(val)); + return((!strcmp(val, WD_BADMODEL)) ? 1 : 0); +} + +/* Retrieve watchdog-enable? option from OBP + * Returns 0 if false, 1 if true + */ +static inline int wd_opt_enable(void) +{ + int opt_node; + + opt_node = prom_getchild(prom_root_node); + opt_node = prom_searchsiblings(opt_node, "options"); + return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1); +} + +/* Retrieve watchdog-reboot? option from OBP + * Returns 0 if false, 1 if true + */ +static inline int wd_opt_reboot(void) +{ + int opt_node; + + opt_node = prom_getchild(prom_root_node); + opt_node = prom_searchsiblings(opt_node, "options"); + return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1); +} + +/* Retrieve watchdog-timeout option from OBP + * Returns OBP value, or 0 if not located + */ +static inline int wd_opt_timeout(void) +{ + int opt_node; + char value[32]; + char *p = value; + + opt_node = prom_getchild(prom_root_node); + opt_node = prom_searchsiblings(opt_node, "options"); + opt_node = prom_getproperty(opt_node, + "watchdog-timeout", + value, + sizeof(value)); + if(-1 != opt_node) { + /* atoi implementation */ + for(opt_node = 0; /* nop */; p++) { + if(*p >= '0' && *p <= '9') { + opt_node = (10*opt_node)+(*p-'0'); + } + else { + break; + } + } + } + return((-1 == opt_node) ? (0) : (opt_node)); +} + +static int wd_open(struct inode *inode, struct file *f) +{ + switch(MINOR(inode->i_rdev)) + { + case WD0_MINOR: + f->private_data = &wd_dev.watchdog[WD0_ID]; + break; + case WD1_MINOR: + f->private_data = &wd_dev.watchdog[WD1_ID]; + break; + case WD2_MINOR: + f->private_data = &wd_dev.watchdog[WD2_ID]; + break; + default: + return(-ENODEV); + } + + /* Register IRQ on first open of device */ + if(0 == wd_dev.initialized) + { + if (request_irq(wd_dev.irq, + &wd_interrupt, + SA_SHIRQ, + WD_OBPNAME, + (void *)wd_dev.regs)) { + printk("%s: Cannot register IRQ %s\n", + WD_OBPNAME, __irq_itoa(wd_dev.irq)); + return(-EBUSY); + } + wd_dev.initialized = 1; + } + + MOD_INC_USE_COUNT; + return(0); +} + +static int wd_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int wd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int setopt = 0; + struct wd_timer* pTimer = (struct wd_timer*)file->private_data; + struct watchdog_info info = { + 0, + 0, + "Altera EPF8820ATC144-4" + }; + + if(NULL == pTimer) { + return(-EINVAL); + } + + switch(cmd) + { + /* Generic Linux IOCTLs */ + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, + (struct watchdog_info *)&info, + sizeof(struct watchdog_info *))) { + return(-EFAULT); + } + break; + case WDIOC_KEEPALIVE: + wd_pingtimer(pTimer); + break; + case WDIOC_SETOPTIONS: + if(copy_from_user(&setopt, (void*) arg, sizeof(unsigned int))) { + return -EFAULT; + } + if(setopt & WDIOS_DISABLECARD) { + if(wd_dev.opt_enable) { + printk( + "%s: cannot disable watchdog in ENABLED mode\n", + WD_OBPNAME); + return(-EINVAL); + } + wd_stoptimer(pTimer); + } + else if(setopt & WDIOS_ENABLECARD) { + wd_starttimer(pTimer); + } + else { + return(-EINVAL); + } + break; + /* Solaris-compatible IOCTLs */ + case WIOCGSTAT: + setopt = wd_getstatus(pTimer); + if(copy_to_user((void*)arg, &setopt, sizeof(unsigned int))) { + return(-EFAULT); + } + break; + case WIOCSTART: + wd_starttimer(pTimer); + break; + case WIOCSTOP: + if(wd_dev.opt_enable) { + printk("%s: cannot disable watchdog in ENABLED mode\n", + WD_OBPNAME); + return(-EINVAL); + } + wd_stoptimer(pTimer); + break; + default: + return(-EINVAL); + } + return(0); +} + +static ssize_t wd_write( struct file *file, + const char *buf, + size_t count, + loff_t *ppos) +{ + struct wd_timer* pTimer = (struct wd_timer*)file->private_data; + + if(NULL == pTimer) { + return(-EINVAL); + } + + wd_pingtimer(pTimer); + return(count); +} + +static ssize_t wd_read(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ +#ifdef WD_DEBUG + wd_dumpregs(); + return(0); +#else + return(-EINVAL); +#endif /* ifdef WD_DEBUG */ +} + +void wd_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Only WD0 will interrupt-- others are NMI and we won't + * see them here.... + */ + spin_lock_irq(&wd_dev.lock); + if((unsigned long)wd_dev.regs == (unsigned long)dev_id) + { + wd_stoptimer(&wd_dev.watchdog[WD0_ID]); + wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD; + } + spin_unlock_irq(&wd_dev.lock); + return; +} + +static struct file_operations wd_fops = { + owner: THIS_MODULE, + ioctl: wd_ioctl, + open: wd_open, + write: wd_write, + read: wd_read, + release: wd_release, +}; + +static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops }; +static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops }; +static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops }; + +void wd_dumpregs(void) +{ + /* Reading from downcounters initiates watchdog countdown-- + * Example is included below for illustration purposes. + */ + int i; + printk("%s: dumping register values\n", WD_OBPNAME); + for(i = WD0_ID; i < WD_NUMDEVS; ++i) { + /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n", + * WD_OBPNAME, + * i, + * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr), + * readw(&wd_dev.watchdog[i].regs->dcntr)); + */ + printk("\t%s%i: limit at 0x%lx: 0x%x\n", + WD_OBPNAME, + i, + (unsigned long)(&wd_dev.watchdog[i].regs->limit), + readw(&wd_dev.watchdog[i].regs->limit)); + printk("\t%s%i: status at 0x%lx: 0x%x\n", + WD_OBPNAME, + i, + (unsigned long)(&wd_dev.watchdog[i].regs->status), + readb(&wd_dev.watchdog[i].regs->status)); + printk("\t%s%i: driver status: 0x%x\n", + WD_OBPNAME, + i, + wd_getstatus(&wd_dev.watchdog[i])); + } + printk("\tintr_mask at 0x%lx: 0x%x\n", + (unsigned long)(&wd_dev.regs->pld_regs.intr_mask), + readb(&wd_dev.regs->pld_regs.intr_mask)); + printk("\tpld_status at 0x%lx: 0x%x\n", + (unsigned long)(&wd_dev.regs->pld_regs.status), + readb(&wd_dev.regs->pld_regs.status)); +} + +/* Enable or disable watchdog interrupts + * Because of the CP1400 defect this should only be + * called during initialzation or by wd_[start|stop]timer() + * + * pTimer - pointer to timer device, or NULL to indicate all timers + * enable - non-zero to enable interrupts, zero to disable + */ +void wd_toggleintr(struct wd_timer* pTimer, int enable) +{ + unsigned char curregs = wd_readb(&wd_dev.regs->pld_regs.intr_mask); + unsigned char setregs = + (NULL == pTimer) ? + (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : + (pTimer->intr_mask); + + (WD_INTR_ON == enable) ? + (curregs &= ~setregs): + (curregs |= setregs); + + wd_writeb(curregs, &wd_dev.regs->pld_regs.intr_mask); + return; +} + +/* Reset countdown timer with 'limit' value and continue countdown. + * This will not start a stopped timer. + * + * pTimer - pointer to timer device + */ +void wd_pingtimer(struct wd_timer* pTimer) +{ + if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) { + wd_readb(&pTimer->regs->dcntr); + } +} + +/* Stop a running watchdog timer-- the timer actually keeps + * running, but the interrupt is masked so that no action is + * taken upon expiration. + * + * pTimer - pointer to timer device + */ +void wd_stoptimer(struct wd_timer* pTimer) +{ + if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) { + wd_toggleintr(pTimer, WD_INTR_OFF); + + if(wd_dev.isbaddoggie) { + pTimer->runstatus |= WD_STAT_BSTOP; + wd_brokentimer((unsigned long)&wd_dev); + } + } +} + +/* Start a watchdog timer with the specified limit value + * If the watchdog is running, it will be restarted with + * the provided limit value. + * + * This function will enable interrupts on the specified + * watchdog. + * + * pTimer - pointer to timer device + * limit - limit (countdown) value in 1/10th seconds + */ +void wd_starttimer(struct wd_timer* pTimer) +{ + if(wd_dev.isbaddoggie) { + pTimer->runstatus &= ~WD_STAT_BSTOP; + } + pTimer->runstatus &= ~WD_STAT_SVCD; + + wd_writew(pTimer->timeout, &pTimer->regs->limit); + wd_toggleintr(pTimer, WD_INTR_ON); +} + +/* Restarts timer with maximum limit value and + * does not unset 'brokenstop' value. + */ +void wd_resetbrokentimer(struct wd_timer* pTimer) +{ + wd_toggleintr(pTimer, WD_INTR_ON); + wd_writew(WD_BLIMIT, &pTimer->regs->limit); +} + +/* Timer device initialization helper. + * Returns 0 on success, other on failure + */ +int wd_inittimer(int whichdog) +{ + struct miscdevice *whichmisc; + volatile struct wd_timer_regblk *whichregs; + char whichident[8]; + int whichmask; + __u16 whichlimit; + + switch(whichdog) + { + case WD0_ID: + whichmisc = &wd0_miscdev; + strcpy(whichident, "RIC"); + whichregs = &wd_dev.regs->wd0_regs; + whichmask = WD0_INTR_MASK; + whichlimit= (0 == wd0_timeout) ? + (wd_dev.opt_timeout): + (wd0_timeout); + break; + case WD1_ID: + whichmisc = &wd1_miscdev; + strcpy(whichident, "XIR"); + whichregs = &wd_dev.regs->wd1_regs; + whichmask = WD1_INTR_MASK; + whichlimit= (0 == wd1_timeout) ? + (wd_dev.opt_timeout): + (wd1_timeout); + break; + case WD2_ID: + whichmisc = &wd2_miscdev; + strcpy(whichident, "POR"); + whichregs = &wd_dev.regs->wd2_regs; + whichmask = WD2_INTR_MASK; + whichlimit= (0 == wd2_timeout) ? + (wd_dev.opt_timeout): + (wd2_timeout); + break; + default: + printk("%s: %s: invalid watchdog id: %i\n", + WD_OBPNAME, __FUNCTION__, whichdog); + return(1); + } + if(0 != misc_register(whichmisc)) + { + return(1); + } + wd_dev.watchdog[whichdog].regs = whichregs; + wd_dev.watchdog[whichdog].timeout = whichlimit; + wd_dev.watchdog[whichdog].intr_mask = whichmask; + wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP; + wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT; + + printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n", + WD_OBPNAME, + whichdog, + whichident, + wd_dev.watchdog[whichdog].timeout / 10, + wd_dev.watchdog[whichdog].timeout % 10, + (0 != wd_dev.opt_enable) ? "in ENABLED mode" : ""); + return(0); +} + +/* Timer method called to reset stopped watchdogs-- + * because of the PLD bug on CP1400, we cannot mask + * interrupts within the PLD so me must continually + * reset the timers ad infinitum. + */ +void wd_brokentimer(unsigned long data) +{ + struct wd_device* pDev = (struct wd_device*)data; + int id, tripped = 0; + + /* kill a running timer instance, in case we + * were called directly instead of by kernel timer + */ + if(timer_pending(&wd_timer)) { + del_timer(&wd_timer); + } + + for(id = WD0_ID; id < WD_NUMDEVS; ++id) { + if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) { + ++tripped; + wd_resetbrokentimer(&pDev->watchdog[id]); + } + } + + if(tripped) { + /* there is at least one timer brokenstopped-- reschedule */ + wd_timer.expires = WD_BTIMEOUT; + add_timer(&wd_timer); + } +} + +int wd_getstatus(struct wd_timer* pTimer) +{ + unsigned char stat = wd_readb(&pTimer->regs->status); + unsigned char intr = wd_readb(&wd_dev.regs->pld_regs.intr_mask); + unsigned char ret = WD_STOPPED; + + /* determine STOPPED */ + if(0 == stat ) { + return(ret); + } + /* determine EXPIRED vs FREERUN vs RUNNING */ + else if(WD_S_EXPIRED & stat) { + ret = WD_EXPIRED; + } + else if(WD_S_RUNNING & stat) { + if(intr & pTimer->intr_mask) { + ret = WD_FREERUN; + } + else { + /* Fudge WD_EXPIRED status for defective CP1400-- + * IF timer is running + * AND brokenstop is set + * AND an interrupt has been serviced + * we are WD_EXPIRED. + * + * IF timer is running + * AND brokenstop is set + * AND no interrupt has been serviced + * we are WD_FREERUN. + */ + if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) { + if(pTimer->runstatus & WD_STAT_SVCD) { + ret = WD_EXPIRED; + } + else { + /* we could as well pretend we are expired */ + ret = WD_FREERUN; + } + } + else { + ret = WD_RUNNING; + } + } + } + + /* determine SERVICED */ + if(pTimer->runstatus & WD_STAT_SVCD) { + ret |= WD_SERVICED; + } + + return(ret); +} + +static int __init wd_init(void) +{ + int id; + struct linux_ebus *ebus = NULL; + struct linux_ebus_device *edev = NULL; + + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if (!strcmp(edev->prom_name, WD_OBPNAME)) + goto ebus_done; + } + } + +ebus_done: + if(!edev) { + printk("%s: unable to locate device\n", WD_OBPNAME); + return -ENODEV; + } + + wd_dev.regs = + ioremap(edev->resource[0].start, sizeof(struct wd_regblk)); + + if(NULL == wd_dev.regs) { + printk("%s: unable to map registers\n", WD_OBPNAME); + return(-ENODEV); + } + + /* initialize device structure from OBP parameters */ + wd_dev.irq = edev->irqs[0]; + wd_dev.opt_enable = wd_opt_enable(); + wd_dev.opt_reboot = wd_opt_reboot(); + wd_dev.opt_timeout = wd_opt_timeout(); + wd_dev.isbaddoggie = wd_isbroken(); + + /* disable all interrupts unless watchdog-enabled? == true */ + if(! wd_dev.opt_enable) { + wd_toggleintr(NULL, WD_INTR_OFF); + } + + /* register miscellaneous devices */ + for(id = WD0_ID; id < WD_NUMDEVS; ++id) { + if(0 != wd_inittimer(id)) { + printk("%s%i: unable to initialize\n", WD_OBPNAME, id); + } + } + + /* warn about possible defective PLD */ + if(wd_dev.isbaddoggie) { + init_timer(&wd_timer); + wd_timer.function = wd_brokentimer; + wd_timer.data = (unsigned long)&wd_dev; + wd_timer.expires = WD_BTIMEOUT; + + printk("%s: PLD defect workaround enabled for model %s\n", + WD_OBPNAME, WD_BADMODEL); + } + return(0); +} + +static void __exit wd_cleanup(void) +{ + int id; + + /* if 'watchdog-enable?' == TRUE, timers are not stopped + * when module is unloaded. All brokenstopped timers will + * also now eventually trip. + */ + for(id = WD0_ID; id < WD_NUMDEVS; ++id) { + if(WD_S_RUNNING == wd_readb(&wd_dev.watchdog[id].regs->status)) { + if(wd_dev.opt_enable) { + printk(KERN_WARNING "%s%i: timer not stopped at release\n", + WD_OBPNAME, id); + } + else { + wd_stoptimer(&wd_dev.watchdog[id]); + if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) { + wd_resetbrokentimer(&wd_dev.watchdog[id]); + printk(KERN_WARNING + "%s%i: defect workaround disabled at release, "\ + "timer expires in ~%01i sec\n", + WD_OBPNAME, id, + wd_readw(&wd_dev.watchdog[id].regs->limit) / 10); + } + } + } + } + + if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) { + del_timer(&wd_timer); + } + if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) { + misc_deregister(&wd0_miscdev); + } + if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) { + misc_deregister(&wd1_miscdev); + } + if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) { + misc_deregister(&wd2_miscdev); + } + if(0 != wd_dev.initialized) { + free_irq(wd_dev.irq, (void *)wd_dev.regs); + } + iounmap(wd_dev.regs); +} + +module_init(wd_init); +module_exit(wd_cleanup); diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 71809b59d..c6c927712 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -1,4 +1,4 @@ -/* $Id: flash.c,v 1.20 2000/11/08 04:57:49 davem Exp $ +/* $Id: flash.c,v 1.21 2001/01/11 15:29:36 davem Exp $ * flash.c: Allow mmap access to the OBP Flash, for OBP updates. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -14,6 +14,7 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/smp_lock.h> +#include <linux/spinlock.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -22,6 +23,7 @@ #include <asm/sbus.h> #include <asm/ebus.h> +static spinlock_t flash_lock = SPIN_LOCK_UNLOCKED; static struct { unsigned long read_base; /* Physical read address */ unsigned long write_base; /* Physical write address */ @@ -38,14 +40,14 @@ flash_mmap(struct file *file, struct vm_area_struct *vma) unsigned long addr; unsigned long size; - lock_kernel(); + spin_lock(&flash_lock); if (flash.read_base == flash.write_base) { addr = flash.read_base; size = flash.read_size; } else { if ((vma->vm_flags & VM_READ) && (vma->vm_flags & VM_WRITE)) { - unlock_kernel(); + spin_unlock(&flash_lock); return -EINVAL; } if (vma->vm_flags & VM_READ) { @@ -55,11 +57,11 @@ flash_mmap(struct file *file, struct vm_area_struct *vma) addr = flash.write_base; size = flash.write_size; } else { - unlock_kernel(); + spin_unlock(&flash_lock); return -ENXIO; } } - unlock_kernel(); + spin_unlock(&flash_lock); if ((vma->vm_pgoff << PAGE_SHIFT) > size) return -ENXIO; @@ -127,9 +129,10 @@ flash_open(struct inode *inode, struct file *file) static int flash_release(struct inode *inode, struct file *file) { - lock_kernel(); + spin_lock(&flash_lock); flash.busy = 0; - unlock_kernel(); + spin_unlock(&flash_lock); + return 0; } diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index f87850b2c..8faec19dd 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -505,9 +505,7 @@ static int jsfd_open(struct inode *inode, struct file *file) static int jsf_release(struct inode *inode, struct file *file) { - lock_kernel(); jsf0.busy = 0; - unlock_kernel(); return 0; } diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c index 6ddccf114..ea37949fa 100644 --- a/drivers/sbus/char/pcikbd.c +++ b/drivers/sbus/char/pcikbd.c @@ -1,4 +1,4 @@ -/* $Id: pcikbd.c,v 1.49 2000/07/13 08:06:40 davem Exp $ +/* $Id: pcikbd.c,v 1.50 2001/01/11 15:29:36 davem Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -746,13 +746,13 @@ static int aux_release(struct inode * inode, struct file * file) { unsigned long flags; - lock_kernel(); aux_fasync(-1, file, 0); - if (--aux_count) - goto out; spin_lock_irqsave(&pcikbd_lock, flags); + if (--aux_count) + goto out; + /* Disable controller ints */ aux_write_cmd(AUX_INTS_OFF); poll_aux_status(); @@ -761,9 +761,8 @@ static int aux_release(struct inode * inode, struct file * file) pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG); poll_aux_status(); - spin_unlock_irqrestore(&pcikbd_lock, flags); out: - unlock_kernel(); + spin_unlock_irqrestore(&pcikbd_lock, flags); return 0; } @@ -780,11 +779,13 @@ static int aux_open(struct inode * inode, struct file * file) if (!aux_present) return -ENODEV; - if (aux_count++) - return 0; - spin_lock_irqsave(&pcikbd_lock, flags); + if (aux_count++) { + spin_unlock_irqrestore(&pcikbd_lock, flags); + return 0; + } + if (!poll_aux_status()) { aux_count--; spin_unlock_irqrestore(&pcikbd_lock, flags); diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c index d8454cf25..6105e4229 100644 --- a/drivers/sbus/char/rtc.c +++ b/drivers/sbus/char/rtc.c @@ -1,4 +1,4 @@ -/* $Id: rtc.c,v 1.23 2000/08/29 07:01:55 davem Exp $ +/* $Id: rtc.c,v 1.24 2001/01/11 15:07:09 davem Exp $ * * Linux/SPARC Real Time Clock Driver * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) @@ -31,11 +31,9 @@ static int rtc_busy = 0; void get_rtc_time(struct rtc_time *t) { unsigned long regs = mstk48t02_regs; - unsigned long flags; u8 tmp; - save_flags(flags); - cli(); + spin_lock_irq(&mostek_lock); tmp = mostek_read(regs + MOSTEK_CREG); tmp |= MSTK_CREG_READ; @@ -52,18 +50,18 @@ void get_rtc_time(struct rtc_time *t) tmp = mostek_read(regs + MOSTEK_CREG); tmp &= ~MSTK_CREG_READ; mostek_write(regs + MOSTEK_CREG, tmp); - restore_flags(flags); + + spin_unlock_irq(&mostek_lock); } /* Set the current date and time inthe real time clock. */ void set_rtc_time(struct rtc_time *t) { unsigned long regs = mstk48t02_regs; - unsigned long flags; u8 tmp; - save_flags(flags); - cli(); + spin_lock_irq(&mostek_lock); + tmp = mostek_read(regs + MOSTEK_CREG); tmp |= MSTK_CREG_WRITE; mostek_write(regs + MOSTEK_CREG, tmp); @@ -79,7 +77,8 @@ void set_rtc_time(struct rtc_time *t) tmp = mostek_read(regs + MOSTEK_CREG); tmp &= ~MSTK_CREG_WRITE; mostek_write(regs + MOSTEK_CREG, tmp); - restore_flags(flags); + + spin_unlock_irq(&mostek_lock); } static long long rtc_lseek(struct file *file, long long offset, int origin) @@ -121,20 +120,24 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int rtc_open(struct inode *inode, struct file *file) { + int ret; + + spin_lock_irq(&mostek_lock); + if (rtc_busy) { + ret = -EBUSY; + } else { + rtc_busy = 1; + ret = 0; + } + spin_unlock_irq(&mostek_lock); - if (rtc_busy) - return -EBUSY; - - rtc_busy = 1; - - return 0; + return ret; } static int rtc_release(struct inode *inode, struct file *file) { - lock_kernel(); rtc_busy = 0; - unlock_kernel(); + return 0; } @@ -150,11 +153,7 @@ static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; EXPORT_NO_SYMBOLS; -#ifdef MODULE -int init_module(void) -#else -int __init rtc_sun_init(void) -#endif +static int __init rtc_sun_init(void) { int error; @@ -173,9 +172,10 @@ int __init rtc_sun_init(void) return 0; } -#ifdef MODULE -void cleanup_module(void) +static void __exit rtc_sun_cleanup(void) { misc_deregister(&rtc_dev); } -#endif + +module_init(rtc_sun_init); +module_exit(rtc_sun_cleanup); diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index f34c12250..467200699 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -1521,15 +1521,17 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) static int kbd_open (struct inode *i, struct file *f) { + spin_lock_irq(&kbd_queue_lock); kbd_active++; if (kbd_opened) - return 0; + goto out; kbd_opened = fg_console + 1; - spin_lock_irq(&kbd_queue_lock); kbd_head = kbd_tail = 0; + + out: spin_unlock_irq(&kbd_queue_lock); return 0; @@ -1538,7 +1540,7 @@ kbd_open (struct inode *i, struct file *f) static int kbd_close (struct inode *i, struct file *f) { - lock_kernel(); + spin_lock_irq(&kbd_queue_lock); if (!--kbd_active) { if (kbd_redirected) kbd_table [kbd_redirected-1].kbdmode = VC_XLATE; @@ -1546,7 +1548,8 @@ kbd_close (struct inode *i, struct file *f) kbd_opened = 0; kbd_fasync (-1, f, 0); } - unlock_kernel(); + spin_unlock_irq(&kbd_queue_lock); + return 0; } diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index fd92bc434..8bb45cbde 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -391,11 +391,14 @@ sun_mouse_inbyte(unsigned char byte, int is_break) static int sun_mouse_open(struct inode * inode, struct file * file) { + spin_lock_irq(&sunmouse.lock); if (sunmouse.active++) - return 0; + goto out; sunmouse.delta_x = sunmouse.delta_y = 0; sunmouse.button_state = 0x80; sunmouse.vuid_mode = VUID_NATIVE; +out: + spin_unlock_irq(&sunmouse.lock); return 0; } @@ -412,10 +415,12 @@ static int sun_mouse_fasync (int fd, struct file *filp, int on) static int sun_mouse_close(struct inode *inode, struct file *file) { - lock_kernel(); sun_mouse_fasync (-1, file, 0); + + spin_lock_irq(&sunmouse.lock); sunmouse.active--; - unlock_kernel(); + spin_unlock_irq(&sunmouse.lock); + return 0; } diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 73158f351..a4c0f347e 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -22,6 +22,7 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/smp_lock.h> +#include <linux/spinlock.h> #include <asm/openprom.h> #include <asm/oplib.h> @@ -181,17 +182,26 @@ struct vfc_dev *vfc_get_dev_ptr(int instance) return vfc_dev_lst[instance]; } +static spinlock_t vfc_dev_lock = SPIN_LOCK_UNLOCKED; + static int vfc_open(struct inode *inode, struct file *file) { struct vfc_dev *dev; + spin_lock(&vfc_dev_lock); dev = vfc_get_dev_ptr(MINOR(inode->i_rdev)); - if (dev == NULL) + if (dev == NULL) { + spin_unlock(&vfc_dev_lock); return -ENODEV; - if (dev->busy) + } + if (dev->busy) { + spin_unlock(&vfc_dev_lock); return -EBUSY; + } dev->busy = 1; + spin_unlock(&vfc_dev_lock); + vfc_lock_device(dev); vfc_csr_init(dev); @@ -209,14 +219,14 @@ static int vfc_release(struct inode *inode,struct file *file) { struct vfc_dev *dev; - lock_kernel(); + spin_lock(&vfc_dev_lock); dev = vfc_get_dev_ptr(MINOR(inode->i_rdev)); if (!dev || !dev->busy) { - unlock_kernel(); + spin_unlock(&vfc_dev_lock); return -EINVAL; } dev->busy = 0; - unlock_kernel(); + spin_unlock(&vfc_dev_lock); return 0; } @@ -611,12 +621,10 @@ static int vfc_mmap(struct inode *inode, struct file *file, unsigned int map_size, ret, map_offset; struct vfc_dev *dev; - lock_kernel(); dev = vfc_get_dev_ptr(MINOR(inode->i_rdev)); - if(dev == NULL) { - unlock_kernel(); + if(dev == NULL) return -ENODEV; - } + map_size = vma->vm_end - vma->vm_start; if(map_size > sizeof(struct vfc_regs)) map_size = sizeof(struct vfc_regs); @@ -626,7 +634,7 @@ static int vfc_mmap(struct inode *inode, struct file *file, map_offset = (unsigned int) (long)dev->phys_regs; ret = io_remap_page_range(vma->vm_start, map_offset, map_size, vma->vm_page_prot, dev->which_io); - unlock_kernel(); + if(ret) return -EAGAIN; |