xref: /openbmc/linux/arch/m68k/mac/misc.c (revision 84fbfc33)
1 /*
2  * Miscellaneous Mac68K-specific stuff
3  */
4 
5 #include <linux/types.h>
6 #include <linux/errno.h>
7 #include <linux/kernel.h>
8 #include <linux/delay.h>
9 #include <linux/sched.h>
10 #include <linux/time.h>
11 #include <linux/rtc.h>
12 #include <linux/mm.h>
13 
14 #include <linux/adb.h>
15 #include <linux/cuda.h>
16 #include <linux/pmu.h>
17 
18 #include <linux/uaccess.h>
19 #include <asm/io.h>
20 #include <asm/segment.h>
21 #include <asm/setup.h>
22 #include <asm/macintosh.h>
23 #include <asm/mac_via.h>
24 #include <asm/mac_oss.h>
25 
26 #include <asm/machdep.h>
27 
28 /* Offset between Unix time (1970-based) and Mac time (1904-based) */
29 
30 #define RTC_OFFSET 2082844800
31 
32 static void (*rom_reset)(void);
33 
34 #ifdef CONFIG_ADB_CUDA
35 static long cuda_read_time(void)
36 {
37 	struct adb_request req;
38 	long time;
39 
40 	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
41 		return 0;
42 	while (!req.complete)
43 		cuda_poll();
44 
45 	time = (req.reply[3] << 24) | (req.reply[4] << 16)
46 		| (req.reply[5] << 8) | req.reply[6];
47 	return time - RTC_OFFSET;
48 }
49 
50 static void cuda_write_time(long data)
51 {
52 	struct adb_request req;
53 	data += RTC_OFFSET;
54 	if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
55 			(data >> 24) & 0xFF, (data >> 16) & 0xFF,
56 			(data >> 8) & 0xFF, data & 0xFF) < 0)
57 		return;
58 	while (!req.complete)
59 		cuda_poll();
60 }
61 
62 static __u8 cuda_read_pram(int offset)
63 {
64 	struct adb_request req;
65 	if (cuda_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM,
66 			(offset >> 8) & 0xFF, offset & 0xFF) < 0)
67 		return 0;
68 	while (!req.complete)
69 		cuda_poll();
70 	return req.reply[3];
71 }
72 
73 static void cuda_write_pram(int offset, __u8 data)
74 {
75 	struct adb_request req;
76 	if (cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM,
77 			(offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
78 		return;
79 	while (!req.complete)
80 		cuda_poll();
81 }
82 #else
83 #define cuda_read_time() 0
84 #define cuda_write_time(n)
85 #define cuda_read_pram NULL
86 #define cuda_write_pram NULL
87 #endif
88 
89 #ifdef CONFIG_ADB_PMU68K
90 static long pmu_read_time(void)
91 {
92 	struct adb_request req;
93 	long time;
94 
95 	if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
96 		return 0;
97 	while (!req.complete)
98 		pmu_poll();
99 
100 	time = (req.reply[1] << 24) | (req.reply[2] << 16)
101 		| (req.reply[3] << 8) | req.reply[4];
102 	return time - RTC_OFFSET;
103 }
104 
105 static void pmu_write_time(long data)
106 {
107 	struct adb_request req;
108 	data += RTC_OFFSET;
109 	if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
110 			(data >> 24) & 0xFF, (data >> 16) & 0xFF,
111 			(data >> 8) & 0xFF, data & 0xFF) < 0)
112 		return;
113 	while (!req.complete)
114 		pmu_poll();
115 }
116 
117 static __u8 pmu_read_pram(int offset)
118 {
119 	struct adb_request req;
120 	if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
121 			(offset >> 8) & 0xFF, offset & 0xFF) < 0)
122 		return 0;
123 	while (!req.complete)
124 		pmu_poll();
125 	return req.reply[3];
126 }
127 
128 static void pmu_write_pram(int offset, __u8 data)
129 {
130 	struct adb_request req;
131 	if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
132 			(offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
133 		return;
134 	while (!req.complete)
135 		pmu_poll();
136 }
137 #else
138 #define pmu_read_time() 0
139 #define pmu_write_time(n)
140 #define pmu_read_pram NULL
141 #define pmu_write_pram NULL
142 #endif
143 
144 /*
145  * VIA PRAM/RTC access routines
146  *
147  * Must be called with interrupts disabled and
148  * the RTC should be enabled.
149  */
150 
151 static __u8 via_pram_readbyte(void)
152 {
153 	int	i,reg;
154 	__u8	data;
155 
156 	reg = via1[vBufB] & ~VIA1B_vRTCClk;
157 
158 	/* Set the RTC data line to be an input. */
159 
160 	via1[vDirB] &= ~VIA1B_vRTCData;
161 
162 	/* The bits of the byte come out in MSB order */
163 
164 	data = 0;
165 	for (i = 0 ; i < 8 ; i++) {
166 		via1[vBufB] = reg;
167 		via1[vBufB] = reg | VIA1B_vRTCClk;
168 		data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
169 	}
170 
171 	/* Return RTC data line to output state */
172 
173 	via1[vDirB] |= VIA1B_vRTCData;
174 
175 	return data;
176 }
177 
178 static void via_pram_writebyte(__u8 data)
179 {
180 	int	i,reg,bit;
181 
182 	reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
183 
184 	/* The bits of the byte go in in MSB order */
185 
186 	for (i = 0 ; i < 8 ; i++) {
187 		bit = data & 0x80? 1 : 0;
188 		data <<= 1;
189 		via1[vBufB] = reg | bit;
190 		via1[vBufB] = reg | bit | VIA1B_vRTCClk;
191 	}
192 }
193 
194 /*
195  * Execute a VIA PRAM/RTC command. For read commands
196  * data should point to a one-byte buffer for the
197  * resulting data. For write commands it should point
198  * to the data byte to for the command.
199  *
200  * This function disables all interrupts while running.
201  */
202 
203 static void via_pram_command(int command, __u8 *data)
204 {
205 	unsigned long flags;
206 	int	is_read;
207 
208 	local_irq_save(flags);
209 
210 	/* Enable the RTC and make sure the strobe line is high */
211 
212 	via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
213 
214 	if (command & 0xFF00) {		/* extended (two-byte) command */
215 		via_pram_writebyte((command & 0xFF00) >> 8);
216 		via_pram_writebyte(command & 0xFF);
217 		is_read = command & 0x8000;
218 	} else {			/* one-byte command */
219 		via_pram_writebyte(command);
220 		is_read = command & 0x80;
221 	}
222 	if (is_read) {
223 		*data = via_pram_readbyte();
224 	} else {
225 		via_pram_writebyte(*data);
226 	}
227 
228 	/* All done, disable the RTC */
229 
230 	via1[vBufB] |= VIA1B_vRTCEnb;
231 
232 	local_irq_restore(flags);
233 }
234 
235 static __u8 via_read_pram(int offset)
236 {
237 	return 0;
238 }
239 
240 static void via_write_pram(int offset, __u8 data)
241 {
242 }
243 
244 /*
245  * Return the current time in seconds since January 1, 1904.
246  *
247  * This only works on machines with the VIA-based PRAM/RTC, which
248  * is basically any machine with Mac II-style ADB.
249  */
250 
251 static long via_read_time(void)
252 {
253 	union {
254 		__u8 cdata[4];
255 		long idata;
256 	} result, last_result;
257 	int count = 1;
258 
259 	via_pram_command(0x81, &last_result.cdata[3]);
260 	via_pram_command(0x85, &last_result.cdata[2]);
261 	via_pram_command(0x89, &last_result.cdata[1]);
262 	via_pram_command(0x8D, &last_result.cdata[0]);
263 
264 	/*
265 	 * The NetBSD guys say to loop until you get the same reading
266 	 * twice in a row.
267 	 */
268 
269 	while (1) {
270 		via_pram_command(0x81, &result.cdata[3]);
271 		via_pram_command(0x85, &result.cdata[2]);
272 		via_pram_command(0x89, &result.cdata[1]);
273 		via_pram_command(0x8D, &result.cdata[0]);
274 
275 		if (result.idata == last_result.idata)
276 			return result.idata - RTC_OFFSET;
277 
278 		if (++count > 10)
279 			break;
280 
281 		last_result.idata = result.idata;
282 	}
283 
284 	pr_err("via_read_time: failed to read a stable value; got 0x%08lx then 0x%08lx\n",
285 	       last_result.idata, result.idata);
286 
287 	return 0;
288 }
289 
290 /*
291  * Set the current time to a number of seconds since January 1, 1904.
292  *
293  * This only works on machines with the VIA-based PRAM/RTC, which
294  * is basically any machine with Mac II-style ADB.
295  */
296 
297 static void via_write_time(long time)
298 {
299 	union {
300 		__u8  cdata[4];
301 		long  idata;
302 	} data;
303 	__u8	temp;
304 
305 	/* Clear the write protect bit */
306 
307 	temp = 0x55;
308 	via_pram_command(0x35, &temp);
309 
310 	data.idata = time + RTC_OFFSET;
311 	via_pram_command(0x01, &data.cdata[3]);
312 	via_pram_command(0x05, &data.cdata[2]);
313 	via_pram_command(0x09, &data.cdata[1]);
314 	via_pram_command(0x0D, &data.cdata[0]);
315 
316 	/* Set the write protect bit */
317 
318 	temp = 0xD5;
319 	via_pram_command(0x35, &temp);
320 }
321 
322 static void via_shutdown(void)
323 {
324 	if (rbv_present) {
325 		via2[rBufB] &= ~0x04;
326 	} else {
327 		/* Direction of vDirB is output */
328 		via2[vDirB] |= 0x04;
329 		/* Send a value of 0 on that line */
330 		via2[vBufB] &= ~0x04;
331 		mdelay(1000);
332 	}
333 }
334 
335 /*
336  * FIXME: not sure how this is supposed to work exactly...
337  */
338 
339 static void oss_shutdown(void)
340 {
341 	oss->rom_ctrl = OSS_POWEROFF;
342 }
343 
344 #ifdef CONFIG_ADB_CUDA
345 
346 static void cuda_restart(void)
347 {
348 	struct adb_request req;
349 	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM) < 0)
350 		return;
351 	while (!req.complete)
352 		cuda_poll();
353 }
354 
355 static void cuda_shutdown(void)
356 {
357 	struct adb_request req;
358 	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN) < 0)
359 		return;
360 
361 	/* Avoid infinite polling loop when PSU is not under Cuda control */
362 	switch (macintosh_config->ident) {
363 	case MAC_MODEL_C660:
364 	case MAC_MODEL_Q605:
365 	case MAC_MODEL_Q605_ACC:
366 	case MAC_MODEL_P475:
367 	case MAC_MODEL_P475F:
368 		return;
369 	}
370 
371 	while (!req.complete)
372 		cuda_poll();
373 }
374 
375 #endif /* CONFIG_ADB_CUDA */
376 
377 #ifdef CONFIG_ADB_PMU68K
378 
379 void pmu_restart(void)
380 {
381 	struct adb_request req;
382 	if (pmu_request(&req, NULL,
383 			2, PMU_SET_INTR_MASK, PMU_INT_ADB|PMU_INT_TICK) < 0)
384 		return;
385 	while (!req.complete)
386 		pmu_poll();
387 	if (pmu_request(&req, NULL, 1, PMU_RESET) < 0)
388 		return;
389 	while (!req.complete)
390 		pmu_poll();
391 }
392 
393 void pmu_shutdown(void)
394 {
395 	struct adb_request req;
396 	if (pmu_request(&req, NULL,
397 			2, PMU_SET_INTR_MASK, PMU_INT_ADB|PMU_INT_TICK) < 0)
398 		return;
399 	while (!req.complete)
400 		pmu_poll();
401 	if (pmu_request(&req, NULL, 5, PMU_SHUTDOWN, 'M', 'A', 'T', 'T') < 0)
402 		return;
403 	while (!req.complete)
404 		pmu_poll();
405 }
406 
407 #endif
408 
409 /*
410  *-------------------------------------------------------------------
411  * Below this point are the generic routines; they'll dispatch to the
412  * correct routine for the hardware on which we're running.
413  *-------------------------------------------------------------------
414  */
415 
416 void mac_pram_read(int offset, __u8 *buffer, int len)
417 {
418 	__u8 (*func)(int);
419 	int i;
420 
421 	switch(macintosh_config->adb_type) {
422 	case MAC_ADB_PB1:
423 	case MAC_ADB_PB2:
424 		func = pmu_read_pram; break;
425 	case MAC_ADB_EGRET:
426 	case MAC_ADB_CUDA:
427 		func = cuda_read_pram; break;
428 	default:
429 		func = via_read_pram;
430 	}
431 	if (!func)
432 		return;
433 	for (i = 0 ; i < len ; i++) {
434 		buffer[i] = (*func)(offset++);
435 	}
436 }
437 
438 void mac_pram_write(int offset, __u8 *buffer, int len)
439 {
440 	void (*func)(int, __u8);
441 	int i;
442 
443 	switch(macintosh_config->adb_type) {
444 	case MAC_ADB_PB1:
445 	case MAC_ADB_PB2:
446 		func = pmu_write_pram; break;
447 	case MAC_ADB_EGRET:
448 	case MAC_ADB_CUDA:
449 		func = cuda_write_pram; break;
450 	default:
451 		func = via_write_pram;
452 	}
453 	if (!func)
454 		return;
455 	for (i = 0 ; i < len ; i++) {
456 		(*func)(offset++, buffer[i]);
457 	}
458 }
459 
460 void mac_poweroff(void)
461 {
462 	if (oss_present) {
463 		oss_shutdown();
464 	} else if (macintosh_config->adb_type == MAC_ADB_II) {
465 		via_shutdown();
466 #ifdef CONFIG_ADB_CUDA
467 	} else if (macintosh_config->adb_type == MAC_ADB_EGRET ||
468 	           macintosh_config->adb_type == MAC_ADB_CUDA) {
469 		cuda_shutdown();
470 #endif
471 #ifdef CONFIG_ADB_PMU68K
472 	} else if (macintosh_config->adb_type == MAC_ADB_PB1
473 		|| macintosh_config->adb_type == MAC_ADB_PB2) {
474 		pmu_shutdown();
475 #endif
476 	}
477 
478 	pr_crit("It is now safe to turn off your Macintosh.\n");
479 	local_irq_disable();
480 	while(1);
481 }
482 
483 void mac_reset(void)
484 {
485 	if (macintosh_config->adb_type == MAC_ADB_II) {
486 		unsigned long flags;
487 
488 		/* need ROMBASE in booter */
489 		/* indeed, plus need to MAP THE ROM !! */
490 
491 		if (mac_bi_data.rombase == 0)
492 			mac_bi_data.rombase = 0x40800000;
493 
494 		/* works on some */
495 		rom_reset = (void *) (mac_bi_data.rombase + 0xa);
496 
497 		if (macintosh_config->ident == MAC_MODEL_SE30) {
498 			/*
499 			 * MSch: Machines known to crash on ROM reset ...
500 			 */
501 		} else {
502 			local_irq_save(flags);
503 
504 			rom_reset();
505 
506 			local_irq_restore(flags);
507 		}
508 #ifdef CONFIG_ADB_CUDA
509 	} else if (macintosh_config->adb_type == MAC_ADB_EGRET ||
510 	           macintosh_config->adb_type == MAC_ADB_CUDA) {
511 		cuda_restart();
512 #endif
513 #ifdef CONFIG_ADB_PMU68K
514 	} else if (macintosh_config->adb_type == MAC_ADB_PB1
515 		|| macintosh_config->adb_type == MAC_ADB_PB2) {
516 		pmu_restart();
517 #endif
518 	} else if (CPU_IS_030) {
519 
520 		/* 030-specific reset routine.  The idea is general, but the
521 		 * specific registers to reset are '030-specific.  Until I
522 		 * have a non-030 machine, I can't test anything else.
523 		 *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
524 		 */
525 
526 		unsigned long rombase = 0x40000000;
527 
528 		/* make a 1-to-1 mapping, using the transparent tran. reg. */
529 		unsigned long virt = (unsigned long) mac_reset;
530 		unsigned long phys = virt_to_phys(mac_reset);
531 		unsigned long addr = (phys&0xFF000000)|0x8777;
532 		unsigned long offset = phys-virt;
533 		local_irq_disable(); /* lets not screw this up, ok? */
534 		__asm__ __volatile__(".chip 68030\n\t"
535 				     "pmove %0,%/tt0\n\t"
536 				     ".chip 68k"
537 				     : : "m" (addr));
538 		/* Now jump to physical address so we can disable MMU */
539 		__asm__ __volatile__(
540                     ".chip 68030\n\t"
541 		    "lea %/pc@(1f),%/a0\n\t"
542 		    "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
543 		    "addl %0,%/sp\n\t"
544 		    "pflusha\n\t"
545 		    "jmp %/a0@\n\t" /* jump into physical memory */
546 		    "0:.long 0\n\t" /* a constant zero. */
547 		    /* OK.  Now reset everything and jump to reset vector. */
548 		    "1:\n\t"
549 		    "lea %/pc@(0b),%/a0\n\t"
550 		    "pmove %/a0@, %/tc\n\t" /* disable mmu */
551 		    "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
552 		    "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
553 		    "movel #0, %/a0\n\t"
554 		    "movec %/a0, %/vbr\n\t" /* clear vector base register */
555 		    "movec %/a0, %/cacr\n\t" /* disable caches */
556 		    "movel #0x0808,%/a0\n\t"
557 		    "movec %/a0, %/cacr\n\t" /* flush i&d caches */
558 		    "movew #0x2700,%/sr\n\t" /* set up status register */
559 		    "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
560 		    "movec %/a0, %/isp\n\t"
561 		    "movel %1@(0x4),%/a0\n\t" /* load reset vector */
562 		    "reset\n\t" /* reset external devices */
563 		    "jmp %/a0@\n\t" /* jump to the reset vector */
564 		    ".chip 68k"
565 		    : : "r" (offset), "a" (rombase) : "a0");
566 	}
567 
568 	/* should never get here */
569 	pr_crit("Restart failed. Please restart manually.\n");
570 	local_irq_disable();
571 	while(1);
572 }
573 
574 /*
575  * This function translates seconds since 1970 into a proper date.
576  *
577  * Algorithm cribbed from glibc2.1, __offtime().
578  */
579 #define SECS_PER_MINUTE (60)
580 #define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
581 #define SECS_PER_DAY   (SECS_PER_HOUR * 24)
582 
583 static void unmktime(unsigned long time, long offset,
584 		     int *yearp, int *monp, int *dayp,
585 		     int *hourp, int *minp, int *secp)
586 {
587         /* How many days come before each month (0-12).  */
588 	static const unsigned short int __mon_yday[2][13] =
589 	{
590 		/* Normal years.  */
591 		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
592 		/* Leap years.  */
593 		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
594 	};
595 	long int days, rem, y, wday, yday;
596 	const unsigned short int *ip;
597 
598 	days = time / SECS_PER_DAY;
599 	rem = time % SECS_PER_DAY;
600 	rem += offset;
601 	while (rem < 0) {
602 		rem += SECS_PER_DAY;
603 		--days;
604 	}
605 	while (rem >= SECS_PER_DAY) {
606 		rem -= SECS_PER_DAY;
607 		++days;
608 	}
609 	*hourp = rem / SECS_PER_HOUR;
610 	rem %= SECS_PER_HOUR;
611 	*minp = rem / SECS_PER_MINUTE;
612 	*secp = rem % SECS_PER_MINUTE;
613 	/* January 1, 1970 was a Thursday. */
614 	wday = (4 + days) % 7; /* Day in the week. Not currently used */
615 	if (wday < 0) wday += 7;
616 	y = 1970;
617 
618 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
619 #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
620 #define __isleap(year)	\
621   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
622 
623 	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
624 	{
625 		/* Guess a corrected year, assuming 365 days per year.  */
626 		long int yg = y + days / 365 - (days % 365 < 0);
627 
628 		/* Adjust DAYS and Y to match the guessed year.  */
629 		days -= ((yg - y) * 365
630 			 + LEAPS_THRU_END_OF (yg - 1)
631 			 - LEAPS_THRU_END_OF (y - 1));
632 		y = yg;
633 	}
634 	*yearp = y - 1900;
635 	yday = days; /* day in the year.  Not currently used. */
636 	ip = __mon_yday[__isleap(y)];
637 	for (y = 11; days < (long int) ip[y]; --y)
638 		continue;
639 	days -= ip[y];
640 	*monp = y;
641 	*dayp = days + 1; /* day in the month */
642 	return;
643 }
644 
645 /*
646  * Read/write the hardware clock.
647  */
648 
649 int mac_hwclk(int op, struct rtc_time *t)
650 {
651 	unsigned long now;
652 
653 	if (!op) { /* read */
654 		switch (macintosh_config->adb_type) {
655 		case MAC_ADB_II:
656 		case MAC_ADB_IOP:
657 			now = via_read_time();
658 			break;
659 		case MAC_ADB_PB1:
660 		case MAC_ADB_PB2:
661 			now = pmu_read_time();
662 			break;
663 		case MAC_ADB_EGRET:
664 		case MAC_ADB_CUDA:
665 			now = cuda_read_time();
666 			break;
667 		default:
668 			now = 0;
669 		}
670 
671 		t->tm_wday = 0;
672 		unmktime(now, 0,
673 			 &t->tm_year, &t->tm_mon, &t->tm_mday,
674 			 &t->tm_hour, &t->tm_min, &t->tm_sec);
675 		pr_debug("%s: read %04d-%02d-%-2d %02d:%02d:%02d\n",
676 		         __func__, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
677 		         t->tm_hour, t->tm_min, t->tm_sec);
678 	} else { /* write */
679 		pr_debug("%s: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
680 		         __func__, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
681 		         t->tm_hour, t->tm_min, t->tm_sec);
682 
683 		now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
684 			     t->tm_hour, t->tm_min, t->tm_sec);
685 
686 		switch (macintosh_config->adb_type) {
687 		case MAC_ADB_II:
688 		case MAC_ADB_IOP:
689 			via_write_time(now);
690 			break;
691 		case MAC_ADB_EGRET:
692 		case MAC_ADB_CUDA:
693 			cuda_write_time(now);
694 			break;
695 		case MAC_ADB_PB1:
696 		case MAC_ADB_PB2:
697 			pmu_write_time(now);
698 			break;
699 		}
700 	}
701 	return 0;
702 }
703 
704 /*
705  * Set minutes/seconds in the hardware clock
706  */
707 
708 int mac_set_clock_mmss (unsigned long nowtime)
709 {
710 	struct rtc_time now;
711 
712 	mac_hwclk(0, &now);
713 	now.tm_sec = nowtime % 60;
714 	now.tm_min = (nowtime / 60) % 60;
715 	mac_hwclk(1, &now);
716 
717 	return 0;
718 }
719