1/*
2 * CMOS/NV-RAM driver for Linux
3 *
4 * Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5 * idea by and with help from Richard Jelinek <rj@suse.de>
6 * Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com)
7 *
8 * This driver allows you to access the contents of the non-volatile memory in
9 * the mc146818rtc.h real-time clock. This chip is built into all PCs and into
10 * many Atari machines. In the former it's called "CMOS-RAM", in the latter
11 * "NVRAM" (NV stands for non-volatile).
12 *
13 * The data are supplied as a (seekable) character device, /dev/nvram. The
14 * size of this file is dependant on the controller.  The usual size is 114,
15 * the number of freely available bytes in the memory (i.e., not used by the
16 * RTC itself).
17 *
18 * Checksums over the NVRAM contents are managed by this driver. In case of a
19 * bad checksum, reads and writes return -EIO. The checksum can be initialized
20 * to a sane state either by ioctl(NVRAM_INIT) (clear whole NVRAM) or
21 * ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid
22 * again; use with care!)
23 *
24 * This file also provides some functions for other parts of the kernel that
25 * want to access the NVRAM: nvram_{read,write,check_checksum,set_checksum}.
26 * Obviously this can be used only if this driver is always configured into
27 * the kernel and is not a module. Since the functions are used by some Atari
28 * drivers, this is the case on the Atari.
29 *
30 *
31 * 	1.1	Cesar Barros: SMP locking fixes
32 * 		added changelog
33 * 	1.2	Erik Gilling: Cobalt Networks support
34 * 		Tim Hockin: general cleanup, Cobalt support
35 */
36
37#define NVRAM_VERSION	"1.2"
38
39#include <linux/module.h>
40#include <linux/config.h>
41#include <linux/sched.h>
42#include <linux/smp_lock.h>
43#include <linux/nvram.h>
44
45#define PC		1
46#define ATARI		2
47#define COBALT		3
48
49/* select machine configuration */
50#if defined(CONFIG_ATARI)
51#  define MACH ATARI
52#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)    /* and others?? */
53#define MACH PC
54#  if defined(CONFIG_COBALT)
55#    include <linux/cobalt-nvram.h>
56#    define MACH COBALT
57#  else
58#    define MACH PC
59#  endif
60#else
61#  error Cannot build nvram driver for this machine configuration.
62#endif
63
64#if MACH == PC
65
66/* RTC in a PC */
67#define CHECK_DRIVER_INIT()	1
68
69/* On PCs, the checksum is built only over bytes 2..31 */
70#define PC_CKS_RANGE_START	2
71#define PC_CKS_RANGE_END	31
72#define PC_CKS_LOC		32
73#define NVRAM_BYTES		(128-NVRAM_FIRST_BYTE)
74
75#define mach_check_checksum	pc_check_checksum
76#define mach_set_checksum	pc_set_checksum
77#define mach_proc_infos		pc_proc_infos
78
79#endif
80
81#if MACH == COBALT
82
83#define CHECK_DRIVER_INIT()     1
84
85#define NVRAM_BYTES		(128-NVRAM_FIRST_BYTE)
86
87#define mach_check_checksum	cobalt_check_checksum
88#define mach_set_checksum	cobalt_set_checksum
89#define mach_proc_infos		cobalt_proc_infos
90
91#endif
92
93#if MACH == ATARI
94
95/* Special parameters for RTC in Atari machines */
96#include <asm/atarihw.h>
97#include <asm/atariints.h>
98#define RTC_PORT(x)		(TT_RTC_BAS + 2*(x))
99#define CHECK_DRIVER_INIT()	(MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))
100
101/* On Ataris, the checksum is over all bytes except the checksum bytes
102 * themselves; these are at the very end */
103#define ATARI_CKS_RANGE_START	0
104#define ATARI_CKS_RANGE_END	47
105#define ATARI_CKS_LOC		48
106
107#define mach_check_checksum	atari_check_checksum
108#define mach_set_checksum	atari_set_checksum
109#define mach_proc_infos		atari_proc_infos
110
111#endif
112
113/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with
114 * rtc_lock held. Due to the index-port/data-port design of the RTC, we
115 * don't want two different things trying to get to it at once. (e.g. the
116 * periodic 11 min sync from time.c vs. this driver.)
117 */
118
119#include <linux/types.h>
120#include <linux/errno.h>
121#include <linux/miscdevice.h>
122#include <linux/slab.h>
123#include <linux/ioport.h>
124#include <linux/fcntl.h>
125#include <linux/mc146818rtc.h>
126#include <linux/init.h>
127#include <linux/proc_fs.h>
128#include <linux/spinlock.h>
129
130#include <asm/io.h>
131#include <asm/uaccess.h>
132#include <asm/system.h>
133
134static spinlock_t nvram_state_lock = SPIN_LOCK_UNLOCKED;
135static int nvram_open_cnt;	/* #times opened */
136static int nvram_open_mode;	/* special open modes */
137#define NVRAM_WRITE		1 /* opened for writing (exclusive) */
138#define NVRAM_EXCL		2 /* opened with O_EXCL */
139
140static int mach_check_checksum(void);
141static void mach_set_checksum(void);
142
143#ifdef CONFIG_PROC_FS
144static int mach_proc_infos(unsigned char *contents, char *buffer, int *len,
145    off_t *begin, off_t offset, int size);
146#endif
147
148/*
149 * These functions are provided to be called internally or by other parts of
150 * the kernel. It's up to the caller to ensure correct checksum before reading
151 * or after writing (needs to be done only once).
152 *
153 * It is worth noting that these functions all access bytes of general
154 * purpose memory in the NVRAM - that is to say, they all add the
155 * NVRAM_FIRST_BYTE offset.  Pass them offsets into NVRAM as if you did not
156 * know about the RTC cruft.
157 */
158
159unsigned char
160__nvram_read_byte(int i)
161{
162	return CMOS_READ(NVRAM_FIRST_BYTE + i);
163}
164
165unsigned char
166nvram_read_byte(int i)
167{
168	unsigned long flags;
169	unsigned char c;
170
171	spin_lock_irqsave(&rtc_lock, flags);
172	c = __nvram_read_byte(i);
173	spin_unlock_irqrestore(&rtc_lock, flags);
174	return c;
175}
176
177/* This races nicely with trying to read with checksum checking (nvram_read) */
178void
179__nvram_write_byte(unsigned char c, int i)
180{
181	CMOS_WRITE(c, NVRAM_FIRST_BYTE + i);
182}
183
184void
185nvram_write_byte(unsigned char c, int i)
186{
187	unsigned long flags;
188
189	spin_lock_irqsave(&rtc_lock, flags);
190	__nvram_write_byte(c, i);
191	spin_unlock_irqrestore(&rtc_lock, flags);
192}
193
194int
195__nvram_check_checksum(void)
196{
197	return mach_check_checksum();
198}
199
200int
201nvram_check_checksum(void)
202{
203	unsigned long flags;
204	int rv;
205
206	spin_lock_irqsave(&rtc_lock, flags);
207	rv = __nvram_check_checksum();
208	spin_unlock_irqrestore(&rtc_lock, flags);
209	return rv;
210}
211
212void
213__nvram_set_checksum(void)
214{
215	mach_set_checksum();
216}
217
218void
219nvram_set_checksum(void)
220{
221	unsigned long flags;
222
223	spin_lock_irqsave(&rtc_lock, flags);
224	__nvram_set_checksum();
225	spin_unlock_irqrestore(&rtc_lock, flags);
226}
227
228/*
229 * The are the file operation function for user access to /dev/nvram
230 */
231
232static long long
233nvram_llseek(struct file *file, loff_t offset, int origin)
234{
235	switch (origin) {
236	case 0:
237		/* nothing to do */
238		break;
239	case 1:
240		offset += file->f_pos;
241		break;
242	case 2:
243		offset += NVRAM_BYTES;
244		break;
245	}
246	return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
247}
248
249static ssize_t
250nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos)
251{
252	unsigned char contents[NVRAM_BYTES];
253	unsigned i = *ppos;
254	unsigned char *tmp;
255
256	spin_lock_irq(&rtc_lock);
257
258	if (!__nvram_check_checksum())
259		goto checksum_err;
260
261	for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
262		*tmp = __nvram_read_byte(i);
263
264	spin_unlock_irq(&rtc_lock);
265
266	if (copy_to_user(buf, contents, tmp - contents))
267		return -EFAULT;
268
269	*ppos = i;
270
271	return tmp - contents;
272
273      checksum_err:
274	spin_unlock_irq(&rtc_lock);
275	return -EIO;
276}
277
278static ssize_t
279nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
280{
281	unsigned char contents[NVRAM_BYTES];
282	unsigned i = *ppos;
283	unsigned char *tmp;
284	int len;
285
286	len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count;
287	if (copy_from_user(contents, buf, len))
288		return -EFAULT;
289
290	spin_lock_irq(&rtc_lock);
291
292	if (!__nvram_check_checksum())
293		goto checksum_err;
294
295	for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
296		__nvram_write_byte(*tmp, i);
297
298	__nvram_set_checksum();
299
300	spin_unlock_irq(&rtc_lock);
301
302	*ppos = i;
303
304	return tmp - contents;
305
306      checksum_err:
307	spin_unlock_irq(&rtc_lock);
308	return -EIO;
309}
310
311static int
312nvram_ioctl(struct inode *inode, struct file *file,
313    unsigned int cmd, unsigned long arg)
314{
315	int i;
316
317	switch (cmd) {
318
319	case NVRAM_INIT:
320		/* initialize NVRAM contents and checksum */
321		if (!capable(CAP_SYS_ADMIN))
322			return -EACCES;
323
324		spin_lock_irq(&rtc_lock);
325
326		for (i = 0; i < NVRAM_BYTES; ++i)
327			__nvram_write_byte(0, i);
328		__nvram_set_checksum();
329
330		spin_unlock_irq(&rtc_lock);
331		return 0;
332
333	case NVRAM_SETCKS:
334		/* just set checksum, contents unchanged (maybe useful after
335		 * checksum garbaged somehow...) */
336		if (!capable(CAP_SYS_ADMIN))
337			return -EACCES;
338
339		spin_lock_irq(&rtc_lock);
340		__nvram_set_checksum();
341		spin_unlock_irq(&rtc_lock);
342		return 0;
343
344	default:
345		return -ENOTTY;
346	}
347}
348
349static int
350nvram_open(struct inode *inode, struct file *file)
351{
352	spin_lock(&nvram_state_lock);
353
354	if ((nvram_open_cnt && (file->f_flags & O_EXCL)) ||
355	    (nvram_open_mode & NVRAM_EXCL) ||
356	    ((file->f_mode & 2) && (nvram_open_mode & NVRAM_WRITE))) {
357		spin_unlock(&nvram_state_lock);
358		return -EBUSY;
359	}
360
361	if (file->f_flags & O_EXCL)
362		nvram_open_mode |= NVRAM_EXCL;
363	if (file->f_mode & 2)
364		nvram_open_mode |= NVRAM_WRITE;
365	nvram_open_cnt++;
366
367	spin_unlock(&nvram_state_lock);
368
369	return 0;
370}
371
372static int
373nvram_release(struct inode *inode, struct file *file)
374{
375	spin_lock(&nvram_state_lock);
376
377	nvram_open_cnt--;
378
379	/* if only one instance is open, clear the EXCL bit */
380	if (nvram_open_mode & NVRAM_EXCL)
381		nvram_open_mode &= ~NVRAM_EXCL;
382	if (file->f_mode & 2)
383		nvram_open_mode &= ~NVRAM_WRITE;
384
385	spin_unlock(&nvram_state_lock);
386
387	return 0;
388}
389
390#ifndef CONFIG_PROC_FS
391static int
392nvram_read_proc(char *buffer, char **start, off_t offset,
393    int size, int *eof, void *data)
394{
395	return 0;
396}
397#else
398
399static int
400nvram_read_proc(char *buffer, char **start, off_t offset,
401    int size, int *eof, void *data)
402{
403	unsigned char contents[NVRAM_BYTES];
404	int i, len = 0;
405	off_t begin = 0;
406
407	spin_lock_irq(&rtc_lock);
408	for (i = 0; i < NVRAM_BYTES; ++i)
409		contents[i] = __nvram_read_byte(i);
410	spin_unlock_irq(&rtc_lock);
411
412	*eof = mach_proc_infos(contents, buffer, &len, &begin, offset, size);
413
414	if (offset >= begin + len)
415		return 0;
416	*start = buffer + (offset - begin);
417	return (size < begin + len - offset) ? size : begin + len - offset;
418
419}
420
421/* This macro frees the machine specific function from bounds checking and
422 * this like that... */
423#define PRINT_PROC(fmt,args...)					\
424	do {							\
425		*len += sprintf(buffer+*len, fmt, ##args);	\
426		if (*begin + *len > offset + size)		\
427			return 0;				\
428		if (*begin + *len < offset) {			\
429			*begin += *len;				\
430			*len = 0;				\
431		}						\
432	} while(0)
433
434#endif /* CONFIG_PROC_FS */
435
436static struct file_operations nvram_fops = {
437	owner:		THIS_MODULE,
438	llseek:		nvram_llseek,
439	read:		nvram_read,
440	write:		nvram_write,
441	ioctl:		nvram_ioctl,
442	open:		nvram_open,
443	release:	nvram_release,
444};
445
446static struct miscdevice nvram_dev = {
447	NVRAM_MINOR,
448	"nvram",
449	&nvram_fops
450};
451
452static int __init
453nvram_init(void)
454{
455	int ret;
456
457	/* First test whether the driver should init at all */
458	if (!CHECK_DRIVER_INIT())
459		return -ENXIO;
460
461	ret = misc_register(&nvram_dev);
462	if (ret) {
463		printk(KERN_ERR "nvram: can't misc_register on minor=%d\n",
464		    NVRAM_MINOR);
465		goto out;
466	}
467	if (!create_proc_read_entry("driver/nvram", 0, 0, nvram_read_proc,
468		NULL)) {
469		printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n");
470		ret = -ENOMEM;
471		goto outmisc;
472	}
473	ret = 0;
474	printk(KERN_INFO "Non-volatile memory driver v" NVRAM_VERSION "\n");
475      out:
476	return ret;
477      outmisc:
478	misc_deregister(&nvram_dev);
479	goto out;
480}
481
482static void __exit
483nvram_cleanup_module(void)
484{
485	remove_proc_entry("driver/nvram", 0);
486	misc_deregister(&nvram_dev);
487}
488
489module_init(nvram_init);
490module_exit(nvram_cleanup_module);
491
492/*
493 * Machine specific functions
494 */
495
496#if MACH == PC
497
498static int
499pc_check_checksum(void)
500{
501	int i;
502	unsigned short sum = 0;
503	unsigned short expect;
504
505	for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
506		sum += __nvram_read_byte(i);
507	expect = __nvram_read_byte(PC_CKS_LOC)<<8 |
508	    __nvram_read_byte(PC_CKS_LOC+1);
509	return ((sum & 0xffff) == expect);
510}
511
512static void
513pc_set_checksum(void)
514{
515	int i;
516	unsigned short sum = 0;
517
518	for (i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; ++i)
519		sum += __nvram_read_byte(i);
520	__nvram_write_byte(sum >> 8, PC_CKS_LOC);
521	__nvram_write_byte(sum & 0xff, PC_CKS_LOC + 1);
522}
523
524#ifdef CONFIG_PROC_FS
525
526static char *floppy_types[] = {
527	"none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M",
528	"3.5'' 2.88M", "3.5'' 2.88M"
529};
530
531static char *gfx_types[] = {
532	"EGA, VGA, ... (with BIOS)",
533	"CGA (40 cols)",
534	"CGA (80 cols)",
535	"monochrome",
536};
537
538static int
539pc_proc_infos(unsigned char *nvram, char *buffer, int *len,
540    off_t *begin, off_t offset, int size)
541{
542	int checksum;
543	int type;
544
545	spin_lock_irq(&rtc_lock);
546	checksum = __nvram_check_checksum();
547	spin_unlock_irq(&rtc_lock);
548
549	PRINT_PROC("Checksum status: %svalid\n", checksum ? "" : "not ");
550
551	PRINT_PROC("# floppies     : %d\n",
552	    (nvram[6] & 1) ? (nvram[6] >> 6) + 1 : 0);
553	PRINT_PROC("Floppy 0 type  : ");
554	type = nvram[2] >> 4;
555	if (type < sizeof (floppy_types) / sizeof (*floppy_types))
556		PRINT_PROC("%s\n", floppy_types[type]);
557	else
558		PRINT_PROC("%d (unknown)\n", type);
559	PRINT_PROC("Floppy 1 type  : ");
560	type = nvram[2] & 0x0f;
561	if (type < sizeof (floppy_types) / sizeof (*floppy_types))
562		PRINT_PROC("%s\n", floppy_types[type]);
563	else
564		PRINT_PROC("%d (unknown)\n", type);
565
566	PRINT_PROC("HD 0 type      : ");
567	type = nvram[4] >> 4;
568	if (type)
569		PRINT_PROC("%02x\n", type == 0x0f ? nvram[11] : type);
570	else
571		PRINT_PROC("none\n");
572
573	PRINT_PROC("HD 1 type      : ");
574	type = nvram[4] & 0x0f;
575	if (type)
576		PRINT_PROC("%02x\n", type == 0x0f ? nvram[12] : type);
577	else
578		PRINT_PROC("none\n");
579
580	PRINT_PROC("HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
581	    nvram[18] | (nvram[19] << 8),
582	    nvram[20], nvram[25],
583	    nvram[21] | (nvram[22] << 8), nvram[23] | (nvram[24] << 8));
584	PRINT_PROC("HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
585	    nvram[39] | (nvram[40] << 8),
586	    nvram[41], nvram[46],
587	    nvram[42] | (nvram[43] << 8), nvram[44] | (nvram[45] << 8));
588
589	PRINT_PROC("DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8));
590	PRINT_PROC("Extended memory: %d kB (configured), %d kB (tested)\n",
591	    nvram[9] | (nvram[10] << 8), nvram[34] | (nvram[35] << 8));
592
593	PRINT_PROC("Gfx adapter    : %s\n", gfx_types[(nvram[6] >> 4) & 3]);
594
595	PRINT_PROC("FPU            : %sinstalled\n",
596	    (nvram[6] & 2) ? "" : "not ");
597
598	return 1;
599}
600#endif
601
602#endif /* MACH == PC */
603
604#if MACH == COBALT
605
606/* the cobalt CMOS has a wider range of it's checksum */
607static int cobalt_check_checksum(void)
608{
609	int i;
610	unsigned short sum = 0;
611	unsigned short expect;
612
613	for (i = COBT_CMOS_CKS_START; i <= COBT_CMOS_CKS_END; ++i) {
614		if ((i == COBT_CMOS_CHECKSUM) || (i == (COBT_CMOS_CHECKSUM+1)))
615			continue;
616
617		sum += __nvram_read_byte(i);
618	}
619	expect = __nvram_read_byte(COBT_CMOS_CHECKSUM) << 8 |
620	    __nvram_read_byte(COBT_CMOS_CHECKSUM+1);
621	return ((sum & 0xffff) == expect);
622}
623
624static void cobalt_set_checksum(void)
625{
626	int i;
627	unsigned short sum = 0;
628
629	for (i = COBT_CMOS_CKS_START; i <= COBT_CMOS_CKS_END; ++i) {
630		if ((i == COBT_CMOS_CHECKSUM) || (i == (COBT_CMOS_CHECKSUM+1)))
631			continue;
632
633		sum += __nvram_read_byte(i);
634	}
635
636	__nvram_write_byte(sum >> 8, COBT_CMOS_CHECKSUM);
637	__nvram_write_byte(sum & 0xff, COBT_CMOS_CHECKSUM+1);
638}
639
640#ifdef CONFIG_PROC_FS
641
642static int cobalt_proc_infos(unsigned char *nvram, char *buffer, int *len,
643	off_t *begin, off_t offset, int size)
644{
645	int i;
646	unsigned int checksum;
647	unsigned int flags;
648	char sernum[14];
649	char *key = "cNoEbTaWlOtR!";
650	unsigned char bto_csum;
651
652	spin_lock_irq(&rtc_lock);
653	checksum = __nvram_check_checksum();
654	spin_unlock_irq(&rtc_lock);
655
656	PRINT_PROC("Checksum status: %svalid\n", checksum ? "" : "not ");
657
658	flags = nvram[COBT_CMOS_FLAG_BYTE_0] << 8
659	    | nvram[COBT_CMOS_FLAG_BYTE_1];
660
661	PRINT_PROC("Console: %s\n",
662		flags & COBT_CMOS_CONSOLE_FLAG ?  "on": "off");
663
664	PRINT_PROC("Firmware Debug Messages: %s\n",
665		flags & COBT_CMOS_DEBUG_FLAG ? "on": "off");
666
667	PRINT_PROC("Auto Prompt: %s\n",
668		flags & COBT_CMOS_AUTO_PROMPT_FLAG ? "on": "off");
669
670	PRINT_PROC("Shutdown Status: %s\n",
671		flags & COBT_CMOS_CLEAN_BOOT_FLAG ? "clean": "dirty");
672
673	PRINT_PROC("Hardware Probe: %s\n",
674		flags & COBT_CMOS_HW_NOPROBE_FLAG ? "partial": "full");
675
676	PRINT_PROC("System Fault: %sdetected\n",
677		flags & COBT_CMOS_SYSFAULT_FLAG ? "": "not ");
678
679	PRINT_PROC("Panic on OOPS: %s\n",
680		flags & COBT_CMOS_OOPSPANIC_FLAG ? "yes": "no");
681
682	PRINT_PROC("Delayed Cache Initialization: %s\n",
683		flags & COBT_CMOS_DELAY_CACHE_FLAG ? "yes": "no");
684
685	PRINT_PROC("Show Logo at Boot: %s\n",
686		flags & COBT_CMOS_NOLOGO_FLAG ? "no": "yes");
687
688	PRINT_PROC("Boot Method: ");
689	switch (nvram[COBT_CMOS_BOOT_METHOD]) {
690	case COBT_CMOS_BOOT_METHOD_DISK:
691		PRINT_PROC("disk\n");
692		break;
693
694	case COBT_CMOS_BOOT_METHOD_ROM:
695		PRINT_PROC("rom\n");
696		break;
697
698	case COBT_CMOS_BOOT_METHOD_NET:
699		PRINT_PROC("net\n");
700		break;
701
702	default:
703		PRINT_PROC("unknown\n");
704		break;
705	}
706
707	PRINT_PROC("Primary Boot Device: %d:%d\n",
708		nvram[COBT_CMOS_BOOT_DEV0_MAJ],
709		nvram[COBT_CMOS_BOOT_DEV0_MIN] );
710	PRINT_PROC("Secondary Boot Device: %d:%d\n",
711		nvram[COBT_CMOS_BOOT_DEV1_MAJ],
712		nvram[COBT_CMOS_BOOT_DEV1_MIN] );
713	PRINT_PROC("Tertiary Boot Device: %d:%d\n",
714		nvram[COBT_CMOS_BOOT_DEV2_MAJ],
715		nvram[COBT_CMOS_BOOT_DEV2_MIN] );
716
717	PRINT_PROC("Uptime: %d\n",
718		nvram[COBT_CMOS_UPTIME_0] << 24 |
719		nvram[COBT_CMOS_UPTIME_1] << 16 |
720		nvram[COBT_CMOS_UPTIME_2] << 8  |
721		nvram[COBT_CMOS_UPTIME_3]);
722
723	PRINT_PROC("Boot Count: %d\n",
724		nvram[COBT_CMOS_BOOTCOUNT_0] << 24 |
725		nvram[COBT_CMOS_BOOTCOUNT_1] << 16 |
726		nvram[COBT_CMOS_BOOTCOUNT_2] << 8  |
727		nvram[COBT_CMOS_BOOTCOUNT_3]);
728
729	/* 13 bytes of serial num */
730	for (i=0 ; i<13 ; i++) {
731		sernum[i] = nvram[COBT_CMOS_SYS_SERNUM_0 + i];
732	}
733	sernum[13] = '\0';
734
735	checksum = 0;
736	for (i=0 ; i<13 ; i++) {
737		checksum += sernum[i] ^ key[i];
738	}
739	checksum = ((checksum & 0x7f) ^ (0xd6)) & 0xff;
740
741	PRINT_PROC("Serial Number: %s", sernum);
742	if (checksum != nvram[COBT_CMOS_SYS_SERNUM_CSUM]) {
743		PRINT_PROC(" (invalid checksum)");
744	}
745	PRINT_PROC("\n");
746
747	PRINT_PROC("Rom Revison: %d.%d.%d\n", nvram[COBT_CMOS_ROM_REV_MAJ],
748		nvram[COBT_CMOS_ROM_REV_MIN], nvram[COBT_CMOS_ROM_REV_REV]);
749
750	PRINT_PROC("BTO Server: %d.%d.%d.%d", nvram[COBT_CMOS_BTO_IP_0],
751		nvram[COBT_CMOS_BTO_IP_1], nvram[COBT_CMOS_BTO_IP_2],
752		nvram[COBT_CMOS_BTO_IP_3]);
753	bto_csum = nvram[COBT_CMOS_BTO_IP_0] + nvram[COBT_CMOS_BTO_IP_1]
754		+ nvram[COBT_CMOS_BTO_IP_2] + nvram[COBT_CMOS_BTO_IP_3];
755	if (bto_csum != nvram[COBT_CMOS_BTO_IP_CSUM]) {
756		PRINT_PROC(" (invalid checksum)");
757	}
758	PRINT_PROC("\n");
759
760	if (flags & COBT_CMOS_VERSION_FLAG
761	 && nvram[COBT_CMOS_VERSION] >= COBT_CMOS_VER_BTOCODE) {
762		PRINT_PROC("BTO Code: 0x%x\n",
763			nvram[COBT_CMOS_BTO_CODE_0] << 24 |
764			nvram[COBT_CMOS_BTO_CODE_1] << 16 |
765			nvram[COBT_CMOS_BTO_CODE_2] << 8 |
766			nvram[COBT_CMOS_BTO_CODE_3]);
767	}
768
769	return 1;
770}
771#endif /* CONFIG_PROC_FS */
772
773#endif /* MACH == COBALT */
774
775#if MACH == ATARI
776
777static int
778atari_check_checksum(void)
779{
780	int i;
781	unsigned char sum = 0;
782
783	for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i)
784		sum += __nvram_read_byte(i);
785	return (__nvram_read_byte(ATARI_CKS_LOC) == (~sum & 0xff) &&
786	    __nvram_read_byte(ATARI_CKS_LOC + 1) == (sum & 0xff));
787}
788
789static void
790atari_set_checksum(void)
791{
792	int i;
793	unsigned char sum = 0;
794
795	for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i)
796		sum += __nvram_read_byte(i);
797	__nvram_write_byte(~sum, ATARI_CKS_LOC);
798	__nvram_write_byte(sum, ATARI_CKS_LOC + 1);
799}
800
801#ifdef CONFIG_PROC_FS
802
803static struct {
804	unsigned char val;
805	char *name;
806} boot_prefs[] = {
807	{ 0x80, "TOS" },
808	{ 0x40, "ASV" },
809	{ 0x20, "NetBSD (?)" },
810	{ 0x10, "Linux" },
811	{ 0x00, "unspecified" }
812};
813
814static char *languages[] = {
815	"English (US)",
816	"German",
817	"French",
818	"English (UK)",
819	"Spanish",
820	"Italian",
821	"6 (undefined)",
822	"Swiss (French)",
823	"Swiss (German)"
824};
825
826static char *dateformat[] = {
827	"MM%cDD%cYY",
828	"DD%cMM%cYY",
829	"YY%cMM%cDD",
830	"YY%cDD%cMM",
831	"4 (undefined)",
832	"5 (undefined)",
833	"6 (undefined)",
834	"7 (undefined)"
835};
836
837static char *colors[] = {
838	"2", "4", "16", "256", "65536", "??", "??", "??"
839};
840
841#define fieldsize(a)	(sizeof(a)/sizeof(*a))
842
843static int
844atari_proc_infos(unsigned char *nvram, char *buffer, int *len,
845    off_t *begin, off_t offset, int size)
846{
847	int checksum = nvram_check_checksum();
848	int i;
849	unsigned vmode;
850
851	PRINT_PROC("Checksum status  : %svalid\n", checksum ? "" : "not ");
852
853	PRINT_PROC("Boot preference  : ");
854	for (i = fieldsize(boot_prefs) - 1; i >= 0; --i) {
855		if (nvram[1] == boot_prefs[i].val) {
856			PRINT_PROC("%s\n", boot_prefs[i].name);
857			break;
858		}
859	}
860	if (i < 0)
861		PRINT_PROC("0x%02x (undefined)\n", nvram[1]);
862
863	PRINT_PROC("SCSI arbitration : %s\n",
864	    (nvram[16] & 0x80) ? "on" : "off");
865	PRINT_PROC("SCSI host ID     : ");
866	if (nvram[16] & 0x80)
867		PRINT_PROC("%d\n", nvram[16] & 7);
868	else
869		PRINT_PROC("n/a\n");
870
871	/* the following entries are defined only for the Falcon */
872	if ((atari_mch_cookie >> 16) != ATARI_MCH_FALCON)
873		return 1;
874
875	PRINT_PROC("OS language      : ");
876	if (nvram[6] < fieldsize(languages))
877		PRINT_PROC("%s\n", languages[nvram[6]]);
878	else
879		PRINT_PROC("%u (undefined)\n", nvram[6]);
880	PRINT_PROC("Keyboard language: ");
881	if (nvram[7] < fieldsize(languages))
882		PRINT_PROC("%s\n", languages[nvram[7]]);
883	else
884		PRINT_PROC("%u (undefined)\n", nvram[7]);
885	PRINT_PROC("Date format      : ");
886	PRINT_PROC(dateformat[nvram[8] & 7],
887	    nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/');
888	PRINT_PROC(", %dh clock\n", nvram[8] & 16 ? 24 : 12);
889	PRINT_PROC("Boot delay       : ");
890	if (nvram[10] == 0)
891		PRINT_PROC("default");
892	else
893		PRINT_PROC("%ds%s\n", nvram[10],
894		    nvram[10] < 8 ? ", no memory test" : "");
895
896	vmode = (nvram[14] << 8) || nvram[15];
897	PRINT_PROC("Video mode       : %s colors, %d columns, %s %s monitor\n",
898	    colors[vmode & 7],
899	    vmode & 8 ? 80 : 40,
900	    vmode & 16 ? "VGA" : "TV", vmode & 32 ? "PAL" : "NTSC");
901	PRINT_PROC("                   %soverscan, compat. mode %s%s\n",
902	    vmode & 64 ? "" : "no ",
903	    vmode & 128 ? "on" : "off",
904	    vmode & 256 ?
905	    (vmode & 16 ? ", line doubling" : ", half screen") : "");
906
907	return 1;
908}
909#endif
910
911#endif /* MACH == ATARI */
912
913MODULE_LICENSE("GPL");
914
915EXPORT_SYMBOL(__nvram_read_byte);
916EXPORT_SYMBOL(nvram_read_byte);
917EXPORT_SYMBOL(__nvram_write_byte);
918EXPORT_SYMBOL(nvram_write_byte);
919EXPORT_SYMBOL(__nvram_check_checksum);
920EXPORT_SYMBOL(nvram_check_checksum);
921EXPORT_SYMBOL(__nvram_set_checksum);
922EXPORT_SYMBOL(nvram_set_checksum);
923