diff options
Diffstat (limited to 'arch/sparc64/kernel/time.c')
-rw-r--r-- | arch/sparc64/kernel/time.c | 373 |
1 files changed, 348 insertions, 25 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index a22930d62adf..7d61f1bfd3d3 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" |
@@ -683,6 +692,83 @@ static void __init set_system_time(void) | |||
683 | } | 692 | } |
684 | } | 693 | } |
685 | 694 | ||
695 | /* davem suggests we keep this within the 4M locked kernel image */ | ||
696 | static u32 starfire_get_time(void) | ||
697 | { | ||
698 | static char obp_gettod[32]; | ||
699 | static u32 unix_tod; | ||
700 | |||
701 | sprintf(obp_gettod, "h# %08x unix-gettod", | ||
702 | (unsigned int) (long) &unix_tod); | ||
703 | prom_feval(obp_gettod); | ||
704 | |||
705 | return unix_tod; | ||
706 | } | ||
707 | |||
708 | static int starfire_set_time(u32 val) | ||
709 | { | ||
710 | /* Do nothing, time is set using the service processor | ||
711 | * console on this platform. | ||
712 | */ | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static u32 hypervisor_get_time(void) | ||
717 | { | ||
718 | register unsigned long func asm("%o5"); | ||
719 | register unsigned long arg0 asm("%o0"); | ||
720 | register unsigned long arg1 asm("%o1"); | ||
721 | int retries = 10000; | ||
722 | |||
723 | retry: | ||
724 | func = HV_FAST_TOD_GET; | ||
725 | arg0 = 0; | ||
726 | arg1 = 0; | ||
727 | __asm__ __volatile__("ta %6" | ||
728 | : "=&r" (func), "=&r" (arg0), "=&r" (arg1) | ||
729 | : "0" (func), "1" (arg0), "2" (arg1), | ||
730 | "i" (HV_FAST_TRAP)); | ||
731 | if (arg0 == HV_EOK) | ||
732 | return arg1; | ||
733 | if (arg0 == HV_EWOULDBLOCK) { | ||
734 | if (--retries > 0) { | ||
735 | udelay(100); | ||
736 | goto retry; | ||
737 | } | ||
738 | printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); | ||
739 | return 0; | ||
740 | } | ||
741 | printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); | ||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int hypervisor_set_time(u32 secs) | ||
746 | { | ||
747 | register unsigned long func asm("%o5"); | ||
748 | register unsigned long arg0 asm("%o0"); | ||
749 | int retries = 10000; | ||
750 | |||
751 | retry: | ||
752 | func = HV_FAST_TOD_SET; | ||
753 | arg0 = secs; | ||
754 | __asm__ __volatile__("ta %4" | ||
755 | : "=&r" (func), "=&r" (arg0) | ||
756 | : "0" (func), "1" (arg0), | ||
757 | "i" (HV_FAST_TRAP)); | ||
758 | if (arg0 == HV_EOK) | ||
759 | return 0; | ||
760 | if (arg0 == HV_EWOULDBLOCK) { | ||
761 | if (--retries > 0) { | ||
762 | udelay(100); | ||
763 | goto retry; | ||
764 | } | ||
765 | printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); | ||
766 | return -EAGAIN; | ||
767 | } | ||
768 | printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); | ||
769 | return -EOPNOTSUPP; | ||
770 | } | ||
771 | |||
686 | void __init clock_probe(void) | 772 | void __init clock_probe(void) |
687 | { | 773 | { |
688 | struct linux_prom_registers clk_reg[2]; | 774 | struct linux_prom_registers clk_reg[2]; |
@@ -702,14 +788,14 @@ void __init clock_probe(void) | |||
702 | 788 | ||
703 | 789 | ||
704 | if (this_is_starfire) { | 790 | if (this_is_starfire) { |
705 | /* davem suggests we keep this within the 4M locked kernel image */ | 791 | xtime.tv_sec = starfire_get_time(); |
706 | static char obp_gettod[256]; | 792 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); |
707 | static u32 unix_tod; | 793 | set_normalized_timespec(&wall_to_monotonic, |
708 | 794 | -xtime.tv_sec, -xtime.tv_nsec); | |
709 | sprintf(obp_gettod, "h# %08x unix-gettod", | 795 | return; |
710 | (unsigned int) (long) &unix_tod); | 796 | } |
711 | prom_feval(obp_gettod); | 797 | if (tlb_type == hypervisor) { |
712 | xtime.tv_sec = unix_tod; | 798 | xtime.tv_sec = hypervisor_get_time(); |
713 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | 799 | xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); |
714 | set_normalized_timespec(&wall_to_monotonic, | 800 | set_normalized_timespec(&wall_to_monotonic, |
715 | -xtime.tv_sec, -xtime.tv_nsec); | 801 | -xtime.tv_sec, -xtime.tv_nsec); |
@@ -981,11 +1067,10 @@ static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_reg | |||
981 | } | 1067 | } |
982 | 1068 | ||
983 | struct freq_table { | 1069 | struct freq_table { |
984 | unsigned long udelay_val_ref; | ||
985 | unsigned long clock_tick_ref; | 1070 | unsigned long clock_tick_ref; |
986 | unsigned int ref_freq; | 1071 | unsigned int ref_freq; |
987 | }; | 1072 | }; |
988 | static DEFINE_PER_CPU(struct freq_table, sparc64_freq_table) = { 0, 0, 0 }; | 1073 | static DEFINE_PER_CPU(struct freq_table, sparc64_freq_table) = { 0, 0 }; |
989 | 1074 | ||
990 | unsigned long sparc64_get_clock_tick(unsigned int cpu) | 1075 | unsigned long sparc64_get_clock_tick(unsigned int cpu) |
991 | { | 1076 | { |
@@ -1007,16 +1092,11 @@ static int sparc64_cpufreq_notifier(struct notifier_block *nb, unsigned long val | |||
1007 | 1092 | ||
1008 | if (!ft->ref_freq) { | 1093 | if (!ft->ref_freq) { |
1009 | ft->ref_freq = freq->old; | 1094 | 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; | 1095 | ft->clock_tick_ref = cpu_data(cpu).clock_tick; |
1012 | } | 1096 | } |
1013 | if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || | 1097 | if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || |
1014 | (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || | 1098 | (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || |
1015 | (val == CPUFREQ_RESUMECHANGE)) { | 1099 | (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 = | 1100 | cpu_data(cpu).clock_tick = |
1021 | cpufreq_scale(ft->clock_tick_ref, | 1101 | cpufreq_scale(ft->clock_tick_ref, |
1022 | ft->ref_freq, | 1102 | ft->ref_freq, |
@@ -1179,3 +1259,246 @@ static int set_rtc_mmss(unsigned long nowtime) | |||
1179 | return retval; | 1259 | return retval; |
1180 | } | 1260 | } |
1181 | } | 1261 | } |
1262 | |||
1263 | #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ | ||
1264 | static unsigned char mini_rtc_status; /* bitmapped status byte. */ | ||
1265 | |||
1266 | /* months start at 0 now */ | ||
1267 | static unsigned char days_in_mo[] = | ||
1268 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | ||
1269 | |||
1270 | #define FEBRUARY 2 | ||
1271 | #define STARTOFTIME 1970 | ||
1272 | #define SECDAY 86400L | ||
1273 | #define SECYR (SECDAY * 365) | ||
1274 | #define leapyear(year) ((year) % 4 == 0 && \ | ||
1275 | ((year) % 100 != 0 || (year) % 400 == 0)) | ||
1276 | #define days_in_year(a) (leapyear(a) ? 366 : 365) | ||
1277 | #define days_in_month(a) (month_days[(a) - 1]) | ||
1278 | |||
1279 | static int month_days[12] = { | ||
1280 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
1281 | }; | ||
1282 | |||
1283 | /* | ||
1284 | * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) | ||
1285 | */ | ||
1286 | static void GregorianDay(struct rtc_time * tm) | ||
1287 | { | ||
1288 | int leapsToDate; | ||
1289 | int lastYear; | ||
1290 | int day; | ||
1291 | int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; | ||
1292 | |||
1293 | lastYear = tm->tm_year - 1; | ||
1294 | |||
1295 | /* | ||
1296 | * Number of leap corrections to apply up to end of last year | ||
1297 | */ | ||
1298 | leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400; | ||
1299 | |||
1300 | /* | ||
1301 | * This year is a leap year if it is divisible by 4 except when it is | ||
1302 | * divisible by 100 unless it is divisible by 400 | ||
1303 | * | ||
1304 | * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was | ||
1305 | */ | ||
1306 | day = tm->tm_mon > 2 && leapyear(tm->tm_year); | ||
1307 | |||
1308 | day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + | ||
1309 | tm->tm_mday; | ||
1310 | |||
1311 | tm->tm_wday = day % 7; | ||
1312 | } | ||
1313 | |||
1314 | static void to_tm(int tim, struct rtc_time *tm) | ||
1315 | { | ||
1316 | register int i; | ||
1317 | register long hms, day; | ||
1318 | |||
1319 | day = tim / SECDAY; | ||
1320 | hms = tim % SECDAY; | ||
1321 | |||
1322 | /* Hours, minutes, seconds are easy */ | ||
1323 | tm->tm_hour = hms / 3600; | ||
1324 | tm->tm_min = (hms % 3600) / 60; | ||
1325 | tm->tm_sec = (hms % 3600) % 60; | ||
1326 | |||
1327 | /* Number of years in days */ | ||
1328 | for (i = STARTOFTIME; day >= days_in_year(i); i++) | ||
1329 | day -= days_in_year(i); | ||
1330 | tm->tm_year = i; | ||
1331 | |||
1332 | /* Number of months in days left */ | ||
1333 | if (leapyear(tm->tm_year)) | ||
1334 | days_in_month(FEBRUARY) = 29; | ||
1335 | for (i = 1; day >= days_in_month(i); i++) | ||
1336 | day -= days_in_month(i); | ||
1337 | days_in_month(FEBRUARY) = 28; | ||
1338 | tm->tm_mon = i; | ||
1339 | |||
1340 | /* Days are what is left over (+1) from all that. */ | ||
1341 | tm->tm_mday = day + 1; | ||
1342 | |||
1343 | /* | ||
1344 | * Determine the day of week | ||
1345 | */ | ||
1346 | GregorianDay(tm); | ||
1347 | } | ||
1348 | |||
1349 | /* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, | ||
1350 | * aka Unix time. So we have to convert to/from rtc_time. | ||
1351 | */ | ||
1352 | static inline void mini_get_rtc_time(struct rtc_time *time) | ||
1353 | { | ||
1354 | unsigned long flags; | ||
1355 | u32 seconds; | ||
1356 | |||
1357 | spin_lock_irqsave(&rtc_lock, flags); | ||
1358 | seconds = 0; | ||
1359 | if (this_is_starfire) | ||
1360 | seconds = starfire_get_time(); | ||
1361 | else if (tlb_type == hypervisor) | ||
1362 | seconds = hypervisor_get_time(); | ||
1363 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1364 | |||
1365 | to_tm(seconds, time); | ||
1366 | time->tm_year -= 1900; | ||
1367 | time->tm_mon -= 1; | ||
1368 | } | ||
1369 | |||
1370 | static inline int mini_set_rtc_time(struct rtc_time *time) | ||
1371 | { | ||
1372 | u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, | ||
1373 | time->tm_mday, time->tm_hour, | ||
1374 | time->tm_min, time->tm_sec); | ||
1375 | unsigned long flags; | ||
1376 | int err; | ||
1377 | |||
1378 | spin_lock_irqsave(&rtc_lock, flags); | ||
1379 | err = -ENODEV; | ||
1380 | if (this_is_starfire) | ||
1381 | err = starfire_set_time(seconds); | ||
1382 | else if (tlb_type == hypervisor) | ||
1383 | err = hypervisor_set_time(seconds); | ||
1384 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1385 | |||
1386 | return err; | ||
1387 | } | ||
1388 | |||
1389 | static int mini_rtc_ioctl(struct inode *inode, struct file *file, | ||
1390 | unsigned int cmd, unsigned long arg) | ||
1391 | { | ||
1392 | struct rtc_time wtime; | ||
1393 | void __user *argp = (void __user *)arg; | ||
1394 | |||
1395 | switch (cmd) { | ||
1396 | |||
1397 | case RTC_PLL_GET: | ||
1398 | return -EINVAL; | ||
1399 | |||
1400 | case RTC_PLL_SET: | ||
1401 | return -EINVAL; | ||
1402 | |||
1403 | case RTC_UIE_OFF: /* disable ints from RTC updates. */ | ||
1404 | return 0; | ||
1405 | |||
1406 | case RTC_UIE_ON: /* enable ints for RTC updates. */ | ||
1407 | return -EINVAL; | ||
1408 | |||
1409 | case RTC_RD_TIME: /* Read the time/date from RTC */ | ||
1410 | /* this doesn't get week-day, who cares */ | ||
1411 | memset(&wtime, 0, sizeof(wtime)); | ||
1412 | mini_get_rtc_time(&wtime); | ||
1413 | |||
1414 | return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0; | ||
1415 | |||
1416 | case RTC_SET_TIME: /* Set the RTC */ | ||
1417 | { | ||
1418 | int year; | ||
1419 | unsigned char leap_yr; | ||
1420 | |||
1421 | if (!capable(CAP_SYS_TIME)) | ||
1422 | return -EACCES; | ||
1423 | |||
1424 | if (copy_from_user(&wtime, argp, sizeof(wtime))) | ||
1425 | return -EFAULT; | ||
1426 | |||
1427 | year = wtime.tm_year + 1900; | ||
1428 | leap_yr = ((!(year % 4) && (year % 100)) || | ||
1429 | !(year % 400)); | ||
1430 | |||
1431 | if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1)) | ||
1432 | return -EINVAL; | ||
1433 | |||
1434 | if (wtime.tm_mday < 0 || wtime.tm_mday > | ||
1435 | (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr))) | ||
1436 | return -EINVAL; | ||
1437 | |||
1438 | if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || | ||
1439 | wtime.tm_min < 0 || wtime.tm_min >= 60 || | ||
1440 | wtime.tm_sec < 0 || wtime.tm_sec >= 60) | ||
1441 | return -EINVAL; | ||
1442 | |||
1443 | return mini_set_rtc_time(&wtime); | ||
1444 | } | ||
1445 | } | ||
1446 | |||
1447 | return -EINVAL; | ||
1448 | } | ||
1449 | |||
1450 | static int mini_rtc_open(struct inode *inode, struct file *file) | ||
1451 | { | ||
1452 | if (mini_rtc_status & RTC_IS_OPEN) | ||
1453 | return -EBUSY; | ||
1454 | |||
1455 | mini_rtc_status |= RTC_IS_OPEN; | ||
1456 | |||
1457 | return 0; | ||
1458 | } | ||
1459 | |||
1460 | static int mini_rtc_release(struct inode *inode, struct file *file) | ||
1461 | { | ||
1462 | mini_rtc_status &= ~RTC_IS_OPEN; | ||
1463 | return 0; | ||
1464 | } | ||
1465 | |||
1466 | |||
1467 | static struct file_operations mini_rtc_fops = { | ||
1468 | .owner = THIS_MODULE, | ||
1469 | .ioctl = mini_rtc_ioctl, | ||
1470 | .open = mini_rtc_open, | ||
1471 | .release = mini_rtc_release, | ||
1472 | }; | ||
1473 | |||
1474 | static struct miscdevice rtc_mini_dev = | ||
1475 | { | ||
1476 | .minor = RTC_MINOR, | ||
1477 | .name = "rtc", | ||
1478 | .fops = &mini_rtc_fops, | ||
1479 | }; | ||
1480 | |||
1481 | static int __init rtc_mini_init(void) | ||
1482 | { | ||
1483 | int retval; | ||
1484 | |||
1485 | if (tlb_type != hypervisor && !this_is_starfire) | ||
1486 | return -ENODEV; | ||
1487 | |||
1488 | printk(KERN_INFO "Mini RTC Driver\n"); | ||
1489 | |||
1490 | retval = misc_register(&rtc_mini_dev); | ||
1491 | if (retval < 0) | ||
1492 | return retval; | ||
1493 | |||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1497 | static void __exit rtc_mini_exit(void) | ||
1498 | { | ||
1499 | misc_deregister(&rtc_mini_dev); | ||
1500 | } | ||
1501 | |||
1502 | |||
1503 | module_init(rtc_mini_init); | ||
1504 | module_exit(rtc_mini_exit); | ||