[PATCH] ppc64 iSeries: fix boot time setting

For quite a while, there has existed a hypervisor bug on legacy iSeries
which means that we do not get the boot time set in the kernel. This
patch works around that bug. This was most noticable when the root
partition needed to be checked at every boot as the kernel thought it
was some time in 1905 until user mode reset the time correctly.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Stephen Rothwell and committed by Linus Torvalds d0e8e291 8f80e5c9

+67 -58
+65 -20
arch/ppc64/kernel/mf.c
··· 1 /* 2 * mf.c 3 * Copyright (C) 2001 Troy D. Armstrong IBM Corporation 4 - * Copyright (C) 2004 Stephen Rothwell IBM Corporation 5 * 6 * This modules exists as an interface between a Linux secondary partition 7 * running on an iSeries and the primary partition's Virtual Service ··· 36 37 #include <asm/time.h> 38 #include <asm/uaccess.h> 39 #include <asm/iSeries/vio.h> 40 #include <asm/iSeries/mf.h> 41 #include <asm/iSeries/HvLpConfig.h> 42 #include <asm/iSeries/ItSpCommArea.h> 43 44 /* 45 * This is the structure layout for the Machine Facilites LPAR event ··· 698 complete(&rtc->com); 699 } 700 701 - int mf_get_rtc(struct rtc_time *tm) 702 { 703 - struct ce_msg_comp_data ce_complete; 704 - struct rtc_time_data rtc_data; 705 - int rc; 706 - 707 - memset(&ce_complete, 0, sizeof(ce_complete)); 708 - memset(&rtc_data, 0, sizeof(rtc_data)); 709 - init_completion(&rtc_data.com); 710 - ce_complete.handler = &get_rtc_time_complete; 711 - ce_complete.token = &rtc_data; 712 - rc = signal_ce_msg_simple(0x40, &ce_complete); 713 - if (rc) 714 - return rc; 715 - wait_for_completion(&rtc_data.com); 716 tm->tm_wday = 0; 717 tm->tm_yday = 0; 718 tm->tm_isdst = 0; 719 - if (rtc_data.rc) { 720 tm->tm_sec = 0; 721 tm->tm_min = 0; 722 tm->tm_hour = 0; 723 tm->tm_mday = 15; 724 tm->tm_mon = 5; 725 tm->tm_year = 52; 726 - return rtc_data.rc; 727 } 728 729 - if ((rtc_data.ce_msg.ce_msg[2] == 0xa9) || 730 - (rtc_data.ce_msg.ce_msg[2] == 0xaf)) { 731 /* TOD clock is not set */ 732 tm->tm_sec = 1; 733 tm->tm_min = 1; ··· 725 mf_set_rtc(tm); 726 } 727 { 728 - u8 *ce_msg = rtc_data.ce_msg.ce_msg; 729 u8 year = ce_msg[5]; 730 u8 sec = ce_msg[6]; 731 u8 min = ce_msg[7]; ··· 751 } 752 753 return 0; 754 } 755 756 int mf_set_rtc(struct rtc_time *tm)
··· 1 /* 2 * mf.c 3 * Copyright (C) 2001 Troy D. Armstrong IBM Corporation 4 + * Copyright (C) 2004-2005 Stephen Rothwell IBM Corporation 5 * 6 * This modules exists as an interface between a Linux secondary partition 7 * running on an iSeries and the primary partition's Virtual Service ··· 36 37 #include <asm/time.h> 38 #include <asm/uaccess.h> 39 + #include <asm/paca.h> 40 #include <asm/iSeries/vio.h> 41 #include <asm/iSeries/mf.h> 42 #include <asm/iSeries/HvLpConfig.h> 43 #include <asm/iSeries/ItSpCommArea.h> 44 + #include <asm/iSeries/ItLpQueue.h> 45 46 /* 47 * This is the structure layout for the Machine Facilites LPAR event ··· 696 complete(&rtc->com); 697 } 698 699 + static int rtc_set_tm(int rc, u8 *ce_msg, struct rtc_time *tm) 700 { 701 tm->tm_wday = 0; 702 tm->tm_yday = 0; 703 tm->tm_isdst = 0; 704 + if (rc) { 705 tm->tm_sec = 0; 706 tm->tm_min = 0; 707 tm->tm_hour = 0; 708 tm->tm_mday = 15; 709 tm->tm_mon = 5; 710 tm->tm_year = 52; 711 + return rc; 712 } 713 714 + if ((ce_msg[2] == 0xa9) || 715 + (ce_msg[2] == 0xaf)) { 716 /* TOD clock is not set */ 717 tm->tm_sec = 1; 718 tm->tm_min = 1; ··· 736 mf_set_rtc(tm); 737 } 738 { 739 u8 year = ce_msg[5]; 740 u8 sec = ce_msg[6]; 741 u8 min = ce_msg[7]; ··· 763 } 764 765 return 0; 766 + } 767 + 768 + int mf_get_rtc(struct rtc_time *tm) 769 + { 770 + struct ce_msg_comp_data ce_complete; 771 + struct rtc_time_data rtc_data; 772 + int rc; 773 + 774 + memset(&ce_complete, 0, sizeof(ce_complete)); 775 + memset(&rtc_data, 0, sizeof(rtc_data)); 776 + init_completion(&rtc_data.com); 777 + ce_complete.handler = &get_rtc_time_complete; 778 + ce_complete.token = &rtc_data; 779 + rc = signal_ce_msg_simple(0x40, &ce_complete); 780 + if (rc) 781 + return rc; 782 + wait_for_completion(&rtc_data.com); 783 + return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm); 784 + } 785 + 786 + struct boot_rtc_time_data { 787 + int busy; 788 + struct ce_msg_data ce_msg; 789 + int rc; 790 + }; 791 + 792 + static void get_boot_rtc_time_complete(void *token, struct ce_msg_data *ce_msg) 793 + { 794 + struct boot_rtc_time_data *rtc = token; 795 + 796 + memcpy(&rtc->ce_msg, ce_msg, sizeof(rtc->ce_msg)); 797 + rtc->rc = 0; 798 + rtc->busy = 0; 799 + } 800 + 801 + int mf_get_boot_rtc(struct rtc_time *tm) 802 + { 803 + struct ce_msg_comp_data ce_complete; 804 + struct boot_rtc_time_data rtc_data; 805 + int rc; 806 + 807 + memset(&ce_complete, 0, sizeof(ce_complete)); 808 + memset(&rtc_data, 0, sizeof(rtc_data)); 809 + rtc_data.busy = 1; 810 + ce_complete.handler = &get_boot_rtc_time_complete; 811 + ce_complete.token = &rtc_data; 812 + rc = signal_ce_msg_simple(0x40, &ce_complete); 813 + if (rc) 814 + return rc; 815 + /* We need to poll here as we are not yet taking interrupts */ 816 + while (rtc_data.busy) { 817 + extern unsigned long lpevent_count; 818 + struct ItLpQueue *lpq = get_paca()->lpqueue_ptr; 819 + if (lpq && ItLpQueue_isLpIntPending(lpq)) 820 + lpevent_count += ItLpQueue_process(lpq, NULL); 821 + } 822 + return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm); 823 } 824 825 int mf_set_rtc(struct rtc_time *tm)
+1 -38
arch/ppc64/kernel/rtc.c
··· 292 293 void iSeries_get_boot_time(struct rtc_time *tm) 294 { 295 - unsigned long time; 296 - static unsigned long lastsec = 1; 297 - 298 - u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); 299 - u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); 300 - int year = 1970; 301 - int year1 = ( dataWord1 >> 24 ) & 0x000000FF; 302 - int year2 = ( dataWord1 >> 16 ) & 0x000000FF; 303 - int sec = ( dataWord1 >> 8 ) & 0x000000FF; 304 - int min = dataWord1 & 0x000000FF; 305 - int hour = ( dataWord2 >> 24 ) & 0x000000FF; 306 - int day = ( dataWord2 >> 8 ) & 0x000000FF; 307 - int mon = dataWord2 & 0x000000FF; 308 - 309 if ( piranha_simulator ) 310 return; 311 312 - BCD_TO_BIN(sec); 313 - BCD_TO_BIN(min); 314 - BCD_TO_BIN(hour); 315 - BCD_TO_BIN(day); 316 - BCD_TO_BIN(mon); 317 - BCD_TO_BIN(year1); 318 - BCD_TO_BIN(year2); 319 - year = year1 * 100 + year2; 320 - 321 - time = mktime(year, mon, day, hour, min, sec); 322 - time += ( jiffies / HZ ); 323 - 324 - /* Now THIS is a nasty hack! 325 - * It ensures that the first two calls get different answers. 326 - * That way the loop in init_time (time.c) will not think 327 - * the clock is stuck. 328 - */ 329 - if ( lastsec ) { 330 - time -= lastsec; 331 - --lastsec; 332 - } 333 - 334 - to_tm(time, tm); 335 - tm->tm_year -= 1900; 336 tm->tm_mon -= 1; 337 } 338 #endif
··· 292 293 void iSeries_get_boot_time(struct rtc_time *tm) 294 { 295 if ( piranha_simulator ) 296 return; 297 298 + mf_get_boot_rtc(tm); 299 tm->tm_mon -= 1; 300 } 301 #endif
+1
include/asm-ppc64/iSeries/mf.h
··· 52 extern void mf_init(void); 53 54 extern int mf_get_rtc(struct rtc_time *tm); 55 extern int mf_set_rtc(struct rtc_time *tm); 56 57 #endif /* _ASM_PPC64_ISERIES_MF_H */
··· 52 extern void mf_init(void); 53 54 extern int mf_get_rtc(struct rtc_time *tm); 55 + extern int mf_get_boot_rtc(struct rtc_time *tm); 56 extern int mf_set_rtc(struct rtc_time *tm); 57 58 #endif /* _ASM_PPC64_ISERIES_MF_H */