at v2.6.13 651 lines 16 kB view raw
1/* 2 * Miscellaneous Mac68K-specific stuff 3 */ 4 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> 15#include <linux/mm.h> 16 17#include <linux/adb.h> 18#include <linux/cuda.h> 19#include <linux/pmu.h> 20 21#include <asm/uaccess.h> 22#include <asm/io.h> 23#include <asm/rtc.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> 30 31#define BOOTINFO_COMPAT_1_0 32#include <asm/bootinfo.h> 33#include <asm/machdep.h> 34 35/* Offset between Unix time (1970-based) and Mac time (1904-based) */ 36 37#define RTC_OFFSET 2082844800 38 39extern struct mac_booter_data mac_bi_data; 40static void (*rom_reset)(void); 41 42#ifdef CONFIG_ADB 43/* 44 * Return the current time as the number of seconds since January 1, 1904. 45 */ 46 47static long adb_read_time(void) 48{ 49 volatile struct adb_request req; 50 long time; 51 52 adb_request((struct adb_request *) &req, NULL, 53 ADBREQ_RAW|ADBREQ_SYNC, 54 2, CUDA_PACKET, CUDA_GET_TIME); 55 56 time = (req.reply[3] << 24) | (req.reply[4] << 16) 57 | (req.reply[5] << 8) | req.reply[6]; 58 return time - RTC_OFFSET; 59} 60 61/* 62 * Set the current system time 63 */ 64 65static void adb_write_time(long data) 66{ 67 volatile struct adb_request req; 68 69 data += RTC_OFFSET; 70 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); 76} 77 78/* 79 * Get a byte from the NVRAM 80 */ 81 82static __u8 adb_read_pram(int offset) 83{ 84 volatile struct adb_request req; 85 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); 90 return req.reply[3]; 91} 92 93/* 94 * Write a byte to the NVRAM 95 */ 96 97static void adb_write_pram(int offset, __u8 data) 98{ 99 volatile struct adb_request req; 100 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, 105 data); 106} 107#endif /* CONFIG_ADB */ 108 109/* 110 * VIA PRAM/RTC access routines 111 * 112 * Must be called with interrupts disabled and 113 * the RTC should be enabled. 114 */ 115 116static __u8 via_pram_readbyte(void) 117{ 118 int i,reg; 119 __u8 data; 120 121 reg = via1[vBufB] & ~VIA1B_vRTCClk; 122 123 /* Set the RTC data line to be an input. */ 124 125 via1[vDirB] &= ~VIA1B_vRTCData; 126 127 /* The bits of the byte come out in MSB order */ 128 129 data = 0; 130 for (i = 0 ; i < 8 ; i++) { 131 via1[vBufB] = reg; 132 via1[vBufB] = reg | VIA1B_vRTCClk; 133 data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData); 134 } 135 136 /* Return RTC data line to output state */ 137 138 via1[vDirB] |= VIA1B_vRTCData; 139 140 return data; 141} 142 143static void via_pram_writebyte(__u8 data) 144{ 145 int i,reg,bit; 146 147 reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData); 148 149 /* The bits of the byte go in in MSB order */ 150 151 for (i = 0 ; i < 8 ; i++) { 152 bit = data & 0x80? 1 : 0; 153 data <<= 1; 154 via1[vBufB] = reg | bit; 155 via1[vBufB] = reg | bit | VIA1B_vRTCClk; 156 } 157} 158 159/* 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. 164 * 165 * This function disables all interrupts while running. 166 */ 167 168static void via_pram_command(int command, __u8 *data) 169{ 170 unsigned long flags; 171 int is_read; 172 173 local_irq_save(flags); 174 175 /* Enable the RTC and make sure the strobe line is high */ 176 177 via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb; 178 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; 186 } 187 if (is_read) { 188 *data = via_pram_readbyte(); 189 } else { 190 via_pram_writebyte(*data); 191 } 192 193 /* All done, disable the RTC */ 194 195 via1[vBufB] |= VIA1B_vRTCEnb; 196 197 local_irq_restore(flags); 198} 199 200static __u8 via_read_pram(int offset) 201{ 202 return 0; 203} 204 205static void via_write_pram(int offset, __u8 data) 206{ 207} 208 209/* 210 * Return the current time in seconds since January 1, 1904. 211 * 212 * This only works on machines with the VIA-based PRAM/RTC, which 213 * is basically any machine with Mac II-style ADB. 214 */ 215 216static long via_read_time(void) 217{ 218 union { 219 __u8 cdata[4]; 220 long idata; 221 } result, last_result; 222 int ct; 223 224 /* 225 * The NetBSD guys say to loop until you get the same reading 226 * twice in a row. 227 */ 228 229 ct = 0; 230 do { 231 if (++ct > 10) { 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); 235 break; 236 } 237 238 last_result.idata = result.idata; 239 result.idata = 0; 240 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); 246 247 return result.idata - RTC_OFFSET; 248} 249 250/* 251 * Set the current time to a number of seconds since January 1, 1904. 252 * 253 * This only works on machines with the VIA-based PRAM/RTC, which 254 * is basically any machine with Mac II-style ADB. 255 */ 256 257static void via_write_time(long time) 258{ 259 union { 260 __u8 cdata[4]; 261 long idata; 262 } data; 263 __u8 temp; 264 265 /* Clear the write protect bit */ 266 267 temp = 0x55; 268 via_pram_command(0x35, &temp); 269 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]); 275 276 /* Set the write protect bit */ 277 278 temp = 0xD5; 279 via_pram_command(0x35, &temp); 280} 281 282static void via_shutdown(void) 283{ 284 if (rbv_present) { 285 via2[rBufB] &= ~0x04; 286 } else { 287 /* Direction of vDirB is output */ 288 via2[vDirB] |= 0x04; 289 /* Send a value of 0 on that line */ 290 via2[vBufB] &= ~0x04; 291 mdelay(1000); 292 } 293} 294 295/* 296 * FIXME: not sure how this is supposed to work exactly... 297 */ 298 299static void oss_shutdown(void) 300{ 301 oss->rom_ctrl = OSS_POWEROFF; 302} 303 304#ifdef CONFIG_ADB_CUDA 305 306static void cuda_restart(void) 307{ 308 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 309 2, CUDA_PACKET, CUDA_RESET_SYSTEM); 310} 311 312static void cuda_shutdown(void) 313{ 314 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 315 2, CUDA_PACKET, CUDA_POWERDOWN); 316} 317 318#endif /* CONFIG_ADB_CUDA */ 319 320#ifdef CONFIG_ADB_PMU 321 322void pmu_restart(void) 323{ 324 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 325 3, PMU_PACKET, PMU_SET_INTR_MASK, 326 PMU_INT_ADB|PMU_INT_TICK); 327 328 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 329 2, PMU_PACKET, PMU_RESET); 330} 331 332void pmu_shutdown(void) 333{ 334 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 335 3, PMU_PACKET, PMU_SET_INTR_MASK, 336 PMU_INT_ADB|PMU_INT_TICK); 337 338 adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC, 339 6, PMU_PACKET, PMU_SHUTDOWN, 340 'M', 'A', 'T', 'T'); 341} 342 343#endif /* CONFIG_ADB_PMU */ 344 345/* 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 *------------------------------------------------------------------- 350 */ 351 352void mac_pram_read(int offset, __u8 *buffer, int len) 353{ 354 __u8 (*func)(int) = NULL; 355 int i; 356 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) { 361#ifdef CONFIG_ADB 362 func = adb_read_pram; 363#else 364 return; 365#endif 366 } else { 367 func = via_read_pram; 368 } 369 for (i = 0 ; i < len ; i++) { 370 buffer[i] = (*func)(offset++); 371 } 372} 373 374void mac_pram_write(int offset, __u8 *buffer, int len) 375{ 376 void (*func)(int, __u8) = NULL; 377 int i; 378 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) { 383#ifdef CONFIG_ADB 384 func = adb_write_pram; 385#else 386 return; 387#endif 388 } else { 389 func = via_write_pram; 390 } 391 for (i = 0 ; i < len ; i++) { 392 (*func)(offset++, buffer[i]); 393 } 394} 395 396void mac_poweroff(void) 397{ 398 /* 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 401 */ 402 403 if (oss_present) { 404 oss_shutdown(); 405 } else if (macintosh_config->adb_type == MAC_ADB_II) { 406 via_shutdown(); 407#ifdef CONFIG_ADB_CUDA 408 } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { 409 cuda_shutdown(); 410#endif 411#ifdef CONFIG_ADB_PMU 412 } else if (macintosh_config->adb_type == MAC_ADB_PB1 413 || macintosh_config->adb_type == MAC_ADB_PB2) { 414 pmu_shutdown(); 415#endif 416 } 417 local_irq_enable(); 418 printk("It is now safe to turn off your Macintosh.\n"); 419 while(1); 420} 421 422void mac_reset(void) 423{ 424 if (macintosh_config->adb_type == MAC_ADB_II) { 425 unsigned long flags; 426 427 /* need ROMBASE in booter */ 428 /* indeed, plus need to MAP THE ROM !! */ 429 430 if (mac_bi_data.rombase == 0) 431 mac_bi_data.rombase = 0x40800000; 432 433 /* works on some */ 434 rom_reset = (void *) (mac_bi_data.rombase + 0xa); 435 436 if (macintosh_config->ident == MAC_MODEL_SE30) { 437 /* 438 * MSch: Machines known to crash on ROM reset ... 439 */ 440 } else { 441 local_irq_save(flags); 442 443 rom_reset(); 444 445 local_irq_restore(flags); 446 } 447#ifdef CONFIG_ADB_CUDA 448 } else if (macintosh_config->adb_type == MAC_ADB_CUDA) { 449 cuda_restart(); 450#endif 451#ifdef CONFIG_ADB_PMU 452 } else if (macintosh_config->adb_type == MAC_ADB_PB1 453 || macintosh_config->adb_type == MAC_ADB_PB2) { 454 pmu_restart(); 455#endif 456 } else if (CPU_IS_030) { 457 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> 462 */ 463 464 unsigned long rombase = 0x40000000; 465 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" 472 "pmove %0,%/tt0\n\t" 473 ".chip 68k" 474 : : "m" ((phys&0xFF000000)|0x8777)); 475 /* Now jump to physical address so we can disable MMU */ 476 __asm__ __volatile__( 477 ".chip 68030\n\t" 478 "lea %/pc@(1f),%/a0\n\t" 479 "addl %0,%/a0\n\t"/* fixup target address and stack ptr */ 480 "addl %0,%/sp\n\t" 481 "pflusha\n\t" 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. */ 485 "1:\n\t" 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 */ 490 "movel #0, %/a0\n\t" 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 */ 501 ".chip 68k" 502 : : "r" (offset), "a" (rombase) : "a0"); 503 } 504 505 /* should never get here */ 506 local_irq_enable(); 507 printk ("Restart failed. Please restart manually.\n"); 508 while(1); 509} 510 511/* 512 * This function translates seconds since 1970 into a proper date. 513 * 514 * Algorithm cribbed from glibc2.1, __offtime(). 515 */ 516#define SECS_PER_MINUTE (60) 517#define SECS_PER_HOUR (SECS_PER_MINUTE * 60) 518#define SECS_PER_DAY (SECS_PER_HOUR * 24) 519 520static void unmktime(unsigned long time, long offset, 521 int *yearp, int *monp, int *dayp, 522 int *hourp, int *minp, int *secp) 523{ 524 /* How many days come before each month (0-12). */ 525 static const unsigned short int __mon_yday[2][13] = 526 { 527 /* Normal years. */ 528 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 529 /* Leap years. */ 530 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 531 }; 532 long int days, rem, y, wday, yday; 533 const unsigned short int *ip; 534 535 days = time / SECS_PER_DAY; 536 rem = time % SECS_PER_DAY; 537 rem += offset; 538 while (rem < 0) { 539 rem += SECS_PER_DAY; 540 --days; 541 } 542 while (rem >= SECS_PER_DAY) { 543 rem -= SECS_PER_DAY; 544 ++days; 545 } 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; 553 y = 1970; 554 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)) 559 560 while (days < 0 || days >= (__isleap (y) ? 366 : 365)) 561 { 562 /* Guess a corrected year, assuming 365 days per year. */ 563 long int yg = y + days / 365 - (days % 365 < 0); 564 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)); 569 y = yg; 570 } 571 *yearp = y - 1900; 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) 575 continue; 576 days -= ip[y]; 577 *monp = y; 578 *dayp = days + 1; /* day in the month */ 579 return; 580} 581 582/* 583 * Read/write the hardware clock. 584 */ 585 586int mac_hwclk(int op, struct rtc_time *t) 587{ 588 unsigned long now; 589 590 if (!op) { /* read */ 591 if (macintosh_config->adb_type == MAC_ADB_II) { 592 now = via_read_time(); 593 } else 594#ifdef CONFIG_ADB 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(); 600 } else 601#endif 602 if (macintosh_config->adb_type == MAC_ADB_IOP) { 603 now = via_read_time(); 604 } else { 605 now = 0; 606 } 607 608 t->tm_wday = 0; 609 unmktime(now, 0, 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); 614 } else { /* write */ 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); 617 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); 621 622 if (macintosh_config->adb_type == MAC_ADB_II) { 623 via_write_time(now); 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)) { 628 adb_write_time(now); 629 } else if (macintosh_config->adb_type == MAC_ADB_IOP) { 630 via_write_time(now); 631 } 632#endif 633 } 634 return 0; 635} 636 637/* 638 * Set minutes/seconds in the hardware clock 639 */ 640 641int mac_set_clock_mmss (unsigned long nowtime) 642{ 643 struct rtc_time now; 644 645 mac_hwclk(0, &now); 646 now.tm_sec = nowtime % 60; 647 now.tm_min = (nowtime / 60) % 60; 648 mac_hwclk(1, &now); 649 650 return 0; 651}