aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/time.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2006-03-01 20:32:46 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-03-20 04:14:10 -0500
commit8ba706a95bb92c3b14b812f6d507890336d19136 (patch)
tree2456134d50898a7bd8efda4adcfa636ab0ea43cf /arch/sparc64/kernel/time.c
parentb830ab665ad96c6b20d51a89b35cbc09ab5a2c29 (diff)
[SPARC64]: Add mini-RTC driver for Starfire and SUN4V.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/time.c')
-rw-r--r--arch/sparc64/kernel/time.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index f6275adbc811..d50ebeae144a 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
49DEFINE_SPINLOCK(mostek_lock); 52DEFINE_SPINLOCK(mostek_lock);
50DEFINE_SPINLOCK(rtc_lock); 53DEFINE_SPINLOCK(rtc_lock);
@@ -702,6 +705,14 @@ static u32 starfire_get_time(void)
702 return unix_tod; 705 return unix_tod;
703} 706}
704 707
708static 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
705static u32 hypervisor_get_time(void) 716static u32 hypervisor_get_time(void)
706{ 717{
707 register unsigned long func asm("%o5"); 718 register unsigned long func asm("%o5");
@@ -731,6 +742,33 @@ retry:
731 return 0; 742 return 0;
732} 743}
733 744
745static 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
751retry:
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
734void __init clock_probe(void) 772void __init clock_probe(void)
735{ 773{
736 struct linux_prom_registers clk_reg[2]; 774 struct linux_prom_registers clk_reg[2];
@@ -1221,3 +1259,244 @@ static int set_rtc_mmss(unsigned long nowtime)
1221 return retval; 1259 return retval;
1222 } 1260 }
1223} 1261}
1262
1263#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
1264static unsigned char mini_rtc_status; /* bitmapped status byte. */
1265
1266/* months start at 0 now */
1267static 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
1279static 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 */
1286static 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
1314static 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 */
1352static 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}
1367
1368static inline int mini_set_rtc_time(struct rtc_time *time)
1369{
1370 u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1,
1371 time->tm_mday, time->tm_hour,
1372 time->tm_min, time->tm_sec);
1373 unsigned long flags;
1374 int err;
1375
1376 spin_lock_irqsave(&rtc_lock, flags);
1377 err = -ENODEV;
1378 if (this_is_starfire)
1379 err = starfire_set_time(seconds);
1380 else if (tlb_type == hypervisor)
1381 err = hypervisor_set_time(seconds);
1382 spin_unlock_irqrestore(&rtc_lock, flags);
1383
1384 return err;
1385}
1386
1387static int mini_rtc_ioctl(struct inode *inode, struct file *file,
1388 unsigned int cmd, unsigned long arg)
1389{
1390 struct rtc_time wtime;
1391 void __user *argp = (void __user *)arg;
1392
1393 switch (cmd) {
1394
1395 case RTC_PLL_GET:
1396 return -EINVAL;
1397
1398 case RTC_PLL_SET:
1399 return -EINVAL;
1400
1401 case RTC_UIE_OFF: /* disable ints from RTC updates. */
1402 return 0;
1403
1404 case RTC_UIE_ON: /* enable ints for RTC updates. */
1405 return -EINVAL;
1406
1407 case RTC_RD_TIME: /* Read the time/date from RTC */
1408 /* this doesn't get week-day, who cares */
1409 memset(&wtime, 0, sizeof(wtime));
1410 mini_get_rtc_time(&wtime);
1411
1412 return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
1413
1414 case RTC_SET_TIME: /* Set the RTC */
1415 {
1416 int year;
1417 unsigned char leap_yr;
1418
1419 if (!capable(CAP_SYS_TIME))
1420 return -EACCES;
1421
1422 if (copy_from_user(&wtime, argp, sizeof(wtime)))
1423 return -EFAULT;
1424
1425 year = wtime.tm_year + 1900;
1426 leap_yr = ((!(year % 4) && (year % 100)) ||
1427 !(year % 400));
1428
1429 if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
1430 return -EINVAL;
1431
1432 if (wtime.tm_mday < 0 || wtime.tm_mday >
1433 (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
1434 return -EINVAL;
1435
1436 if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
1437 wtime.tm_min < 0 || wtime.tm_min >= 60 ||
1438 wtime.tm_sec < 0 || wtime.tm_sec >= 60)
1439 return -EINVAL;
1440
1441 return mini_set_rtc_time(&wtime);
1442 }
1443 }
1444
1445 return -EINVAL;
1446}
1447
1448static int mini_rtc_open(struct inode *inode, struct file *file)
1449{
1450 if (mini_rtc_status & RTC_IS_OPEN)
1451 return -EBUSY;
1452
1453 mini_rtc_status |= RTC_IS_OPEN;
1454
1455 return 0;
1456}
1457
1458static int mini_rtc_release(struct inode *inode, struct file *file)
1459{
1460 mini_rtc_status &= ~RTC_IS_OPEN;
1461 return 0;
1462}
1463
1464
1465static struct file_operations mini_rtc_fops = {
1466 .owner = THIS_MODULE,
1467 .ioctl = mini_rtc_ioctl,
1468 .open = mini_rtc_open,
1469 .release = mini_rtc_release,
1470};
1471
1472static struct miscdevice rtc_mini_dev =
1473{
1474 .minor = RTC_MINOR,
1475 .name = "rtc",
1476 .fops = &mini_rtc_fops,
1477};
1478
1479static int __init rtc_mini_init(void)
1480{
1481 int retval;
1482
1483 if (tlb_type != hypervisor && !this_is_starfire)
1484 return -ENODEV;
1485
1486 printk(KERN_INFO "Mini RTC Driver\n");
1487
1488 retval = misc_register(&rtc_mini_dev);
1489 if (retval < 0)
1490 return retval;
1491
1492 return 0;
1493}
1494
1495static void __exit rtc_mini_exit(void)
1496{
1497 misc_deregister(&rtc_mini_dev);
1498}
1499
1500
1501module_init(rtc_mini_init);
1502module_exit(rtc_mini_exit);