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