/* * Support for periodic interrupts (100 per second) and for getting * the current time from the RTC on Power Macintoshes. * * We use the decrementer register for our periodic interrupts. * * Paul Mackerras August 1996. * Copyright (C) 1996 Paul Mackerras. * Copyright (C) 2003-2005 Benjamin Herrenschmidt. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef DEBUG #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif /* Apparently the RTC stores seconds since 1 Jan 1904 */ #define RTC_OFFSET 2082844800 /* * Calibrate the decrementer frequency with the VIA timer 1. */ #define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ extern struct timezone sys_tz; extern void to_tm(int tim, struct rtc_time * tm); void pmac_get_rtc_time(struct rtc_time *tm) { switch(sys_ctrler) { #ifdef CONFIG_ADB_PMU case SYS_CTRLER_PMU: { /* TODO: Move that to a function in the PMU driver */ struct adb_request req; unsigned int now; if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) return; pmu_wait_complete(&req); if (req.reply_len != 4) printk(KERN_ERR "pmac_get_rtc_time: PMU returned a %d" " bytes reply\n", req.reply_len); now = (req.reply[0] << 24) + (req.reply[1] << 16) + (req.reply[2] << 8) + req.reply[3]; DBG("get: %u -> %u\n", (int)now, (int)(now - RTC_OFFSET)); now -= RTC_OFFSET; to_tm(now, tm); tm->tm_year -= 1900; tm->tm_mon -= 1; DBG("-> tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n", tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); break; } #endif /* CONFIG_ADB_PMU */ #ifdef CONFIG_PMAC_SMU case SYS_CTRLER_SMU: smu_get_rtc_time(tm, 1); break; #endif /* CONFIG_PMAC_SMU */ default: ; } } int pmac_set_rtc_time(struct rtc_time *tm) { switch(sys_ctrler) { #ifdef CONFIG_ADB_PMU case SYS_CTRLER_PMU: { /* TODO: Move that to a function in the PMU driver */ struct adb_request req; unsigned int nowtime; DBG("set: tm_mday: %d, tm_mon: %d, tm_year: %d," " %d:%02d:%02d\n", tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); nowtime = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); DBG("-> %u -> %u\n", (int)nowtime, (int)(nowtime + RTC_OFFSET)); nowtime += RTC_OFFSET; if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24, nowtime >> 16, nowtime >> 8, nowtime) < 0) return -ENXIO; pmu_wait_complete(&req); if (req.reply_len != 0) printk(KERN_ERR "pmac_set_rtc_time: PMU returned a %d" " bytes reply\n", req.reply_len); return 0; } #endif /* CONFIG_ADB_PMU */ #ifdef CONFIG_PMAC_SMU case SYS_CTRLER_SMU: return smu_set_rtc_time(tm, 1); #endif /* CONFIG_PMAC_SMU */ default: return -ENODEV; } } unsigned long __init pmac_get_boot_time(void) { struct rtc_time tm; pmac_get_rtc_time(&tm); return mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } /* * Query the OF and get the decr frequency. * FIXME: merge this with generic_calibrate_decr */ void __init pmac_calibrate_decr(void) { struct device_node *cpu; unsigned int freq, *fp; struct div_result divres; /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. */ cpu = find_type_devices("cpu"); if (cpu == 0) panic("can't find cpu node in time_init"); fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); if (fp == 0) panic("can't get cpu timebase frequency"); freq = *fp; printk("time_init: decrementer frequency = %u.%.6u MHz\n", freq/1000000, freq%1000000); tb_ticks_per_jiffy = freq / HZ; tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; tb_ticks_per_usec = freq / 1000000; tb_to_us = mulhwu_scale_factor(freq, 1000000); div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); tb_to_xs = divres.result_low; ppc_tb_freq = freq; fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL); if (fp == 0) panic("can't get cpu processor frequency"); ppc_proc_freq = *fp; }