summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Config.in3
-rw-r--r--drivers/char/Makefile15
-rw-r--r--drivers/char/agp/agpgart_be.c2
-rw-r--r--drivers/char/ds1620.c436
-rw-r--r--drivers/char/efirtc.c2
-rw-r--r--drivers/char/h8.c74
-rw-r--r--drivers/char/h8.h6
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mem.c12
-rw-r--r--drivers/char/nwbutton.c276
-rw-r--r--drivers/char/nwbutton.h48
-rw-r--r--drivers/char/nwflash.c708
-rw-r--r--drivers/char/raw.c7
-rw-r--r--drivers/char/rtc.c8
-rw-r--r--drivers/char/wdt285.c204
-rw-r--r--drivers/char/wdt977.c204
16 files changed, 1950 insertions, 59 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index c34978479..97e1f1f58 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -122,7 +122,8 @@ endmenu
tristate '/dev/nvram support' CONFIG_NVRAM
tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
-if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
+bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC
+if [ "$CONFIG_OBSOLETE" = "y" -a "$CONFIG_ALPHA_BOOK1" = "y" ]; then
bool 'Tadpole ANA H8 Support' CONFIG_H8
fi
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 4667e1fa3..7f3c6133b 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -158,13 +158,17 @@ obj-$(CONFIG_ADBMOUSE) += adbmouse.o busmouse.o
obj-$(CONFIG_PC110_PAD) += pc110pad.o
obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_RTC) += rtc.o
+obj-$(CONFIG_EFI_RTC) += efirtc.o
ifeq ($(CONFIG_PPC),)
obj-$(CONFIG_NVRAM) += nvram.o
endif
-obj-$(CONFIG_I810_RNG) += i810_rng.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o
+obj-$(CONFIG_21825_WATCHDOG) += wdt285.o
+obj-$(CONFIG_977_WATCHDOG) += wdt977.o
+obj-$(CONFIG_DS1620) += ds1620.o
+
#
# for external dependencies in arm/config.in and video/config.in
#
@@ -205,7 +209,7 @@ ifeq ($(CONFIG_I2C_PARPORT),y)
L_I2C = y
else
ifeq ($(CONFIG_I2C_PARPORT),m)
- M_I2C = y
+ L_I2C = m
endif
endif
@@ -277,6 +281,13 @@ ifeq ($(CONFIG_DZ),y)
L_OBJS += dz.o
endif
+obj-$(CONFIG_NWBUTTON) += nwbutton.o
+obj-$(CONFIG_NWFLASH) += nwflash.o
+
+ifeq ($(CONFIG_DZ),y)
+ L_OBJS += dz.o
+endif
+
ifeq ($(CONFIG_DRM),y)
SUB_DIRS += drm
ALL_SUB_DIRS += drm
diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c
index a21556a2b..c478395f8 100644
--- a/drivers/char/agp/agpgart_be.c
+++ b/drivers/char/agp/agpgart_be.c
@@ -1397,8 +1397,6 @@ static int __init sis_generic_setup (struct pci_dev *pdev)
agp_bridge.free_by_type = agp_generic_free_by_type;
return 0;
-
- (void) pdev; /* unused */
}
#endif /* CONFIG_AGP_SIS */
diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c
new file mode 100644
index 000000000..e3a37611d
--- /dev/null
+++ b/drivers/char/ds1620.c
@@ -0,0 +1,436 @@
+/*
+ * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
+ * thermometer driver (as used in the Rebel.com NetWinder)
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/capability.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/therm.h>
+
+#ifdef CONFIG_PROC_FS
+/* define for /proc interface */
+#define THERM_USE_PROC
+#endif
+
+/* Definitions for DS1620 chip */
+#define THERM_START_CONVERT 0xee
+#define THERM_RESET 0xaf
+#define THERM_READ_CONFIG 0xac
+#define THERM_READ_TEMP 0xaa
+#define THERM_READ_TL 0xa2
+#define THERM_READ_TH 0xa1
+#define THERM_WRITE_CONFIG 0x0c
+#define THERM_WRITE_TL 0x02
+#define THERM_WRITE_TH 0x01
+
+#define CFG_CPU 2
+#define CFG_1SHOT 1
+
+static const char *fan_state[] = { "off", "on", "on (hardwired)" };
+
+/*
+ * Start of NetWinder specifics
+ * Note! We have to hold the gpio lock with IRQs disabled over the
+ * whole of our transaction to the Dallas chip, since there is a
+ * chance that the WaveArtist driver could touch these bits to
+ * enable or disable the speaker.
+ */
+extern spinlock_t gpio_lock;
+extern unsigned int system_rev;
+
+static inline void netwinder_ds1620_set_clk(int clk)
+{
+ gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
+}
+
+static inline void netwinder_ds1620_set_data(int dat)
+{
+ gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
+}
+
+static inline int netwinder_ds1620_get_data(void)
+{
+ return gpio_read() & GPIO_DATA;
+}
+
+static inline void netwinder_ds1620_set_data_dir(int dir)
+{
+ gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
+}
+
+static inline void netwinder_ds1620_reset(void)
+{
+ cpld_modify(CPLD_DS_ENABLE, 0);
+ cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
+}
+
+static inline void netwinder_lock(unsigned long *flags)
+{
+ spin_lock_irqsave(&gpio_lock, *flags);
+}
+
+static inline void netwinder_unlock(unsigned long *flags)
+{
+ spin_unlock_irqrestore(&gpio_lock, *flags);
+}
+
+static inline void netwinder_set_fan(int i)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static inline int netwinder_get_fan(void)
+{
+ if ((system_rev & 0xf000) == 0x4000)
+ return FAN_ALWAYS_ON;
+
+ return (gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
+}
+
+/*
+ * End of NetWinder specifics
+ */
+
+static void ds1620_send_bits(int nr, int value)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ netwinder_ds1620_set_data(value & 1);
+ netwinder_ds1620_set_clk(0);
+ udelay(1);
+ netwinder_ds1620_set_clk(1);
+ udelay(1);
+
+ value >>= 1;
+ }
+}
+
+static unsigned int ds1620_recv_bits(int nr)
+{
+ unsigned int value = 0, mask = 1;
+ int i;
+
+ netwinder_ds1620_set_data(0);
+
+ for (i = 0; i < nr; i++) {
+ netwinder_ds1620_set_clk(0);
+ udelay(1);
+
+ if (netwinder_ds1620_get_data())
+ value |= mask;
+
+ mask <<= 1;
+
+ netwinder_ds1620_set_clk(1);
+ udelay(1);
+ }
+
+ return value;
+}
+
+static void ds1620_out(int cmd, int bits, int value)
+{
+ unsigned long flags;
+
+ netwinder_lock(&flags);
+ netwinder_ds1620_set_clk(1);
+ netwinder_ds1620_set_data_dir(0);
+ netwinder_ds1620_reset();
+
+ udelay(1);
+
+ ds1620_send_bits(8, cmd);
+ if (bits)
+ ds1620_send_bits(bits, value);
+
+ udelay(1);
+
+ netwinder_ds1620_reset();
+ netwinder_unlock(&flags);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2);
+}
+
+static unsigned int ds1620_in(int cmd, int bits)
+{
+ unsigned long flags;
+ unsigned int value;
+
+ netwinder_lock(&flags);
+ netwinder_ds1620_set_clk(1);
+ netwinder_ds1620_set_data_dir(0);
+ netwinder_ds1620_reset();
+
+ udelay(1);
+
+ ds1620_send_bits(8, cmd);
+
+ netwinder_ds1620_set_data_dir(1);
+ value = ds1620_recv_bits(bits);
+
+ netwinder_ds1620_reset();
+ netwinder_unlock(&flags);
+
+ return value;
+}
+
+static int cvt_9_to_int(unsigned int val)
+{
+ if (val & 0x100)
+ val |= 0xfffffe00;
+
+ return val;
+}
+
+static void ds1620_write_state(struct therm *therm)
+{
+ ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
+ ds1620_out(THERM_WRITE_TL, 9, therm->lo);
+ ds1620_out(THERM_WRITE_TH, 9, therm->hi);
+ ds1620_out(THERM_START_CONVERT, 0, 0);
+}
+
+static void ds1620_read_state(struct therm *therm)
+{
+ therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
+ therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
+}
+
+static ssize_t
+ds1620_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+ signed int cur_temp;
+ signed char cur_temp_degF;
+
+ /* Can't seek (pread) on this device */
+ if (ptr != &file->f_pos)
+ return -ESPIPE;
+
+ cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
+
+ /* convert to Fahrenheit, as per wdt.c */
+ cur_temp_degF = (cur_temp * 9) / 5 + 32;
+
+ if (copy_to_user(buf, &cur_temp_degF, 1))
+ return -EFAULT;
+
+ return 1;
+}
+
+static int
+ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct therm therm;
+ int i;
+
+ switch(cmd) {
+ case CMD_SET_THERMOSTATE:
+ case CMD_SET_THERMOSTATE2:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (cmd == CMD_SET_THERMOSTATE) {
+ if (get_user(therm.hi, (int *)arg))
+ return -EFAULT;
+ therm.lo = therm.hi - 3;
+ } else {
+ if (copy_from_user(&therm, (void *)arg, sizeof(therm)))
+ return -EFAULT;
+ }
+
+ therm.lo <<= 1;
+ therm.hi <<= 1;
+
+ ds1620_write_state(&therm);
+ break;
+
+ case CMD_GET_THERMOSTATE:
+ case CMD_GET_THERMOSTATE2:
+ ds1620_read_state(&therm);
+
+ therm.lo >>= 1;
+ therm.hi >>= 1;
+
+ if (cmd == CMD_GET_THERMOSTATE) {
+ if (put_user(therm.hi, (int *)arg))
+ return -EFAULT;
+ } else {
+ if (copy_to_user((void *)arg, &therm, sizeof(therm)))
+ return -EFAULT;
+ }
+ break;
+
+ case CMD_GET_TEMPERATURE:
+ case CMD_GET_TEMPERATURE2:
+ i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
+
+ if (cmd == CMD_GET_TEMPERATURE)
+ i >>= 1;
+
+ return put_user(i, (int *)arg) ? -EFAULT : 0;
+
+ case CMD_GET_STATUS:
+ i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
+
+ return put_user(i, (int *)arg) ? -EFAULT : 0;
+
+ case CMD_GET_FAN:
+ i = netwinder_get_fan();
+
+ return put_user(i, (int *)arg) ? -EFAULT : 0;
+
+ case CMD_SET_FAN:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(i, (int *)arg))
+ return -EFAULT;
+
+ netwinder_set_fan(i);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int
+ds1620_open(struct inode *inode, struct file *file)
+{
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+ds1620_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef THERM_USE_PROC
+static int
+proc_therm_ds1620_read(char *buf, char **start, off_t offset,
+ int len, int *eof, void *unused)
+{
+ struct therm th;
+ int temp;
+
+ ds1620_read_state(&th);
+ temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
+
+ len = sprintf(buf, "Thermostat: HI %i.%i, LOW %i.%i; "
+ "temperature: %i.%i C, fan %s\n",
+ th.hi >> 1, th.hi & 1 ? 5 : 0,
+ th.lo >> 1, th.lo & 1 ? 5 : 0,
+ temp >> 1, temp & 1 ? 5 : 0,
+ fan_state[netwinder_get_fan()]);
+
+ return len;
+}
+
+static struct proc_dir_entry *proc_therm_ds1620;
+#endif
+
+static struct file_operations ds1620_fops = {
+ NULL, /* lseek */
+ ds1620_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ ds1620_ioctl, /* ioctl */
+ NULL, /* mmap */
+ ds1620_open, /* open */
+ NULL, /* flush */
+ ds1620_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+};
+
+static struct miscdevice ds1620_miscdev = {
+ TEMP_MINOR,
+ "temp",
+ &ds1620_fops
+};
+
+int __init ds1620_init(void)
+{
+ int ret;
+ struct therm th, th_start;
+
+ if (!machine_is_netwinder())
+ return -ENODEV;
+
+ ds1620_out(THERM_RESET, 0, 0);
+ ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
+ ds1620_out(THERM_START_CONVERT, 0, 0);
+
+ /*
+ * Trigger the fan to start by setting
+ * temperature high point low. This kicks
+ * the fan into action.
+ */
+ ds1620_read_state(&th);
+ th_start.lo = 0;
+ th_start.hi = 1;
+ ds1620_write_state(&th_start);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2*HZ);
+
+ ds1620_write_state(&th);
+
+ ret = misc_register(&ds1620_miscdev);
+ if (ret < 0)
+ return ret;
+
+#ifdef THERM_USE_PROC
+ proc_therm_ds1620 = create_proc_entry("therm", 0, 0);
+ if (proc_therm_ds1620)
+ proc_therm_ds1620->read_proc = proc_therm_ds1620_read;
+ else
+ printk(KERN_ERR "therm: unable to register /proc/therm\n");
+#endif
+
+ ds1620_read_state(&th);
+ ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
+
+ printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
+ "current %i.%i C, fan %s.\n",
+ th.hi >> 1, th.hi & 1 ? 5 : 0,
+ th.lo >> 1, th.lo & 1 ? 5 : 0,
+ ret >> 1, ret & 1 ? 5 : 0,
+ fan_state[netwinder_get_fan()]);
+
+ return 0;
+}
+
+void __exit ds1620_exit(void)
+{
+#ifdef THERM_USE_PROC
+ remove_proc_entry("therm", NULL);
+#endif
+ misc_deregister(&ds1620_miscdev);
+}
+
+module_init(ds1620_init);
+module_exit(ds1620_exit);
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index 391cfcfc0..0f26dedd9 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -334,6 +334,7 @@ efi_rtc_read_proc(char *page, char **start, off_t off,
if (len<0) len = 0;
return len;
}
+
static int __init
efi_rtc_init(void)
{
@@ -345,6 +346,7 @@ efi_rtc_init(void)
return 0;
}
+
static int __exit
efi_rtc_exit(void)
{
diff --git a/drivers/char/h8.c b/drivers/char/h8.c
index 0583923d9..acc898cb8 100644
--- a/drivers/char/h8.c
+++ b/drivers/char/h8.c
@@ -6,6 +6,8 @@
*
* Fixes:
* June 1999, AV added releasing /proc/driver/h8
+ * Feb 2000, Borislav Deianov
+ * changed queues to use list.h instead of lists.h
*/
#include <linux/config.h>
@@ -23,7 +25,7 @@
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
-#include <linux/lists.h>
+#include <linux/list.h>
#include <linux/ioport.h>
#include <linux/poll.h>
#include <linux/init.h>
@@ -52,18 +54,13 @@
/*
* Forward declarations.
*/
-int h8_init(void);
+static int h8_init(void);
int h8_display_blank(void);
int h8_display_unblank(void);
static void h8_intr(int irq, void *dev_id, struct pt_regs *regs);
-#ifdef CONFIG_PROC_FS
static int h8_get_info(char *, char **, off_t, int);
-#else
-static int h8_get_info(char *, char **, off_t, int) {}
-#error "Somebody needs to learn C. Badly."
-#endif
/*
* Support Routines.
@@ -141,7 +138,9 @@ unsigned int h8_state = H8_IDLE;
unsigned int h8_index = -1;
unsigned int h8_enabled = 0;
-queue_head_t h8_actq, h8_cmdq, h8_freeq;
+LIST_HEAD(h8_actq);
+LIST_HEAD(h8_cmdq);
+LIST_HEAD(h8_freeq);
/*
* Globals used in thermal control of Alphabook1.
@@ -170,7 +169,7 @@ int speed_tab[6] = {230, 153, 115, 57, 28, 14};
static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
{
u_char stat_reg, data_reg;
- h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
+ h8_cmd_q_t *qp = list_entry(h8_actq.next, h8_cmd_q_t, link);
stat_reg = H8_GET_STATUS;
data_reg = H8_READ_DATA;
@@ -260,7 +259,7 @@ static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
return;
} else if (data_reg == H8_SYNC_BYTE) {
h8_state = H8_IDLE;
- if (!QUEUE_IS_EMPTY(&h8_actq, link))
+ if (!list_empty(&h8_actq))
h8_send_next_cmd_byte();
} else {
Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
@@ -276,10 +275,10 @@ static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
/* If command reception finished. */
if (qp->cnt == qp->nrsp) {
h8_state = H8_IDLE;
- QUEUE_REMOVE(&h8_actq, qp, link);
+ list_del(&qp->link);
h8_cmd_done (qp);
/* More commands to send over? */
- if (!QUEUE_IS_EMPTY(&h8_cmdq, link))
+ if (!list_empty(&h8_cmdq))
h8_start_new_cmd();
}
return;
@@ -317,9 +316,6 @@ static int __init h8_init(void)
misc_register(&h8_device);
request_region(h8_base, 8, "h8");
- QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *);
- QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *);
- QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *);
h8_alloc_queues();
h8_hw_init();
@@ -364,9 +360,9 @@ static void __init h8_hw_init(void)
return;
}
-#ifdef CONFIG_PROC_FS
static int h8_get_info(char *buf, char **start, off_t fpos, int length)
{
+#ifdef CONFIG_PROC_FS
char *p;
if (!h8_enabled)
@@ -387,8 +383,10 @@ static int h8_get_info(char *buf, char **start, off_t fpos, int length)
);
return p - buf;
-}
+#else
+ return 0;
#endif
+}
/* Called from console driver -- must make sure h8_enabled. */
int h8_display_blank(void)
@@ -440,7 +438,7 @@ h8_alloc_queues(void)
save_flags(flags); cli();
for (i = 0; i < H8_Q_ALLOC_AMOUNT; i++) {
/* place each at front of freeq */
- QUEUE_ENTER(&h8_freeq, &qp[i], link, h8_cmd_q_t *);
+ list_add(&qp[i].link, &h8_freeq);
}
restore_flags(flags);
return (1);
@@ -458,15 +456,15 @@ h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
/* get cmd buf */
save_flags(flags); cli();
- while (QUEUE_IS_EMPTY(&h8_freeq, link)) {
+ while (list_empty(&h8_freeq)) {
Dprintk("H8: need to allocate more cmd buffers\n");
restore_flags(flags);
h8_alloc_queues();
save_flags(flags); cli();
}
/* get first element from queue */
- qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_freeq, link);
- QUEUE_REMOVE(&h8_freeq, qp, link);
+ qp = list_entry(h8_freeq.next, h8_cmd_q_t, link);
+ list_del(&qp->link);
restore_flags(flags);
@@ -479,7 +477,8 @@ h8_q_cmd(u_char *cmd, int cmd_size, int resp_size)
/* queue it at the end of the cmd queue */
save_flags(flags); cli();
- QUEUE_ENTER(&h8_cmdq, qp, link, h8_cmd_q_t *);
+ /* XXX this actually puts it at the start of cmd queue, bug? */
+ list_add(&qp->link, &h8_cmdq);
restore_flags(flags);
@@ -500,13 +499,13 @@ h8_start_new_cmd(void)
return;
}
- if (!QUEUE_IS_EMPTY(&h8_actq, link)) {
+ if (!list_empty(&h8_actq)) {
Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
restore_flags(flags);
return;
}
- if (QUEUE_IS_EMPTY(&h8_cmdq, link)) {
+ if (list_empty(&h8_cmdq)) {
Dprintk("h8_start_new_cmd: no command to dequeue\n");
restore_flags(flags);
return;
@@ -515,9 +514,10 @@ h8_start_new_cmd(void)
* Take first command off of the command queue and put
* it on the active queue.
*/
- qp = (h8_cmd_q_t *) QUEUE_FIRST(&h8_cmdq, link);
- QUEUE_REMOVE(&h8_cmdq, qp, link);
- QUEUE_ENTER(&h8_actq, qp, link, h8_cmd_q_t *);
+ qp = list_entry(h8_cmdq.next, h8_cmd_q_t, link);
+ list_del(&qp->link);
+ /* XXX should this go to the end of the active queue? */
+ list_add(&qp->link, &h8_actq);
h8_state = H8_XMIT;
if (h8_debug & 0x1)
Dprintk("h8_start_new_cmd: Starting a command\n");
@@ -532,7 +532,7 @@ h8_start_new_cmd(void)
void
h8_send_next_cmd_byte(void)
{
- h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link);
+ h8_cmd_q_t *qp = list_entry(h8_actq.next, h8_cmd_q_t, link);
int cnt;
cnt = qp->cnt;
@@ -689,7 +689,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
if (h8_debug & 0x40000)
printk("H8: Sync command done - byte returned was 0x%x\n",
qp->rcvbuf[0]);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_SN:
@@ -697,7 +697,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n",
qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2],
qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_HW_VER:
@@ -705,13 +705,13 @@ h8_cmd_done(h8_cmd_q_t *qp)
case H8_RD_MAX_TEMP:
printk("H8: Max recorded CPU temp %d, Sys temp %d\n",
qp->rcvbuf[0], qp->rcvbuf[1]);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_MIN_TEMP:
printk("H8: Min recorded CPU temp %d, Sys temp %d\n",
qp->rcvbuf[0], qp->rcvbuf[1]);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_CURR_TEMP:
@@ -719,7 +719,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
xx.byte[0] = qp->rcvbuf[0];
xx.byte[1] = qp->rcvbuf[1];
wake_up(&h8_sync_wait);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_SYS_VARIENT:
@@ -740,7 +740,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
xx.byte[0] = qp->rcvbuf[1];
h8_sync_channel |= H8_GET_EXT_STATUS;
wake_up(&h8_sync_wait);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_USER_CFG:
@@ -755,7 +755,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
case H8_RD_INT_BATT_STATUS:
printk("H8: Read int batt status cmd done - returned was %x %x %x\n",
qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]);
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_RD_EXT_BATT_STATUS:
@@ -767,7 +767,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
printk("H8: Device control cmd done - byte returned was 0x%x\n",
qp->rcvbuf[0]);
}
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_CTL_TFT_BRT_DC:
@@ -788,7 +788,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n",
qp->rcvbuf[0]);
}
- QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *);
+ list_add(&qp->link, &h8_freeq);
break;
case H8_CTL_LOWER_TEMP:
diff --git a/drivers/char/h8.h b/drivers/char/h8.h
index b59aadea8..986eef591 100644
--- a/drivers/char/h8.h
+++ b/drivers/char/h8.h
@@ -229,7 +229,7 @@ struct h8_data {
* H8 command buffers
*/
typedef struct h8_cmd_q {
- DLNODE(struct h8_cmd_q) link; /* double linked list */
+ struct list_head link; /* double linked list */
int ncmd; /* number of bytes in command */
int nrsp; /* number of bytes in response */
int cnt; /* number of bytes sent/received */
@@ -238,10 +238,6 @@ typedef struct h8_cmd_q {
u_char rcvbuf[H8_MAX_CMD_SIZE]; /* buffer to store response */
} h8_cmd_q_t;
-typedef struct __queue_head {
- DLNODE(struct h8_cmd_q) link;
-} queue_head_t;
-
union intr_buf {
u_char byte[2];
u_int word;
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index bb00a32fa..fcf0644ff 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -783,13 +783,13 @@ int __init lp_init (void)
return -EIO;
}
+ devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL);
+
if (parport_register_driver (&lp_driver)) {
printk ("lp: unable to register with parport\n");
return -EIO;
}
- devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL);
-
if (!lp_count) {
printk (KERN_INFO "lp: driver loaded but no devices found\n");
#ifndef CONFIG_PARPORT_1284
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index e70860ea9..824ef94e4 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -5,6 +5,7 @@
*
* Added devfs support.
* Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
+ * Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
*/
#include <linux/config.h>
@@ -285,8 +286,7 @@ static ssize_t write_kmem(struct file * file, const char * buf,
return do_write_mem(file, (void*)p, p, buf, count, ppos);
}
-#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \
- defined(CONFIG_HAVE_IO_PORTS)
+#if (!defined(__mc68000__) && !defined(__mips__)) || defined(CONFIG_HAVE_IO_PORTS)
static ssize_t read_port(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
@@ -435,7 +435,7 @@ out:
static int mmap_zero(struct file * file, struct vm_area_struct * vma)
{
if (vma->vm_flags & VM_SHARED)
- return -EINVAL;
+ return map_zero_setup(vma);
if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
@@ -515,8 +515,7 @@ static struct file_operations null_fops = {
write: write_null,
};
-#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \
- defined(CONFIG_HAVE_IO_PORTS)
+#if (!defined(__mc68000__) && !defined(__mips__)) || defined(CONFIG_HAVE_IO_PORTS)
static struct file_operations port_fops = {
llseek: memory_lseek,
read: read_port,
@@ -550,8 +549,7 @@ static int memory_open(struct inode * inode, struct file * filp)
case 3:
filp->f_op = &null_fops;
break;
-#if (!defined(CONFIG_PPC) && !defined(__mc68000__) && !defined(__mips__)) || \
- defined(CONFIG_HAVE_IO_PORTS)
+#if (!defined(__mc68000__) && !defined(__mips__)) || defined(CONFIG_HAVE_IO_PORTS)
case 4:
filp->f_op = &port_fops;
break;
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
new file mode 100644
index 000000000..7c9bafc3b
--- /dev/null
+++ b/drivers/char/nwbutton.c
@@ -0,0 +1,276 @@
+/*
+ * NetWinder Button Driver-
+ * Copyright (C) Alex Holden <alex@linuxhacker.org> 1998, 1999.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#define __NWBUTTON_C /* Tell the header file who we are */
+#include "nwbutton.h"
+
+static int button_press_count = 0; /* The count of button presses */
+static struct timer_list button_timer; /* Times for the end of a sequence */
+static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue); /* Used for blocking read */
+static char button_output_buffer[32]; /* Stores data to write out of device */
+static int bcount = 0; /* The number of bytes in the buffer */
+static int bdelay = BUTTON_DELAY; /* The delay, in jiffies */
+static struct button_callback button_callback_list[32]; /* The callback list */
+static int callback_count = 0; /* The number of callbacks registered */
+static int reboot_count = NUM_PRESSES_REBOOT; /* Number of presses to reboot */
+
+/*
+ * This function is called by other drivers to register a callback function
+ * to be called when a particular number of button presses occurs.
+ * The callback list is a static array of 32 entries (I somehow doubt many
+ * people are ever going to want to register more than 32 different actions
+ * to be performed by the kernel on different numbers of button presses ;).
+ * However, if an attempt to register a 33rd entry (perhaps a stuck loop
+ * somewhere registering the same entry over and over?) it will fail to
+ * do so and return -ENOMEM. If an attempt is made to register a null pointer,
+ * it will fail to do so and return -EINVAL.
+ * Because callbacks can be unregistered at random the list can become
+ * fragmented, so we need to search through the list until we find the first
+ * free entry.
+ */
+
+int button_add_callback (void (*callback) (void), int count)
+{
+ int lp = 0;
+ if (callback_count == 32) {
+ return -ENOMEM;
+ }
+ if (!callback) {
+ return -EINVAL;
+ }
+ callback_count++;
+ for (; (button_callback_list [lp].callback); lp++);
+ button_callback_list [lp].callback = callback;
+ button_callback_list [lp].count = count;
+ return 0;
+}
+
+/*
+ * This function is called by other drivers to deregister a callback function.
+ * If you attempt to unregister a callback which does not exist, it will fail
+ * with -EINVAL. If there is more than one entry with the same address,
+ * because it searches the list from end to beginning, it will unregister the
+ * last one to be registered first (FILO- First In Last Out).
+ * Note that this is not neccessarily true if the entries are not submitted
+ * at the same time, because another driver could have unregistered a callback
+ * between the submissions creating a gap earlier in the list, which would
+ * be filled first at submission time.
+ */
+
+int button_del_callback (void (*callback) (void))
+{
+ int lp = 31;
+ if (!callback) {
+ return -EINVAL;
+ }
+ while (lp >= 0) {
+ if ((button_callback_list [lp].callback) == callback) {
+ button_callback_list [lp].callback = NULL;
+ button_callback_list [lp].count = 0;
+ callback_count--;
+ return 0;
+ };
+ lp--;
+ };
+ return -EINVAL;
+}
+
+/*
+ * This function is called by button_sequence_finished to search through the
+ * list of callback functions, and call any of them whose count argument
+ * matches the current count of button presses. It starts at the beginning
+ * of the list and works up to the end. It will refuse to follow a null
+ * pointer (which should never happen anyway).
+ */
+
+static void button_consume_callbacks (int bpcount)
+{
+ int lp = 0;
+ for (; lp <= 31; lp++) {
+ if ((button_callback_list [lp].count) == bpcount) {
+ if (button_callback_list [lp].callback) {
+ button_callback_list[lp].callback();
+ }
+ }
+ }
+}
+
+/*
+ * This function is called when the button_timer times out.
+ * ie. When you don't press the button for bdelay jiffies, this is taken to
+ * mean you have ended the sequence of key presses, and this function is
+ * called to wind things up (write the press_count out to /dev/button, call
+ * any matching registered function callbacks, initiate reboot, etc.).
+ */
+
+static void button_sequence_finished (unsigned long parameters)
+{
+#ifdef CONFIG_NWBUTTON_REBOOT /* Reboot using button is enabled */
+ if (button_press_count == reboot_count) {
+ kill_proc (1, SIGINT, 1); /* Ask init to reboot us */
+ }
+#endif /* CONFIG_NWBUTTON_REBOOT */
+ button_consume_callbacks (button_press_count);
+ bcount = sprintf (button_output_buffer, "%d\n", button_press_count);
+ button_press_count = 0; /* Reset the button press counter */
+ wake_up_interruptible (&button_wait_queue);
+}
+
+/*
+ * This handler is called when the orange button is pressed (GPIO 10 of the
+ * SuperIO chip, which maps to logical IRQ 26). If the press_count is 0,
+ * this is the first press, so it starts a timer and increments the counter.
+ * If it is higher than 0, it deletes the old timer, starts a new one, and
+ * increments the counter.
+ */
+
+static void button_handler (int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (button_press_count) {
+ del_timer (&button_timer);
+ }
+ button_press_count++;
+ init_timer (&button_timer);
+ button_timer.function = button_sequence_finished;
+ button_timer.expires = (jiffies + bdelay);
+ add_timer (&button_timer);
+}
+
+/*
+ * This function is called when a user space program attempts to read
+ * /dev/nwbutton. It puts the device to sleep on the wait queue until
+ * button_sequence_finished writes some data to the buffer and flushes
+ * the queue, at which point it writes the data out to the device and
+ * returns the number of characters it has written. This function is
+ * reentrant, so that many processes can be attempting to read from the
+ * device at any one time.
+ */
+
+static int button_read (struct file *filp, char *buffer,
+ size_t count, loff_t *ppos)
+{
+ interruptible_sleep_on (&button_wait_queue);
+ return (copy_to_user (buffer, &button_output_buffer, bcount))
+ ? -EFAULT : bcount;
+}
+
+/*
+ * This function is called when a user space process attempts to open the
+ * device. If the driver is compiled into the kernel it does nothing but
+ * succeed, but if it is compiled in as a module it also increments the
+ * module usage count to prevent the module from being removed whilst a
+ * process has the device open.
+ */
+
+static int button_open (struct inode *inode, struct file *filp)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * This function is called when a user space process attempts to close the
+ * device. If the driver is compiled into the kernel it does nothing at all,
+ * but if it is compiled in as a module it also decrements the module usage
+ * count so that it will be possible to unload the module again once all the
+ * user processes have closed the device.
+ */
+
+static int button_release (struct inode *inode, struct file *filp)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * This structure is the file operations structure, which specifies what
+ * callbacks functions the kernel should call when a user mode process
+ * attempts to perform these operations on the device.
+ */
+
+static struct file_operations button_fops = {
+ NULL, /* lseek */
+ button_read,
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ button_open,
+ NULL, /* flush */
+ button_release,
+};
+
+/*
+ * This structure is the misc device structure, which specifies the minor
+ * device number (158 in this case), the name of the device (for /proc/misc),
+ * and the address of the above file operations structure.
+ */
+
+static struct miscdevice button_misc_device = {
+ BUTTON_MINOR,
+ "nwbutton",
+ &button_fops,
+};
+
+/*
+ * This function is called to initialise the driver, either from misc.c at
+ * bootup if the driver is compiled into the kernel, or from init_module
+ * below at module insert time. It attempts to register the device node
+ * and the IRQ and fails with a warning message if either fails, though
+ * neither ever should because the device number and IRQ are unique to
+ * this driver.
+ */
+
+static int __init nwbutton_init(void)
+{
+ if (!machine_is_netwinder())
+ return -ENODEV;
+
+ printk (KERN_INFO "NetWinder Button Driver Version %s (C) Alex Holden "
+ "<alex@linuxhacker.org> 1998.\n", VERSION);
+
+ if (misc_register (&button_misc_device)) {
+ printk (KERN_WARNING "nwbutton: Couldn't register device 10, "
+ "%d.\n", BUTTON_MINOR);
+ return -EBUSY;
+ }
+
+ if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, SA_INTERRUPT,
+ "nwbutton", NULL)) {
+ printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n",
+ IRQ_NETWINDER_BUTTON);
+ misc_deregister (&button_misc_device);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void __exit nwbutton_exit (void)
+{
+ free_irq (IRQ_NETWINDER_BUTTON, NULL);
+ misc_deregister (&button_misc_device);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(nwbutton_init);
+module_exit(nwbutton_exit);
diff --git a/drivers/char/nwbutton.h b/drivers/char/nwbutton.h
new file mode 100644
index 000000000..ba5067443
--- /dev/null
+++ b/drivers/char/nwbutton.h
@@ -0,0 +1,48 @@
+#ifndef __NWBUTTON_H
+#define __NWBUTTON_H
+
+/*
+ * NetWinder Button Driver-
+ * Copyright (C) Alex Holden <alex@linuxhacker.org> 1998, 1999.
+ */
+
+#ifdef __NWBUTTON_C /* Actually compiling the driver itself */
+
+/* Various defines: */
+
+#define NUM_PRESSES_REBOOT 2 /* How many presses to activate shutdown */
+#define BUTTON_DELAY 30 /* How many jiffies for sequence to end */
+#define VERSION "0.3" /* Driver version number */
+#define BUTTON_MINOR 158 /* Major 10, Minor 158, /dev/nwbutton */
+
+/* Structure definitions: */
+
+struct button_callback {
+ void (*callback) (void);
+ int count;
+};
+
+/* Function prototypes: */
+
+static void button_sequence_finished (unsigned long parameters);
+static void button_handler (int irq, void *dev_id, struct pt_regs *regs);
+static int button_read (struct file *filp, char *buffer,
+ size_t count, loff_t *ppos);
+static int button_open (struct inode *inode, struct file *filp);
+static int button_release (struct inode *inode, struct file *filp);
+int button_init (void);
+int button_add_callback (void (*callback) (void), int count);
+int button_del_callback (void (*callback) (void));
+static void button_consume_callbacks (int bpcount);
+#ifdef MODULE
+int init_module (void);
+void cleanup_module (void);
+#endif /* MODULE */
+
+#else /* Not compiling the driver itself */
+
+extern int button_add_callback (void (*callback) (void), int count);
+extern int button_del_callback (void (*callback) (void));
+
+#endif /* __NWBUTTON_C */
+#endif /* __NWBUTTON_H */
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
new file mode 100644
index 000000000..a905be058
--- /dev/null
+++ b/drivers/char/nwflash.c
@@ -0,0 +1,708 @@
+/*
+ * Flash memory interface rev.5 driver for the Intel
+ * Flash chips used on the NetWinder.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/dec21285.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/*****************************************************************************/
+#include <asm/nwflash.h>
+
+//#define MINIKERNEL 1 //export flash write, erase routines for MiniKernel
+
+#ifndef MINIKERNEL
+#define MSTATIC static
+#else
+#define MSTATIC
+#endif
+
+#define NWFLASH_VERSION "6.2"
+
+MSTATIC void kick_open(void);
+MSTATIC int get_flash_id(void);
+MSTATIC int erase_block(int nBlock);
+MSTATIC int write_block(unsigned long p, const char *buf, int count);
+static int open_flash(struct inode *inodep, struct file *filep);
+static int release_flash(struct inode *inodep, struct file *filep);
+static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cmd, unsigned long arg);
+static ssize_t flash_read(struct file *file, char *buf, size_t count, loff_t * ppos);
+static ssize_t flash_write(struct file *file, const char *buf, size_t count, loff_t * ppos);
+static long long flash_llseek(struct file *file, long long offset, int orig);
+
+#define KFLASH_SIZE 1024*1024 //1 Meg
+#define KFLASH_SIZE4 4*1024*1024 //4 Meg
+#define KFLASH_ID 0x89A6 //Intel flash
+#define KFLASH_ID4 0xB0D4 //Intel flash 4Meg
+
+static int flashdebug = 0; //if set - we will display progress msgs
+
+static int gbWriteEnable = 0;
+static int gbWriteBase64Enable = 0;
+MSTATIC int gbFlashSize = KFLASH_SIZE;
+
+extern spinlock_t gpio_lock;
+
+static struct file_operations flash_fops =
+{
+ flash_llseek, /* llseek */
+ flash_read, /* read */
+ flash_write, /* write */
+ NULL, /* no special readdir */
+ NULL, /* no special select */
+ flash_ioctl,
+ NULL, /* no special mmap */
+ open_flash,
+ NULL, /* no special flush */
+ release_flash,
+ NULL, /* no special fsync */
+ NULL, /* no special fasync */
+ NULL, /* no special check_media_change */
+ NULL /* no special revaldate */
+};
+
+static struct miscdevice flash_miscdev =
+{
+ FLASH_MINOR,
+ "nwflash",
+ &flash_fops
+};
+
+/*
+ * the delay routine - it is often required to let the flash "breeze"...
+ */
+void flash_wait(int timeout)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(timeout);
+}
+
+MSTATIC int get_flash_id(void)
+{
+ volatile unsigned int c1, c2;
+
+ /*
+ * try to get flash chip ID
+ */
+ kick_open();
+ c2 = inb(0x80);
+ *(unsigned char *) (FLASH_BASE + 0x8000) = 0x90;
+ udelay(15);
+ c1 = *(unsigned char *) FLASH_BASE;
+ c2 = inb(0x80);
+
+ /*
+ * on 4 Meg flash the second byte is actually at offset 2...
+ */
+ if (c1 == 0xB0)
+ c2 = *(unsigned char *) (FLASH_BASE + 2);
+ else
+ c2 = *(unsigned char *) (FLASH_BASE + 1);
+
+ c2 += (c1 << 8);
+
+ /*
+ * set it back to read mode
+ */
+ *(unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
+
+ if (c2 == KFLASH_ID4)
+ gbFlashSize = KFLASH_SIZE4;
+
+ return c2;
+}
+
+static int open_flash(struct inode *inodep, struct file *filep)
+{
+ int id;
+
+ id = get_flash_id();
+ if ((id != KFLASH_ID) && (id != KFLASH_ID4)) {
+ printk("Flash: incorrect ID 0x%04X.\n", id);
+ return -ENXIO;
+ }
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int release_flash(struct inode *inodep, struct file *filep)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cmd, unsigned long arg)
+{
+// printk("Flash_ioctl: cmd = 0x%X.\n",cmd);
+
+ switch (cmd) {
+ case CMD_WRITE_DISABLE:
+ gbWriteBase64Enable = 0;
+ gbWriteEnable = 0;
+ break;
+
+ case CMD_WRITE_ENABLE:
+ gbWriteEnable = 1;
+ break;
+
+ case CMD_WRITE_BASE64K_ENABLE:
+ gbWriteBase64Enable = 1;
+ break;
+
+ default:
+ gbWriteBase64Enable = 0;
+ gbWriteEnable = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+static ssize_t flash_read(struct file *file, char *buf, size_t count, loff_t * ppos)
+{
+ unsigned long p = file->f_pos;
+ int read;
+
+ if (flashdebug)
+ printk("Flash_dev: flash_read: offset=0x%X, buffer=0x%X, count=0x%X.\n",
+ (unsigned int) p, (unsigned int) buf, count);
+
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (count > gbFlashSize - p)
+ count = gbFlashSize - p;
+
+ /*
+ * flash virtual address
+ */
+ p += FLASH_BASE;
+
+ read = 0;
+
+ if (copy_to_user(buf, (void *) p, count))
+ return -EFAULT;
+ read += count;
+ file->f_pos += read;
+ return read;
+}
+
+static ssize_t flash_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+ unsigned long p = file->f_pos;
+ int written;
+ int nBlock, temp, rc;
+ int i, j;
+
+
+ if (flashdebug)
+ printk("Flash_dev: flash_write: offset=0x%X, buffer=0x%X, count=0x%X.\n",
+ (unsigned int) p, (unsigned int) buf, count);
+
+ if (!gbWriteEnable)
+ return -EINVAL;
+
+ if (p < 64 * 1024 && (!gbWriteBase64Enable))
+ return -EINVAL;
+
+ if (count < 0)
+ return -EINVAL;
+
+ /*
+ * if write size to big - error!
+ */
+ if (count > gbFlashSize - p)
+ return -EINVAL;
+
+
+ if (verify_area(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+
+ written = 0;
+
+ leds_event(led_claim);
+ leds_event(led_green_on);
+
+ nBlock = (int) p >> 16; //block # of 64K bytes
+
+ /*
+ * # of 64K blocks to erase and write
+ */
+ temp = ((int) (p + count) >> 16) - nBlock + 1;
+
+ /*
+ * write ends at exactly 64k boundry?
+ */
+ if (((int) (p + count) & 0xFFFF) == 0)
+ temp -= 1;
+
+ if (flashdebug)
+ printk("FlashWrite: writing %d block(s) starting at %d.\n", temp, nBlock);
+
+ for (; temp; temp--, nBlock++) {
+ if (flashdebug)
+ printk("FlashWrite: erasing block %d.\n", nBlock);
+
+ /*
+ * first we have to erase the block(s), where we will write...
+ */
+ i = 0;
+ j = 0;
+ RetryBlock:
+ do {
+ rc = erase_block(nBlock);
+ i++;
+ } while (rc && i < 10);
+
+ if (rc) {
+ if (flashdebug)
+ printk("FlashWrite: erase error %X. Aborting...\n", rc);
+
+ break;
+ }
+ if (flashdebug)
+ printk("FlashWrite: writing offset %X, from buf %X, bytes left %X.\n",
+ (unsigned int) p, (unsigned int) buf, count - written);
+
+ /*
+ * write_block will limit write to space left in this block
+ */
+ rc = write_block(p, buf, count - written);
+ j++;
+
+ /*
+ * if somehow write verify failed? Can't happen??
+ */
+ if (!rc) {
+ /*
+ * retry up to 10 times
+ */
+ if (j < 10)
+ goto RetryBlock;
+ else
+ /*
+ * else quit with error...
+ */
+ rc = -1;
+
+ }
+ if (rc < 0) {
+ if (flashdebug)
+ printk("FlashWrite: write error %X. Aborting...\n", rc);
+ break;
+ }
+ p += rc;
+ buf += rc;
+ written += rc;
+ file->f_pos += rc;
+
+ if (flashdebug)
+ printk("FlashWrite: written 0x%X bytes OK.\n", written);
+ }
+
+ /*
+ * restore reg on exit
+ */
+ leds_event(led_release);
+
+ return written;
+}
+
+
+/*
+ * The memory devices use the full 32/64 bits of the offset, and so we cannot
+ * check against negative addresses: they are ok. The return value is weird,
+ * though, in that case (0).
+ *
+ * also note that seeking relative to the "end of file" isn't supported:
+ * it has no meaning, so it returns -EINVAL.
+ */
+static long long flash_llseek(struct file *file, long long offset, int orig)
+{
+ if (flashdebug)
+ printk("Flash_dev: flash_lseek, offset=0x%X, orig=0x%X.\n",
+ (unsigned int) offset, (unsigned int) orig);
+
+ switch (orig) {
+ case 0:
+ if (offset < 0)
+ return -EINVAL;
+
+ if ((unsigned int) offset > gbFlashSize)
+ return -EINVAL;
+
+ file->f_pos = (unsigned int) offset;
+ return file->f_pos;
+ case 1:
+ if ((file->f_pos + offset) > gbFlashSize)
+ return -EINVAL;
+ if ((file->f_pos + offset) < 0)
+ return -EINVAL;
+ file->f_pos += offset;
+ return file->f_pos;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * assume that main Write routine did the parameter checking...
+ * so just go ahead and erase, what requested!
+ */
+
+MSTATIC int erase_block(int nBlock)
+{
+ volatile unsigned int c1;
+ volatile unsigned char *pWritePtr;
+ int temp, temp1;
+
+ /*
+ * orange LED == erase
+ */
+ leds_event(led_amber_on);
+
+ /*
+ * reset footbridge to the correct offset 0 (...0..3)
+ */
+ *CSR_ROMWRITEREG = 0;
+
+ /*
+ * dummy ROM read
+ */
+ c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
+
+ kick_open();
+ /*
+ * reset status if old errors
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
+
+ /*
+ * erase a block...
+ * aim at the middle of a current block...
+ */
+ pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + 0x8000 + (nBlock << 16)));
+ /*
+ * dummy read
+ */
+ c1 = *pWritePtr;
+
+ kick_open();
+ /*
+ * erase
+ */
+ *(volatile unsigned char *) pWritePtr = 0x20;
+
+ /*
+ * confirm
+ */
+ *(volatile unsigned char *) pWritePtr = 0xD0;
+
+ /*
+ * wait 10 ms
+ */
+ flash_wait(HZ / 100);
+
+ /*
+ * wait while erasing in process (up to 10 sec)
+ */
+ temp = jiffies + 10 * HZ;
+ c1 = 0;
+ while (!(c1 & 0x80) && time_before(jiffies, temp)) {
+ flash_wait(HZ / 100);
+ /*
+ * read any address
+ */
+ c1 = *(volatile unsigned char *) (pWritePtr);
+ // printk("Flash_erase: status=%X.\n",c1);
+ }
+
+ /*
+ * set flash for normal read access
+ */
+ kick_open();
+// *(volatile unsigned char*)(FLASH_BASE+0x8000) = 0xFF;
+ *(volatile unsigned char *) pWritePtr = 0xFF; //back to normal operation
+
+ /*
+ * check if erase errors were reported
+ */
+ if (c1 & 0x20) {
+ if (flashdebug)
+ printk("Flash_erase: err at %X.\n", (unsigned int) pWritePtr);
+ /*
+ * reset error
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
+
+ return -2;
+ }
+
+ /*
+ * just to make sure - verify if erased OK...
+ */
+ flash_wait(HZ / 100);
+
+ pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + (nBlock << 16)));
+
+ for (temp = 0; temp < 16 * 1024; temp++, pWritePtr += 4) {
+ if ((temp1 = *(volatile unsigned int *) pWritePtr) != 0xFFFFFFFF) {
+ if (flashdebug)
+ printk("Flash_erase: verify err at %X = %X.\n",
+ (unsigned int) pWritePtr, temp1);
+ return -1;
+ }
+ }
+
+ return 0;
+
+}
+
+/*
+ * write_block will limit number of bytes written to the space in this block
+ */
+MSTATIC int write_block(unsigned long p, const char *buf, int count)
+{
+ volatile unsigned int c1;
+ volatile unsigned int c2;
+ unsigned char *pWritePtr;
+ unsigned int uAddress;
+ unsigned int offset;
+ unsigned int timeout;
+ unsigned int timeout1;
+
+ /*
+ * red LED == write
+ */
+ leds_event(led_amber_off);
+ leds_event(led_red_on);
+
+ pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
+
+ /*
+ * check if write will end in this block....
+ */
+ offset = p & 0xFFFF;
+
+ if (offset + count > 0x10000)
+ count = 0x10000 - offset;
+
+ /*
+ * wait up to 30 sec for this block
+ */
+ timeout = jiffies + 30 * HZ;
+
+ for (offset = 0; offset < count; offset++, pWritePtr++) {
+ uAddress = (unsigned int) pWritePtr;
+ uAddress &= 0xFFFFFFFC;
+ if (__get_user(c2, buf + offset))
+ return -EFAULT;
+
+ WriteRetry:
+ /*
+ * dummy read
+ */
+ c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
+
+ /*
+ * kick open the write gate
+ */
+ kick_open();
+
+ /*
+ * program footbridge to the correct offset...0..3
+ */
+ *CSR_ROMWRITEREG = (unsigned int) pWritePtr & 3;
+
+ /*
+ * write cmd
+ */
+ *(volatile unsigned char *) (uAddress) = 0x40;
+
+ /*
+ * data to write
+ */
+ *(volatile unsigned char *) (uAddress) = c2;
+
+ /*
+ * get status
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x10000) = 0x70;
+
+ c1 = 0;
+
+ /*
+ * wait up to 1 sec for this byte
+ */
+ timeout1 = jiffies + 1 * HZ;
+
+ /*
+ * while not ready...
+ */
+ while (!(c1 & 0x80) && time_before(jiffies, timeout1))
+ c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
+
+ /*
+ * if timeout getting status
+ */
+ if (time_after_eq(jiffies, timeout1)) {
+ kick_open();
+ /*
+ * reset err
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
+
+ goto WriteRetry;
+ }
+ /*
+ * switch on read access, as a default flash operation mode
+ */
+ kick_open();
+ /*
+ * read access
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
+
+ /*
+ * if hardware reports an error writing, and not timeout -
+ * reset the chip and retry
+ */
+ if (c1 & 0x10) {
+ kick_open();
+ /*
+ * reset err
+ */
+ *(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
+
+ /*
+ * before timeout?
+ */
+ if (time_before(jiffies, timeout)) {
+ if (flashdebug)
+ printk("FlashWrite: Retrying write (addr=0x%X)...\n",
+ (unsigned int) pWritePtr - FLASH_BASE);
+
+ /*
+ * no LED == waiting
+ */
+ leds_event(led_amber_off);
+ /*
+ * wait couple ms
+ */
+ flash_wait(HZ / 100);
+ /*
+ * red LED == write
+ */
+ leds_event(led_red_on);
+
+ goto WriteRetry;
+ } else {
+ printk("Timeout in flash write! (addr=0x%X) Aborting...\n",
+ (unsigned int) pWritePtr - FLASH_BASE);
+ /*
+ * return error -2
+ */
+ return -2;
+
+ }
+ }
+ }
+
+ /*
+ * green LED == read/verify
+ */
+ leds_event(led_amber_off);
+ leds_event(led_green_on);
+
+ flash_wait(HZ / 100);
+
+ pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
+
+ for (offset = 0; offset < count; offset++) {
+ char c, c1;
+ if (__get_user(c, buf))
+ return -EFAULT;
+ buf++;
+ if ((c1 = *pWritePtr++) != c) {
+ if (flashdebug)
+ printk("flash write verify error at 0x%X! (%02X!=%02X) Retrying...\n",
+ (unsigned int) pWritePtr, c1, c);
+ return 0;
+ }
+ }
+
+ return count;
+}
+
+
+MSTATIC void kick_open(void)
+{
+ unsigned long flags;
+
+ /*
+ * we want to write a bit pattern XXX1 to Xilinx to enable
+ * the write gate, which will be open for about the next 2ms.
+ */
+ spin_lock_irqsave(&gpio_lock, flags);
+ cpld_modify(1, 1);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ /*
+ * let the ISA bus to catch on...
+ */
+ udelay(25);
+}
+
+MSTATIC int __init nwflash_init(void)
+{
+ int ret = -ENODEV;
+
+ if (machine_is_netwinder()) {
+ int id;
+
+ id = get_flash_id();
+ printk("Flash ROM driver v.%s, flash device ID 0x%04X, size %d Mb.\n",
+ NWFLASH_VERSION, id, gbFlashSize / (1024 * 1024));
+
+ misc_register(&flash_miscdev);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+MSTATIC void __exit nwflash_exit(void)
+{
+ misc_deregister(&flash_miscdev);
+}
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_PARM(flashdebug, "i");
+
+module_init(nwflash_init);
+module_exit(nwflash_exit);
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 6ffbb517c..61dfd6e42 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -197,7 +197,12 @@ int raw_ctl_ioctl(struct inode *inode,
raw_device_bindings[minor] =
bdget(kdev_t_to_nr(MKDEV(rq.block_major, rq.block_minor)));
} else {
- kdev_t dev=to_kdev_t(raw_device_bindings[minor]->bd_dev);
+ kdev_t dev;
+ if (!raw_device_bindings[minor]) {
+ err = -ENODEV;
+ break;
+ }
+ dev = to_kdev_t(raw_device_bindings[minor]->bd_dev);
rq.block_major = MAJOR(dev);
rq.block_minor = MINOR(dev);
err = copy_to_user((void *) arg, &rq, sizeof(rq));
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 6296e789b..f68c65afe 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -36,9 +36,11 @@
* 1.09b Jeff Garzik: Modularize, init cleanup
* 1.09c Jeff Garzik: SMP cleanup
* 1.10 Paul Barton-Davis: add support for async I/O
+ * 1.10a Andrea Arcangeli: Alpha updates
+ * 1.10b Andrew Morton: SMP lock fix
*/
-#define RTC_VERSION "1.10"
+#define RTC_VERSION "1.10b"
#define RTC_IRQ 8 /* Can't see this changing soon. */
#define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */
@@ -378,7 +380,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
|| RTC_ALWAYS_BCD) {
if (yrs > 169) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&rtc_lock, flags);
return -EINVAL;
}
if (yrs >= 100)
@@ -706,7 +708,9 @@ static void __exit rtc_exit (void)
free_irq (rtc_irq, &rtc_port);
#else
release_region (RTC_PORT (0), RTC_IO_EXTENT);
+#ifndef __alpha__
free_irq (RTC_IRQ, NULL);
+#endif
#endif /* __sparc__ */
}
diff --git a/drivers/char/wdt285.c b/drivers/char/wdt285.c
new file mode 100644
index 000000000..b5dc94e2f
--- /dev/null
+++ b/drivers/char/wdt285.c
@@ -0,0 +1,204 @@
+/*
+ * Intel 21285 watchdog driver
+ * Copyright (c) Phil Blundell <pb@nexus.co.uk>, 1998
+ *
+ * based on
+ *
+ * SoftDog 0.05: A Software Watchdog Device
+ *
+ * (c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ * http://www.cymru.net
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/system.h>
+#include <asm/dec21285.h>
+
+/*
+ * Define this to stop the watchdog actually rebooting the machine.
+ */
+#undef ONLY_TESTING
+
+#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */
+
+#define FCLK (50*1000*1000) /* 50MHz */
+
+static int soft_margin = TIMER_MARGIN; /* in seconds */
+static int timer_alive = 0;
+
+#ifdef ONLY_TESTING
+/*
+ * If the timer expires..
+ */
+
+static void watchdog_fire(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_CRIT "Watchdog: Would Reboot.\n");
+ *CSR_TIMER4_CNTL = 0;
+ *CSR_TIMER4_CLR = 0;
+}
+#endif
+
+static void watchdog_ping(void)
+{
+ /*
+ * Refresh the timer.
+ */
+ *CSR_TIMER4_LOAD = soft_margin * (FCLK / 256);
+}
+
+/*
+ * Allow only one person to hold it open
+ */
+
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+ if(timer_alive)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ /*
+ * Ahead watchdog factor ten, Mr Sulu
+ */
+ *CSR_TIMER4_CLR = 0;
+ watchdog_ping();
+ *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD
+ | TIMER_CNTL_DIV256;
+#ifdef ONLY_TESTING
+ request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL);
+#else
+ *CSR_SA110_CNTL |= 1 << 13;
+#endif
+ timer_alive = 1;
+ return 0;
+}
+
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+#ifdef ONLY_TESTING
+ free_irq(IRQ_TIMER4, NULL);
+ timer_alive = 0;
+ MOD_DEC_USE_COUNT;
+#else
+ /*
+ * It's irreversible!
+ */
+#endif
+ return 0;
+}
+
+static ssize_t watchdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /*
+ * Refresh the timer.
+ */
+ if(len)
+ {
+ watchdog_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static int watchdog_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int i;
+ static struct watchdog_info ident=
+ {
+ 0,
+ 0,
+ "Footbridge Watchdog"
+ };
+ switch(cmd)
+ {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info));
+ if (i)
+ return i;
+ else
+ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0,(int *)arg);
+ case WDIOC_KEEPALIVE:
+ watchdog_ping();
+ return 0;
+ }
+}
+
+static struct file_operations watchdog_fops=
+{
+ NULL, /* Seek */
+ NULL, /* Read */
+ watchdog_write, /* Write */
+ NULL, /* Readdir */
+ NULL, /* Select */
+ watchdog_ioctl, /* Ioctl */
+ NULL, /* MMap */
+ watchdog_open,
+ NULL, /* flush */
+ watchdog_release,
+ NULL,
+ NULL /* Fasync */
+};
+
+static struct miscdevice watchdog_miscdev=
+{
+ WATCHDOG_MINOR,
+ "watchdog",
+ &watchdog_fops
+};
+
+static int __init footbridge_watchdog_init(void)
+{
+ if (machine_is_netwinder())
+ return -ENODEV;
+
+ misc_register(&watchdog_miscdev);
+ printk("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
+ soft_margin);
+ if (machine_is_cats())
+ printk("Warning: Watchdog reset may not work on this machine.\n");
+ return 0;
+}
+
+static void __exit footbridge_watchdog_exit(void)
+{
+ misc_deregister(&watchdog_miscdev);
+}
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
+MODULE_DESCRIPTION("21285 watchdog driver");
+
+MODULE_PARM(soft_margin,"i");
+MODULE_PARM_DESC(soft_margin,"Watchdog timeout in seconds");
+
+module_init(footbridge_watchdog_init);
+module_exit(footbridge_watchdog_exit);
diff --git a/drivers/char/wdt977.c b/drivers/char/wdt977.c
new file mode 100644
index 000000000..c45e63d60
--- /dev/null
+++ b/drivers/char/wdt977.c
@@ -0,0 +1,204 @@
+/*
+ * Wdt977 0.01: A Watchdog Device for Netwinder W83977AF chip
+ *
+ * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
+ *
+ * -----------------------
+ *
+ * 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.
+ *
+ * -----------------------
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define WATCHDOG_MINOR 130
+
+static int timeout = 3;
+static int timer_alive = 0;
+static int testmode = 0;
+
+/*
+ * Allow only one person to hold it open
+ */
+
+static int wdt977_open(struct inode *inode, struct file *file)
+{
+ if(timer_alive)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ timer_alive++;
+
+ //max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog.
+ if (timeout>255)
+ timeout = 255;
+
+ printk(KERN_INFO "Watchdog: active, current timeout %d min.\n",timeout);
+
+ // unlock the SuperIO chip
+ outb(0x87,0x370);
+ outb(0x87,0x370);
+
+ //select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
+ //F2 has the timeout in minutes
+ //F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
+ // at timeout, and to reset timer on kbd/mouse activity (not now)
+ //F4 is used to just clear the TIMEOUT'ed state (bit 0)
+
+ outb(0x07,0x370);
+ outb(0x08,0x371);
+ outb(0xF2,0x370);
+ outb(timeout,0x371);
+ outb(0xF3,0x370);
+ outb(0x00,0x371); //another setting is 0E for kbd/mouse/LED
+ outb(0xF4,0x370);
+ outb(0x00,0x371);
+
+ //at last select device Aux1 (dev=7) and set GP16 as a watchdog output
+ if (!testmode)
+ {
+ outb(0x07,0x370);
+ outb(0x07,0x371);
+ outb(0xE6,0x370);
+ outb(0x08,0x371);
+ }
+
+ // lock the SuperIO chip
+ outb(0xAA,0x370);
+
+ return 0;
+}
+
+static int wdt977_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we defined ...NOWAYOUT
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+
+ // unlock the SuperIO chip
+ outb(0x87,0x370);
+ outb(0x87,0x370);
+
+ //select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
+ //F3 is reset to its default state
+ //F4 can clear the TIMEOUT'ed state (bit 0) - back to default
+ //We can not use GP17 as a PowerLed, as we use its usage as a RedLed
+
+ outb(0x07,0x370);
+ outb(0x08,0x371);
+ outb(0xF2,0x370);
+ outb(0xFF,0x371);
+ outb(0xF3,0x370);
+ outb(0x00,0x371);
+ outb(0xF4,0x370);
+ outb(0x00,0x371);
+ outb(0xF2,0x370);
+ outb(0x00,0x371);
+
+ //at last select device Aux1 (dev=7) and set GP16 as a watchdog output
+ outb(0x07,0x370);
+ outb(0x07,0x371);
+ outb(0xE6,0x370);
+ outb(0x08,0x371);
+
+ // lock the SuperIO chip
+ outb(0xAA,0x370);
+
+ MOD_DEC_USE_COUNT;
+ timer_alive=0;
+
+ printk(KERN_INFO "Watchdog: shutdown.\n");
+#endif
+ return 0;
+}
+
+static ssize_t wdt977_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+
+ //max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog.
+ if (timeout>255)
+ timeout = 255;
+
+ /*
+ * Refresh the timer.
+ */
+
+ //we have a hw bug somewhere, so each 977 minute is actually only 30sec
+ //as such limit the max timeout to half of max of 255 minutes...
+// if (timeout>126)
+// timeout = 126;
+
+ // unlock the SuperIO chip
+ outb(0x87,0x370);
+ outb(0x87,0x370);
+
+ //select device Aux2 (device=8) and kicks watchdog reg F2
+ //F2 has the timeout in minutes
+
+ outb(0x07,0x370);
+ outb(0x08,0x371);
+ outb(0xF2,0x370);
+ outb(timeout,0x371);
+
+ // lock the SuperIO chip
+ outb(0xAA,0x370);
+
+ return 1;
+}
+
+static struct file_operations wdt977_fops=
+{
+ NULL, /* Seek */
+ NULL, /* Read */
+ wdt977_write, /* Write */
+ NULL, /* Readdir */
+ NULL, /* Select */
+ NULL, /* Ioctl */
+ NULL, /* MMap */
+ wdt977_open,
+ NULL, /* flush */
+ wdt977_release,
+ NULL,
+ NULL /* Fasync */
+};
+
+static struct miscdevice wdt977_miscdev=
+{
+ WATCHDOG_MINOR,
+ "watchdog",
+ &wdt977_fops
+};
+
+static int __init nwwatchdog_init(void)
+{
+ if (!machine_is_netwinder())
+ return -ENODEV;
+
+ misc_register(&wdt977_miscdev);
+ printk(KERN_INFO "NetWinder Watchdog sleeping.\n");
+ return 0;
+}
+
+static void __exit nwwatchdog_exit(void)
+{
+ misc_deregister(&wdt977_miscdev);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(nwwatchdog_init);
+module_exit(nwwatchdog_exit);