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