1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * CMOS/NV-RAM driver for Atari. Adapted from drivers/char/nvram.c. |
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 | * Further contributions from Cesar Barros, Erik Gilling, Tim Hockin and |
8 | * Wim Van Sebroeck. |
9 | */ |
10 | |
11 | #include <linux/errno.h> |
12 | #include <linux/init.h> |
13 | #include <linux/mc146818rtc.h> |
14 | #include <linux/module.h> |
15 | #include <linux/nvram.h> |
16 | #include <linux/proc_fs.h> |
17 | #include <linux/seq_file.h> |
18 | #include <linux/spinlock.h> |
19 | #include <linux/types.h> |
20 | #include <asm/atarihw.h> |
21 | #include <asm/atariints.h> |
22 | |
23 | #define NVRAM_BYTES 50 |
24 | |
25 | /* It is worth noting that these functions all access bytes of general |
26 | * purpose memory in the NVRAM - that is to say, they all add the |
27 | * NVRAM_FIRST_BYTE offset. Pass them offsets into NVRAM as if you did not |
28 | * know about the RTC cruft. |
29 | */ |
30 | |
31 | /* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with |
32 | * rtc_lock held. Due to the index-port/data-port design of the RTC, we |
33 | * don't want two different things trying to get to it at once. (e.g. the |
34 | * periodic 11 min sync from kernel/time/ntp.c vs. this driver.) |
35 | */ |
36 | |
37 | static unsigned char __nvram_read_byte(int i) |
38 | { |
39 | return CMOS_READ(NVRAM_FIRST_BYTE + i); |
40 | } |
41 | |
42 | /* This races nicely with trying to read with checksum checking */ |
43 | static void __nvram_write_byte(unsigned char c, int i) |
44 | { |
45 | CMOS_WRITE(c, NVRAM_FIRST_BYTE + i); |
46 | } |
47 | |
48 | /* On Ataris, the checksum is over all bytes except the checksum bytes |
49 | * themselves; these are at the very end. |
50 | */ |
51 | #define ATARI_CKS_RANGE_START 0 |
52 | #define ATARI_CKS_RANGE_END 47 |
53 | #define ATARI_CKS_LOC 48 |
54 | |
55 | static int __nvram_check_checksum(void) |
56 | { |
57 | int i; |
58 | unsigned char sum = 0; |
59 | |
60 | for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i) |
61 | sum += __nvram_read_byte(i); |
62 | return (__nvram_read_byte(ATARI_CKS_LOC) == (~sum & 0xff)) && |
63 | (__nvram_read_byte(ATARI_CKS_LOC + 1) == (sum & 0xff)); |
64 | } |
65 | |
66 | static void __nvram_set_checksum(void) |
67 | { |
68 | int i; |
69 | unsigned char sum = 0; |
70 | |
71 | for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i) |
72 | sum += __nvram_read_byte(i); |
73 | __nvram_write_byte(c: ~sum, ATARI_CKS_LOC); |
74 | __nvram_write_byte(c: sum, ATARI_CKS_LOC + 1); |
75 | } |
76 | |
77 | long atari_nvram_set_checksum(void) |
78 | { |
79 | spin_lock_irq(lock: &rtc_lock); |
80 | __nvram_set_checksum(); |
81 | spin_unlock_irq(lock: &rtc_lock); |
82 | return 0; |
83 | } |
84 | |
85 | long atari_nvram_initialize(void) |
86 | { |
87 | loff_t i; |
88 | |
89 | spin_lock_irq(lock: &rtc_lock); |
90 | for (i = 0; i < NVRAM_BYTES; ++i) |
91 | __nvram_write_byte(c: 0, i); |
92 | __nvram_set_checksum(); |
93 | spin_unlock_irq(lock: &rtc_lock); |
94 | return 0; |
95 | } |
96 | |
97 | ssize_t atari_nvram_read(char *buf, size_t count, loff_t *ppos) |
98 | { |
99 | char *p = buf; |
100 | loff_t i; |
101 | |
102 | spin_lock_irq(lock: &rtc_lock); |
103 | if (!__nvram_check_checksum()) { |
104 | spin_unlock_irq(lock: &rtc_lock); |
105 | return -EIO; |
106 | } |
107 | for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) |
108 | *p = __nvram_read_byte(i); |
109 | spin_unlock_irq(lock: &rtc_lock); |
110 | |
111 | *ppos = i; |
112 | return p - buf; |
113 | } |
114 | |
115 | ssize_t atari_nvram_write(char *buf, size_t count, loff_t *ppos) |
116 | { |
117 | char *p = buf; |
118 | loff_t i; |
119 | |
120 | spin_lock_irq(lock: &rtc_lock); |
121 | if (!__nvram_check_checksum()) { |
122 | spin_unlock_irq(lock: &rtc_lock); |
123 | return -EIO; |
124 | } |
125 | for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) |
126 | __nvram_write_byte(c: *p, i); |
127 | __nvram_set_checksum(); |
128 | spin_unlock_irq(lock: &rtc_lock); |
129 | |
130 | *ppos = i; |
131 | return p - buf; |
132 | } |
133 | |
134 | ssize_t atari_nvram_get_size(void) |
135 | { |
136 | return NVRAM_BYTES; |
137 | } |
138 | |
139 | #ifdef CONFIG_PROC_FS |
140 | static struct { |
141 | unsigned char val; |
142 | const char *name; |
143 | } boot_prefs[] = { |
144 | { 0x80, "TOS" }, |
145 | { 0x40, "ASV" }, |
146 | { 0x20, "NetBSD (?)" }, |
147 | { 0x10, "Linux" }, |
148 | { 0x00, "unspecified" }, |
149 | }; |
150 | |
151 | static const char * const languages[] = { |
152 | "English (US)" , |
153 | "German" , |
154 | "French" , |
155 | "English (UK)" , |
156 | "Spanish" , |
157 | "Italian" , |
158 | "6 (undefined)" , |
159 | "Swiss (French)" , |
160 | "Swiss (German)" , |
161 | }; |
162 | |
163 | static const char * const dateformat[] = { |
164 | "MM%cDD%cYY" , |
165 | "DD%cMM%cYY" , |
166 | "YY%cMM%cDD" , |
167 | "YY%cDD%cMM" , |
168 | "4 (undefined)" , |
169 | "5 (undefined)" , |
170 | "6 (undefined)" , |
171 | "7 (undefined)" , |
172 | }; |
173 | |
174 | static const char * const colors[] = { |
175 | "2" , "4" , "16" , "256" , "65536" , "??" , "??" , "??" |
176 | }; |
177 | |
178 | static void atari_nvram_proc_read(unsigned char *nvram, struct seq_file *seq, |
179 | void *offset) |
180 | { |
181 | int checksum; |
182 | int i; |
183 | unsigned int vmode; |
184 | |
185 | spin_lock_irq(lock: &rtc_lock); |
186 | checksum = __nvram_check_checksum(); |
187 | spin_unlock_irq(lock: &rtc_lock); |
188 | |
189 | seq_printf(m: seq, fmt: "Checksum status : %svalid\n" , checksum ? "" : "not " ); |
190 | |
191 | seq_puts(m: seq, s: "Boot preference : " ); |
192 | for (i = ARRAY_SIZE(boot_prefs) - 1; i >= 0; --i) |
193 | if (nvram[1] == boot_prefs[i].val) { |
194 | seq_printf(m: seq, fmt: "%s\n" , boot_prefs[i].name); |
195 | break; |
196 | } |
197 | if (i < 0) |
198 | seq_printf(m: seq, fmt: "0x%02x (undefined)\n" , nvram[1]); |
199 | |
200 | seq_printf(m: seq, fmt: "SCSI arbitration : %s\n" , |
201 | (nvram[16] & 0x80) ? "on" : "off" ); |
202 | seq_puts(m: seq, s: "SCSI host ID : " ); |
203 | if (nvram[16] & 0x80) |
204 | seq_printf(m: seq, fmt: "%d\n" , nvram[16] & 7); |
205 | else |
206 | seq_puts(m: seq, s: "n/a\n" ); |
207 | |
208 | if (!MACH_IS_FALCON) |
209 | return; |
210 | |
211 | seq_puts(m: seq, s: "OS language : " ); |
212 | if (nvram[6] < ARRAY_SIZE(languages)) |
213 | seq_printf(m: seq, fmt: "%s\n" , languages[nvram[6]]); |
214 | else |
215 | seq_printf(m: seq, fmt: "%u (undefined)\n" , nvram[6]); |
216 | seq_puts(m: seq, s: "Keyboard language: " ); |
217 | if (nvram[7] < ARRAY_SIZE(languages)) |
218 | seq_printf(m: seq, fmt: "%s\n" , languages[nvram[7]]); |
219 | else |
220 | seq_printf(m: seq, fmt: "%u (undefined)\n" , nvram[7]); |
221 | seq_puts(m: seq, s: "Date format : " ); |
222 | seq_printf(m: seq, fmt: dateformat[nvram[8] & 7], |
223 | nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/'); |
224 | seq_printf(m: seq, fmt: ", %dh clock\n" , nvram[8] & 16 ? 24 : 12); |
225 | seq_puts(m: seq, s: "Boot delay : " ); |
226 | if (nvram[10] == 0) |
227 | seq_puts(m: seq, s: "default\n" ); |
228 | else |
229 | seq_printf(m: seq, fmt: "%ds%s\n" , nvram[10], |
230 | nvram[10] < 8 ? ", no memory test" : "" ); |
231 | |
232 | vmode = (nvram[14] << 8) | nvram[15]; |
233 | seq_printf(m: seq, |
234 | fmt: "Video mode : %s colors, %d columns, %s %s monitor\n" , |
235 | colors[vmode & 7], vmode & 8 ? 80 : 40, |
236 | vmode & 16 ? "VGA" : "TV" , vmode & 32 ? "PAL" : "NTSC" ); |
237 | seq_printf(m: seq, |
238 | fmt: " %soverscan, compat. mode %s%s\n" , |
239 | vmode & 64 ? "" : "no " , vmode & 128 ? "on" : "off" , |
240 | vmode & 256 ? |
241 | (vmode & 16 ? ", line doubling" : ", half screen" ) : "" ); |
242 | } |
243 | |
244 | static int nvram_proc_read(struct seq_file *seq, void *offset) |
245 | { |
246 | unsigned char contents[NVRAM_BYTES]; |
247 | int i; |
248 | |
249 | spin_lock_irq(lock: &rtc_lock); |
250 | for (i = 0; i < NVRAM_BYTES; ++i) |
251 | contents[i] = __nvram_read_byte(i); |
252 | spin_unlock_irq(lock: &rtc_lock); |
253 | |
254 | atari_nvram_proc_read(nvram: contents, seq, offset); |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | static int __init atari_nvram_init(void) |
260 | { |
261 | if (!(MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))) |
262 | return -ENODEV; |
263 | |
264 | if (!proc_create_single("driver/nvram" , 0, NULL, nvram_proc_read)) { |
265 | pr_err("nvram: can't create /proc/driver/nvram\n" ); |
266 | return -ENOMEM; |
267 | } |
268 | |
269 | return 0; |
270 | } |
271 | device_initcall(atari_nvram_init); |
272 | #endif /* CONFIG_PROC_FS */ |
273 | |