diff options
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
-rw-r--r-- | arch/x86/kernel/apic/apic.c | 72 |
1 files changed, 57 insertions, 15 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index f5291362da1a..dba2828b779a 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c | |||
@@ -722,7 +722,7 @@ static __initdata unsigned long lapic_cal_pm1, lapic_cal_pm2; | |||
722 | static __initdata unsigned long lapic_cal_j1, lapic_cal_j2; | 722 | static __initdata unsigned long lapic_cal_j1, lapic_cal_j2; |
723 | 723 | ||
724 | /* | 724 | /* |
725 | * Temporary interrupt handler. | 725 | * Temporary interrupt handler and polled calibration function. |
726 | */ | 726 | */ |
727 | static void __init lapic_cal_handler(struct clock_event_device *dev) | 727 | static void __init lapic_cal_handler(struct clock_event_device *dev) |
728 | { | 728 | { |
@@ -851,7 +851,8 @@ bool __init apic_needs_pit(void) | |||
851 | static int __init calibrate_APIC_clock(void) | 851 | static int __init calibrate_APIC_clock(void) |
852 | { | 852 | { |
853 | struct clock_event_device *levt = this_cpu_ptr(&lapic_events); | 853 | struct clock_event_device *levt = this_cpu_ptr(&lapic_events); |
854 | void (*real_handler)(struct clock_event_device *dev); | 854 | u64 tsc_perj = 0, tsc_start = 0; |
855 | unsigned long jif_start; | ||
855 | unsigned long deltaj; | 856 | unsigned long deltaj; |
856 | long delta, deltatsc; | 857 | long delta, deltatsc; |
857 | int pm_referenced = 0; | 858 | int pm_referenced = 0; |
@@ -878,28 +879,64 @@ static int __init calibrate_APIC_clock(void) | |||
878 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" | 879 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" |
879 | "calibrating APIC timer ...\n"); | 880 | "calibrating APIC timer ...\n"); |
880 | 881 | ||
882 | /* | ||
883 | * There are platforms w/o global clockevent devices. Instead of | ||
884 | * making the calibration conditional on that, use a polling based | ||
885 | * approach everywhere. | ||
886 | */ | ||
881 | local_irq_disable(); | 887 | local_irq_disable(); |
882 | 888 | ||
883 | /* Replace the global interrupt handler */ | ||
884 | real_handler = global_clock_event->event_handler; | ||
885 | global_clock_event->event_handler = lapic_cal_handler; | ||
886 | |||
887 | /* | 889 | /* |
888 | * Setup the APIC counter to maximum. There is no way the lapic | 890 | * Setup the APIC counter to maximum. There is no way the lapic |
889 | * can underflow in the 100ms detection time frame | 891 | * can underflow in the 100ms detection time frame |
890 | */ | 892 | */ |
891 | __setup_APIC_LVTT(0xffffffff, 0, 0); | 893 | __setup_APIC_LVTT(0xffffffff, 0, 0); |
892 | 894 | ||
893 | /* Let the interrupts run */ | 895 | /* |
896 | * Methods to terminate the calibration loop: | ||
897 | * 1) Global clockevent if available (jiffies) | ||
898 | * 2) TSC if available and frequency is known | ||
899 | */ | ||
900 | jif_start = READ_ONCE(jiffies); | ||
901 | |||
902 | if (tsc_khz) { | ||
903 | tsc_start = rdtsc(); | ||
904 | tsc_perj = div_u64((u64)tsc_khz * 1000, HZ); | ||
905 | } | ||
906 | |||
907 | /* | ||
908 | * Enable interrupts so the tick can fire, if a global | ||
909 | * clockevent device is available | ||
910 | */ | ||
894 | local_irq_enable(); | 911 | local_irq_enable(); |
895 | 912 | ||
896 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) | 913 | while (lapic_cal_loops <= LAPIC_CAL_LOOPS) { |
897 | cpu_relax(); | 914 | /* Wait for a tick to elapse */ |
915 | while (1) { | ||
916 | if (tsc_khz) { | ||
917 | u64 tsc_now = rdtsc(); | ||
918 | if ((tsc_now - tsc_start) >= tsc_perj) { | ||
919 | tsc_start += tsc_perj; | ||
920 | break; | ||
921 | } | ||
922 | } else { | ||
923 | unsigned long jif_now = READ_ONCE(jiffies); | ||
898 | 924 | ||
899 | local_irq_disable(); | 925 | if (time_after(jif_now, jif_start)) { |
926 | jif_start = jif_now; | ||
927 | break; | ||
928 | } | ||
929 | } | ||
930 | cpu_relax(); | ||
931 | } | ||
900 | 932 | ||
901 | /* Restore the real event handler */ | 933 | /* Invoke the calibration routine */ |
902 | global_clock_event->event_handler = real_handler; | 934 | local_irq_disable(); |
935 | lapic_cal_handler(NULL); | ||
936 | local_irq_enable(); | ||
937 | } | ||
938 | |||
939 | local_irq_disable(); | ||
903 | 940 | ||
904 | /* Build delta t1-t2 as apic timer counts down */ | 941 | /* Build delta t1-t2 as apic timer counts down */ |
905 | delta = lapic_cal_t1 - lapic_cal_t2; | 942 | delta = lapic_cal_t1 - lapic_cal_t2; |
@@ -943,10 +980,11 @@ static int __init calibrate_APIC_clock(void) | |||
943 | levt->features &= ~CLOCK_EVT_FEAT_DUMMY; | 980 | levt->features &= ~CLOCK_EVT_FEAT_DUMMY; |
944 | 981 | ||
945 | /* | 982 | /* |
946 | * PM timer calibration failed or not turned on | 983 | * PM timer calibration failed or not turned on so lets try APIC |
947 | * so lets try APIC timer based calibration | 984 | * timer based calibration, if a global clockevent device is |
985 | * available. | ||
948 | */ | 986 | */ |
949 | if (!pm_referenced) { | 987 | if (!pm_referenced && global_clock_event) { |
950 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); | 988 | apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); |
951 | 989 | ||
952 | /* | 990 | /* |
@@ -1141,6 +1179,10 @@ void clear_local_APIC(void) | |||
1141 | apic_write(APIC_LVT0, v | APIC_LVT_MASKED); | 1179 | apic_write(APIC_LVT0, v | APIC_LVT_MASKED); |
1142 | v = apic_read(APIC_LVT1); | 1180 | v = apic_read(APIC_LVT1); |
1143 | apic_write(APIC_LVT1, v | APIC_LVT_MASKED); | 1181 | apic_write(APIC_LVT1, v | APIC_LVT_MASKED); |
1182 | if (!x2apic_enabled()) { | ||
1183 | v = apic_read(APIC_LDR) & ~APIC_LDR_MASK; | ||
1184 | apic_write(APIC_LDR, v); | ||
1185 | } | ||
1144 | if (maxlvt >= 4) { | 1186 | if (maxlvt >= 4) { |
1145 | v = apic_read(APIC_LVTPC); | 1187 | v = apic_read(APIC_LVTPC); |
1146 | apic_write(APIC_LVTPC, v | APIC_LVT_MASKED); | 1188 | apic_write(APIC_LVTPC, v | APIC_LVT_MASKED); |