11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * CMOS/NV-RAM driver for Linux 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> 51da177e4SLinus Torvalds * idea by and with help from Richard Jelinek <rj@suse.de> 61da177e4SLinus Torvalds * Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com) 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This driver allows you to access the contents of the non-volatile memory in 91da177e4SLinus Torvalds * the mc146818rtc.h real-time clock. This chip is built into all PCs and into 101da177e4SLinus Torvalds * many Atari machines. In the former it's called "CMOS-RAM", in the latter 111da177e4SLinus Torvalds * "NVRAM" (NV stands for non-volatile). 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * The data are supplied as a (seekable) character device, /dev/nvram. The 141da177e4SLinus Torvalds * size of this file is dependent on the controller. The usual size is 114, 151da177e4SLinus Torvalds * the number of freely available bytes in the memory (i.e., not used by the 161da177e4SLinus Torvalds * RTC itself). 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Checksums over the NVRAM contents are managed by this driver. In case of a 191da177e4SLinus Torvalds * bad checksum, reads and writes return -EIO. The checksum can be initialized 201da177e4SLinus Torvalds * to a sane state either by ioctl(NVRAM_INIT) (clear whole NVRAM) or 211da177e4SLinus Torvalds * ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid 221da177e4SLinus Torvalds * again; use with care!) 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * 1.1 Cesar Barros: SMP locking fixes 251da177e4SLinus Torvalds * added changelog 261da177e4SLinus Torvalds * 1.2 Erik Gilling: Cobalt Networks support 271da177e4SLinus Torvalds * Tim Hockin: general cleanup, Cobalt support 288587b33fSWim Van Sebroeck * 1.3 Wim Van Sebroeck: convert PRINT_PROC to seq_file 291da177e4SLinus Torvalds */ 301da177e4SLinus Torvalds 318587b33fSWim Van Sebroeck #define NVRAM_VERSION "1.3" 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #include <linux/module.h> 341da177e4SLinus Torvalds #include <linux/nvram.h> 351da177e4SLinus Torvalds #include <linux/types.h> 361da177e4SLinus Torvalds #include <linux/errno.h> 371da177e4SLinus Torvalds #include <linux/miscdevice.h> 381da177e4SLinus Torvalds #include <linux/ioport.h> 391da177e4SLinus Torvalds #include <linux/fcntl.h> 401da177e4SLinus Torvalds #include <linux/mc146818rtc.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/proc_fs.h> 438587b33fSWim Van Sebroeck #include <linux/seq_file.h> 441da177e4SLinus Torvalds #include <linux/spinlock.h> 45971ddcf8SWim Van Sebroeck #include <linux/io.h> 46971ddcf8SWim Van Sebroeck #include <linux/uaccess.h> 47613655faSArnd Bergmann #include <linux/mutex.h> 48b808b1d6SAl Viro #include <linux/pagemap.h> 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds 51613655faSArnd Bergmann static DEFINE_MUTEX(nvram_mutex); 521da177e4SLinus Torvalds static DEFINE_SPINLOCK(nvram_state_lock); 531da177e4SLinus Torvalds static int nvram_open_cnt; /* #times opened */ 541da177e4SLinus Torvalds static int nvram_open_mode; /* special open modes */ 551da177e4SLinus Torvalds #define NVRAM_WRITE 1 /* opened for writing (exclusive) */ 561da177e4SLinus Torvalds #define NVRAM_EXCL 2 /* opened with O_EXCL */ 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 59*437ace37SFinn Thain static void pc_nvram_proc_read(unsigned char *contents, struct seq_file *seq, 608587b33fSWim Van Sebroeck void *offset); 611da177e4SLinus Torvalds #endif 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* 641da177e4SLinus Torvalds * These functions are provided to be called internally or by other parts of 651da177e4SLinus Torvalds * the kernel. It's up to the caller to ensure correct checksum before reading 661da177e4SLinus Torvalds * or after writing (needs to be done only once). 671da177e4SLinus Torvalds * 681da177e4SLinus Torvalds * It is worth noting that these functions all access bytes of general 691da177e4SLinus Torvalds * purpose memory in the NVRAM - that is to say, they all add the 701da177e4SLinus Torvalds * NVRAM_FIRST_BYTE offset. Pass them offsets into NVRAM as if you did not 711da177e4SLinus Torvalds * know about the RTC cruft. 721da177e4SLinus Torvalds */ 731da177e4SLinus Torvalds 74*437ace37SFinn Thain #define NVRAM_BYTES (128 - NVRAM_FIRST_BYTE) 75*437ace37SFinn Thain 76*437ace37SFinn Thain /* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with 77*437ace37SFinn Thain * rtc_lock held. Due to the index-port/data-port design of the RTC, we 78*437ace37SFinn Thain * don't want two different things trying to get to it at once. (e.g. the 79*437ace37SFinn Thain * periodic 11 min sync from kernel/time/ntp.c vs. this driver.) 80*437ace37SFinn Thain */ 81*437ace37SFinn Thain 82971ddcf8SWim Van Sebroeck unsigned char __nvram_read_byte(int i) 831da177e4SLinus Torvalds { 841da177e4SLinus Torvalds return CMOS_READ(NVRAM_FIRST_BYTE + i); 851da177e4SLinus Torvalds } 86971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(__nvram_read_byte); 871da177e4SLinus Torvalds 88971ddcf8SWim Van Sebroeck unsigned char nvram_read_byte(int i) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds unsigned long flags; 911da177e4SLinus Torvalds unsigned char c; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds spin_lock_irqsave(&rtc_lock, flags); 941da177e4SLinus Torvalds c = __nvram_read_byte(i); 951da177e4SLinus Torvalds spin_unlock_irqrestore(&rtc_lock, flags); 961da177e4SLinus Torvalds return c; 971da177e4SLinus Torvalds } 98971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(nvram_read_byte); 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds /* This races nicely with trying to read with checksum checking (nvram_read) */ 101971ddcf8SWim Van Sebroeck void __nvram_write_byte(unsigned char c, int i) 1021da177e4SLinus Torvalds { 1031da177e4SLinus Torvalds CMOS_WRITE(c, NVRAM_FIRST_BYTE + i); 1041da177e4SLinus Torvalds } 105971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(__nvram_write_byte); 1061da177e4SLinus Torvalds 107971ddcf8SWim Van Sebroeck void nvram_write_byte(unsigned char c, int i) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds unsigned long flags; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds spin_lock_irqsave(&rtc_lock, flags); 1121da177e4SLinus Torvalds __nvram_write_byte(c, i); 1131da177e4SLinus Torvalds spin_unlock_irqrestore(&rtc_lock, flags); 1141da177e4SLinus Torvalds } 115971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(nvram_write_byte); 1161da177e4SLinus Torvalds 117*437ace37SFinn Thain /* On PCs, the checksum is built only over bytes 2..31 */ 118*437ace37SFinn Thain #define PC_CKS_RANGE_START 2 119*437ace37SFinn Thain #define PC_CKS_RANGE_END 31 120*437ace37SFinn Thain #define PC_CKS_LOC 32 121*437ace37SFinn Thain 122971ddcf8SWim Van Sebroeck int __nvram_check_checksum(void) 1231da177e4SLinus Torvalds { 124*437ace37SFinn Thain int i; 125*437ace37SFinn Thain unsigned short sum = 0; 126*437ace37SFinn Thain unsigned short expect; 127*437ace37SFinn Thain 128*437ace37SFinn Thain for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i) 129*437ace37SFinn Thain sum += __nvram_read_byte(i); 130*437ace37SFinn Thain expect = __nvram_read_byte(PC_CKS_LOC)<<8 | 131*437ace37SFinn Thain __nvram_read_byte(PC_CKS_LOC+1); 132*437ace37SFinn Thain return (sum & 0xffff) == expect; 1331da177e4SLinus Torvalds } 134971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(__nvram_check_checksum); 1351da177e4SLinus Torvalds 136971ddcf8SWim Van Sebroeck int nvram_check_checksum(void) 1371da177e4SLinus Torvalds { 1381da177e4SLinus Torvalds unsigned long flags; 1391da177e4SLinus Torvalds int rv; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds spin_lock_irqsave(&rtc_lock, flags); 1421da177e4SLinus Torvalds rv = __nvram_check_checksum(); 1431da177e4SLinus Torvalds spin_unlock_irqrestore(&rtc_lock, flags); 1441da177e4SLinus Torvalds return rv; 1451da177e4SLinus Torvalds } 146971ddcf8SWim Van Sebroeck EXPORT_SYMBOL(nvram_check_checksum); 1471da177e4SLinus Torvalds 148971ddcf8SWim Van Sebroeck static void __nvram_set_checksum(void) 1491da177e4SLinus Torvalds { 150*437ace37SFinn Thain int i; 151*437ace37SFinn Thain unsigned short sum = 0; 152*437ace37SFinn Thain 153*437ace37SFinn Thain for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i) 154*437ace37SFinn Thain sum += __nvram_read_byte(i); 155*437ace37SFinn Thain __nvram_write_byte(sum >> 8, PC_CKS_LOC); 156*437ace37SFinn Thain __nvram_write_byte(sum & 0xff, PC_CKS_LOC + 1); 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 159681ea4b9SAdrian Bunk #if 0 160971ddcf8SWim Van Sebroeck void nvram_set_checksum(void) 1611da177e4SLinus Torvalds { 1621da177e4SLinus Torvalds unsigned long flags; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds spin_lock_irqsave(&rtc_lock, flags); 1651da177e4SLinus Torvalds __nvram_set_checksum(); 1661da177e4SLinus Torvalds spin_unlock_irqrestore(&rtc_lock, flags); 1671da177e4SLinus Torvalds } 168681ea4b9SAdrian Bunk #endif /* 0 */ 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* 1711da177e4SLinus Torvalds * The are the file operation function for user access to /dev/nvram 1721da177e4SLinus Torvalds */ 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) 1751da177e4SLinus Torvalds { 176b808b1d6SAl Viro return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE, 177b808b1d6SAl Viro NVRAM_BYTES); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 180971ddcf8SWim Van Sebroeck static ssize_t nvram_read(struct file *file, char __user *buf, 181971ddcf8SWim Van Sebroeck size_t count, loff_t *ppos) 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds unsigned char contents[NVRAM_BYTES]; 1841da177e4SLinus Torvalds unsigned i = *ppos; 1851da177e4SLinus Torvalds unsigned char *tmp; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds if (!__nvram_check_checksum()) 1901da177e4SLinus Torvalds goto checksum_err; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp) 1931da177e4SLinus Torvalds *tmp = __nvram_read_byte(i); 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds if (copy_to_user(buf, contents, tmp - contents)) 1981da177e4SLinus Torvalds return -EFAULT; 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds *ppos = i; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds return tmp - contents; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds checksum_err: 2051da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 2061da177e4SLinus Torvalds return -EIO; 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 209971ddcf8SWim Van Sebroeck static ssize_t nvram_write(struct file *file, const char __user *buf, 210971ddcf8SWim Van Sebroeck size_t count, loff_t *ppos) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds unsigned char contents[NVRAM_BYTES]; 2131da177e4SLinus Torvalds unsigned i = *ppos; 2141da177e4SLinus Torvalds unsigned char *tmp; 2151da177e4SLinus Torvalds 216a01c7800SH. Peter Anvin if (i >= NVRAM_BYTES) 217a01c7800SH. Peter Anvin return 0; /* Past EOF */ 218a01c7800SH. Peter Anvin 219a01c7800SH. Peter Anvin if (count > NVRAM_BYTES - i) 220a01c7800SH. Peter Anvin count = NVRAM_BYTES - i; 221a01c7800SH. Peter Anvin if (count > NVRAM_BYTES) 222a01c7800SH. Peter Anvin return -EFAULT; /* Can't happen, but prove it to gcc */ 223a01c7800SH. Peter Anvin 224a01c7800SH. Peter Anvin if (copy_from_user(contents, buf, count)) 2251da177e4SLinus Torvalds return -EFAULT; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds if (!__nvram_check_checksum()) 2301da177e4SLinus Torvalds goto checksum_err; 2311da177e4SLinus Torvalds 232a01c7800SH. Peter Anvin for (tmp = contents; count--; ++i, ++tmp) 2331da177e4SLinus Torvalds __nvram_write_byte(*tmp, i); 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds __nvram_set_checksum(); 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds *ppos = i; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds return tmp - contents; 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds checksum_err: 2441da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 2451da177e4SLinus Torvalds return -EIO; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 24855929332SArnd Bergmann static long nvram_ioctl(struct file *file, unsigned int cmd, 24955929332SArnd Bergmann unsigned long arg) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds int i; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds switch (cmd) { 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds case NVRAM_INIT: 2561da177e4SLinus Torvalds /* initialize NVRAM contents and checksum */ 2571da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2581da177e4SLinus Torvalds return -EACCES; 2591da177e4SLinus Torvalds 260613655faSArnd Bergmann mutex_lock(&nvram_mutex); 2611da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds for (i = 0; i < NVRAM_BYTES; ++i) 2641da177e4SLinus Torvalds __nvram_write_byte(0, i); 2651da177e4SLinus Torvalds __nvram_set_checksum(); 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 268613655faSArnd Bergmann mutex_unlock(&nvram_mutex); 2691da177e4SLinus Torvalds return 0; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds case NVRAM_SETCKS: 2721da177e4SLinus Torvalds /* just set checksum, contents unchanged (maybe useful after 2731da177e4SLinus Torvalds * checksum garbaged somehow...) */ 2741da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) 2751da177e4SLinus Torvalds return -EACCES; 2761da177e4SLinus Torvalds 277613655faSArnd Bergmann mutex_lock(&nvram_mutex); 2781da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 2791da177e4SLinus Torvalds __nvram_set_checksum(); 2801da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 281613655faSArnd Bergmann mutex_unlock(&nvram_mutex); 2821da177e4SLinus Torvalds return 0; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds default: 2851da177e4SLinus Torvalds return -ENOTTY; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 289971ddcf8SWim Van Sebroeck static int nvram_open(struct inode *inode, struct file *file) 2901da177e4SLinus Torvalds { 2911da177e4SLinus Torvalds spin_lock(&nvram_state_lock); 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || 2941da177e4SLinus Torvalds (nvram_open_mode & NVRAM_EXCL) || 295aeb5d727SAl Viro ((file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE))) { 2961da177e4SLinus Torvalds spin_unlock(&nvram_state_lock); 2971da177e4SLinus Torvalds return -EBUSY; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds if (file->f_flags & O_EXCL) 3011da177e4SLinus Torvalds nvram_open_mode |= NVRAM_EXCL; 302aeb5d727SAl Viro if (file->f_mode & FMODE_WRITE) 3031da177e4SLinus Torvalds nvram_open_mode |= NVRAM_WRITE; 3041da177e4SLinus Torvalds nvram_open_cnt++; 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds spin_unlock(&nvram_state_lock); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds return 0; 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds 311971ddcf8SWim Van Sebroeck static int nvram_release(struct inode *inode, struct file *file) 3121da177e4SLinus Torvalds { 3131da177e4SLinus Torvalds spin_lock(&nvram_state_lock); 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds nvram_open_cnt--; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds /* if only one instance is open, clear the EXCL bit */ 3181da177e4SLinus Torvalds if (nvram_open_mode & NVRAM_EXCL) 3191da177e4SLinus Torvalds nvram_open_mode &= ~NVRAM_EXCL; 320aeb5d727SAl Viro if (file->f_mode & FMODE_WRITE) 3211da177e4SLinus Torvalds nvram_open_mode &= ~NVRAM_WRITE; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds spin_unlock(&nvram_state_lock); 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds return 0; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds #ifndef CONFIG_PROC_FS 3298587b33fSWim Van Sebroeck static int nvram_add_proc_fs(void) 3301da177e4SLinus Torvalds { 3311da177e4SLinus Torvalds return 0; 3321da177e4SLinus Torvalds } 3338587b33fSWim Van Sebroeck 3341da177e4SLinus Torvalds #else 3351da177e4SLinus Torvalds 3368587b33fSWim Van Sebroeck static int nvram_proc_read(struct seq_file *seq, void *offset) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds unsigned char contents[NVRAM_BYTES]; 3398587b33fSWim Van Sebroeck int i = 0; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 3421da177e4SLinus Torvalds for (i = 0; i < NVRAM_BYTES; ++i) 3431da177e4SLinus Torvalds contents[i] = __nvram_read_byte(i); 3441da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 3451da177e4SLinus Torvalds 346*437ace37SFinn Thain pc_nvram_proc_read(contents, seq, offset); 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds return 0; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds 3518587b33fSWim Van Sebroeck static int nvram_add_proc_fs(void) 3528587b33fSWim Van Sebroeck { 3533f3942acSChristoph Hellwig if (!proc_create_single("driver/nvram", 0, NULL, nvram_proc_read)) 3548587b33fSWim Van Sebroeck return -ENOMEM; 3558587b33fSWim Van Sebroeck return 0; 3568587b33fSWim Van Sebroeck } 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 3591da177e4SLinus Torvalds 36062322d25SArjan van de Ven static const struct file_operations nvram_fops = { 3611da177e4SLinus Torvalds .owner = THIS_MODULE, 3621da177e4SLinus Torvalds .llseek = nvram_llseek, 3631da177e4SLinus Torvalds .read = nvram_read, 3641da177e4SLinus Torvalds .write = nvram_write, 36555929332SArnd Bergmann .unlocked_ioctl = nvram_ioctl, 3661da177e4SLinus Torvalds .open = nvram_open, 3671da177e4SLinus Torvalds .release = nvram_release, 3681da177e4SLinus Torvalds }; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds static struct miscdevice nvram_dev = { 3711da177e4SLinus Torvalds NVRAM_MINOR, 3721da177e4SLinus Torvalds "nvram", 3731da177e4SLinus Torvalds &nvram_fops 3741da177e4SLinus Torvalds }; 3751da177e4SLinus Torvalds 376971ddcf8SWim Van Sebroeck static int __init nvram_init(void) 3771da177e4SLinus Torvalds { 3781da177e4SLinus Torvalds int ret; 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds ret = misc_register(&nvram_dev); 3811da177e4SLinus Torvalds if (ret) { 3821da177e4SLinus Torvalds printk(KERN_ERR "nvram: can't misc_register on minor=%d\n", 3831da177e4SLinus Torvalds NVRAM_MINOR); 3841da177e4SLinus Torvalds goto out; 3851da177e4SLinus Torvalds } 3868587b33fSWim Van Sebroeck ret = nvram_add_proc_fs(); 3878587b33fSWim Van Sebroeck if (ret) { 3881da177e4SLinus Torvalds printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n"); 3891da177e4SLinus Torvalds goto outmisc; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds ret = 0; 3921da177e4SLinus Torvalds printk(KERN_INFO "Non-volatile memory driver v" NVRAM_VERSION "\n"); 3931da177e4SLinus Torvalds out: 3941da177e4SLinus Torvalds return ret; 3951da177e4SLinus Torvalds outmisc: 3961da177e4SLinus Torvalds misc_deregister(&nvram_dev); 3971da177e4SLinus Torvalds goto out; 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 400971ddcf8SWim Van Sebroeck static void __exit nvram_cleanup_module(void) 4011da177e4SLinus Torvalds { 4021da177e4SLinus Torvalds remove_proc_entry("driver/nvram", NULL); 4031da177e4SLinus Torvalds misc_deregister(&nvram_dev); 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds module_init(nvram_init); 4071da177e4SLinus Torvalds module_exit(nvram_cleanup_module); 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 4101da177e4SLinus Torvalds 411a116eaf1SLABBE Corentin static const char * const floppy_types[] = { 4121da177e4SLinus Torvalds "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", 4131da177e4SLinus Torvalds "3.5'' 2.88M", "3.5'' 2.88M" 4141da177e4SLinus Torvalds }; 4151da177e4SLinus Torvalds 416a116eaf1SLABBE Corentin static const char * const gfx_types[] = { 4171da177e4SLinus Torvalds "EGA, VGA, ... (with BIOS)", 4181da177e4SLinus Torvalds "CGA (40 cols)", 4191da177e4SLinus Torvalds "CGA (80 cols)", 4201da177e4SLinus Torvalds "monochrome", 4211da177e4SLinus Torvalds }; 4221da177e4SLinus Torvalds 423*437ace37SFinn Thain static void pc_nvram_proc_read(unsigned char *nvram, struct seq_file *seq, 4248587b33fSWim Van Sebroeck void *offset) 4251da177e4SLinus Torvalds { 4261da177e4SLinus Torvalds int checksum; 4271da177e4SLinus Torvalds int type; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds spin_lock_irq(&rtc_lock); 4301da177e4SLinus Torvalds checksum = __nvram_check_checksum(); 4311da177e4SLinus Torvalds spin_unlock_irq(&rtc_lock); 4321da177e4SLinus Torvalds 4338587b33fSWim Van Sebroeck seq_printf(seq, "Checksum status: %svalid\n", checksum ? "" : "not "); 4341da177e4SLinus Torvalds 4358587b33fSWim Van Sebroeck seq_printf(seq, "# floppies : %d\n", 4361da177e4SLinus Torvalds (nvram[6] & 1) ? (nvram[6] >> 6) + 1 : 0); 4378587b33fSWim Van Sebroeck seq_printf(seq, "Floppy 0 type : "); 4381da177e4SLinus Torvalds type = nvram[2] >> 4; 439fe971071STobias Klauser if (type < ARRAY_SIZE(floppy_types)) 4408587b33fSWim Van Sebroeck seq_printf(seq, "%s\n", floppy_types[type]); 4411da177e4SLinus Torvalds else 4428587b33fSWim Van Sebroeck seq_printf(seq, "%d (unknown)\n", type); 4438587b33fSWim Van Sebroeck seq_printf(seq, "Floppy 1 type : "); 4441da177e4SLinus Torvalds type = nvram[2] & 0x0f; 445fe971071STobias Klauser if (type < ARRAY_SIZE(floppy_types)) 4468587b33fSWim Van Sebroeck seq_printf(seq, "%s\n", floppy_types[type]); 4471da177e4SLinus Torvalds else 4488587b33fSWim Van Sebroeck seq_printf(seq, "%d (unknown)\n", type); 4491da177e4SLinus Torvalds 4508587b33fSWim Van Sebroeck seq_printf(seq, "HD 0 type : "); 4511da177e4SLinus Torvalds type = nvram[4] >> 4; 4521da177e4SLinus Torvalds if (type) 4538587b33fSWim Van Sebroeck seq_printf(seq, "%02x\n", type == 0x0f ? nvram[11] : type); 4541da177e4SLinus Torvalds else 4558587b33fSWim Van Sebroeck seq_printf(seq, "none\n"); 4561da177e4SLinus Torvalds 4578587b33fSWim Van Sebroeck seq_printf(seq, "HD 1 type : "); 4581da177e4SLinus Torvalds type = nvram[4] & 0x0f; 4591da177e4SLinus Torvalds if (type) 4608587b33fSWim Van Sebroeck seq_printf(seq, "%02x\n", type == 0x0f ? nvram[12] : type); 4611da177e4SLinus Torvalds else 4628587b33fSWim Van Sebroeck seq_printf(seq, "none\n"); 4631da177e4SLinus Torvalds 4648587b33fSWim Van Sebroeck seq_printf(seq, "HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", 4651da177e4SLinus Torvalds nvram[18] | (nvram[19] << 8), 4661da177e4SLinus Torvalds nvram[20], nvram[25], 4671da177e4SLinus Torvalds nvram[21] | (nvram[22] << 8), nvram[23] | (nvram[24] << 8)); 4688587b33fSWim Van Sebroeck seq_printf(seq, "HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n", 4691da177e4SLinus Torvalds nvram[39] | (nvram[40] << 8), 4701da177e4SLinus Torvalds nvram[41], nvram[46], 4711da177e4SLinus Torvalds nvram[42] | (nvram[43] << 8), nvram[44] | (nvram[45] << 8)); 4721da177e4SLinus Torvalds 4738587b33fSWim Van Sebroeck seq_printf(seq, "DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8)); 4748587b33fSWim Van Sebroeck seq_printf(seq, "Extended memory: %d kB (configured), %d kB (tested)\n", 4751da177e4SLinus Torvalds nvram[9] | (nvram[10] << 8), nvram[34] | (nvram[35] << 8)); 4761da177e4SLinus Torvalds 4778587b33fSWim Van Sebroeck seq_printf(seq, "Gfx adapter : %s\n", 4788587b33fSWim Van Sebroeck gfx_types[(nvram[6] >> 4) & 3]); 4791da177e4SLinus Torvalds 4808587b33fSWim Van Sebroeck seq_printf(seq, "FPU : %sinstalled\n", 4811da177e4SLinus Torvalds (nvram[6] & 2) ? "" : "not "); 4821da177e4SLinus Torvalds 4838587b33fSWim Van Sebroeck return; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds 486*437ace37SFinn Thain #endif /* CONFIG_PROC_FS */ 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 4891da177e4SLinus Torvalds MODULE_ALIAS_MISCDEV(NVRAM_MINOR); 490