xref: /openbmc/linux/arch/m68k/mac/misc.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Miscellaneous Mac68K-specific stuff
41da177e4SLinus Torvalds  */
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #include <linux/types.h>
71da177e4SLinus Torvalds #include <linux/errno.h>
81da177e4SLinus Torvalds #include <linux/kernel.h>
91da177e4SLinus Torvalds #include <linux/delay.h>
101da177e4SLinus Torvalds #include <linux/sched.h>
111da177e4SLinus Torvalds #include <linux/time.h>
121da177e4SLinus Torvalds #include <linux/rtc.h>
131da177e4SLinus Torvalds #include <linux/mm.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/adb.h>
161da177e4SLinus Torvalds #include <linux/cuda.h>
171da177e4SLinus Torvalds #include <linux/pmu.h>
181da177e4SLinus Torvalds 
197c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
201da177e4SLinus Torvalds #include <asm/io.h>
211da177e4SLinus Torvalds #include <asm/setup.h>
221da177e4SLinus Torvalds #include <asm/macintosh.h>
231da177e4SLinus Torvalds #include <asm/mac_via.h>
241da177e4SLinus Torvalds #include <asm/mac_oss.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <asm/machdep.h>
271da177e4SLinus Torvalds 
285b9bfb8eSArnd Bergmann /*
295b9bfb8eSArnd Bergmann  * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU
305b9bfb8eSArnd Bergmann  * times wrap in 2040. If we need to handle later times, the read_time functions
315b9bfb8eSArnd Bergmann  * need to be changed to interpret wrapped times as post-2040.
325b9bfb8eSArnd Bergmann  */
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #define RTC_OFFSET 2082844800
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds static void (*rom_reset)(void);
371da177e4SLinus Torvalds 
38d3b41b6bSFinn Thain #if IS_ENABLED(CONFIG_NVRAM)
393272244cSAl Viro #ifdef CONFIG_ADB_CUDA
cuda_pram_read_byte(int offset)40cda67df5SFinn Thain static unsigned char cuda_pram_read_byte(int offset)
413272244cSAl Viro {
423272244cSAl Viro 	struct adb_request req;
4331b1c780SFinn Thain 
443272244cSAl Viro 	if (cuda_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM,
453272244cSAl Viro 			 (offset >> 8) & 0xFF, offset & 0xFF) < 0)
463272244cSAl Viro 		return 0;
473272244cSAl Viro 	while (!req.complete)
483272244cSAl Viro 		cuda_poll();
493272244cSAl Viro 	return req.reply[3];
503272244cSAl Viro }
513272244cSAl Viro 
cuda_pram_write_byte(unsigned char data,int offset)52cda67df5SFinn Thain static void cuda_pram_write_byte(unsigned char data, int offset)
533272244cSAl Viro {
543272244cSAl Viro 	struct adb_request req;
5531b1c780SFinn Thain 
563272244cSAl Viro 	if (cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM,
573272244cSAl Viro 			 (offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
583272244cSAl Viro 		return;
593272244cSAl Viro 	while (!req.complete)
603272244cSAl Viro 		cuda_poll();
613272244cSAl Viro }
626df2afbaSFinn Thain #endif /* CONFIG_ADB_CUDA */
633272244cSAl Viro 
64ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
pmu_pram_read_byte(int offset)65cda67df5SFinn Thain static unsigned char pmu_pram_read_byte(int offset)
663272244cSAl Viro {
673272244cSAl Viro 	struct adb_request req;
6831b1c780SFinn Thain 
69aefcb746SFinn Thain 	if (pmu_request(&req, NULL, 3, PMU_READ_XPRAM,
70aefcb746SFinn Thain 	                offset & 0xFF, 1) < 0)
713272244cSAl Viro 		return 0;
72aefcb746SFinn Thain 	pmu_wait_complete(&req);
73aefcb746SFinn Thain 
74aefcb746SFinn Thain 	return req.reply[0];
753272244cSAl Viro }
763272244cSAl Viro 
pmu_pram_write_byte(unsigned char data,int offset)77cda67df5SFinn Thain static void pmu_pram_write_byte(unsigned char data, int offset)
783272244cSAl Viro {
793272244cSAl Viro 	struct adb_request req;
8031b1c780SFinn Thain 
81aefcb746SFinn Thain 	if (pmu_request(&req, NULL, 4, PMU_WRITE_XPRAM,
82aefcb746SFinn Thain 	                offset & 0xFF, 1, data) < 0)
833272244cSAl Viro 		return;
84aefcb746SFinn Thain 	pmu_wait_complete(&req);
853272244cSAl Viro }
86ebd72227SFinn Thain #endif /* CONFIG_ADB_PMU */
87d3b41b6bSFinn Thain #endif /* CONFIG_NVRAM */
883272244cSAl Viro 
891da177e4SLinus Torvalds /*
901da177e4SLinus Torvalds  * VIA PRAM/RTC access routines
911da177e4SLinus Torvalds  *
921da177e4SLinus Torvalds  * Must be called with interrupts disabled and
931da177e4SLinus Torvalds  * the RTC should be enabled.
941da177e4SLinus Torvalds  */
951da177e4SLinus Torvalds 
via_rtc_recv(void)96cda67df5SFinn Thain static __u8 via_rtc_recv(void)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	int i, reg;
991da177e4SLinus Torvalds 	__u8 data;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	reg = via1[vBufB] & ~VIA1B_vRTCClk;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	/* Set the RTC data line to be an input. */
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	via1[vDirB] &= ~VIA1B_vRTCData;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	/* The bits of the byte come out in MSB order */
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	data = 0;
1101da177e4SLinus Torvalds 	for (i = 0 ; i < 8 ; i++) {
1111da177e4SLinus Torvalds 		via1[vBufB] = reg;
1121da177e4SLinus Torvalds 		via1[vBufB] = reg | VIA1B_vRTCClk;
1131da177e4SLinus Torvalds 		data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
1141da177e4SLinus Torvalds 	}
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	/* Return RTC data line to output state */
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	via1[vDirB] |= VIA1B_vRTCData;
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	return data;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
via_rtc_send(__u8 data)123cda67df5SFinn Thain static void via_rtc_send(__u8 data)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds 	int i, reg, bit;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
1281da177e4SLinus Torvalds 
129550a998fSGeert Uytterhoeven 	/* The bits of the byte go into the RTC in MSB order */
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	for (i = 0 ; i < 8 ; i++) {
1321da177e4SLinus Torvalds 		bit = data & 0x80? 1 : 0;
1331da177e4SLinus Torvalds 		data <<= 1;
1341da177e4SLinus Torvalds 		via1[vBufB] = reg | bit;
1351da177e4SLinus Torvalds 		via1[vBufB] = reg | bit | VIA1B_vRTCClk;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds /*
140a71fa0e3SFinn Thain  * These values can be found in Inside Macintosh vol. III ch. 2
141a71fa0e3SFinn Thain  * which has a description of the RTC chip in the original Mac.
142a71fa0e3SFinn Thain  */
143a71fa0e3SFinn Thain 
144a71fa0e3SFinn Thain #define RTC_FLG_READ            BIT(7)
145a71fa0e3SFinn Thain #define RTC_FLG_WRITE_PROTECT   BIT(7)
146a71fa0e3SFinn Thain #define RTC_CMD_READ(r)         (RTC_FLG_READ | (r << 2))
147a71fa0e3SFinn Thain #define RTC_CMD_WRITE(r)        (r << 2)
148a71fa0e3SFinn Thain #define RTC_REG_SECONDS_0       0
149a71fa0e3SFinn Thain #define RTC_REG_SECONDS_1       1
150a71fa0e3SFinn Thain #define RTC_REG_SECONDS_2       2
151a71fa0e3SFinn Thain #define RTC_REG_SECONDS_3       3
152a71fa0e3SFinn Thain #define RTC_REG_WRITE_PROTECT   13
153a71fa0e3SFinn Thain 
154a71fa0e3SFinn Thain /*
155aefcb746SFinn Thain  * Inside Mac has no information about two-byte RTC commands but
156aefcb746SFinn Thain  * the MAME/MESS source code has the essentials.
157aefcb746SFinn Thain  */
158aefcb746SFinn Thain 
159aefcb746SFinn Thain #define RTC_REG_XPRAM           14
160aefcb746SFinn Thain #define RTC_CMD_XPRAM_READ      (RTC_CMD_READ(RTC_REG_XPRAM) << 8)
161aefcb746SFinn Thain #define RTC_CMD_XPRAM_WRITE     (RTC_CMD_WRITE(RTC_REG_XPRAM) << 8)
162aefcb746SFinn Thain #define RTC_CMD_XPRAM_ARG(a)    (((a & 0xE0) << 3) | ((a & 0x1F) << 2))
163aefcb746SFinn Thain 
164aefcb746SFinn Thain /*
1651da177e4SLinus Torvalds  * Execute a VIA PRAM/RTC command. For read commands
1661da177e4SLinus Torvalds  * data should point to a one-byte buffer for the
1671da177e4SLinus Torvalds  * resulting data. For write commands it should point
1681da177e4SLinus Torvalds  * to the data byte to for the command.
1691da177e4SLinus Torvalds  *
1701da177e4SLinus Torvalds  * This function disables all interrupts while running.
1711da177e4SLinus Torvalds  */
1721da177e4SLinus Torvalds 
via_rtc_command(int command,__u8 * data)173a71fa0e3SFinn Thain static void via_rtc_command(int command, __u8 *data)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds 	unsigned long flags;
1761da177e4SLinus Torvalds 	int is_read;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	local_irq_save(flags);
1791da177e4SLinus Torvalds 
180a71fa0e3SFinn Thain 	/* The least significant bits must be 0b01 according to Inside Mac */
181a71fa0e3SFinn Thain 
182a71fa0e3SFinn Thain 	command = (command & ~3) | 1;
183a71fa0e3SFinn Thain 
1841da177e4SLinus Torvalds 	/* Enable the RTC and make sure the strobe line is high */
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	if (command & 0xFF00) {		/* extended (two-byte) command */
189cda67df5SFinn Thain 		via_rtc_send((command & 0xFF00) >> 8);
190cda67df5SFinn Thain 		via_rtc_send(command & 0xFF);
191a71fa0e3SFinn Thain 		is_read = command & (RTC_FLG_READ << 8);
1921da177e4SLinus Torvalds 	} else {			/* one-byte command */
193cda67df5SFinn Thain 		via_rtc_send(command);
194a71fa0e3SFinn Thain 		is_read = command & RTC_FLG_READ;
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 	if (is_read) {
197cda67df5SFinn Thain 		*data = via_rtc_recv();
1981da177e4SLinus Torvalds 	} else {
199cda67df5SFinn Thain 		via_rtc_send(*data);
2001da177e4SLinus Torvalds 	}
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	/* All done, disable the RTC */
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	via1[vBufB] |= VIA1B_vRTCEnb;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	local_irq_restore(flags);
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
209d3b41b6bSFinn Thain #if IS_ENABLED(CONFIG_NVRAM)
via_pram_read_byte(int offset)210cda67df5SFinn Thain static unsigned char via_pram_read_byte(int offset)
2111da177e4SLinus Torvalds {
212aefcb746SFinn Thain 	unsigned char temp;
213aefcb746SFinn Thain 
214aefcb746SFinn Thain 	via_rtc_command(RTC_CMD_XPRAM_READ | RTC_CMD_XPRAM_ARG(offset), &temp);
215aefcb746SFinn Thain 
216aefcb746SFinn Thain 	return temp;
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
via_pram_write_byte(unsigned char data,int offset)219cda67df5SFinn Thain static void via_pram_write_byte(unsigned char data, int offset)
2201da177e4SLinus Torvalds {
221aefcb746SFinn Thain 	unsigned char temp;
222aefcb746SFinn Thain 
223aefcb746SFinn Thain 	temp = 0x55;
224aefcb746SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
225aefcb746SFinn Thain 
226aefcb746SFinn Thain 	temp = data;
227aefcb746SFinn Thain 	via_rtc_command(RTC_CMD_XPRAM_WRITE | RTC_CMD_XPRAM_ARG(offset), &temp);
228aefcb746SFinn Thain 
229aefcb746SFinn Thain 	temp = 0x55 | RTC_FLG_WRITE_PROTECT;
230aefcb746SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
2311da177e4SLinus Torvalds }
232d3b41b6bSFinn Thain #endif /* CONFIG_NVRAM */
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds /*
2351da177e4SLinus Torvalds  * Return the current time in seconds since January 1, 1904.
2361da177e4SLinus Torvalds  *
2371da177e4SLinus Torvalds  * This only works on machines with the VIA-based PRAM/RTC, which
2381da177e4SLinus Torvalds  * is basically any machine with Mac II-style ADB.
2391da177e4SLinus Torvalds  */
2401da177e4SLinus Torvalds 
via_read_time(void)2415b9bfb8eSArnd Bergmann static time64_t via_read_time(void)
2421da177e4SLinus Torvalds {
2431da177e4SLinus Torvalds 	union {
2441da177e4SLinus Torvalds 		__u8 cdata[4];
2455b9bfb8eSArnd Bergmann 		__u32 idata;
2461da177e4SLinus Torvalds 	} result, last_result;
24775a23850SFinn Thain 	int count = 1;
24875a23850SFinn Thain 
249a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0), &last_result.cdata[3]);
250a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1), &last_result.cdata[2]);
251a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2), &last_result.cdata[1]);
252a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3), &last_result.cdata[0]);
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	/*
2551da177e4SLinus Torvalds 	 * The NetBSD guys say to loop until you get the same reading
2561da177e4SLinus Torvalds 	 * twice in a row.
2571da177e4SLinus Torvalds 	 */
2581da177e4SLinus Torvalds 
25975a23850SFinn Thain 	while (1) {
260a71fa0e3SFinn Thain 		via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0),
261a71fa0e3SFinn Thain 		                &result.cdata[3]);
262a71fa0e3SFinn Thain 		via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1),
263a71fa0e3SFinn Thain 		                &result.cdata[2]);
264a71fa0e3SFinn Thain 		via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2),
265a71fa0e3SFinn Thain 		                &result.cdata[1]);
266a71fa0e3SFinn Thain 		via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3),
267a71fa0e3SFinn Thain 		                &result.cdata[0]);
2681da177e4SLinus Torvalds 
26975a23850SFinn Thain 		if (result.idata == last_result.idata)
2705b9bfb8eSArnd Bergmann 			return (time64_t)result.idata - RTC_OFFSET;
27175a23850SFinn Thain 
27275a23850SFinn Thain 		if (++count > 10)
27375a23850SFinn Thain 			break;
27475a23850SFinn Thain 
27575a23850SFinn Thain 		last_result.idata = result.idata;
27675a23850SFinn Thain 	}
27775a23850SFinn Thain 
2785b9bfb8eSArnd Bergmann 	pr_err("%s: failed to read a stable value; got 0x%08x then 0x%08x\n",
2795b9bfb8eSArnd Bergmann 	       __func__, last_result.idata, result.idata);
28075a23850SFinn Thain 
28175a23850SFinn Thain 	return 0;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds /*
2851da177e4SLinus Torvalds  * Set the current time to a number of seconds since January 1, 1904.
2861da177e4SLinus Torvalds  *
2871da177e4SLinus Torvalds  * This only works on machines with the VIA-based PRAM/RTC, which
2881da177e4SLinus Torvalds  * is basically any machine with Mac II-style ADB.
2891da177e4SLinus Torvalds  */
2901da177e4SLinus Torvalds 
via_set_rtc_time(struct rtc_time * tm)2910792a2c8SFinn Thain static void via_set_rtc_time(struct rtc_time *tm)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	union {
2941da177e4SLinus Torvalds 		__u8 cdata[4];
2955b9bfb8eSArnd Bergmann 		__u32 idata;
2961da177e4SLinus Torvalds 	} data;
2971da177e4SLinus Torvalds 	__u8 temp;
2980792a2c8SFinn Thain 	time64_t time;
2990792a2c8SFinn Thain 
3000792a2c8SFinn Thain 	time = mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
3010792a2c8SFinn Thain 	                tm->tm_hour, tm->tm_min, tm->tm_sec);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	/* Clear the write protect bit */
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	temp = 0x55;
306a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
3071da177e4SLinus Torvalds 
3085b9bfb8eSArnd Bergmann 	data.idata = lower_32_bits(time + RTC_OFFSET);
309a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_0), &data.cdata[3]);
310a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_1), &data.cdata[2]);
311a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_2), &data.cdata[1]);
312a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_3), &data.cdata[0]);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	/* Set the write protect bit */
3151da177e4SLinus Torvalds 
316a71fa0e3SFinn Thain 	temp = 0x55 | RTC_FLG_WRITE_PROTECT;
317a71fa0e3SFinn Thain 	via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
3181da177e4SLinus Torvalds }
3191da177e4SLinus Torvalds 
via_shutdown(void)3201da177e4SLinus Torvalds static void via_shutdown(void)
3211da177e4SLinus Torvalds {
3221da177e4SLinus Torvalds 	if (rbv_present) {
3231da177e4SLinus Torvalds 		via2[rBufB] &= ~0x04;
3241da177e4SLinus Torvalds 	} else {
3251da177e4SLinus Torvalds 		/* Direction of vDirB is output */
3261da177e4SLinus Torvalds 		via2[vDirB] |= 0x04;
3271da177e4SLinus Torvalds 		/* Send a value of 0 on that line */
3281da177e4SLinus Torvalds 		via2[vBufB] &= ~0x04;
3291da177e4SLinus Torvalds 		mdelay(1000);
3301da177e4SLinus Torvalds 	}
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
oss_shutdown(void)3331da177e4SLinus Torvalds static void oss_shutdown(void)
3341da177e4SLinus Torvalds {
3351da177e4SLinus Torvalds 	oss->rom_ctrl = OSS_POWEROFF;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
cuda_restart(void)3391da177e4SLinus Torvalds static void cuda_restart(void)
3401da177e4SLinus Torvalds {
3413272244cSAl Viro 	struct adb_request req;
34231b1c780SFinn Thain 
3433272244cSAl Viro 	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM) < 0)
3443272244cSAl Viro 		return;
3453272244cSAl Viro 	while (!req.complete)
3463272244cSAl Viro 		cuda_poll();
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds 
cuda_shutdown(void)3491da177e4SLinus Torvalds static void cuda_shutdown(void)
3501da177e4SLinus Torvalds {
3513272244cSAl Viro 	struct adb_request req;
35231b1c780SFinn Thain 
3533272244cSAl Viro 	if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN) < 0)
3543272244cSAl Viro 		return;
35541e93a30SFinn Thain 
35641e93a30SFinn Thain 	/* Avoid infinite polling loop when PSU is not under Cuda control */
35741e93a30SFinn Thain 	switch (macintosh_config->ident) {
35841e93a30SFinn Thain 	case MAC_MODEL_C660:
35941e93a30SFinn Thain 	case MAC_MODEL_Q605:
36041e93a30SFinn Thain 	case MAC_MODEL_Q605_ACC:
36141e93a30SFinn Thain 	case MAC_MODEL_P475:
36241e93a30SFinn Thain 	case MAC_MODEL_P475F:
36341e93a30SFinn Thain 		return;
36441e93a30SFinn Thain 	}
36541e93a30SFinn Thain 
3663272244cSAl Viro 	while (!req.complete)
3673272244cSAl Viro 		cuda_poll();
3681da177e4SLinus Torvalds }
3691da177e4SLinus Torvalds #endif /* CONFIG_ADB_CUDA */
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds /*
3721da177e4SLinus Torvalds  *-------------------------------------------------------------------
3731da177e4SLinus Torvalds  * Below this point are the generic routines; they'll dispatch to the
3741da177e4SLinus Torvalds  * correct routine for the hardware on which we're running.
3751da177e4SLinus Torvalds  *-------------------------------------------------------------------
3761da177e4SLinus Torvalds  */
3771da177e4SLinus Torvalds 
378d3b41b6bSFinn Thain #if IS_ENABLED(CONFIG_NVRAM)
mac_pram_read_byte(int addr)379cda67df5SFinn Thain unsigned char mac_pram_read_byte(int addr)
3801da177e4SLinus Torvalds {
3813272244cSAl Viro 	switch (macintosh_config->adb_type) {
3826df2afbaSFinn Thain 	case MAC_ADB_IOP:
3836df2afbaSFinn Thain 	case MAC_ADB_II:
3843272244cSAl Viro 	case MAC_ADB_PB1:
385cda67df5SFinn Thain 		return via_pram_read_byte(addr);
3866df2afbaSFinn Thain #ifdef CONFIG_ADB_CUDA
387f74faec6SFinn Thain 	case MAC_ADB_EGRET:
3883272244cSAl Viro 	case MAC_ADB_CUDA:
389cda67df5SFinn Thain 		return cuda_pram_read_byte(addr);
3906df2afbaSFinn Thain #endif
391ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
3926df2afbaSFinn Thain 	case MAC_ADB_PB2:
393cda67df5SFinn Thain 		return pmu_pram_read_byte(addr);
3946df2afbaSFinn Thain #endif
3953272244cSAl Viro 	default:
396cda67df5SFinn Thain 		return 0xFF;
3971da177e4SLinus Torvalds 	}
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
mac_pram_write_byte(unsigned char val,int addr)400cda67df5SFinn Thain void mac_pram_write_byte(unsigned char val, int addr)
4011da177e4SLinus Torvalds {
4023272244cSAl Viro 	switch (macintosh_config->adb_type) {
4036df2afbaSFinn Thain 	case MAC_ADB_IOP:
4046df2afbaSFinn Thain 	case MAC_ADB_II:
4053272244cSAl Viro 	case MAC_ADB_PB1:
406cda67df5SFinn Thain 		via_pram_write_byte(val, addr);
40731b1c780SFinn Thain 		break;
4086df2afbaSFinn Thain #ifdef CONFIG_ADB_CUDA
409f74faec6SFinn Thain 	case MAC_ADB_EGRET:
4103272244cSAl Viro 	case MAC_ADB_CUDA:
411cda67df5SFinn Thain 		cuda_pram_write_byte(val, addr);
41231b1c780SFinn Thain 		break;
4136df2afbaSFinn Thain #endif
414ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
4156df2afbaSFinn Thain 	case MAC_ADB_PB2:
416cda67df5SFinn Thain 		pmu_pram_write_byte(val, addr);
4176df2afbaSFinn Thain 		break;
4186df2afbaSFinn Thain #endif
4193272244cSAl Viro 	default:
420cda67df5SFinn Thain 		break;
4211da177e4SLinus Torvalds 	}
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
mac_pram_get_size(void)424d3b41b6bSFinn Thain ssize_t mac_pram_get_size(void)
425d3b41b6bSFinn Thain {
426d3b41b6bSFinn Thain 	return 256;
427d3b41b6bSFinn Thain }
428d3b41b6bSFinn Thain #endif /* CONFIG_NVRAM */
429d3b41b6bSFinn Thain 
mac_poweroff(void)4301da177e4SLinus Torvalds void mac_poweroff(void)
4311da177e4SLinus Torvalds {
4321da177e4SLinus Torvalds 	if (oss_present) {
4331da177e4SLinus Torvalds 		oss_shutdown();
4341da177e4SLinus Torvalds 	} else if (macintosh_config->adb_type == MAC_ADB_II) {
4351da177e4SLinus Torvalds 		via_shutdown();
4361da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
437f74faec6SFinn Thain 	} else if (macintosh_config->adb_type == MAC_ADB_EGRET ||
438f74faec6SFinn Thain 	           macintosh_config->adb_type == MAC_ADB_CUDA) {
4391da177e4SLinus Torvalds 		cuda_shutdown();
4401da177e4SLinus Torvalds #endif
441ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
44254c99077SFinn Thain 	} else if (macintosh_config->adb_type == MAC_ADB_PB2) {
4431da177e4SLinus Torvalds 		pmu_shutdown();
4441da177e4SLinus Torvalds #endif
4451da177e4SLinus Torvalds 	}
446558d5ad2SFinn Thain 
447889121b4SGeert Uytterhoeven 	pr_crit("It is now safe to turn off your Macintosh.\n");
448558d5ad2SFinn Thain 	local_irq_disable();
4491da177e4SLinus Torvalds 	while(1);
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
mac_reset(void)4521da177e4SLinus Torvalds void mac_reset(void)
4531da177e4SLinus Torvalds {
4541da177e4SLinus Torvalds #ifdef CONFIG_ADB_CUDA
455*68d38724SFinn Thain 	if (macintosh_config->adb_type == MAC_ADB_EGRET ||
456f74faec6SFinn Thain 	    macintosh_config->adb_type == MAC_ADB_CUDA) {
4571da177e4SLinus Torvalds 		cuda_restart();
458*68d38724SFinn Thain 	} else
4591da177e4SLinus Torvalds #endif
460ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
461*68d38724SFinn Thain 	if (macintosh_config->adb_type == MAC_ADB_PB2) {
4621da177e4SLinus Torvalds 		pmu_restart();
463*68d38724SFinn Thain 	} else
4641da177e4SLinus Torvalds #endif
465*68d38724SFinn Thain 	if (CPU_IS_030) {
4661da177e4SLinus Torvalds 		/* 030-specific reset routine.  The idea is general, but the
4671da177e4SLinus Torvalds 		 * specific registers to reset are '030-specific.  Until I
4681da177e4SLinus Torvalds 		 * have a non-030 machine, I can't test anything else.
4691da177e4SLinus Torvalds 		 *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
4701da177e4SLinus Torvalds 		 */
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 		unsigned long rombase = 0x40000000;
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 		/* make a 1-to-1 mapping, using the transparent tran. reg. */
4751da177e4SLinus Torvalds 		unsigned long virt = (unsigned long) mac_reset;
4761da177e4SLinus Torvalds 		unsigned long phys = virt_to_phys(mac_reset);
47777add9f3SAl Viro 		unsigned long addr = (phys&0xFF000000)|0x8777;
4781da177e4SLinus Torvalds 		unsigned long offset = phys-virt;
47931b1c780SFinn Thain 
4801da177e4SLinus Torvalds 		local_irq_disable(); /* lets not screw this up, ok? */
4811da177e4SLinus Torvalds 		__asm__ __volatile__(".chip 68030\n\t"
4821da177e4SLinus Torvalds 				     "pmove %0,%/tt0\n\t"
4831da177e4SLinus Torvalds 				     ".chip 68k"
48477add9f3SAl Viro 				     : : "m" (addr));
4851da177e4SLinus Torvalds 		/* Now jump to physical address so we can disable MMU */
4861da177e4SLinus Torvalds 		__asm__ __volatile__(
4871da177e4SLinus Torvalds 		    ".chip 68030\n\t"
4881da177e4SLinus Torvalds 		    "lea %/pc@(1f),%/a0\n\t"
4891da177e4SLinus Torvalds 		    "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
4901da177e4SLinus Torvalds 		    "addl %0,%/sp\n\t"
4911da177e4SLinus Torvalds 		    "pflusha\n\t"
4921da177e4SLinus Torvalds 		    "jmp %/a0@\n\t" /* jump into physical memory */
4931da177e4SLinus Torvalds 		    "0:.long 0\n\t" /* a constant zero. */
4941da177e4SLinus Torvalds 		    /* OK.  Now reset everything and jump to reset vector. */
4951da177e4SLinus Torvalds 		    "1:\n\t"
4961da177e4SLinus Torvalds 		    "lea %/pc@(0b),%/a0\n\t"
4971da177e4SLinus Torvalds 		    "pmove %/a0@, %/tc\n\t" /* disable mmu */
4981da177e4SLinus Torvalds 		    "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
4991da177e4SLinus Torvalds 		    "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
5001da177e4SLinus Torvalds 		    "movel #0, %/a0\n\t"
5011da177e4SLinus Torvalds 		    "movec %/a0, %/vbr\n\t" /* clear vector base register */
5021da177e4SLinus Torvalds 		    "movec %/a0, %/cacr\n\t" /* disable caches */
5031da177e4SLinus Torvalds 		    "movel #0x0808,%/a0\n\t"
5041da177e4SLinus Torvalds 		    "movec %/a0, %/cacr\n\t" /* flush i&d caches */
5051da177e4SLinus Torvalds 		    "movew #0x2700,%/sr\n\t" /* set up status register */
5061da177e4SLinus Torvalds 		    "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
5071da177e4SLinus Torvalds 		    "movec %/a0, %/isp\n\t"
5081da177e4SLinus Torvalds 		    "movel %1@(0x4),%/a0\n\t" /* load reset vector */
5091da177e4SLinus Torvalds 		    "reset\n\t" /* reset external devices */
5101da177e4SLinus Torvalds 		    "jmp %/a0@\n\t" /* jump to the reset vector */
5111da177e4SLinus Torvalds 		    ".chip 68k"
5121da177e4SLinus Torvalds 		    : : "r" (offset), "a" (rombase) : "a0");
513*68d38724SFinn Thain 	} else {
514*68d38724SFinn Thain 		/* need ROMBASE in booter */
515*68d38724SFinn Thain 		/* indeed, plus need to MAP THE ROM !! */
516*68d38724SFinn Thain 
517*68d38724SFinn Thain 		if (mac_bi_data.rombase == 0)
518*68d38724SFinn Thain 			mac_bi_data.rombase = 0x40800000;
519*68d38724SFinn Thain 
520*68d38724SFinn Thain 		/* works on some */
521*68d38724SFinn Thain 		rom_reset = (void *)(mac_bi_data.rombase + 0xa);
522*68d38724SFinn Thain 
523*68d38724SFinn Thain 		local_irq_disable();
524*68d38724SFinn Thain 		rom_reset();
5251da177e4SLinus Torvalds 	}
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds 	/* should never get here */
528889121b4SGeert Uytterhoeven 	pr_crit("Restart failed. Please restart manually.\n");
529558d5ad2SFinn Thain 	local_irq_disable();
5301da177e4SLinus Torvalds 	while(1);
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds /*
5341da177e4SLinus Torvalds  * This function translates seconds since 1970 into a proper date.
5351da177e4SLinus Torvalds  *
5361da177e4SLinus Torvalds  * Algorithm cribbed from glibc2.1, __offtime().
5375b9bfb8eSArnd Bergmann  *
5385b9bfb8eSArnd Bergmann  * This is roughly same as rtc_time64_to_tm(), which we should probably
5395b9bfb8eSArnd Bergmann  * use here, but it's only available when CONFIG_RTC_LIB is enabled.
5401da177e4SLinus Torvalds  */
5411da177e4SLinus Torvalds #define SECS_PER_MINUTE (60)
5421da177e4SLinus Torvalds #define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
5431da177e4SLinus Torvalds #define SECS_PER_DAY   (SECS_PER_HOUR * 24)
5441da177e4SLinus Torvalds 
unmktime(time64_t time,long offset,int * yearp,int * monp,int * dayp,int * hourp,int * minp,int * secp)5455b9bfb8eSArnd Bergmann static void unmktime(time64_t time, long offset,
5461da177e4SLinus Torvalds 		     int *yearp, int *monp, int *dayp,
5471da177e4SLinus Torvalds 		     int *hourp, int *minp, int *secp)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds         /* How many days come before each month (0-12).  */
5501da177e4SLinus Torvalds 	static const unsigned short int __mon_yday[2][13] =
5511da177e4SLinus Torvalds 	{
5521da177e4SLinus Torvalds 		/* Normal years.  */
5531da177e4SLinus Torvalds 		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
5541da177e4SLinus Torvalds 		/* Leap years.  */
5551da177e4SLinus Torvalds 		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
5561da177e4SLinus Torvalds 	};
5575b9bfb8eSArnd Bergmann 	int days, rem, y, wday, yday;
5581da177e4SLinus Torvalds 	const unsigned short int *ip;
5591da177e4SLinus Torvalds 
5605b9bfb8eSArnd Bergmann 	days = div_u64_rem(time, SECS_PER_DAY, &rem);
5611da177e4SLinus Torvalds 	rem += offset;
5621da177e4SLinus Torvalds 	while (rem < 0) {
5631da177e4SLinus Torvalds 		rem += SECS_PER_DAY;
5641da177e4SLinus Torvalds 		--days;
5651da177e4SLinus Torvalds 	}
5661da177e4SLinus Torvalds 	while (rem >= SECS_PER_DAY) {
5671da177e4SLinus Torvalds 		rem -= SECS_PER_DAY;
5681da177e4SLinus Torvalds 		++days;
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 	*hourp = rem / SECS_PER_HOUR;
5711da177e4SLinus Torvalds 	rem %= SECS_PER_HOUR;
5721da177e4SLinus Torvalds 	*minp = rem / SECS_PER_MINUTE;
5731da177e4SLinus Torvalds 	*secp = rem % SECS_PER_MINUTE;
5741da177e4SLinus Torvalds 	/* January 1, 1970 was a Thursday. */
5751da177e4SLinus Torvalds 	wday = (4 + days) % 7; /* Day in the week. Not currently used */
5761da177e4SLinus Torvalds 	if (wday < 0) wday += 7;
5771da177e4SLinus Torvalds 	y = 1970;
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
5801da177e4SLinus Torvalds #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
5811da177e4SLinus Torvalds #define __isleap(year)	\
5821da177e4SLinus Torvalds   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
5851da177e4SLinus Torvalds 	{
5861da177e4SLinus Torvalds 		/* Guess a corrected year, assuming 365 days per year.  */
5871da177e4SLinus Torvalds 		long int yg = y + days / 365 - (days % 365 < 0);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 		/* Adjust DAYS and Y to match the guessed year.  */
59031b1c780SFinn Thain 		days -= (yg - y) * 365 +
59131b1c780SFinn Thain 			LEAPS_THRU_END_OF(yg - 1) - LEAPS_THRU_END_OF(y - 1);
5921da177e4SLinus Torvalds 		y = yg;
5931da177e4SLinus Torvalds 	}
5941da177e4SLinus Torvalds 	*yearp = y - 1900;
5951da177e4SLinus Torvalds 	yday = days; /* day in the year.  Not currently used. */
5961da177e4SLinus Torvalds 	ip = __mon_yday[__isleap(y)];
5971da177e4SLinus Torvalds 	for (y = 11; days < (long int) ip[y]; --y)
5981da177e4SLinus Torvalds 		continue;
5991da177e4SLinus Torvalds 	days -= ip[y];
6001da177e4SLinus Torvalds 	*monp = y;
6011da177e4SLinus Torvalds 	*dayp = days + 1; /* day in the month */
6021da177e4SLinus Torvalds 	return;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds /*
6061da177e4SLinus Torvalds  * Read/write the hardware clock.
6071da177e4SLinus Torvalds  */
6081da177e4SLinus Torvalds 
mac_hwclk(int op,struct rtc_time * t)6091da177e4SLinus Torvalds int mac_hwclk(int op, struct rtc_time *t)
6101da177e4SLinus Torvalds {
6115b9bfb8eSArnd Bergmann 	time64_t now;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	if (!op) { /* read */
6143272244cSAl Viro 		switch (macintosh_config->adb_type) {
6153272244cSAl Viro 		case MAC_ADB_IOP:
6166df2afbaSFinn Thain 		case MAC_ADB_II:
6176df2afbaSFinn Thain 		case MAC_ADB_PB1:
6181da177e4SLinus Torvalds 			now = via_read_time();
6193272244cSAl Viro 			break;
6206df2afbaSFinn Thain #ifdef CONFIG_ADB_CUDA
621f74faec6SFinn Thain 		case MAC_ADB_EGRET:
6223272244cSAl Viro 		case MAC_ADB_CUDA:
6230792a2c8SFinn Thain 			now = cuda_get_time();
6243272244cSAl Viro 			break;
6256df2afbaSFinn Thain #endif
626ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
6276df2afbaSFinn Thain 		case MAC_ADB_PB2:
6280792a2c8SFinn Thain 			now = pmu_get_time();
6296df2afbaSFinn Thain 			break;
6306df2afbaSFinn Thain #endif
6313272244cSAl Viro 		default:
6321da177e4SLinus Torvalds 			now = 0;
6331da177e4SLinus Torvalds 		}
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 		t->tm_wday = 0;
6361da177e4SLinus Torvalds 		unmktime(now, 0,
6371da177e4SLinus Torvalds 			 &t->tm_year, &t->tm_mon, &t->tm_mday,
6381da177e4SLinus Torvalds 			 &t->tm_hour, &t->tm_min, &t->tm_sec);
63990625444SAndy Shevchenko 		pr_debug("%s: read %ptR\n", __func__, t);
6401da177e4SLinus Torvalds 	} else { /* write */
64190625444SAndy Shevchenko 		pr_debug("%s: tried to write %ptR\n", __func__, t);
6421da177e4SLinus Torvalds 
6433272244cSAl Viro 		switch (macintosh_config->adb_type) {
6443272244cSAl Viro 		case MAC_ADB_IOP:
6456df2afbaSFinn Thain 		case MAC_ADB_II:
6466df2afbaSFinn Thain 		case MAC_ADB_PB1:
6470792a2c8SFinn Thain 			via_set_rtc_time(t);
6483272244cSAl Viro 			break;
6496df2afbaSFinn Thain #ifdef CONFIG_ADB_CUDA
650f74faec6SFinn Thain 		case MAC_ADB_EGRET:
6513272244cSAl Viro 		case MAC_ADB_CUDA:
6520792a2c8SFinn Thain 			cuda_set_rtc_time(t);
6533272244cSAl Viro 			break;
6546df2afbaSFinn Thain #endif
655ebd72227SFinn Thain #ifdef CONFIG_ADB_PMU
6563272244cSAl Viro 		case MAC_ADB_PB2:
6570792a2c8SFinn Thain 			pmu_set_rtc_time(t);
6583272244cSAl Viro 			break;
6596df2afbaSFinn Thain #endif
6606df2afbaSFinn Thain 		default:
6616df2afbaSFinn Thain 			return -ENODEV;
6621da177e4SLinus Torvalds 		}
6631da177e4SLinus Torvalds 	}
6641da177e4SLinus Torvalds 	return 0;
6651da177e4SLinus Torvalds }
666