diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-05-12 00:18:50 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-05-12 00:39:27 -0400 |
commit | d037e0532e7dbb5a7936cbc6747206d2352f2974 (patch) | |
tree | dadc69601b421bc240a4478be331ba8a9c65c254 /arch | |
parent | 95d71e663e79b3e8c64bd7b7321389394b16276e (diff) |
[SPARC64]: Add support for bq4802 TOD chip, as found on ultra45.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/sparc64/kernel/time.c | 230 |
1 files changed, 210 insertions, 20 deletions
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 259063f41f95..6b9a06e42542 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c | |||
@@ -55,6 +55,7 @@ DEFINE_SPINLOCK(rtc_lock); | |||
55 | void __iomem *mstk48t02_regs = NULL; | 55 | void __iomem *mstk48t02_regs = NULL; |
56 | #ifdef CONFIG_PCI | 56 | #ifdef CONFIG_PCI |
57 | unsigned long ds1287_regs = 0UL; | 57 | unsigned long ds1287_regs = 0UL; |
58 | static void __iomem *bq4802_regs; | ||
58 | #endif | 59 | #endif |
59 | 60 | ||
60 | static void __iomem *mstk48t08_regs; | 61 | static void __iomem *mstk48t08_regs; |
@@ -565,12 +566,14 @@ static void __init set_system_time(void) | |||
565 | void __iomem *mregs = mstk48t02_regs; | 566 | void __iomem *mregs = mstk48t02_regs; |
566 | #ifdef CONFIG_PCI | 567 | #ifdef CONFIG_PCI |
567 | unsigned long dregs = ds1287_regs; | 568 | unsigned long dregs = ds1287_regs; |
569 | void __iomem *bregs = bq4802_regs; | ||
568 | #else | 570 | #else |
569 | unsigned long dregs = 0UL; | 571 | unsigned long dregs = 0UL; |
572 | void __iomem *bregs = 0UL; | ||
570 | #endif | 573 | #endif |
571 | u8 tmp; | 574 | u8 tmp; |
572 | 575 | ||
573 | if (!mregs && !dregs) { | 576 | if (!mregs && !dregs && !bregs) { |
574 | prom_printf("Something wrong, clock regs not mapped yet.\n"); | 577 | prom_printf("Something wrong, clock regs not mapped yet.\n"); |
575 | prom_halt(); | 578 | prom_halt(); |
576 | } | 579 | } |
@@ -589,6 +592,33 @@ static void __init set_system_time(void) | |||
589 | day = MSTK_REG_DOM(mregs); | 592 | day = MSTK_REG_DOM(mregs); |
590 | mon = MSTK_REG_MONTH(mregs); | 593 | mon = MSTK_REG_MONTH(mregs); |
591 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); | 594 | year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); |
595 | } else if (bregs) { | ||
596 | unsigned char val = readb(bregs + 0x0e); | ||
597 | unsigned int century; | ||
598 | |||
599 | /* BQ4802 RTC chip. */ | ||
600 | |||
601 | writeb(val | 0x08, bregs + 0x0e); | ||
602 | |||
603 | sec = readb(bregs + 0x00); | ||
604 | min = readb(bregs + 0x02); | ||
605 | hour = readb(bregs + 0x04); | ||
606 | day = readb(bregs + 0x06); | ||
607 | mon = readb(bregs + 0x09); | ||
608 | year = readb(bregs + 0x0a); | ||
609 | century = readb(bregs + 0x0f); | ||
610 | |||
611 | writeb(val, bregs + 0x0e); | ||
612 | |||
613 | BCD_TO_BIN(sec); | ||
614 | BCD_TO_BIN(min); | ||
615 | BCD_TO_BIN(hour); | ||
616 | BCD_TO_BIN(day); | ||
617 | BCD_TO_BIN(mon); | ||
618 | BCD_TO_BIN(year); | ||
619 | BCD_TO_BIN(century); | ||
620 | |||
621 | year += (century * 100); | ||
592 | } else { | 622 | } else { |
593 | /* Dallas 12887 RTC chip. */ | 623 | /* Dallas 12887 RTC chip. */ |
594 | 624 | ||
@@ -712,7 +742,8 @@ static int __init clock_model_matches(const char *model) | |||
712 | strcmp(model, "m5819") && | 742 | strcmp(model, "m5819") && |
713 | strcmp(model, "m5819p") && | 743 | strcmp(model, "m5819p") && |
714 | strcmp(model, "m5823") && | 744 | strcmp(model, "m5823") && |
715 | strcmp(model, "ds1287")) | 745 | strcmp(model, "ds1287") && |
746 | strcmp(model, "bq4802")) | ||
716 | return 0; | 747 | return 0; |
717 | 748 | ||
718 | return 1; | 749 | return 1; |
@@ -722,9 +753,13 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id | |||
722 | { | 753 | { |
723 | struct device_node *dp = op->node; | 754 | struct device_node *dp = op->node; |
724 | const char *model = of_get_property(dp, "model", NULL); | 755 | const char *model = of_get_property(dp, "model", NULL); |
756 | const char *compat = of_get_property(dp, "compatible", NULL); | ||
725 | unsigned long size, flags; | 757 | unsigned long size, flags; |
726 | void __iomem *regs; | 758 | void __iomem *regs; |
727 | 759 | ||
760 | if (!model) | ||
761 | model = compat; | ||
762 | |||
728 | if (!model || !clock_model_matches(model)) | 763 | if (!model || !clock_model_matches(model)) |
729 | return -ENODEV; | 764 | return -ENODEV; |
730 | 765 | ||
@@ -746,6 +781,8 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id | |||
746 | !strcmp(model, "m5819p") || | 781 | !strcmp(model, "m5819p") || |
747 | !strcmp(model, "m5823")) { | 782 | !strcmp(model, "m5823")) { |
748 | ds1287_regs = (unsigned long) regs; | 783 | ds1287_regs = (unsigned long) regs; |
784 | } else if (!strcmp(model, "bq4802")) { | ||
785 | bq4802_regs = regs; | ||
749 | } else | 786 | } else |
750 | #endif | 787 | #endif |
751 | if (model[5] == '0' && model[6] == '2') { | 788 | if (model[5] == '0' && model[6] == '2') { |
@@ -1070,8 +1107,10 @@ static int set_rtc_mmss(unsigned long nowtime) | |||
1070 | void __iomem *mregs = mstk48t02_regs; | 1107 | void __iomem *mregs = mstk48t02_regs; |
1071 | #ifdef CONFIG_PCI | 1108 | #ifdef CONFIG_PCI |
1072 | unsigned long dregs = ds1287_regs; | 1109 | unsigned long dregs = ds1287_regs; |
1110 | void __iomem *bregs = bq4802_regs; | ||
1073 | #else | 1111 | #else |
1074 | unsigned long dregs = 0UL; | 1112 | unsigned long dregs = 0UL; |
1113 | void __iomem *bregs = 0UL; | ||
1075 | #endif | 1114 | #endif |
1076 | unsigned long flags; | 1115 | unsigned long flags; |
1077 | u8 tmp; | 1116 | u8 tmp; |
@@ -1080,7 +1119,7 @@ static int set_rtc_mmss(unsigned long nowtime) | |||
1080 | * Not having a register set can lead to trouble. | 1119 | * Not having a register set can lead to trouble. |
1081 | * Also starfire doesn't have a tod clock. | 1120 | * Also starfire doesn't have a tod clock. |
1082 | */ | 1121 | */ |
1083 | if (!mregs && !dregs) | 1122 | if (!mregs && !dregs & !bregs) |
1084 | return -1; | 1123 | return -1; |
1085 | 1124 | ||
1086 | if (mregs) { | 1125 | if (mregs) { |
@@ -1129,6 +1168,37 @@ static int set_rtc_mmss(unsigned long nowtime) | |||
1129 | 1168 | ||
1130 | return -1; | 1169 | return -1; |
1131 | } | 1170 | } |
1171 | } else if (bregs) { | ||
1172 | int retval = 0; | ||
1173 | unsigned char val = readb(bregs + 0x0e); | ||
1174 | |||
1175 | /* BQ4802 RTC chip. */ | ||
1176 | |||
1177 | writeb(val | 0x08, bregs + 0x0e); | ||
1178 | |||
1179 | chip_minutes = readb(bregs + 0x02); | ||
1180 | BCD_TO_BIN(chip_minutes); | ||
1181 | real_seconds = nowtime % 60; | ||
1182 | real_minutes = nowtime / 60; | ||
1183 | if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) | ||
1184 | real_minutes += 30; | ||
1185 | real_minutes %= 60; | ||
1186 | |||
1187 | if (abs(real_minutes - chip_minutes) < 30) { | ||
1188 | BIN_TO_BCD(real_seconds); | ||
1189 | BIN_TO_BCD(real_minutes); | ||
1190 | writeb(real_seconds, bregs + 0x00); | ||
1191 | writeb(real_minutes, bregs + 0x02); | ||
1192 | } else { | ||
1193 | printk(KERN_WARNING | ||
1194 | "set_rtc_mmss: can't update from %d to %d\n", | ||
1195 | chip_minutes, real_minutes); | ||
1196 | retval = -1; | ||
1197 | } | ||
1198 | |||
1199 | writeb(val, bregs + 0x0e); | ||
1200 | |||
1201 | return retval; | ||
1132 | } else { | 1202 | } else { |
1133 | int retval = 0; | 1203 | int retval = 0; |
1134 | unsigned char save_control, save_freq_select; | 1204 | unsigned char save_control, save_freq_select; |
@@ -1259,38 +1329,152 @@ static void to_tm(int tim, struct rtc_time *tm) | |||
1259 | /* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, | 1329 | /* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, |
1260 | * aka Unix time. So we have to convert to/from rtc_time. | 1330 | * aka Unix time. So we have to convert to/from rtc_time. |
1261 | */ | 1331 | */ |
1262 | static inline void mini_get_rtc_time(struct rtc_time *time) | 1332 | static void starfire_get_rtc_time(struct rtc_time *time) |
1263 | { | 1333 | { |
1264 | unsigned long flags; | 1334 | u32 seconds = starfire_get_time(); |
1265 | u32 seconds; | ||
1266 | 1335 | ||
1267 | spin_lock_irqsave(&rtc_lock, flags); | 1336 | to_tm(seconds, time); |
1268 | seconds = 0; | 1337 | time->tm_year -= 1900; |
1269 | if (this_is_starfire) | 1338 | time->tm_mon -= 1; |
1270 | seconds = starfire_get_time(); | 1339 | } |
1271 | else if (tlb_type == hypervisor) | 1340 | |
1272 | seconds = hypervisor_get_time(); | 1341 | static int starfire_set_rtc_time(struct rtc_time *time) |
1273 | spin_unlock_irqrestore(&rtc_lock, flags); | 1342 | { |
1343 | u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, | ||
1344 | time->tm_mday, time->tm_hour, | ||
1345 | time->tm_min, time->tm_sec); | ||
1346 | |||
1347 | return starfire_set_time(seconds); | ||
1348 | } | ||
1349 | |||
1350 | static void hypervisor_get_rtc_time(struct rtc_time *time) | ||
1351 | { | ||
1352 | u32 seconds = hypervisor_get_time(); | ||
1274 | 1353 | ||
1275 | to_tm(seconds, time); | 1354 | to_tm(seconds, time); |
1276 | time->tm_year -= 1900; | 1355 | time->tm_year -= 1900; |
1277 | time->tm_mon -= 1; | 1356 | time->tm_mon -= 1; |
1278 | } | 1357 | } |
1279 | 1358 | ||
1280 | static inline int mini_set_rtc_time(struct rtc_time *time) | 1359 | static int hypervisor_set_rtc_time(struct rtc_time *time) |
1281 | { | 1360 | { |
1282 | u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, | 1361 | u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, |
1283 | time->tm_mday, time->tm_hour, | 1362 | time->tm_mday, time->tm_hour, |
1284 | time->tm_min, time->tm_sec); | 1363 | time->tm_min, time->tm_sec); |
1364 | |||
1365 | return hypervisor_set_time(seconds); | ||
1366 | } | ||
1367 | |||
1368 | static void bq4802_get_rtc_time(struct rtc_time *time) | ||
1369 | { | ||
1370 | unsigned char val = readb(bq4802_regs + 0x0e); | ||
1371 | unsigned int century; | ||
1372 | |||
1373 | writeb(val | 0x08, bq4802_regs + 0x0e); | ||
1374 | |||
1375 | time->tm_sec = readb(bq4802_regs + 0x00); | ||
1376 | time->tm_min = readb(bq4802_regs + 0x02); | ||
1377 | time->tm_hour = readb(bq4802_regs + 0x04); | ||
1378 | time->tm_mday = readb(bq4802_regs + 0x06); | ||
1379 | time->tm_mon = readb(bq4802_regs + 0x09); | ||
1380 | time->tm_year = readb(bq4802_regs + 0x0a); | ||
1381 | time->tm_wday = readb(bq4802_regs + 0x08); | ||
1382 | century = readb(bq4802_regs + 0x0f); | ||
1383 | |||
1384 | writeb(val, bq4802_regs + 0x0e); | ||
1385 | |||
1386 | BCD_TO_BIN(time->tm_sec); | ||
1387 | BCD_TO_BIN(time->tm_min); | ||
1388 | BCD_TO_BIN(time->tm_hour); | ||
1389 | BCD_TO_BIN(time->tm_mday); | ||
1390 | BCD_TO_BIN(time->tm_mon); | ||
1391 | BCD_TO_BIN(time->tm_year); | ||
1392 | BCD_TO_BIN(time->tm_wday); | ||
1393 | BCD_TO_BIN(century); | ||
1394 | |||
1395 | time->tm_year += (century * 100); | ||
1396 | time->tm_year -= 1900; | ||
1397 | |||
1398 | time->tm_mon--; | ||
1399 | } | ||
1400 | |||
1401 | static int bq4802_set_rtc_time(struct rtc_time *time) | ||
1402 | { | ||
1403 | unsigned char val = readb(bq4802_regs + 0x0e); | ||
1404 | unsigned char sec, min, hrs, day, mon, yrs, century; | ||
1405 | unsigned int year; | ||
1406 | |||
1407 | year = time->tm_year + 1900; | ||
1408 | century = year / 100; | ||
1409 | yrs = year % 100; | ||
1410 | |||
1411 | mon = time->tm_mon + 1; /* tm_mon starts at zero */ | ||
1412 | day = time->tm_mday; | ||
1413 | hrs = time->tm_hour; | ||
1414 | min = time->tm_min; | ||
1415 | sec = time->tm_sec; | ||
1416 | |||
1417 | BIN_TO_BCD(sec); | ||
1418 | BIN_TO_BCD(min); | ||
1419 | BIN_TO_BCD(hrs); | ||
1420 | BIN_TO_BCD(day); | ||
1421 | BIN_TO_BCD(mon); | ||
1422 | BIN_TO_BCD(yrs); | ||
1423 | BIN_TO_BCD(century); | ||
1424 | |||
1425 | writeb(val | 0x08, bq4802_regs + 0x0e); | ||
1426 | |||
1427 | writeb(sec, bq4802_regs + 0x00); | ||
1428 | writeb(min, bq4802_regs + 0x02); | ||
1429 | writeb(hrs, bq4802_regs + 0x04); | ||
1430 | writeb(day, bq4802_regs + 0x06); | ||
1431 | writeb(mon, bq4802_regs + 0x09); | ||
1432 | writeb(yrs, bq4802_regs + 0x0a); | ||
1433 | writeb(century, bq4802_regs + 0x0f); | ||
1434 | |||
1435 | writeb(val, bq4802_regs + 0x0e); | ||
1436 | |||
1437 | return 0; | ||
1438 | } | ||
1439 | |||
1440 | struct mini_rtc_ops { | ||
1441 | void (*get_rtc_time)(struct rtc_time *); | ||
1442 | int (*set_rtc_time)(struct rtc_time *); | ||
1443 | }; | ||
1444 | |||
1445 | static struct mini_rtc_ops starfire_rtc_ops = { | ||
1446 | .get_rtc_time = starfire_get_rtc_time, | ||
1447 | .set_rtc_time = starfire_set_rtc_time, | ||
1448 | }; | ||
1449 | |||
1450 | static struct mini_rtc_ops hypervisor_rtc_ops = { | ||
1451 | .get_rtc_time = hypervisor_get_rtc_time, | ||
1452 | .set_rtc_time = hypervisor_set_rtc_time, | ||
1453 | }; | ||
1454 | |||
1455 | static struct mini_rtc_ops bq4802_rtc_ops = { | ||
1456 | .get_rtc_time = bq4802_get_rtc_time, | ||
1457 | .set_rtc_time = bq4802_set_rtc_time, | ||
1458 | }; | ||
1459 | |||
1460 | static struct mini_rtc_ops *mini_rtc_ops; | ||
1461 | |||
1462 | static inline void mini_get_rtc_time(struct rtc_time *time) | ||
1463 | { | ||
1464 | unsigned long flags; | ||
1465 | |||
1466 | spin_lock_irqsave(&rtc_lock, flags); | ||
1467 | mini_rtc_ops->get_rtc_time(time); | ||
1468 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
1469 | } | ||
1470 | |||
1471 | static inline int mini_set_rtc_time(struct rtc_time *time) | ||
1472 | { | ||
1285 | unsigned long flags; | 1473 | unsigned long flags; |
1286 | int err; | 1474 | int err; |
1287 | 1475 | ||
1288 | spin_lock_irqsave(&rtc_lock, flags); | 1476 | spin_lock_irqsave(&rtc_lock, flags); |
1289 | err = -ENODEV; | 1477 | err = mini_rtc_ops->set_rtc_time(time); |
1290 | if (this_is_starfire) | ||
1291 | err = starfire_set_time(seconds); | ||
1292 | else if (tlb_type == hypervisor) | ||
1293 | err = hypervisor_set_time(seconds); | ||
1294 | spin_unlock_irqrestore(&rtc_lock, flags); | 1478 | spin_unlock_irqrestore(&rtc_lock, flags); |
1295 | 1479 | ||
1296 | return err; | 1480 | return err; |
@@ -1391,7 +1575,13 @@ static int __init rtc_mini_init(void) | |||
1391 | { | 1575 | { |
1392 | int retval; | 1576 | int retval; |
1393 | 1577 | ||
1394 | if (tlb_type != hypervisor && !this_is_starfire) | 1578 | if (tlb_type == hypervisor) |
1579 | mini_rtc_ops = &hypervisor_rtc_ops; | ||
1580 | else if (this_is_starfire) | ||
1581 | mini_rtc_ops = &starfire_rtc_ops; | ||
1582 | else if (bq4802_regs) | ||
1583 | mini_rtc_ops = &bq4802_rtc_ops; | ||
1584 | else | ||
1395 | return -ENODEV; | 1585 | return -ENODEV; |
1396 | 1586 | ||
1397 | printk(KERN_INFO "Mini RTC Driver\n"); | 1587 | printk(KERN_INFO "Mini RTC Driver\n"); |