2 * Miscellaneous Mac68K-specific stuff
5 #include <linux/config.h>
6 #include <linux/types.h>
7 #include <linux/errno.h>
8 #include <linux/miscdevice.h>
9 #include <linux/kernel.h>
10 #include <linux/delay.h>
11 #include <linux/sched.h>
12 #include <linux/slab.h>
13 #include <linux/time.h>
14 #include <linux/rtc.h>
17 #include <linux/adb.h>
18 #include <linux/cuda.h>
19 #include <linux/pmu.h>
21 #include <asm/uaccess.h>
24 #include <asm/system.h>
25 #include <asm/segment.h>
26 #include <asm/setup.h>
27 #include <asm/macintosh.h>
28 #include <asm/mac_via.h>
29 #include <asm/mac_oss.h>
31 #define BOOTINFO_COMPAT_1_0
32 #include <asm/bootinfo.h>
33 #include <asm/machdep.h>
35 /* Offset between Unix time (1970-based) and Mac time (1904-based) */
37 #define RTC_OFFSET 2082844800
39 extern struct mac_booter_data mac_bi_data
;
40 static void (*rom_reset
)(void);
44 * Return the current time as the number of seconds since January 1, 1904.
47 static long adb_read_time(void)
49 volatile struct adb_request req
;
52 adb_request((struct adb_request
*) &req
, NULL
,
53 ADBREQ_RAW
|ADBREQ_SYNC
,
54 2, CUDA_PACKET
, CUDA_GET_TIME
);
56 time
= (req
.reply
[3] << 24) | (req
.reply
[4] << 16)
57 | (req
.reply
[5] << 8) | req
.reply
[6];
58 return time
- RTC_OFFSET
;
62 * Set the current system time
65 static void adb_write_time(long data
)
67 volatile struct adb_request req
;
71 adb_request((struct adb_request
*) &req
, NULL
,
72 ADBREQ_RAW
|ADBREQ_SYNC
,
73 6, CUDA_PACKET
, CUDA_SET_TIME
,
74 (data
>> 24) & 0xFF, (data
>> 16) & 0xFF,
75 (data
>> 8) & 0xFF, data
& 0xFF);
79 * Get a byte from the NVRAM
82 static __u8
adb_read_pram(int offset
)
84 volatile struct adb_request req
;
86 adb_request((struct adb_request
*) &req
, NULL
,
87 ADBREQ_RAW
|ADBREQ_SYNC
,
88 4, CUDA_PACKET
, CUDA_GET_PRAM
,
89 (offset
>> 8) & 0xFF, offset
& 0xFF);
94 * Write a byte to the NVRAM
97 static void adb_write_pram(int offset
, __u8 data
)
99 volatile struct adb_request req
;
101 adb_request((struct adb_request
*) &req
, NULL
,
102 ADBREQ_RAW
|ADBREQ_SYNC
,
103 5, CUDA_PACKET
, CUDA_SET_PRAM
,
104 (offset
>> 8) & 0xFF, offset
& 0xFF,
107 #endif /* CONFIG_ADB */
110 * VIA PRAM/RTC access routines
112 * Must be called with interrupts disabled and
113 * the RTC should be enabled.
116 static __u8
via_pram_readbyte(void)
121 reg
= via1
[vBufB
] & ~VIA1B_vRTCClk
;
123 /* Set the RTC data line to be an input. */
125 via1
[vDirB
] &= ~VIA1B_vRTCData
;
127 /* The bits of the byte come out in MSB order */
130 for (i
= 0 ; i
< 8 ; i
++) {
132 via1
[vBufB
] = reg
| VIA1B_vRTCClk
;
133 data
= (data
<< 1) | (via1
[vBufB
] & VIA1B_vRTCData
);
136 /* Return RTC data line to output state */
138 via1
[vDirB
] |= VIA1B_vRTCData
;
143 static void via_pram_writebyte(__u8 data
)
147 reg
= via1
[vBufB
] & ~(VIA1B_vRTCClk
| VIA1B_vRTCData
);
149 /* The bits of the byte go in in MSB order */
151 for (i
= 0 ; i
< 8 ; i
++) {
152 bit
= data
& 0x80? 1 : 0;
154 via1
[vBufB
] = reg
| bit
;
155 via1
[vBufB
] = reg
| bit
| VIA1B_vRTCClk
;
160 * Execute a VIA PRAM/RTC command. For read commands
161 * data should point to a one-byte buffer for the
162 * resulting data. For write commands it should point
163 * to the data byte to for the command.
165 * This function disables all interrupts while running.
168 static void via_pram_command(int command
, __u8
*data
)
173 local_irq_save(flags
);
175 /* Enable the RTC and make sure the strobe line is high */
177 via1
[vBufB
] = (via1
[vBufB
] | VIA1B_vRTCClk
) & ~VIA1B_vRTCEnb
;
179 if (command
& 0xFF00) { /* extended (two-byte) command */
180 via_pram_writebyte((command
& 0xFF00) >> 8);
181 via_pram_writebyte(command
& 0xFF);
182 is_read
= command
& 0x8000;
183 } else { /* one-byte command */
184 via_pram_writebyte(command
);
185 is_read
= command
& 0x80;
188 *data
= via_pram_readbyte();
190 via_pram_writebyte(*data
);
193 /* All done, disable the RTC */
195 via1
[vBufB
] |= VIA1B_vRTCEnb
;
197 local_irq_restore(flags
);
200 static __u8
via_read_pram(int offset
)
205 static void via_write_pram(int offset
, __u8 data
)
210 * Return the current time in seconds since January 1, 1904.
212 * This only works on machines with the VIA-based PRAM/RTC, which
213 * is basically any machine with Mac II-style ADB.
216 static long via_read_time(void)
221 } result
, last_result
;
225 * The NetBSD guys say to loop until you get the same reading
232 printk("via_read_time: couldn't get valid time, "
233 "last read = 0x%08lx and 0x%08lx\n",
234 last_result
.idata
, result
.idata
);
238 last_result
.idata
= result
.idata
;
241 via_pram_command(0x81, &result
.cdata
[3]);
242 via_pram_command(0x85, &result
.cdata
[2]);
243 via_pram_command(0x89, &result
.cdata
[1]);
244 via_pram_command(0x8D, &result
.cdata
[0]);
245 } while (result
.idata
!= last_result
.idata
);
247 return result
.idata
- RTC_OFFSET
;
251 * Set the current time to a number of seconds since January 1, 1904.
253 * This only works on machines with the VIA-based PRAM/RTC, which
254 * is basically any machine with Mac II-style ADB.
257 static void via_write_time(long time
)
265 /* Clear the write protect bit */
268 via_pram_command(0x35, &temp
);
270 data
.idata
= time
+ RTC_OFFSET
;
271 via_pram_command(0x01, &data
.cdata
[3]);
272 via_pram_command(0x05, &data
.cdata
[2]);
273 via_pram_command(0x09, &data
.cdata
[1]);
274 via_pram_command(0x0D, &data
.cdata
[0]);
276 /* Set the write protect bit */
279 via_pram_command(0x35, &temp
);
282 static void via_shutdown(void)
285 via2
[rBufB
] &= ~0x04;
287 /* Direction of vDirB is output */
289 /* Send a value of 0 on that line */
290 via2
[vBufB
] &= ~0x04;
296 * FIXME: not sure how this is supposed to work exactly...
299 static void oss_shutdown(void)
301 oss
->rom_ctrl
= OSS_POWEROFF
;
304 #ifdef CONFIG_ADB_CUDA
306 static void cuda_restart(void)
308 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
309 2, CUDA_PACKET
, CUDA_RESET_SYSTEM
);
312 static void cuda_shutdown(void)
314 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
315 2, CUDA_PACKET
, CUDA_POWERDOWN
);
318 #endif /* CONFIG_ADB_CUDA */
320 #ifdef CONFIG_ADB_PMU
322 void pmu_restart(void)
324 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
325 3, PMU_PACKET
, PMU_SET_INTR_MASK
,
326 PMU_INT_ADB
|PMU_INT_TICK
);
328 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
329 2, PMU_PACKET
, PMU_RESET
);
332 void pmu_shutdown(void)
334 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
335 3, PMU_PACKET
, PMU_SET_INTR_MASK
,
336 PMU_INT_ADB
|PMU_INT_TICK
);
338 adb_request(NULL
, NULL
, ADBREQ_RAW
|ADBREQ_SYNC
,
339 6, PMU_PACKET
, PMU_SHUTDOWN
,
343 #endif /* CONFIG_ADB_PMU */
346 *-------------------------------------------------------------------
347 * Below this point are the generic routines; they'll dispatch to the
348 * correct routine for the hardware on which we're running.
349 *-------------------------------------------------------------------
352 void mac_pram_read(int offset
, __u8
*buffer
, int len
)
354 __u8 (*func
)(int) = NULL
;
357 if (macintosh_config
->adb_type
== MAC_ADB_IISI
||
358 macintosh_config
->adb_type
== MAC_ADB_PB1
||
359 macintosh_config
->adb_type
== MAC_ADB_PB2
||
360 macintosh_config
->adb_type
== MAC_ADB_CUDA
) {
362 func
= adb_read_pram
;
367 func
= via_read_pram
;
369 for (i
= 0 ; i
< len
; i
++) {
370 buffer
[i
] = (*func
)(offset
++);
374 void mac_pram_write(int offset
, __u8
*buffer
, int len
)
376 void (*func
)(int, __u8
) = NULL
;
379 if (macintosh_config
->adb_type
== MAC_ADB_IISI
||
380 macintosh_config
->adb_type
== MAC_ADB_PB1
||
381 macintosh_config
->adb_type
== MAC_ADB_PB2
||
382 macintosh_config
->adb_type
== MAC_ADB_CUDA
) {
384 func
= adb_write_pram
;
389 func
= via_write_pram
;
391 for (i
= 0 ; i
< len
; i
++) {
392 (*func
)(offset
++, buffer
[i
]);
396 void mac_poweroff(void)
399 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
400 * work using the ADB packet method. --David Kilzer
405 } else if (macintosh_config
->adb_type
== MAC_ADB_II
) {
407 #ifdef CONFIG_ADB_CUDA
408 } else if (macintosh_config
->adb_type
== MAC_ADB_CUDA
) {
411 #ifdef CONFIG_ADB_PMU
412 } else if (macintosh_config
->adb_type
== MAC_ADB_PB1
413 || macintosh_config
->adb_type
== MAC_ADB_PB2
) {
418 printk("It is now safe to turn off your Macintosh.\n");
424 if (macintosh_config
->adb_type
== MAC_ADB_II
) {
427 /* need ROMBASE in booter */
428 /* indeed, plus need to MAP THE ROM !! */
430 if (mac_bi_data
.rombase
== 0)
431 mac_bi_data
.rombase
= 0x40800000;
434 rom_reset
= (void *) (mac_bi_data
.rombase
+ 0xa);
436 if (macintosh_config
->ident
== MAC_MODEL_SE30
) {
438 * MSch: Machines known to crash on ROM reset ...
441 local_irq_save(flags
);
445 local_irq_restore(flags
);
447 #ifdef CONFIG_ADB_CUDA
448 } else if (macintosh_config
->adb_type
== MAC_ADB_CUDA
) {
451 #ifdef CONFIG_ADB_PMU
452 } else if (macintosh_config
->adb_type
== MAC_ADB_PB1
453 || macintosh_config
->adb_type
== MAC_ADB_PB2
) {
456 } else if (CPU_IS_030
) {
458 /* 030-specific reset routine. The idea is general, but the
459 * specific registers to reset are '030-specific. Until I
460 * have a non-030 machine, I can't test anything else.
461 * -- C. Scott Ananian <cananian@alumni.princeton.edu>
464 unsigned long rombase
= 0x40000000;
466 /* make a 1-to-1 mapping, using the transparent tran. reg. */
467 unsigned long virt
= (unsigned long) mac_reset
;
468 unsigned long phys
= virt_to_phys(mac_reset
);
469 unsigned long offset
= phys
-virt
;
470 local_irq_disable(); /* lets not screw this up, ok? */
471 __asm__
__volatile__(".chip 68030\n\t"
474 : : "m" ((phys
&0xFF000000)|0x8777));
475 /* Now jump to physical address so we can disable MMU */
476 __asm__
__volatile__(
478 "lea %/pc@(1f),%/a0\n\t"
479 "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
482 "jmp %/a0@\n\t" /* jump into physical memory */
483 "0:.long 0\n\t" /* a constant zero. */
484 /* OK. Now reset everything and jump to reset vector. */
486 "lea %/pc@(0b),%/a0\n\t"
487 "pmove %/a0@, %/tc\n\t" /* disable mmu */
488 "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
489 "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
491 "movec %/a0, %/vbr\n\t" /* clear vector base register */
492 "movec %/a0, %/cacr\n\t" /* disable caches */
493 "movel #0x0808,%/a0\n\t"
494 "movec %/a0, %/cacr\n\t" /* flush i&d caches */
495 "movew #0x2700,%/sr\n\t" /* set up status register */
496 "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
497 "movec %/a0, %/isp\n\t"
498 "movel %1@(0x4),%/a0\n\t" /* load reset vector */
499 "reset\n\t" /* reset external devices */
500 "jmp %/a0@\n\t" /* jump to the reset vector */
502 : : "r" (offset
), "a" (rombase
) : "a0");
505 /* should never get here */
507 printk ("Restart failed. Please restart manually.\n");
512 * This function translates seconds since 1970 into a proper date.
514 * Algorithm cribbed from glibc2.1, __offtime().
516 #define SECS_PER_MINUTE (60)
517 #define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
518 #define SECS_PER_DAY (SECS_PER_HOUR * 24)
520 static void unmktime(unsigned long time
, long offset
,
521 int *yearp
, int *monp
, int *dayp
,
522 int *hourp
, int *minp
, int *secp
)
524 /* How many days come before each month (0-12). */
525 static const unsigned short int __mon_yday
[2][13] =
528 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
530 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
532 long int days
, rem
, y
, wday
, yday
;
533 const unsigned short int *ip
;
535 days
= time
/ SECS_PER_DAY
;
536 rem
= time
% SECS_PER_DAY
;
542 while (rem
>= SECS_PER_DAY
) {
546 *hourp
= rem
/ SECS_PER_HOUR
;
547 rem
%= SECS_PER_HOUR
;
548 *minp
= rem
/ SECS_PER_MINUTE
;
549 *secp
= rem
% SECS_PER_MINUTE
;
550 /* January 1, 1970 was a Thursday. */
551 wday
= (4 + days
) % 7; /* Day in the week. Not currently used */
552 if (wday
< 0) wday
+= 7;
555 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
556 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
557 #define __isleap(year) \
558 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
560 while (days
< 0 || days
>= (__isleap (y
) ? 366 : 365))
562 /* Guess a corrected year, assuming 365 days per year. */
563 long int yg
= y
+ days
/ 365 - (days
% 365 < 0);
565 /* Adjust DAYS and Y to match the guessed year. */
566 days
-= ((yg
- y
) * 365
567 + LEAPS_THRU_END_OF (yg
- 1)
568 - LEAPS_THRU_END_OF (y
- 1));
572 yday
= days
; /* day in the year. Not currently used. */
573 ip
= __mon_yday
[__isleap(y
)];
574 for (y
= 11; days
< (long int) ip
[y
]; --y
)
578 *dayp
= days
+ 1; /* day in the month */
583 * Read/write the hardware clock.
586 int mac_hwclk(int op
, struct rtc_time
*t
)
590 if (!op
) { /* read */
591 if (macintosh_config
->adb_type
== MAC_ADB_II
) {
592 now
= via_read_time();
595 if ((macintosh_config
->adb_type
== MAC_ADB_IISI
) ||
596 (macintosh_config
->adb_type
== MAC_ADB_PB1
) ||
597 (macintosh_config
->adb_type
== MAC_ADB_PB2
) ||
598 (macintosh_config
->adb_type
== MAC_ADB_CUDA
)) {
599 now
= adb_read_time();
602 if (macintosh_config
->adb_type
== MAC_ADB_IOP
) {
603 now
= via_read_time();
610 &t
->tm_year
, &t
->tm_mon
, &t
->tm_mday
,
611 &t
->tm_hour
, &t
->tm_min
, &t
->tm_sec
);
612 printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
613 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
, t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
615 printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
616 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
, t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
618 #if 0 /* it trashes my rtc */
619 now
= mktime(t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
620 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
622 if (macintosh_config
->adb_type
== MAC_ADB_II
) {
624 } else if ((macintosh_config
->adb_type
== MAC_ADB_IISI
) ||
625 (macintosh_config
->adb_type
== MAC_ADB_PB1
) ||
626 (macintosh_config
->adb_type
== MAC_ADB_PB2
) ||
627 (macintosh_config
->adb_type
== MAC_ADB_CUDA
)) {
629 } else if (macintosh_config
->adb_type
== MAC_ADB_IOP
) {
638 * Set minutes/seconds in the hardware clock
641 int mac_set_clock_mmss (unsigned long nowtime
)
646 now
.tm_sec
= nowtime
% 60;
647 now
.tm_min
= (nowtime
/ 60) % 60;