/* * Advantech Panel PC 55S Watchdog Timer driver for Linux 2.2.x * Also adds /proc/vga to control the color DSTN display sleep mode. * you can use echo "1" > /proc/vga to enable or 0 to disable the display. * * Based on acquirewdt.c. * Modified by Martin Uhlherr * * It is allowable to write of a timeout value to the WDT. * The value is expected to be a single 8-bit hex digit. * Allowable values are 1 - 60 seconds, which will default to WDT_TIMO * if outside these bounds. * * * * Original copyright messages: * * (c) Copyright 1996 Alan Cox , All Rights Reserved. * http://www.redhat.com * * 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. * * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * * (c) Copyright 1995 Alan Cox * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PROC_FS #include #endif static int ppcwdt_is_open = 0; /* You must set these - there is no sane way to probe for this board. */ #define WDT_STOP 0x43 /* read will reset wdt */ #define WDT_START 0x443 /* write timeout 1-60 */ #define VGA_SELECT 0x03d6 /* select VGA sleep control line */ #define VGA_ATTENTION 0x052 /* value to select VGA register */ #define VGA_SLEEP 0x03d7 /* register that enables sleep */ #define VGA_SLEEP_BIT 0x08 /* bit controlling VGA sleep in register */ #define WD_TIMO 60 /* 1 minute default timeout */ /* * Kernel methods. */ static void ppcwdt_ping(int timeout) { /* Write a watchdog value */ outb_p(timeout, WDT_START); } /* * allowable to write of a timeout value to the WDT. * Value is expected to be a single 8-bit hex digit. * Allowable values are 1 - 60 seconds, will default to WDT_TIMO * if outside these bounds. */ static ssize_t ppcwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos) { /* * Can't seek (pwrite) on this device */ if (ppos != &file->f_pos) return -ESPIPE; if (count != 1) { ppcwdt_ping(WD_TIMO); return 0; } else { unsigned char timeout; copy_from_user(&timeout, buf, count); if (timeout > 60) { timeout = WD_TIMO; } ppcwdt_ping(timeout); return 1; } return 0; } static ssize_t ppcwdt_read(struct file *file, char *buf, size_t count, loff_t * ppos) { return -EINVAL; } static int ppcwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { /* * reported by WDIOC_GETSUPPORT */ static struct watchdog_info ident = { WDIOF_KEEPALIVEPING, 1, "PPC55S WDT" }; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *) arg, &ident, sizeof(ident))) return -EFAULT; break; /* * this is silly, you can only call this when you have a file * descriptor, which means that the watchdog is enabled */ case WDIOC_GETSTATUS: if (copy_to_user((int *) arg, &ppcwdt_is_open, sizeof(int))) return -EFAULT; break; case WDIOC_KEEPALIVE: ppcwdt_ping(WD_TIMO); break; default: return -ENOIOCTLCMD; } return 0; } static int ppcwdt_open(struct inode *inode, struct file *file) { switch (MINOR(inode->i_rdev)) { case WATCHDOG_MINOR: if (ppcwdt_is_open) return -EBUSY; MOD_INC_USE_COUNT; /* * Activate */ ppcwdt_is_open = 1; outb_p(WD_TIMO, WDT_START); /* default to 1 minute */ return 0; default: return -ENODEV; } } static int ppcwdt_close(struct inode *inode, struct file *file) { if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { #ifndef CONFIG_WATCHDOG_NOWAYOUT inb_p(WDT_STOP); #endif ppcwdt_is_open = 0; } MOD_DEC_USE_COUNT; return 0; } /* * Notifier for system down */ static int ppcwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) { /* * Turn the card off */ inb_p(WDT_STOP); } return NOTIFY_DONE; } /* * Kernel Interfaces */ #ifdef CONFIG_PROC_FS #define PRINT_PROC(fmt,args...) \ do \ { \ *len += sprintf( buffer+*len, fmt, ##args ); \ if (*begin + *len > offset + size) \ return( 0 ); \ if (*begin + *len < offset) \ { \ *begin += *len; \ *len = 0; \ } \ } while(0) #endif struct proc_dir_entry *proc_vga; static int vga_proc_infos(char *buffer, int *len, off_t * begin, off_t offset, int size) { unsigned char byte = 0; outb_p(VGA_ATTENTION, VGA_SELECT); /* enable VGA register */ byte = inb_p(VGA_SLEEP) & VGA_SLEEP_BIT; PRINT_PROC("VGA is currently: %s\n", byte ? "OFF" : "ON"); return 1; } static int vga_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data) { int len = 0; off_t begin = 0; *eof = vga_proc_infos(buffer, &len, &begin, offset, size); if (offset >= begin + len) return (0); *start = buffer + (begin - offset); return (size < begin + len - offset ? size : begin + len - offset); } static int vga_write_proc(struct file *file, const char *buffer, unsigned long size, void *data) { char commandBuffer[8]; int length; if (size > sizeof(commandBuffer) - 1) { return -EINVAL; } copy_from_user(commandBuffer, buffer, size); commandBuffer[size] = '\0'; length = strlen(commandBuffer); if (commandBuffer[length - 1] == '\n') { commandBuffer[--length] = '\0'; } if (length != 1) { return -EINVAL; } if (commandBuffer[0] == '0') { /* * disable VGA display */ unsigned char byte = 0; outb_p(VGA_ATTENTION, VGA_SELECT); /* enable VGA register */ byte = inb_p(VGA_SLEEP) | VGA_SLEEP_BIT; outb_p(byte, VGA_SLEEP); } else if (commandBuffer[0] == '1') { /* enable VGA display */ unsigned char byte = 0; outb_p(VGA_ATTENTION, VGA_SELECT); /* enable VGA register */ byte = inb_p(VGA_SLEEP) & ~VGA_SLEEP_BIT; outb_p(byte, VGA_SLEEP); } else { return -EINVAL; } return size; } static struct file_operations ppcwdt_fops = { NULL, ppcwdt_read, ppcwdt_write, NULL, /* No Readdir */ NULL, /* No Select */ ppcwdt_ioctl, NULL, /* No mmap */ ppcwdt_open, NULL, /* flush */ ppcwdt_close }; static struct miscdevice ppcwdt_miscdev = { WATCHDOG_MINOR, "watchdog", &ppcwdt_fops }; /* * The WDT card needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block ppcwdt_notifier = { ppcwdt_notify_sys, NULL, 0 }; #ifdef MODULE #define ppcwdt_init init_module void cleanup_module(void) { #ifdef CONFIG_PROC_FS if (proc_vga) { remove_proc_entry("vga", 0); } #endif misc_deregister(&ppcwdt_miscdev); unregister_reboot_notifier(&ppcwdt_notifier); release_region(WDT_STOP, 1); release_region(WDT_START, 1); } #endif __initfunc(int ppcwdt_init(void)) { printk("WDT driver for PPC55S Panel PC\n"); misc_register(&ppcwdt_miscdev); request_region(WDT_STOP, 1, "PPC55S WDT"); request_region(WDT_START, 1, "PPC55S WDT"); register_reboot_notifier(&ppcwdt_notifier); #ifdef CONFIG_PROC_FS if ((proc_vga = create_proc_entry("vga", 0, 0))) { proc_vga->read_proc = vga_read_proc; proc_vga->write_proc = vga_write_proc; } #endif return 0; }