Commit | Line | Data |
---|---|---|
437ace37 FT |
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 | ||
1278cf66 | 37 | static unsigned char __nvram_read_byte(int i) |
437ace37 FT |
38 | { |
39 | return CMOS_READ(NVRAM_FIRST_BYTE + i); | |
40 | } | |
41 | ||
437ace37 | 42 | /* This races nicely with trying to read with checksum checking */ |
1278cf66 | 43 | static void __nvram_write_byte(unsigned char c, int i) |
437ace37 FT |
44 | { |
45 | CMOS_WRITE(c, NVRAM_FIRST_BYTE + i); | |
46 | } | |
47 | ||
437ace37 FT |
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 | ||
1278cf66 | 55 | static int __nvram_check_checksum(void) |
437ace37 FT |
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 | ||
437ace37 FT |
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(~sum, ATARI_CKS_LOC); | |
74 | __nvram_write_byte(sum, ATARI_CKS_LOC + 1); | |
75 | } | |
76 | ||
d3b41b6b | 77 | long atari_nvram_set_checksum(void) |
666047fe FT |
78 | { |
79 | spin_lock_irq(&rtc_lock); | |
80 | __nvram_set_checksum(); | |
81 | spin_unlock_irq(&rtc_lock); | |
82 | return 0; | |
83 | } | |
84 | ||
d3b41b6b | 85 | long atari_nvram_initialize(void) |
666047fe FT |
86 | { |
87 | loff_t i; | |
88 | ||
89 | spin_lock_irq(&rtc_lock); | |
90 | for (i = 0; i < NVRAM_BYTES; ++i) | |
91 | __nvram_write_byte(0, i); | |
92 | __nvram_set_checksum(); | |
93 | spin_unlock_irq(&rtc_lock); | |
94 | return 0; | |
95 | } | |
96 | ||
d3b41b6b | 97 | ssize_t atari_nvram_read(char *buf, size_t count, loff_t *ppos) |
a084dbf6 FT |
98 | { |
99 | char *p = buf; | |
100 | loff_t i; | |
101 | ||
102 | spin_lock_irq(&rtc_lock); | |
103 | if (!__nvram_check_checksum()) { | |
104 | spin_unlock_irq(&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(&rtc_lock); | |
110 | ||
111 | *ppos = i; | |
112 | return p - buf; | |
113 | } | |
114 | ||
d3b41b6b | 115 | ssize_t atari_nvram_write(char *buf, size_t count, loff_t *ppos) |
a084dbf6 FT |
116 | { |
117 | char *p = buf; | |
118 | loff_t i; | |
119 | ||
120 | spin_lock_irq(&rtc_lock); | |
121 | if (!__nvram_check_checksum()) { | |
122 | spin_unlock_irq(&rtc_lock); | |
123 | return -EIO; | |
124 | } | |
125 | for (i = *ppos; count > 0 && i < NVRAM_BYTES; --count, ++i, ++p) | |
126 | __nvram_write_byte(*p, i); | |
127 | __nvram_set_checksum(); | |
128 | spin_unlock_irq(&rtc_lock); | |
129 | ||
130 | *ppos = i; | |
131 | return p - buf; | |
132 | } | |
133 | ||
d3b41b6b | 134 | ssize_t atari_nvram_get_size(void) |
a084dbf6 FT |
135 | { |
136 | return NVRAM_BYTES; | |
137 | } | |
138 | ||
437ace37 FT |
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(&rtc_lock); | |
186 | checksum = __nvram_check_checksum(); | |
187 | spin_unlock_irq(&rtc_lock); | |
188 | ||
189 | seq_printf(seq, "Checksum status : %svalid\n", checksum ? "" : "not "); | |
190 | ||
191 | seq_puts(seq, "Boot preference : "); | |
192 | for (i = ARRAY_SIZE(boot_prefs) - 1; i >= 0; --i) | |
193 | if (nvram[1] == boot_prefs[i].val) { | |
194 | seq_printf(seq, "%s\n", boot_prefs[i].name); | |
195 | break; | |
196 | } | |
197 | if (i < 0) | |
198 | seq_printf(seq, "0x%02x (undefined)\n", nvram[1]); | |
199 | ||
200 | seq_printf(seq, "SCSI arbitration : %s\n", | |
201 | (nvram[16] & 0x80) ? "on" : "off"); | |
202 | seq_puts(seq, "SCSI host ID : "); | |
203 | if (nvram[16] & 0x80) | |
204 | seq_printf(seq, "%d\n", nvram[16] & 7); | |
205 | else | |
206 | seq_puts(seq, "n/a\n"); | |
207 | ||
208 | if (!MACH_IS_FALCON) | |
209 | return; | |
210 | ||
211 | seq_puts(seq, "OS language : "); | |
212 | if (nvram[6] < ARRAY_SIZE(languages)) | |
213 | seq_printf(seq, "%s\n", languages[nvram[6]]); | |
214 | else | |
215 | seq_printf(seq, "%u (undefined)\n", nvram[6]); | |
216 | seq_puts(seq, "Keyboard language: "); | |
217 | if (nvram[7] < ARRAY_SIZE(languages)) | |
218 | seq_printf(seq, "%s\n", languages[nvram[7]]); | |
219 | else | |
220 | seq_printf(seq, "%u (undefined)\n", nvram[7]); | |
221 | seq_puts(seq, "Date format : "); | |
222 | seq_printf(seq, dateformat[nvram[8] & 7], | |
223 | nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/'); | |
224 | seq_printf(seq, ", %dh clock\n", nvram[8] & 16 ? 24 : 12); | |
225 | seq_puts(seq, "Boot delay : "); | |
226 | if (nvram[10] == 0) | |
227 | seq_puts(seq, "default\n"); | |
228 | else | |
229 | seq_printf(seq, "%ds%s\n", nvram[10], | |
230 | nvram[10] < 8 ? ", no memory test" : ""); | |
231 | ||
232 | vmode = (nvram[14] << 8) | nvram[15]; | |
233 | seq_printf(seq, | |
234 | "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(seq, | |
238 | " %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(&rtc_lock); | |
250 | for (i = 0; i < NVRAM_BYTES; ++i) | |
251 | contents[i] = __nvram_read_byte(i); | |
252 | spin_unlock_irq(&rtc_lock); | |
253 | ||
254 | atari_nvram_proc_read(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 */ |