diff options
Diffstat (limited to 'arch/sparc64/kernel/time.c')
-rw-r--r-- | arch/sparc64/kernel/time.c | 389 |
1 files changed, 349 insertions, 40 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index a22930d62adf..e55b5c6ece02 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include <linux/cpufreq.h> | 30 | #include <linux/cpufreq.h> |
31 | #include <linux/percpu.h> | 31 | #include <linux/percpu.h> |
32 | #include <linux/profile.h> | 32 | #include <linux/profile.h> |
33 | #include <linux/miscdevice.h> | ||
34 | #include <linux/rtc.h> | ||
33 | 35 | ||
34 | #include <asm/oplib.h> | 36 | #include <asm/oplib.h> |
35 | #include <asm/mostek.h> | 37 | #include <asm/mostek.h> |
@@ -45,6 +47,7 @@ | |||
45 | #include <asm/smp.h> | 47 | #include <asm/smp.h> |
46 | #include <asm/sections.h> | 48 | #include <asm/sections.h> |
47 | #include <asm/cpudata.h> | 49 | #include <asm/cpudata.h> |
50 | #include <asm/uaccess.h> | ||
48 | 51 | ||
49 | DEFINE_SPINLOCK(mostek_lock); | 52 | DEFINE_SPINLOCK(mostek_lock); |
50 | DEFINE_SPINLOCK(rtc_lock); | 53 | DEFINE_SPINLOCK(rtc_lock); |
@@ -193,16 +196,22 @@ struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations; | |||
193 | 196 | ||
194 | static void stick_init_tick(unsigned long offset) | 197 | static void stick_init_tick(unsigned long offset) |
195 | { | 198 | { |
196 | tick_disable_protection(); | 199 | /* Writes to the %tick and %stick register are not |
197 | 200 | * allowed on sun4v. The Hypervisor controls that | |
198 | /* Let the user get at STICK too. */ | 201 | * bit, per-strand. |
199 | __asm__ __volatile__( | 202 | */ |
200 | " rd %%asr24, %%g2\n" | 203 | if (tlb_type != hypervisor) { |
201 | " andn %%g2, %0, %%g2\n" | 204 | tick_disable_protection(); |
202 | " wr %%g2, 0, %%asr24" | 205 | |
203 | : /* no outputs */ | 206 | /* Let the user get at STICK too. */ |
204 | : "r" (TICK_PRIV_BIT) | 207 | __asm__ __volatile__( |
205 | : "g1", "g2"); | 208 | " rd %%asr24, %%g2\n" |
209 | " andn %%g2, %0, %%g2\n" | ||
210 | " wr %%g2, 0, %%asr24" | ||
211 | : /* no outputs */ | ||
212 | : "r" (TICK_PRIV_BIT) | ||
213 | : "g1", "g2"); | ||
214 | } | ||
206 | 215 | ||
207 | __asm__ __volatile__( | 216 | __asm__ __volatile__( |
208 | " rd %%asr24, %%g1\n" | 217 | " rd %%asr24, %%g1\n" |
@@ -632,23 +641,8 @@ static void __init set_system_time(void) | |||
632 | mon = MSTK_REG_MONTH(mregs); | 641 | mon = MSTK_REG_MONTH(mregs); |
633 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); | 642 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); |
634 | } else { | 643 | } else { |
635 | int i; | ||
636 | |||
637 | /* Dallas 12887 RTC chip. */ | 644 | /* Dallas 12887 RTC chip. */ |
638 | 645 | ||
639 | /* Stolen from arch/i386/kernel/time.c, see there for | ||
640 | * credits and descriptive comments. | ||
641 | */ | ||
642 | for (i = 0; i < 1000000; i++) { | ||
643 | if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) | ||
644 | break; | ||
645 | udelay(10); | ||
646 | } | ||
647 | for (i = 0; i < 1000000; i++) { | ||
648 | if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) | ||
649 | break; | ||
650 | udelay(10); | ||
651 | } | ||
652 | do { | 646 | do { |
653 | sec = CMOS_READ(RTC_SECONDS); | 647 | sec = CMOS_READ(RTC_SECONDS); |
654 | min = CMOS_READ(RTC_MINUTES); | 648 | min = CMOS_READ(RTC_MINUTES); |
@@ -657,6 +651,7 @@ static void __init set_system_time(void) | |||
657 | mon = CMOS_READ(RTC_MONTH); | 651 | mon = CMOS_READ(RTC_MONTH); |
658 | year = CMOS_READ(RTC_YEAR); | 652 | year = CMOS_READ(RTC_YEAR); |
659 | } while (sec != CMOS_READ(RTC_SECONDS)); | 653 | } while (sec != CMOS_READ(RTC_SECONDS)); |
654 | |||
660 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { | 655 | if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { |
661 | BCD_TO_BIN(sec); | 656 | BCD_TO_BIN(sec); |
662 | BCD_TO_BIN(min); | 657 | BCD_TO_BIN(min); |
@@ -683,6 +678,83 @@ static void __init set_system_time(void) | |||
683 | } | 678 | } |
684 | } | 679 | } |
685 | 680 | ||
681 | /* davem suggests we keep this within the 4M locked kernel image */ | ||
682 | static u32 starfire_get_time(void) | ||
683 | { | ||
684 | static char obp_gettod[32]; | ||
685 | static u32 unix_tod; | ||
686 | |||
687 | sprintf(obp_gettod, "h# %08x unix-gettod", | ||
688 | (unsigned int) (long) &unix_tod); | ||
689 | prom_feval(obp_gettod); | ||
690 | |||
691 | return unix_tod; | ||
692 | } | ||
693 | |||
694 | static int starfire_set_time(u32 val) | ||
695 | { | ||
696 | /* Do nothing, time is set using the service processor | ||
697 | * console on this platform. | ||
698 | */ | ||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | static u32 hypervisor_get_time(void) | ||
703 | { | ||
704 | register unsigned long func asm("%o5"); | ||
705 | register unsigned long arg0 asm("%o0"); | ||
706 | register unsigned long arg1 asm("%o1"); | ||
707 | int retries = 10000; | ||
708 | |||
709 | retry: | ||
710 | func = HV_FAST_TOD_GET; | ||
711 | arg0 = 0; | ||
712 | arg1 = 0; | ||
713 | __asm__ __volatile__("ta %6" | ||
714 | : "=&r" (func), "=&r" (arg0), "=&r" (arg1) | ||
715 | : "0" (func), "1" (arg0), "2" (arg1), | ||
716 | "i" (HV_FAST_TRAP)); | ||
717 | if (arg0 == HV_EOK) | ||
718 | return arg1; | ||
719 | if (arg0 == HV_EWOULDBLOCK) { | ||
720 | if (--retries > 0) { | ||
721 | udelay(100); | ||
722 | goto retry; | ||
723 | } | ||
724 | printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); | ||
725 | return 0; | ||
726 | } | ||
727 | printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | static int hypervisor_set_time(u32 secs) | ||
732 | { | ||
733 | register unsigned long func asm("%o5"); | ||
734 | register unsigned long arg0 asm("%o0"); | ||
735 | int retries = 10000; | ||
736 | |||
737 | retry: | ||
738 | func = HV_FAST_TOD_SET; | ||
739 | arg0 = secs; | ||
740 | __asm__ __volatile__("ta %4" | ||
741 | : "=&r" (func), "=&r" (arg0) | ||
742 | : "0" (func), "1" (arg0), | ||
743 | "i" (HV_FAST_TRAP)); | ||
744 | if (arg0 == HV_EOK) | ||
745 | return 0; | ||
746 | if (arg0 == HV_EWOULDBLOCK) { | ||
747 | if (--retries > 0) { | ||
748 | udelay(100); | ||
749 | goto retry; | ||
750 | } | ||
751 | printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); | ||
752 | return -EAGAIN; | ||
753 | } | ||
754 | printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); | ||
755 | return -EOPNOTSUPP; | ||
756 | } | ||
757 | |||
686 | void __init clock_probe(void) | 758 | void __init clock_probe(void) |
687 | { | 759 | { |
688 | struct linux_prom_registers clk_reg[2]; | 760 | struct linux_prom_registers clk_reg[2]; |
@@ -702,14 +774,14 @@ void __init clock_probe(void) | |||
702 | 774 | ||
703 | 775 | ||
704 | if (this_is_starfire) { | 776 | if (this_is_starfire) { |
705 | /* davem suggests we keep this within the 4M locked kernel image */ | 777 | xtime.tv_sec = starfire_get_time(); |
706 | static char obp_gettod[256]; | 778 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); |
707 | static u32 unix_tod; | 779 | set_normalized_timespec(&wall_to_monotonic, |
708 | 780 | -xtime.tv_sec, -xtime.tv_nsec); | |
709 | sprintf(obp_gettod, "h# %08x unix-gettod", | 781 | return; |
710 | (unsigned int) (long) &unix_tod); | 782 | } |
711 | prom_feval(obp_gettod); | 783 | if (tlb_type == hypervisor) { |
712 | xtime.tv_sec = unix_tod; | 784 | xtime.tv_sec = hypervisor_get_time(); |
713 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | 785 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); |
714 | set_normalized_timespec(&wall_to_monotonic, | 786 | set_normalized_timespec(&wall_to_monotonic, |
715 | -xtime.tv_sec, -xtime.tv_nsec); | 787 | -xtime.tv_sec, -xtime.tv_nsec); |
@@ -981,11 +1053,10 @@ static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_reg | |||
981 | } | 1053 | } |
982 | 1054 | ||
983 | struct freq_table { | 1055 | struct freq_table { |
984 | unsigned long udelay_val_ref; | ||
985 | unsigned long clock_tick_ref; | 1056 | unsigned long clock_tick_ref; |
986 | unsigned int ref_freq; | 1057 | unsigned int ref_freq; |
987 | }; | 1058 | }; |
988 | static DEFINE_PER_CPU(struct freq_table, sparc64_freq_table) = { 0, 0, 0 }; | 1059 | static DEFINE_PER_CPU(struct freq_table, sparc64_freq_table) = { 0, 0 }; |
989 | 1060 | ||
990 | unsigned long sparc64_get_clock_tick(unsigned int cpu) | 1061 | unsigned long sparc64_get_clock_tick(unsigned int cpu) |
991 | { | 1062 | { |
@@ -1007,16 +1078,11 @@ static int sparc64_cpufreq_notifier(struct notifier_block *nb, unsigned long val | |||
1007 | 1078 | ||
1008 | if (!ft->ref_freq) { | 1079 | if (!ft->ref_freq) { |
1009 | ft->ref_freq = freq->old; | 1080 | ft->ref_freq = freq->old; |
1010 | ft->udelay_val_ref = cpu_data(cpu).udelay_val; | ||
1011 | ft->clock_tick_ref = cpu_data(cpu).clock_tick; | 1081 | ft->clock_tick_ref = cpu_data(cpu).clock_tick; |
1012 | } | 1082 | } |
1013 | if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || | 1083 | if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || |
1014 | (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || | 1084 | (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || |
1015 | (val == CPUFREQ_RESUMECHANGE)) { | 1085 | (val == CPUFREQ_RESUMECHANGE)) { |
1016 | cpu_data(cpu).udelay_val = | ||
1017 | cpufreq_scale(ft->udelay_val_ref, | ||
1018 | ft->ref_freq, | ||
1019 | freq->new); | ||
1020 | cpu_data(cpu).clock_tick = | 1086 | cpu_data(cpu).clock_tick = |
1021 | cpufreq_scale(ft->clock_tick_ref, | 1087 | cpufreq_scale(ft->clock_tick_ref, |
1022 | ft->ref_freq, | 1088 | ft->ref_freq, |
@@ -1179,3 +1245,246 @@ static int set_rtc_mmss(unsigned long nowtime) | |||
1179 | return retval; | 1245 | return retval; |
1180 | } | 1246 | } |
1181 | } | 1247 | } |
1248 | |||
1249 | #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ | ||
1250 | static unsigned char mini_rtc_status; /* bitmapped status byte. */ | ||
1251 | |||
1252 | /* months start at 0 now */ | ||
1253 | static unsigned char days_in_mo[] = | ||
1254 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | ||
1255 | |||
1256 | #define FEBRUARY 2 | ||
1257 | #define STARTOFTIME 1970 | ||
1258 | #define SECDAY 86400L | ||
1259 | #define SECYR (SECDAY * 365) | ||
1260 | #define leapyear(year) ((year) % 4 == 0 && \ | ||
1261 | ((year) % 100 != 0 || (year) % 400 == 0)) | ||
1262 | #define days_in_year(a) (leapyear(a) ? 366 : 365) | ||
1263 | #define days_in_month(a) (month_days[(a) - 1]) | ||
1264 | |||
1265 | static int month_days[12] = { | ||
1266 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
1267 | }; | ||
1268 | |||
1269 | /* | ||
1270 | * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) | ||
1271 | */ | ||
1272 | static void GregorianDay(struct rtc_time * tm) | ||
1273 | { | ||
1274 | int leapsToDate; | ||
1275 | int lastYear; | ||
1276 | int day; | ||
1277 | int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; | ||
1278 | |||
1279 | lastYear = tm->tm_year - 1; | ||
1280 | |||
1281 | /* | ||
1282 | * Number of leap corrections to apply up to end of last year | ||
1283 | */ | ||
1284 | leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400; | ||
1285 | |||
1286 | /* | ||
1287 | * This year is a leap year if it is divisible by 4 except when it is | ||
1288 | * divisible by 100 unless it is divisible by 400 | ||
1289 | * | ||
1290 | * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was | ||
1291 | */ | ||
1292 | day = tm->tm_mon > 2 && leapyear(tm->tm_year); | ||
1293 | |||
1294 | day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + | ||
1295 | tm->tm_mday; | ||
1296 | |||
1297 | tm->tm_wday = day % 7; | ||
1298 | } | ||
1299 | |||
1300 | static void to_tm(int tim, struct rtc_time *tm) | ||
1301 | { | ||
1302 | register int i; | ||
1303 | register long hms, day; | ||
1304 | |||
1305 | day = tim / SECDAY; | ||
1306 | hms = tim % SECDAY; | ||
1307 | |||
1308 | /* Hours, minutes, seconds are easy */ | ||
1309 | tm->tm_hour = hms / 3600; | ||
1310 | tm->tm_min = (hms % 3600) / 60; | ||
1311 | tm->tm_sec = (hms % 3600) % 60; | ||
1312 | |||
1313 | /* Number of years in days */ | ||
1314 | for (i = STARTOFTIME; day >= days_in_year(i); i++) | ||
1315 | day -= days_in_year(i); | ||
1316 | tm->tm_year = i; | ||
1317 | |||
1318 | /* Number of months in days left */ | ||
1319 | if (leapyear(tm->tm_year)) | ||
1320 | days_in_month(FEBRUARY) = 29; | ||
1321 | for (i = 1; day >= days_in_month(i); i++) | ||
1322 | day -= days_in_month(i); | ||
1323 | days_in_month(FEBRUARY) = 28; | ||
1324 | tm->tm_mon = i; | ||
1325 | |||
1326 | /* Days are what is left over (+1) from all that. */ | ||
1327 | tm->tm_mday = day + 1; | ||
1328 | |||
1329 | /* | ||
1330 | * Determine the day of week | ||
1331 | */ | ||
1332 | GregorianDay(tm); | ||
1333 | } | ||
1334 | |||
1335 | /* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, | ||
1336 | * aka Unix time. So we have to convert to/from rtc_time. | ||
1337 | */ | ||
1338 | static inline void mini_get_rtc_time(struct rtc_time *time) | ||
1339 | { | ||
1340 | unsigned long flags; | ||
1341 | u32 seconds; | ||
1342 | |||
1343 | spin_lock_irqsave(&rtc_lock, flags); | ||
1344 | seconds = 0; | ||
1345 | if (this_is_starfire) | ||
1346 | seconds = starfire_get_time(); | ||
1347 | else if (tlb_type == hypervisor) | ||
1348 | seconds = hypervisor_get_time(); | ||
1349 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1350 | |||
1351 | to_tm(seconds, time); | ||
1352 | time->tm_year -= 1900; | ||
1353 | time->tm_mon -= 1; | ||
1354 | } | ||
1355 | |||
1356 | static inline int mini_set_rtc_time(struct rtc_time *time) | ||
1357 | { | ||
1358 | u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, | ||
1359 | time->tm_mday, time->tm_hour, | ||
1360 | time->tm_min, time->tm_sec); | ||
1361 | unsigned long flags; | ||
1362 | int err; | ||
1363 | |||
1364 | spin_lock_irqsave(&rtc_lock, flags); | ||
1365 | err = -ENODEV; | ||
1366 | if (this_is_starfire) | ||
1367 | err = starfire_set_time(seconds); | ||
1368 | else if (tlb_type == hypervisor) | ||
1369 | err = hypervisor_set_time(seconds); | ||
1370 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1371 | |||
1372 | return err; | ||
1373 | } | ||
1374 | |||
1375 | static int mini_rtc_ioctl(struct inode *inode, struct file *file, | ||
1376 | unsigned int cmd, unsigned long arg) | ||
1377 | { | ||
1378 | struct rtc_time wtime; | ||
1379 | void __user *argp = (void __user *)arg; | ||
1380 | |||
1381 | switch (cmd) { | ||
1382 | |||
1383 | case RTC_PLL_GET: | ||
1384 | return -EINVAL; | ||
1385 | |||
1386 | case RTC_PLL_SET: | ||
1387 | return -EINVAL; | ||
1388 | |||
1389 | case RTC_UIE_OFF: /* disable ints from RTC updates. */ | ||
1390 | return 0; | ||
1391 | |||
1392 | case RTC_UIE_ON: /* enable ints for RTC updates. */ | ||
1393 | return -EINVAL; | ||
1394 | |||
1395 | case RTC_RD_TIME: /* Read the time/date from RTC */ | ||
1396 | /* this doesn't get week-day, who cares */ | ||
1397 | memset(&wtime, 0, sizeof(wtime)); | ||
1398 | mini_get_rtc_time(&wtime); | ||
1399 | |||
1400 | return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0; | ||
1401 | |||
1402 | case RTC_SET_TIME: /* Set the RTC */ | ||
1403 | { | ||
1404 | int year; | ||
1405 | unsigned char leap_yr; | ||
1406 | |||
1407 | if (!capable(CAP_SYS_TIME)) | ||
1408 | return -EACCES; | ||
1409 | |||
1410 | if (copy_from_user(&wtime, argp, sizeof(wtime))) | ||
1411 | return -EFAULT; | ||
1412 | |||
1413 | year = wtime.tm_year + 1900; | ||
1414 | leap_yr = ((!(year % 4) && (year % 100)) || | ||
1415 | !(year % 400)); | ||
1416 | |||
1417 | if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1)) | ||
1418 | return -EINVAL; | ||
1419 | |||
1420 | if (wtime.tm_mday < 0 || wtime.tm_mday > | ||
1421 | (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr))) | ||
1422 | return -EINVAL; | ||
1423 | |||
1424 | if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || | ||
1425 | wtime.tm_min < 0 || wtime.tm_min >= 60 || | ||
1426 | wtime.tm_sec < 0 || wtime.tm_sec >= 60) | ||
1427 | return -EINVAL; | ||
1428 | |||
1429 | return mini_set_rtc_time(&wtime); | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | return -EINVAL; | ||
1434 | } | ||
1435 | |||
1436 | static int mini_rtc_open(struct inode *inode, struct file *file) | ||
1437 | { | ||
1438 | if (mini_rtc_status & RTC_IS_OPEN) | ||
1439 | return -EBUSY; | ||
1440 | |||
1441 | mini_rtc_status |= RTC_IS_OPEN; | ||
1442 | |||
1443 | return 0; | ||
1444 | } | ||
1445 | |||
1446 | static int mini_rtc_release(struct inode *inode, struct file *file) | ||
1447 | { | ||
1448 | mini_rtc_status &= ~RTC_IS_OPEN; | ||
1449 | return 0; | ||
1450 | } | ||
1451 | |||
1452 | |||
1453 | static struct file_operations mini_rtc_fops = { | ||
1454 | .owner = THIS_MODULE, | ||
1455 | .ioctl = mini_rtc_ioctl, | ||
1456 | .open = mini_rtc_open, | ||
1457 | .release = mini_rtc_release, | ||
1458 | }; | ||
1459 | |||
1460 | static struct miscdevice rtc_mini_dev = | ||
1461 | { | ||
1462 | .minor = RTC_MINOR, | ||
1463 | .name = "rtc", | ||
1464 | .fops = &mini_rtc_fops, | ||
1465 | }; | ||
1466 | |||
1467 | static int __init rtc_mini_init(void) | ||
1468 | { | ||
1469 | int retval; | ||
1470 | |||
1471 | if (tlb_type != hypervisor && !this_is_starfire) | ||
1472 | return -ENODEV; | ||
1473 | |||
1474 | printk(KERN_INFO "Mini RTC Driver\n"); | ||
1475 | |||
1476 | retval = misc_register(&rtc_mini_dev); | ||
1477 | if (retval < 0) | ||
1478 | return retval; | ||
1479 | |||
1480 | return 0; | ||
1481 | } | ||
1482 | |||
1483 | static void __exit rtc_mini_exit(void) | ||
1484 | { | ||
1485 | misc_deregister(&rtc_mini_dev); | ||
1486 | } | ||
1487 | |||
1488 | |||
1489 | module_init(rtc_mini_init); | ||
1490 | module_exit(rtc_mini_exit); | ||