diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/Config.in | 3 | ||||
-rw-r--r-- | drivers/char/Makefile | 15 | ||||
-rw-r--r-- | drivers/char/agp/agpgart_be.c | 2 | ||||
-rw-r--r-- | drivers/char/ds1620.c | 436 | ||||
-rw-r--r-- | drivers/char/efirtc.c | 2 | ||||
-rw-r--r-- | drivers/char/h8.c | 74 | ||||
-rw-r--r-- | drivers/char/h8.h | 6 | ||||
-rw-r--r-- | drivers/char/lp.c | 4 | ||||
-rw-r--r-- | drivers/char/mem.c | 12 | ||||
-rw-r--r-- | drivers/char/nwbutton.c | 276 | ||||
-rw-r--r-- | drivers/char/nwbutton.h | 48 | ||||
-rw-r--r-- | drivers/char/nwflash.c | 708 | ||||
-rw-r--r-- | drivers/char/raw.c | 7 | ||||
-rw-r--r-- | drivers/char/rtc.c | 8 | ||||
-rw-r--r-- | drivers/char/wdt285.c | 204 | ||||
-rw-r--r-- | drivers/char/wdt977.c | 204 |
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); |