162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Miscellaneous Mac68K-specific stuff 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/types.h> 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/time.h> 1262306a36Sopenharmony_ci#include <linux/rtc.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/adb.h> 1662306a36Sopenharmony_ci#include <linux/cuda.h> 1762306a36Sopenharmony_ci#include <linux/pmu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci#include <asm/io.h> 2162306a36Sopenharmony_ci#include <asm/setup.h> 2262306a36Sopenharmony_ci#include <asm/macintosh.h> 2362306a36Sopenharmony_ci#include <asm/mac_via.h> 2462306a36Sopenharmony_ci#include <asm/mac_oss.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/machdep.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU 3062306a36Sopenharmony_ci * times wrap in 2040. If we need to handle later times, the read_time functions 3162306a36Sopenharmony_ci * need to be changed to interpret wrapped times as post-2040. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define RTC_OFFSET 2082844800 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void (*rom_reset)(void); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NVRAM) 3962306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 4062306a36Sopenharmony_cistatic unsigned char cuda_pram_read_byte(int offset) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct adb_request req; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (cuda_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM, 4562306a36Sopenharmony_ci (offset >> 8) & 0xFF, offset & 0xFF) < 0) 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci while (!req.complete) 4862306a36Sopenharmony_ci cuda_poll(); 4962306a36Sopenharmony_ci return req.reply[3]; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void cuda_pram_write_byte(unsigned char data, int offset) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct adb_request req; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM, 5762306a36Sopenharmony_ci (offset >> 8) & 0xFF, offset & 0xFF, data) < 0) 5862306a36Sopenharmony_ci return; 5962306a36Sopenharmony_ci while (!req.complete) 6062306a36Sopenharmony_ci cuda_poll(); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci#endif /* CONFIG_ADB_CUDA */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 6562306a36Sopenharmony_cistatic unsigned char pmu_pram_read_byte(int offset) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct adb_request req; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (pmu_request(&req, NULL, 3, PMU_READ_XPRAM, 7062306a36Sopenharmony_ci offset & 0xFF, 1) < 0) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci pmu_wait_complete(&req); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return req.reply[0]; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void pmu_pram_write_byte(unsigned char data, int offset) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct adb_request req; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (pmu_request(&req, NULL, 4, PMU_WRITE_XPRAM, 8262306a36Sopenharmony_ci offset & 0xFF, 1, data) < 0) 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci pmu_wait_complete(&req); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci#endif /* CONFIG_ADB_PMU */ 8762306a36Sopenharmony_ci#endif /* CONFIG_NVRAM */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * VIA PRAM/RTC access routines 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Must be called with interrupts disabled and 9362306a36Sopenharmony_ci * the RTC should be enabled. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic __u8 via_rtc_recv(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int i, reg; 9962306a36Sopenharmony_ci __u8 data; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci reg = via1[vBufB] & ~VIA1B_vRTCClk; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Set the RTC data line to be an input. */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci via1[vDirB] &= ~VIA1B_vRTCData; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* The bits of the byte come out in MSB order */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci data = 0; 11062306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 11162306a36Sopenharmony_ci via1[vBufB] = reg; 11262306a36Sopenharmony_ci via1[vBufB] = reg | VIA1B_vRTCClk; 11362306a36Sopenharmony_ci data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Return RTC data line to output state */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci via1[vDirB] |= VIA1B_vRTCData; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return data; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void via_rtc_send(__u8 data) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci int i, reg, bit; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* The bits of the byte go into the RTC in MSB order */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 13262306a36Sopenharmony_ci bit = data & 0x80? 1 : 0; 13362306a36Sopenharmony_ci data <<= 1; 13462306a36Sopenharmony_ci via1[vBufB] = reg | bit; 13562306a36Sopenharmony_ci via1[vBufB] = reg | bit | VIA1B_vRTCClk; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * These values can be found in Inside Macintosh vol. III ch. 2 14162306a36Sopenharmony_ci * which has a description of the RTC chip in the original Mac. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define RTC_FLG_READ BIT(7) 14562306a36Sopenharmony_ci#define RTC_FLG_WRITE_PROTECT BIT(7) 14662306a36Sopenharmony_ci#define RTC_CMD_READ(r) (RTC_FLG_READ | (r << 2)) 14762306a36Sopenharmony_ci#define RTC_CMD_WRITE(r) (r << 2) 14862306a36Sopenharmony_ci#define RTC_REG_SECONDS_0 0 14962306a36Sopenharmony_ci#define RTC_REG_SECONDS_1 1 15062306a36Sopenharmony_ci#define RTC_REG_SECONDS_2 2 15162306a36Sopenharmony_ci#define RTC_REG_SECONDS_3 3 15262306a36Sopenharmony_ci#define RTC_REG_WRITE_PROTECT 13 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * Inside Mac has no information about two-byte RTC commands but 15662306a36Sopenharmony_ci * the MAME/MESS source code has the essentials. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define RTC_REG_XPRAM 14 16062306a36Sopenharmony_ci#define RTC_CMD_XPRAM_READ (RTC_CMD_READ(RTC_REG_XPRAM) << 8) 16162306a36Sopenharmony_ci#define RTC_CMD_XPRAM_WRITE (RTC_CMD_WRITE(RTC_REG_XPRAM) << 8) 16262306a36Sopenharmony_ci#define RTC_CMD_XPRAM_ARG(a) (((a & 0xE0) << 3) | ((a & 0x1F) << 2)) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * Execute a VIA PRAM/RTC command. For read commands 16662306a36Sopenharmony_ci * data should point to a one-byte buffer for the 16762306a36Sopenharmony_ci * resulting data. For write commands it should point 16862306a36Sopenharmony_ci * to the data byte to for the command. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * This function disables all interrupts while running. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void via_rtc_command(int command, __u8 *data) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long flags; 17662306a36Sopenharmony_ci int is_read; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci local_irq_save(flags); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* The least significant bits must be 0b01 according to Inside Mac */ 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci command = (command & ~3) | 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Enable the RTC and make sure the strobe line is high */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (command & 0xFF00) { /* extended (two-byte) command */ 18962306a36Sopenharmony_ci via_rtc_send((command & 0xFF00) >> 8); 19062306a36Sopenharmony_ci via_rtc_send(command & 0xFF); 19162306a36Sopenharmony_ci is_read = command & (RTC_FLG_READ << 8); 19262306a36Sopenharmony_ci } else { /* one-byte command */ 19362306a36Sopenharmony_ci via_rtc_send(command); 19462306a36Sopenharmony_ci is_read = command & RTC_FLG_READ; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci if (is_read) { 19762306a36Sopenharmony_ci *data = via_rtc_recv(); 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci via_rtc_send(*data); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* All done, disable the RTC */ 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci via1[vBufB] |= VIA1B_vRTCEnb; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci local_irq_restore(flags); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NVRAM) 21062306a36Sopenharmony_cistatic unsigned char via_pram_read_byte(int offset) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci unsigned char temp; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci via_rtc_command(RTC_CMD_XPRAM_READ | RTC_CMD_XPRAM_ARG(offset), &temp); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return temp; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void via_pram_write_byte(unsigned char data, int offset) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned char temp; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci temp = 0x55; 22462306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci temp = data; 22762306a36Sopenharmony_ci via_rtc_command(RTC_CMD_XPRAM_WRITE | RTC_CMD_XPRAM_ARG(offset), &temp); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci temp = 0x55 | RTC_FLG_WRITE_PROTECT; 23062306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci#endif /* CONFIG_NVRAM */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * Return the current time in seconds since January 1, 1904. 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * This only works on machines with the VIA-based PRAM/RTC, which 23862306a36Sopenharmony_ci * is basically any machine with Mac II-style ADB. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic time64_t via_read_time(void) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci union { 24462306a36Sopenharmony_ci __u8 cdata[4]; 24562306a36Sopenharmony_ci __u32 idata; 24662306a36Sopenharmony_ci } result, last_result; 24762306a36Sopenharmony_ci int count = 1; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0), &last_result.cdata[3]); 25062306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1), &last_result.cdata[2]); 25162306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2), &last_result.cdata[1]); 25262306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3), &last_result.cdata[0]); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * The NetBSD guys say to loop until you get the same reading 25662306a36Sopenharmony_ci * twice in a row. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci while (1) { 26062306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0), 26162306a36Sopenharmony_ci &result.cdata[3]); 26262306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1), 26362306a36Sopenharmony_ci &result.cdata[2]); 26462306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2), 26562306a36Sopenharmony_ci &result.cdata[1]); 26662306a36Sopenharmony_ci via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3), 26762306a36Sopenharmony_ci &result.cdata[0]); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (result.idata == last_result.idata) 27062306a36Sopenharmony_ci return (time64_t)result.idata - RTC_OFFSET; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (++count > 10) 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci last_result.idata = result.idata; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pr_err("%s: failed to read a stable value; got 0x%08x then 0x%08x\n", 27962306a36Sopenharmony_ci __func__, last_result.idata, result.idata); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* 28562306a36Sopenharmony_ci * Set the current time to a number of seconds since January 1, 1904. 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * This only works on machines with the VIA-based PRAM/RTC, which 28862306a36Sopenharmony_ci * is basically any machine with Mac II-style ADB. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void via_set_rtc_time(struct rtc_time *tm) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci union { 29462306a36Sopenharmony_ci __u8 cdata[4]; 29562306a36Sopenharmony_ci __u32 idata; 29662306a36Sopenharmony_ci } data; 29762306a36Sopenharmony_ci __u8 temp; 29862306a36Sopenharmony_ci time64_t time; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci time = mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 30162306a36Sopenharmony_ci tm->tm_hour, tm->tm_min, tm->tm_sec); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Clear the write protect bit */ 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci temp = 0x55; 30662306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci data.idata = lower_32_bits(time + RTC_OFFSET); 30962306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_0), &data.cdata[3]); 31062306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_1), &data.cdata[2]); 31162306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_2), &data.cdata[1]); 31262306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_3), &data.cdata[0]); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Set the write protect bit */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci temp = 0x55 | RTC_FLG_WRITE_PROTECT; 31762306a36Sopenharmony_ci via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void via_shutdown(void) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci if (rbv_present) { 32362306a36Sopenharmony_ci via2[rBufB] &= ~0x04; 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci /* Direction of vDirB is output */ 32662306a36Sopenharmony_ci via2[vDirB] |= 0x04; 32762306a36Sopenharmony_ci /* Send a value of 0 on that line */ 32862306a36Sopenharmony_ci via2[vBufB] &= ~0x04; 32962306a36Sopenharmony_ci mdelay(1000); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void oss_shutdown(void) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci oss->rom_ctrl = OSS_POWEROFF; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 33962306a36Sopenharmony_cistatic void cuda_restart(void) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct adb_request req; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM) < 0) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci while (!req.complete) 34662306a36Sopenharmony_ci cuda_poll(); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void cuda_shutdown(void) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct adb_request req; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN) < 0) 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Avoid infinite polling loop when PSU is not under Cuda control */ 35762306a36Sopenharmony_ci switch (macintosh_config->ident) { 35862306a36Sopenharmony_ci case MAC_MODEL_C660: 35962306a36Sopenharmony_ci case MAC_MODEL_Q605: 36062306a36Sopenharmony_ci case MAC_MODEL_Q605_ACC: 36162306a36Sopenharmony_ci case MAC_MODEL_P475: 36262306a36Sopenharmony_ci case MAC_MODEL_P475F: 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci while (!req.complete) 36762306a36Sopenharmony_ci cuda_poll(); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci#endif /* CONFIG_ADB_CUDA */ 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci *------------------------------------------------------------------- 37362306a36Sopenharmony_ci * Below this point are the generic routines; they'll dispatch to the 37462306a36Sopenharmony_ci * correct routine for the hardware on which we're running. 37562306a36Sopenharmony_ci *------------------------------------------------------------------- 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NVRAM) 37962306a36Sopenharmony_ciunsigned char mac_pram_read_byte(int addr) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci switch (macintosh_config->adb_type) { 38262306a36Sopenharmony_ci case MAC_ADB_IOP: 38362306a36Sopenharmony_ci case MAC_ADB_II: 38462306a36Sopenharmony_ci case MAC_ADB_PB1: 38562306a36Sopenharmony_ci return via_pram_read_byte(addr); 38662306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 38762306a36Sopenharmony_ci case MAC_ADB_EGRET: 38862306a36Sopenharmony_ci case MAC_ADB_CUDA: 38962306a36Sopenharmony_ci return cuda_pram_read_byte(addr); 39062306a36Sopenharmony_ci#endif 39162306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 39262306a36Sopenharmony_ci case MAC_ADB_PB2: 39362306a36Sopenharmony_ci return pmu_pram_read_byte(addr); 39462306a36Sopenharmony_ci#endif 39562306a36Sopenharmony_ci default: 39662306a36Sopenharmony_ci return 0xFF; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_civoid mac_pram_write_byte(unsigned char val, int addr) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci switch (macintosh_config->adb_type) { 40362306a36Sopenharmony_ci case MAC_ADB_IOP: 40462306a36Sopenharmony_ci case MAC_ADB_II: 40562306a36Sopenharmony_ci case MAC_ADB_PB1: 40662306a36Sopenharmony_ci via_pram_write_byte(val, addr); 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 40962306a36Sopenharmony_ci case MAC_ADB_EGRET: 41062306a36Sopenharmony_ci case MAC_ADB_CUDA: 41162306a36Sopenharmony_ci cuda_pram_write_byte(val, addr); 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci#endif 41462306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 41562306a36Sopenharmony_ci case MAC_ADB_PB2: 41662306a36Sopenharmony_ci pmu_pram_write_byte(val, addr); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci#endif 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cissize_t mac_pram_get_size(void) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return 256; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci#endif /* CONFIG_NVRAM */ 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_civoid mac_poweroff(void) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci if (oss_present) { 43362306a36Sopenharmony_ci oss_shutdown(); 43462306a36Sopenharmony_ci } else if (macintosh_config->adb_type == MAC_ADB_II) { 43562306a36Sopenharmony_ci via_shutdown(); 43662306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 43762306a36Sopenharmony_ci } else if (macintosh_config->adb_type == MAC_ADB_EGRET || 43862306a36Sopenharmony_ci macintosh_config->adb_type == MAC_ADB_CUDA) { 43962306a36Sopenharmony_ci cuda_shutdown(); 44062306a36Sopenharmony_ci#endif 44162306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 44262306a36Sopenharmony_ci } else if (macintosh_config->adb_type == MAC_ADB_PB2) { 44362306a36Sopenharmony_ci pmu_shutdown(); 44462306a36Sopenharmony_ci#endif 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci pr_crit("It is now safe to turn off your Macintosh.\n"); 44862306a36Sopenharmony_ci local_irq_disable(); 44962306a36Sopenharmony_ci while(1); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_civoid mac_reset(void) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (macintosh_config->adb_type == MAC_ADB_II && 45562306a36Sopenharmony_ci macintosh_config->ident != MAC_MODEL_SE30) { 45662306a36Sopenharmony_ci /* need ROMBASE in booter */ 45762306a36Sopenharmony_ci /* indeed, plus need to MAP THE ROM !! */ 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (mac_bi_data.rombase == 0) 46062306a36Sopenharmony_ci mac_bi_data.rombase = 0x40800000; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* works on some */ 46362306a36Sopenharmony_ci rom_reset = (void *) (mac_bi_data.rombase + 0xa); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci local_irq_disable(); 46662306a36Sopenharmony_ci rom_reset(); 46762306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 46862306a36Sopenharmony_ci } else if (macintosh_config->adb_type == MAC_ADB_EGRET || 46962306a36Sopenharmony_ci macintosh_config->adb_type == MAC_ADB_CUDA) { 47062306a36Sopenharmony_ci cuda_restart(); 47162306a36Sopenharmony_ci#endif 47262306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 47362306a36Sopenharmony_ci } else if (macintosh_config->adb_type == MAC_ADB_PB2) { 47462306a36Sopenharmony_ci pmu_restart(); 47562306a36Sopenharmony_ci#endif 47662306a36Sopenharmony_ci } else if (CPU_IS_030) { 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* 030-specific reset routine. The idea is general, but the 47962306a36Sopenharmony_ci * specific registers to reset are '030-specific. Until I 48062306a36Sopenharmony_ci * have a non-030 machine, I can't test anything else. 48162306a36Sopenharmony_ci * -- C. Scott Ananian <cananian@alumni.princeton.edu> 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci unsigned long rombase = 0x40000000; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* make a 1-to-1 mapping, using the transparent tran. reg. */ 48762306a36Sopenharmony_ci unsigned long virt = (unsigned long) mac_reset; 48862306a36Sopenharmony_ci unsigned long phys = virt_to_phys(mac_reset); 48962306a36Sopenharmony_ci unsigned long addr = (phys&0xFF000000)|0x8777; 49062306a36Sopenharmony_ci unsigned long offset = phys-virt; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci local_irq_disable(); /* lets not screw this up, ok? */ 49362306a36Sopenharmony_ci __asm__ __volatile__(".chip 68030\n\t" 49462306a36Sopenharmony_ci "pmove %0,%/tt0\n\t" 49562306a36Sopenharmony_ci ".chip 68k" 49662306a36Sopenharmony_ci : : "m" (addr)); 49762306a36Sopenharmony_ci /* Now jump to physical address so we can disable MMU */ 49862306a36Sopenharmony_ci __asm__ __volatile__( 49962306a36Sopenharmony_ci ".chip 68030\n\t" 50062306a36Sopenharmony_ci "lea %/pc@(1f),%/a0\n\t" 50162306a36Sopenharmony_ci "addl %0,%/a0\n\t"/* fixup target address and stack ptr */ 50262306a36Sopenharmony_ci "addl %0,%/sp\n\t" 50362306a36Sopenharmony_ci "pflusha\n\t" 50462306a36Sopenharmony_ci "jmp %/a0@\n\t" /* jump into physical memory */ 50562306a36Sopenharmony_ci "0:.long 0\n\t" /* a constant zero. */ 50662306a36Sopenharmony_ci /* OK. Now reset everything and jump to reset vector. */ 50762306a36Sopenharmony_ci "1:\n\t" 50862306a36Sopenharmony_ci "lea %/pc@(0b),%/a0\n\t" 50962306a36Sopenharmony_ci "pmove %/a0@, %/tc\n\t" /* disable mmu */ 51062306a36Sopenharmony_ci "pmove %/a0@, %/tt0\n\t" /* disable tt0 */ 51162306a36Sopenharmony_ci "pmove %/a0@, %/tt1\n\t" /* disable tt1 */ 51262306a36Sopenharmony_ci "movel #0, %/a0\n\t" 51362306a36Sopenharmony_ci "movec %/a0, %/vbr\n\t" /* clear vector base register */ 51462306a36Sopenharmony_ci "movec %/a0, %/cacr\n\t" /* disable caches */ 51562306a36Sopenharmony_ci "movel #0x0808,%/a0\n\t" 51662306a36Sopenharmony_ci "movec %/a0, %/cacr\n\t" /* flush i&d caches */ 51762306a36Sopenharmony_ci "movew #0x2700,%/sr\n\t" /* set up status register */ 51862306a36Sopenharmony_ci "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */ 51962306a36Sopenharmony_ci "movec %/a0, %/isp\n\t" 52062306a36Sopenharmony_ci "movel %1@(0x4),%/a0\n\t" /* load reset vector */ 52162306a36Sopenharmony_ci "reset\n\t" /* reset external devices */ 52262306a36Sopenharmony_ci "jmp %/a0@\n\t" /* jump to the reset vector */ 52362306a36Sopenharmony_ci ".chip 68k" 52462306a36Sopenharmony_ci : : "r" (offset), "a" (rombase) : "a0"); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* should never get here */ 52862306a36Sopenharmony_ci pr_crit("Restart failed. Please restart manually.\n"); 52962306a36Sopenharmony_ci local_irq_disable(); 53062306a36Sopenharmony_ci while(1); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* 53462306a36Sopenharmony_ci * This function translates seconds since 1970 into a proper date. 53562306a36Sopenharmony_ci * 53662306a36Sopenharmony_ci * Algorithm cribbed from glibc2.1, __offtime(). 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * This is roughly same as rtc_time64_to_tm(), which we should probably 53962306a36Sopenharmony_ci * use here, but it's only available when CONFIG_RTC_LIB is enabled. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci#define SECS_PER_MINUTE (60) 54262306a36Sopenharmony_ci#define SECS_PER_HOUR (SECS_PER_MINUTE * 60) 54362306a36Sopenharmony_ci#define SECS_PER_DAY (SECS_PER_HOUR * 24) 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic void unmktime(time64_t time, long offset, 54662306a36Sopenharmony_ci int *yearp, int *monp, int *dayp, 54762306a36Sopenharmony_ci int *hourp, int *minp, int *secp) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci /* How many days come before each month (0-12). */ 55062306a36Sopenharmony_ci static const unsigned short int __mon_yday[2][13] = 55162306a36Sopenharmony_ci { 55262306a36Sopenharmony_ci /* Normal years. */ 55362306a36Sopenharmony_ci { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 55462306a36Sopenharmony_ci /* Leap years. */ 55562306a36Sopenharmony_ci { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 55662306a36Sopenharmony_ci }; 55762306a36Sopenharmony_ci int days, rem, y, wday, yday; 55862306a36Sopenharmony_ci const unsigned short int *ip; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci days = div_u64_rem(time, SECS_PER_DAY, &rem); 56162306a36Sopenharmony_ci rem += offset; 56262306a36Sopenharmony_ci while (rem < 0) { 56362306a36Sopenharmony_ci rem += SECS_PER_DAY; 56462306a36Sopenharmony_ci --days; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci while (rem >= SECS_PER_DAY) { 56762306a36Sopenharmony_ci rem -= SECS_PER_DAY; 56862306a36Sopenharmony_ci ++days; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci *hourp = rem / SECS_PER_HOUR; 57162306a36Sopenharmony_ci rem %= SECS_PER_HOUR; 57262306a36Sopenharmony_ci *minp = rem / SECS_PER_MINUTE; 57362306a36Sopenharmony_ci *secp = rem % SECS_PER_MINUTE; 57462306a36Sopenharmony_ci /* January 1, 1970 was a Thursday. */ 57562306a36Sopenharmony_ci wday = (4 + days) % 7; /* Day in the week. Not currently used */ 57662306a36Sopenharmony_ci if (wday < 0) wday += 7; 57762306a36Sopenharmony_ci y = 1970; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) 58062306a36Sopenharmony_ci#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) 58162306a36Sopenharmony_ci#define __isleap(year) \ 58262306a36Sopenharmony_ci ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci while (days < 0 || days >= (__isleap (y) ? 366 : 365)) 58562306a36Sopenharmony_ci { 58662306a36Sopenharmony_ci /* Guess a corrected year, assuming 365 days per year. */ 58762306a36Sopenharmony_ci long int yg = y + days / 365 - (days % 365 < 0); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Adjust DAYS and Y to match the guessed year. */ 59062306a36Sopenharmony_ci days -= (yg - y) * 365 + 59162306a36Sopenharmony_ci LEAPS_THRU_END_OF(yg - 1) - LEAPS_THRU_END_OF(y - 1); 59262306a36Sopenharmony_ci y = yg; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci *yearp = y - 1900; 59562306a36Sopenharmony_ci yday = days; /* day in the year. Not currently used. */ 59662306a36Sopenharmony_ci ip = __mon_yday[__isleap(y)]; 59762306a36Sopenharmony_ci for (y = 11; days < (long int) ip[y]; --y) 59862306a36Sopenharmony_ci continue; 59962306a36Sopenharmony_ci days -= ip[y]; 60062306a36Sopenharmony_ci *monp = y; 60162306a36Sopenharmony_ci *dayp = days + 1; /* day in the month */ 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * Read/write the hardware clock. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ciint mac_hwclk(int op, struct rtc_time *t) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci time64_t now; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!op) { /* read */ 61462306a36Sopenharmony_ci switch (macintosh_config->adb_type) { 61562306a36Sopenharmony_ci case MAC_ADB_IOP: 61662306a36Sopenharmony_ci case MAC_ADB_II: 61762306a36Sopenharmony_ci case MAC_ADB_PB1: 61862306a36Sopenharmony_ci now = via_read_time(); 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 62162306a36Sopenharmony_ci case MAC_ADB_EGRET: 62262306a36Sopenharmony_ci case MAC_ADB_CUDA: 62362306a36Sopenharmony_ci now = cuda_get_time(); 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci#endif 62662306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 62762306a36Sopenharmony_ci case MAC_ADB_PB2: 62862306a36Sopenharmony_ci now = pmu_get_time(); 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci#endif 63162306a36Sopenharmony_ci default: 63262306a36Sopenharmony_ci now = 0; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci t->tm_wday = 0; 63662306a36Sopenharmony_ci unmktime(now, 0, 63762306a36Sopenharmony_ci &t->tm_year, &t->tm_mon, &t->tm_mday, 63862306a36Sopenharmony_ci &t->tm_hour, &t->tm_min, &t->tm_sec); 63962306a36Sopenharmony_ci pr_debug("%s: read %ptR\n", __func__, t); 64062306a36Sopenharmony_ci } else { /* write */ 64162306a36Sopenharmony_ci pr_debug("%s: tried to write %ptR\n", __func__, t); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci switch (macintosh_config->adb_type) { 64462306a36Sopenharmony_ci case MAC_ADB_IOP: 64562306a36Sopenharmony_ci case MAC_ADB_II: 64662306a36Sopenharmony_ci case MAC_ADB_PB1: 64762306a36Sopenharmony_ci via_set_rtc_time(t); 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 65062306a36Sopenharmony_ci case MAC_ADB_EGRET: 65162306a36Sopenharmony_ci case MAC_ADB_CUDA: 65262306a36Sopenharmony_ci cuda_set_rtc_time(t); 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci#endif 65562306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 65662306a36Sopenharmony_ci case MAC_ADB_PB2: 65762306a36Sopenharmony_ci pmu_set_rtc_time(t); 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci default: 66162306a36Sopenharmony_ci return -ENODEV; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 666