Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/m68k/mvme16x/config.c | |
3 | * | |
4 | * Copyright (C) 1995 Richard Hirst [richard@sleepie.demon.co.uk] | |
5 | * | |
6 | * Based on: | |
7 | * | |
8 | * linux/amiga/config.c | |
9 | * | |
10 | * Copyright (C) 1993 Hamish Macdonald | |
11 | * | |
12 | * This file is subject to the terms and conditions of the GNU General Public | |
13 | * License. See the file README.legal in the main directory of this archive | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/types.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/mm.h> | |
813dcf7a | 20 | #include <linux/seq_file.h> |
1da177e4 | 21 | #include <linux/tty.h> |
3d744eee | 22 | #include <linux/clocksource.h> |
1da177e4 LT |
23 | #include <linux/console.h> |
24 | #include <linux/linkage.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/major.h> | |
1da177e4 LT |
27 | #include <linux/rtc.h> |
28 | #include <linux/interrupt.h> | |
612e484f | 29 | #include <linux/module.h> |
1da177e4 LT |
30 | |
31 | #include <asm/bootinfo.h> | |
4c3c522b | 32 | #include <asm/bootinfo-vme.h> |
abe48101 | 33 | #include <asm/byteorder.h> |
1da177e4 LT |
34 | #include <asm/setup.h> |
35 | #include <asm/irq.h> | |
36 | #include <asm/traps.h> | |
1da177e4 LT |
37 | #include <asm/machdep.h> |
38 | #include <asm/mvme16xhw.h> | |
91d7b75a | 39 | #include <asm/config.h> |
1da177e4 LT |
40 | |
41 | extern t_bdid mvme_bdid; | |
42 | ||
43 | static MK48T08ptr_t volatile rtc = (MK48T08ptr_t)MVME_RTC_BASE; | |
44 | ||
1da177e4 | 45 | static void mvme16x_get_model(char *model); |
f9a01539 | 46 | extern void mvme16x_sched_init(void); |
1da177e4 | 47 | extern int mvme16x_hwclk (int, struct rtc_time *); |
1da177e4 | 48 | extern void mvme16x_reset (void); |
1da177e4 LT |
49 | |
50 | int bcd2int (unsigned char b); | |
51 | ||
1da177e4 LT |
52 | |
53 | unsigned short mvme16x_config; | |
612e484f | 54 | EXPORT_SYMBOL(mvme16x_config); |
1da177e4 LT |
55 | |
56 | ||
a4df02a2 | 57 | int __init mvme16x_parse_bootinfo(const struct bi_record *bi) |
1da177e4 | 58 | { |
abe48101 GU |
59 | uint16_t tag = be16_to_cpu(bi->tag); |
60 | if (tag == BI_VME_TYPE || tag == BI_VME_BRDINFO) | |
1da177e4 LT |
61 | return 0; |
62 | else | |
63 | return 1; | |
64 | } | |
65 | ||
66 | void mvme16x_reset(void) | |
67 | { | |
3c8bc6b7 GU |
68 | pr_info("\r\n\nCalled mvme16x_reset\r\n" |
69 | "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r"); | |
1da177e4 LT |
70 | /* The string of returns is to delay the reset until the whole |
71 | * message is output. Assert reset bit in GCSR */ | |
72 | *(volatile char *)0xfff40107 = 0x80; | |
73 | } | |
74 | ||
75 | static void mvme16x_get_model(char *model) | |
76 | { | |
77 | p_bdid p = &mvme_bdid; | |
78 | char suf[4]; | |
79 | ||
80 | suf[1] = p->brdsuffix[0]; | |
81 | suf[2] = p->brdsuffix[1]; | |
82 | suf[3] = '\0'; | |
83 | suf[0] = suf[1] ? '-' : '\0'; | |
84 | ||
abe48101 | 85 | sprintf(model, "Motorola MVME%x%s", be16_to_cpu(p->brdno), suf); |
1da177e4 LT |
86 | } |
87 | ||
88 | ||
813dcf7a | 89 | static void mvme16x_get_hardware_list(struct seq_file *m) |
1da177e4 | 90 | { |
abe48101 | 91 | uint16_t brdno = be16_to_cpu(mvme_bdid.brdno); |
1da177e4 | 92 | |
abe48101 | 93 | if (brdno == 0x0162 || brdno == 0x0172) |
1da177e4 LT |
94 | { |
95 | unsigned char rev = *(unsigned char *)MVME162_VERSION_REG; | |
96 | ||
813dcf7a | 97 | seq_printf (m, "VMEchip2 %spresent\n", |
1da177e4 | 98 | rev & MVME16x_CONFIG_NO_VMECHIP2 ? "NOT " : ""); |
813dcf7a | 99 | seq_printf (m, "SCSI interface %spresent\n", |
1da177e4 | 100 | rev & MVME16x_CONFIG_NO_SCSICHIP ? "NOT " : ""); |
813dcf7a | 101 | seq_printf (m, "Ethernet i/f %spresent\n", |
1da177e4 LT |
102 | rev & MVME16x_CONFIG_NO_ETHERNET ? "NOT " : ""); |
103 | } | |
1da177e4 LT |
104 | } |
105 | ||
200a3d35 RZ |
106 | /* |
107 | * This function is called during kernel startup to initialize | |
108 | * the mvme16x IRQ handling routines. Should probably ensure | |
109 | * that the base vectors for the VMEChip2 and PCCChip2 are valid. | |
110 | */ | |
111 | ||
66a3f820 | 112 | static void __init mvme16x_init_IRQ (void) |
200a3d35 | 113 | { |
f30a6484 | 114 | m68k_setup_user_interrupt(VEC_USER, 192); |
200a3d35 | 115 | } |
1da177e4 | 116 | |
19999a8b FT |
117 | #define PCC2CHIP (0xfff42000) |
118 | #define PCCSCCMICR (PCC2CHIP + 0x1d) | |
119 | #define PCCSCCTICR (PCC2CHIP + 0x1e) | |
120 | #define PCCSCCRICR (PCC2CHIP + 0x1f) | |
121 | #define PCCTPIACKR (PCC2CHIP + 0x25) | |
f9994894 KJ |
122 | |
123 | #ifdef CONFIG_EARLY_PRINTK | |
124 | ||
125 | /**** cd2401 registers ****/ | |
126 | #define CD2401_ADDR (0xfff45000) | |
127 | ||
128 | #define CyGFRCR (0x81) | |
129 | #define CyCCR (0x13) | |
130 | #define CyCLR_CHAN (0x40) | |
131 | #define CyINIT_CHAN (0x20) | |
132 | #define CyCHIP_RESET (0x10) | |
133 | #define CyENB_XMTR (0x08) | |
134 | #define CyDIS_XMTR (0x04) | |
135 | #define CyENB_RCVR (0x02) | |
136 | #define CyDIS_RCVR (0x01) | |
137 | #define CyCAR (0xee) | |
138 | #define CyIER (0x11) | |
139 | #define CyMdmCh (0x80) | |
140 | #define CyRxExc (0x20) | |
141 | #define CyRxData (0x08) | |
142 | #define CyTxMpty (0x02) | |
143 | #define CyTxRdy (0x01) | |
144 | #define CyLICR (0x26) | |
145 | #define CyRISR (0x89) | |
146 | #define CyTIMEOUT (0x80) | |
147 | #define CySPECHAR (0x70) | |
148 | #define CyOVERRUN (0x08) | |
149 | #define CyPARITY (0x04) | |
150 | #define CyFRAME (0x02) | |
151 | #define CyBREAK (0x01) | |
152 | #define CyREOIR (0x84) | |
153 | #define CyTEOIR (0x85) | |
154 | #define CyMEOIR (0x86) | |
155 | #define CyNOTRANS (0x08) | |
156 | #define CyRFOC (0x30) | |
157 | #define CyRDR (0xf8) | |
158 | #define CyTDR (0xf8) | |
159 | #define CyMISR (0x8b) | |
160 | #define CyRISR (0x89) | |
161 | #define CyTISR (0x8a) | |
162 | #define CyMSVR1 (0xde) | |
163 | #define CyMSVR2 (0xdf) | |
164 | #define CyDSR (0x80) | |
165 | #define CyDCD (0x40) | |
166 | #define CyCTS (0x20) | |
167 | #define CyDTR (0x02) | |
168 | #define CyRTS (0x01) | |
169 | #define CyRTPRL (0x25) | |
170 | #define CyRTPRH (0x24) | |
171 | #define CyCOR1 (0x10) | |
172 | #define CyPARITY_NONE (0x00) | |
173 | #define CyPARITY_E (0x40) | |
174 | #define CyPARITY_O (0xC0) | |
175 | #define Cy_5_BITS (0x04) | |
176 | #define Cy_6_BITS (0x05) | |
177 | #define Cy_7_BITS (0x06) | |
178 | #define Cy_8_BITS (0x07) | |
179 | #define CyCOR2 (0x17) | |
180 | #define CyETC (0x20) | |
181 | #define CyCtsAE (0x02) | |
182 | #define CyCOR3 (0x16) | |
183 | #define Cy_1_STOP (0x02) | |
184 | #define Cy_2_STOP (0x04) | |
185 | #define CyCOR4 (0x15) | |
186 | #define CyREC_FIFO (0x0F) /* Receive FIFO threshold */ | |
187 | #define CyCOR5 (0x14) | |
188 | #define CyCOR6 (0x18) | |
189 | #define CyCOR7 (0x07) | |
190 | #define CyRBPR (0xcb) | |
191 | #define CyRCOR (0xc8) | |
192 | #define CyTBPR (0xc3) | |
193 | #define CyTCOR (0xc0) | |
194 | #define CySCHR1 (0x1f) | |
195 | #define CySCHR2 (0x1e) | |
196 | #define CyTPR (0xda) | |
197 | #define CyPILR1 (0xe3) | |
198 | #define CyPILR2 (0xe0) | |
199 | #define CyPILR3 (0xe1) | |
200 | #define CyCMR (0x1b) | |
201 | #define CyASYNC (0x02) | |
202 | #define CyLICR (0x26) | |
203 | #define CyLIVR (0x09) | |
204 | #define CySCRL (0x23) | |
205 | #define CySCRH (0x22) | |
206 | #define CyTFTC (0x80) | |
207 | ||
c46f46d0 | 208 | void mvme16x_cons_write(struct console *co, const char *str, unsigned count) |
f9994894 KJ |
209 | { |
210 | volatile unsigned char *base_addr = (u_char *)CD2401_ADDR; | |
211 | volatile u_char sink; | |
212 | u_char ier; | |
213 | int port; | |
214 | u_char do_lf = 0; | |
215 | int i = 0; | |
216 | ||
217 | /* Ensure transmitter is enabled! */ | |
218 | ||
219 | port = 0; | |
220 | base_addr[CyCAR] = (u_char)port; | |
221 | while (base_addr[CyCCR]) | |
222 | ; | |
223 | base_addr[CyCCR] = CyENB_XMTR; | |
224 | ||
225 | ier = base_addr[CyIER]; | |
226 | base_addr[CyIER] = CyTxMpty; | |
227 | ||
228 | while (1) { | |
19999a8b | 229 | if (in_8(PCCSCCTICR) & 0x20) |
f9994894 KJ |
230 | { |
231 | /* We have a Tx int. Acknowledge it */ | |
19999a8b | 232 | sink = in_8(PCCTPIACKR); |
f9994894 KJ |
233 | if ((base_addr[CyLICR] >> 2) == port) { |
234 | if (i == count) { | |
235 | /* Last char of string is now output */ | |
236 | base_addr[CyTEOIR] = CyNOTRANS; | |
237 | break; | |
238 | } | |
239 | if (do_lf) { | |
240 | base_addr[CyTDR] = '\n'; | |
241 | str++; | |
242 | i++; | |
243 | do_lf = 0; | |
244 | } | |
245 | else if (*str == '\n') { | |
246 | base_addr[CyTDR] = '\r'; | |
247 | do_lf = 1; | |
248 | } | |
249 | else { | |
250 | base_addr[CyTDR] = *str++; | |
251 | i++; | |
252 | } | |
253 | base_addr[CyTEOIR] = 0; | |
254 | } | |
255 | else | |
256 | base_addr[CyTEOIR] = CyNOTRANS; | |
257 | } | |
258 | } | |
259 | ||
260 | base_addr[CyIER] = ier; | |
261 | } | |
262 | ||
f9994894 | 263 | #endif |
1da177e4 LT |
264 | |
265 | void __init config_mvme16x(void) | |
266 | { | |
267 | p_bdid p = &mvme_bdid; | |
268 | char id[40]; | |
abe48101 | 269 | uint16_t brdno = be16_to_cpu(p->brdno); |
1da177e4 | 270 | |
1da177e4 LT |
271 | mach_sched_init = mvme16x_sched_init; |
272 | mach_init_IRQ = mvme16x_init_IRQ; | |
1da177e4 | 273 | mach_hwclk = mvme16x_hwclk; |
1da177e4 | 274 | mach_reset = mvme16x_reset; |
1da177e4 LT |
275 | mach_get_model = mvme16x_get_model; |
276 | mach_get_hardware_list = mvme16x_get_hardware_list; | |
277 | ||
278 | /* Report board revision */ | |
279 | ||
280 | if (strncmp("BDID", p->bdid, 4)) | |
281 | { | |
3c8bc6b7 | 282 | pr_crit("Bug call .BRD_ID returned garbage - giving up\n"); |
1da177e4 LT |
283 | while (1) |
284 | ; | |
285 | } | |
286 | /* Board type is only set by newer versions of vmelilo/tftplilo */ | |
287 | if (vme_brdtype == 0) | |
abe48101 | 288 | vme_brdtype = brdno; |
1da177e4 LT |
289 | |
290 | mvme16x_get_model(id); | |
3c8bc6b7 GU |
291 | pr_info("BRD_ID: %s BUG %x.%x %02x/%02x/%02x\n", id, p->rev >> 4, |
292 | p->rev & 0xf, p->yr, p->mth, p->day); | |
abe48101 | 293 | if (brdno == 0x0162 || brdno == 0x172) |
1da177e4 LT |
294 | { |
295 | unsigned char rev = *(unsigned char *)MVME162_VERSION_REG; | |
296 | ||
297 | mvme16x_config = rev | MVME16x_CONFIG_GOT_SCCA; | |
298 | ||
3c8bc6b7 GU |
299 | pr_info("MVME%x Hardware status:\n", brdno); |
300 | pr_info(" CPU Type 68%s040\n", | |
301 | rev & MVME16x_CONFIG_GOT_FPU ? "" : "LC"); | |
302 | pr_info(" CPU clock %dMHz\n", | |
303 | rev & MVME16x_CONFIG_SPEED_32 ? 32 : 25); | |
304 | pr_info(" VMEchip2 %spresent\n", | |
305 | rev & MVME16x_CONFIG_NO_VMECHIP2 ? "NOT " : ""); | |
306 | pr_info(" SCSI interface %spresent\n", | |
307 | rev & MVME16x_CONFIG_NO_SCSICHIP ? "NOT " : ""); | |
308 | pr_info(" Ethernet interface %spresent\n", | |
309 | rev & MVME16x_CONFIG_NO_ETHERNET ? "NOT " : ""); | |
1da177e4 LT |
310 | } |
311 | else | |
312 | { | |
313 | mvme16x_config = MVME16x_CONFIG_GOT_LP | MVME16x_CONFIG_GOT_CD2401; | |
1da177e4 LT |
314 | } |
315 | } | |
316 | ||
2850bc27 | 317 | static irqreturn_t mvme16x_abort_int (int irq, void *dev_id) |
1da177e4 | 318 | { |
1da177e4 LT |
319 | unsigned long *new = (unsigned long *)vectors; |
320 | unsigned long *old = (unsigned long *)0xffe00000; | |
321 | volatile unsigned char uc, *ucp; | |
abe48101 | 322 | uint16_t brdno = be16_to_cpu(mvme_bdid.brdno); |
1da177e4 | 323 | |
abe48101 | 324 | if (brdno == 0x0162 || brdno == 0x172) |
1da177e4 LT |
325 | { |
326 | ucp = (volatile unsigned char *)0xfff42043; | |
327 | uc = *ucp | 8; | |
328 | *ucp = uc; | |
329 | } | |
330 | else | |
331 | { | |
332 | *(volatile unsigned long *)0xfff40074 = 0x40000000; | |
333 | } | |
334 | *(new+4) = *(old+4); /* Illegal instruction */ | |
335 | *(new+9) = *(old+9); /* Trace */ | |
336 | *(new+47) = *(old+47); /* Trap #15 */ | |
337 | ||
abe48101 | 338 | if (brdno == 0x0162 || brdno == 0x172) |
1da177e4 LT |
339 | *(new+0x5e) = *(old+0x5e); /* ABORT switch */ |
340 | else | |
341 | *(new+0x6e) = *(old+0x6e); /* ABORT switch */ | |
342 | return IRQ_HANDLED; | |
343 | } | |
344 | ||
3d744eee FT |
345 | static u64 mvme16x_read_clk(struct clocksource *cs); |
346 | ||
347 | static struct clocksource mvme16x_clk = { | |
348 | .name = "pcc", | |
349 | .rating = 250, | |
350 | .read = mvme16x_read_clk, | |
351 | .mask = CLOCKSOURCE_MASK(32), | |
352 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
353 | }; | |
354 | ||
355 | static u32 clk_total; | |
356 | ||
357 | #define PCC_TIMER_CLOCK_FREQ 1000000 | |
358 | #define PCC_TIMER_CYCLES (PCC_TIMER_CLOCK_FREQ / HZ) | |
359 | ||
19999a8b FT |
360 | #define PCCTCMP1 (PCC2CHIP + 0x04) |
361 | #define PCCTCNT1 (PCC2CHIP + 0x08) | |
362 | #define PCCTOVR1 (PCC2CHIP + 0x17) | |
363 | #define PCCTIC1 (PCC2CHIP + 0x1b) | |
364 | ||
365 | #define PCCTOVR1_TIC_EN 0x01 | |
366 | #define PCCTOVR1_COC_EN 0x02 | |
367 | #define PCCTOVR1_OVR_CLR 0x04 | |
368 | ||
43262178 | 369 | #define PCCTIC1_INT_LEVEL 6 |
19999a8b FT |
370 | #define PCCTIC1_INT_CLR 0x08 |
371 | #define PCCTIC1_INT_EN 0x10 | |
372 | ||
2850bc27 | 373 | static irqreturn_t mvme16x_timer_int (int irq, void *dev_id) |
1da177e4 | 374 | { |
1efdd4bd FT |
375 | unsigned long flags; |
376 | ||
377 | local_irq_save(flags); | |
43262178 FT |
378 | out_8(PCCTOVR1, PCCTOVR1_OVR_CLR | PCCTOVR1_TIC_EN | PCCTOVR1_COC_EN); |
379 | out_8(PCCTIC1, PCCTIC1_INT_EN | PCCTIC1_INT_CLR | PCCTIC1_INT_LEVEL); | |
3d744eee | 380 | clk_total += PCC_TIMER_CYCLES; |
42f1d57f | 381 | legacy_timer_tick(1); |
1efdd4bd FT |
382 | local_irq_restore(flags); |
383 | ||
384 | return IRQ_HANDLED; | |
1da177e4 LT |
385 | } |
386 | ||
f9a01539 | 387 | void mvme16x_sched_init(void) |
1da177e4 | 388 | { |
abe48101 | 389 | uint16_t brdno = be16_to_cpu(mvme_bdid.brdno); |
1da177e4 LT |
390 | int irq; |
391 | ||
1da177e4 | 392 | /* Using PCCchip2 or MC2 chip tick timer 1 */ |
3d744eee | 393 | if (request_irq(MVME16x_IRQ_TIMER, mvme16x_timer_int, IRQF_TIMER, "timer", |
42f1d57f | 394 | NULL)) |
1da177e4 LT |
395 | panic ("Couldn't register timer int"); |
396 | ||
43262178 FT |
397 | out_be32(PCCTCNT1, 0); |
398 | out_be32(PCCTCMP1, PCC_TIMER_CYCLES); | |
399 | out_8(PCCTOVR1, PCCTOVR1_OVR_CLR | PCCTOVR1_TIC_EN | PCCTOVR1_COC_EN); | |
400 | out_8(PCCTIC1, PCCTIC1_INT_EN | PCCTIC1_INT_CLR | PCCTIC1_INT_LEVEL); | |
401 | ||
3d744eee FT |
402 | clocksource_register_hz(&mvme16x_clk, PCC_TIMER_CLOCK_FREQ); |
403 | ||
abe48101 | 404 | if (brdno == 0x0162 || brdno == 0x172) |
1da177e4 LT |
405 | irq = MVME162_IRQ_ABORT; |
406 | else | |
407 | irq = MVME167_IRQ_ABORT; | |
408 | if (request_irq(irq, mvme16x_abort_int, 0, | |
409 | "abort", mvme16x_abort_int)) | |
410 | panic ("Couldn't register abort int"); | |
411 | } | |
412 | ||
3d744eee | 413 | static u64 mvme16x_read_clk(struct clocksource *cs) |
1da177e4 | 414 | { |
3d744eee | 415 | unsigned long flags; |
19999a8b | 416 | u8 overflow, tmp; |
3d744eee FT |
417 | u32 ticks; |
418 | ||
419 | local_irq_save(flags); | |
19999a8b FT |
420 | tmp = in_8(PCCTOVR1) >> 4; |
421 | ticks = in_be32(PCCTCNT1); | |
422 | overflow = in_8(PCCTOVR1) >> 4; | |
423 | if (overflow != tmp) | |
424 | ticks = in_be32(PCCTCNT1); | |
425 | ticks += overflow * PCC_TIMER_CYCLES; | |
3d744eee FT |
426 | ticks += clk_total; |
427 | local_irq_restore(flags); | |
428 | ||
429 | return ticks; | |
1da177e4 LT |
430 | } |
431 | ||
432 | int bcd2int (unsigned char b) | |
433 | { | |
434 | return ((b>>4)*10 + (b&15)); | |
435 | } | |
436 | ||
437 | int mvme16x_hwclk(int op, struct rtc_time *t) | |
438 | { | |
1da177e4 LT |
439 | if (!op) { |
440 | rtc->ctrl = RTC_READ; | |
441 | t->tm_year = bcd2int (rtc->bcd_year); | |
b65769fc | 442 | t->tm_mon = bcd2int(rtc->bcd_mth) - 1; |
1da177e4 LT |
443 | t->tm_mday = bcd2int (rtc->bcd_dom); |
444 | t->tm_hour = bcd2int (rtc->bcd_hr); | |
445 | t->tm_min = bcd2int (rtc->bcd_min); | |
446 | t->tm_sec = bcd2int (rtc->bcd_sec); | |
447 | rtc->ctrl = 0; | |
b65769fc FT |
448 | if (t->tm_year < 70) |
449 | t->tm_year += 100; | |
a7b68ed1 GU |
450 | } else { |
451 | /* FIXME Setting the time is not yet supported */ | |
452 | return -EOPNOTSUPP; | |
1da177e4 LT |
453 | } |
454 | return 0; | |
455 | } |