1/* 2 * mrst.c: Intel Moorestown platform specific setup code 3 * 4 * (C) Copyright 2008 Intel Corporation 5 * Author: Jacob Pan (jacob.jun.pan@intel.com) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; version 2 10 * of the License. 11 */ 12#include <linux/init.h> 13#include <linux/kernel.h> 14#include <linux/sfi.h> 15#include <linux/irq.h> 16#include <linux/module.h> 17 18#include <asm/setup.h> 19#include <asm/mpspec_def.h> 20#include <asm/hw_irq.h> 21#include <asm/apic.h> 22#include <asm/io_apic.h> 23#include <asm/mrst.h> 24#include <asm/io.h> 25#include <asm/i8259.h> 26#include <asm/apb_timer.h> 27 28/* 29 * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, 30 * cmdline option x86_mrst_timer can be used to override the configuration 31 * to prefer one or the other. 32 * at runtime, there are basically three timer configurations: 33 * 1. per cpu apbt clock only 34 * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only 35 * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. 36 * 37 * by default (without cmdline option), platform code first detects cpu type 38 * to see if we are on lincroft or penwell, then set up both lapic or apbt 39 * clocks accordingly. 40 * i.e. by default, medfield uses configuration #2, moorestown uses #1. 41 * config #3 is supported but not recommended on medfield. 42 * 43 * rating and feature summary: 44 * lapic (with C3STOP) --------- 100 45 * apbt (always-on) ------------ 110 46 * lapic (always-on,ARAT) ------ 150 47 */ 48 49__cpuinitdata enum mrst_timer_options mrst_timer_options; 50 51static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; 52static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; 53enum mrst_cpu_type __mrst_cpu_chip; 54EXPORT_SYMBOL_GPL(__mrst_cpu_chip); 55 56int sfi_mtimer_num; 57 58struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; 59EXPORT_SYMBOL_GPL(sfi_mrtc_array); 60int sfi_mrtc_num; 61 62static inline void assign_to_mp_irq(struct mpc_intsrc *m, 63 struct mpc_intsrc *mp_irq) 64{ 65 memcpy(mp_irq, m, sizeof(struct mpc_intsrc)); 66} 67 68static inline int mp_irq_cmp(struct mpc_intsrc *mp_irq, 69 struct mpc_intsrc *m) 70{ 71 return memcmp(mp_irq, m, sizeof(struct mpc_intsrc)); 72} 73 74static void save_mp_irq(struct mpc_intsrc *m) 75{ 76 int i; 77 78 for (i = 0; i < mp_irq_entries; i++) { 79 if (!mp_irq_cmp(&mp_irqs[i], m)) 80 return; 81 } 82 83 assign_to_mp_irq(m, &mp_irqs[mp_irq_entries]); 84 if (++mp_irq_entries == MAX_IRQ_SOURCES) 85 panic("Max # of irq sources exceeded!!\n"); 86} 87 88/* parse all the mtimer info to a static mtimer array */ 89static int __init sfi_parse_mtmr(struct sfi_table_header *table) 90{ 91 struct sfi_table_simple *sb; 92 struct sfi_timer_table_entry *pentry; 93 struct mpc_intsrc mp_irq; 94 int totallen; 95 96 sb = (struct sfi_table_simple *)table; 97 if (!sfi_mtimer_num) { 98 sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, 99 struct sfi_timer_table_entry); 100 pentry = (struct sfi_timer_table_entry *) sb->pentry; 101 totallen = sfi_mtimer_num * sizeof(*pentry); 102 memcpy(sfi_mtimer_array, pentry, totallen); 103 } 104 105 printk(KERN_INFO "SFI: MTIMER info (num = %d):\n", sfi_mtimer_num); 106 pentry = sfi_mtimer_array; 107 for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { 108 printk(KERN_INFO "timer[%d]: paddr = 0x%08x, freq = %dHz," 109 " irq = %d\n", totallen, (u32)pentry->phys_addr, 110 pentry->freq_hz, pentry->irq); 111 if (!pentry->irq) 112 continue; 113 mp_irq.type = MP_IOAPIC; 114 mp_irq.irqtype = mp_INT; 115/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ 116 mp_irq.irqflag = 5; 117 mp_irq.srcbus = 0; 118 mp_irq.srcbusirq = pentry->irq; /* IRQ */ 119 mp_irq.dstapic = MP_APIC_ALL; 120 mp_irq.dstirq = pentry->irq; 121 save_mp_irq(&mp_irq); 122 } 123 124 return 0; 125} 126 127struct sfi_timer_table_entry *sfi_get_mtmr(int hint) 128{ 129 int i; 130 if (hint < sfi_mtimer_num) { 131 if (!sfi_mtimer_usage[hint]) { 132 pr_debug("hint taken for timer %d irq %d\n",\ 133 hint, sfi_mtimer_array[hint].irq); 134 sfi_mtimer_usage[hint] = 1; 135 return &sfi_mtimer_array[hint]; 136 } 137 } 138 /* take the first timer available */ 139 for (i = 0; i < sfi_mtimer_num;) { 140 if (!sfi_mtimer_usage[i]) { 141 sfi_mtimer_usage[i] = 1; 142 return &sfi_mtimer_array[i]; 143 } 144 i++; 145 } 146 return NULL; 147} 148 149void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) 150{ 151 int i; 152 for (i = 0; i < sfi_mtimer_num;) { 153 if (mtmr->irq == sfi_mtimer_array[i].irq) { 154 sfi_mtimer_usage[i] = 0; 155 return; 156 } 157 i++; 158 } 159} 160 161/* parse all the mrtc info to a global mrtc array */ 162int __init sfi_parse_mrtc(struct sfi_table_header *table) 163{ 164 struct sfi_table_simple *sb; 165 struct sfi_rtc_table_entry *pentry; 166 struct mpc_intsrc mp_irq; 167 168 int totallen; 169 170 sb = (struct sfi_table_simple *)table; 171 if (!sfi_mrtc_num) { 172 sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, 173 struct sfi_rtc_table_entry); 174 pentry = (struct sfi_rtc_table_entry *)sb->pentry; 175 totallen = sfi_mrtc_num * sizeof(*pentry); 176 memcpy(sfi_mrtc_array, pentry, totallen); 177 } 178 179 printk(KERN_INFO "SFI: RTC info (num = %d):\n", sfi_mrtc_num); 180 pentry = sfi_mrtc_array; 181 for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { 182 printk(KERN_INFO "RTC[%d]: paddr = 0x%08x, irq = %d\n", 183 totallen, (u32)pentry->phys_addr, pentry->irq); 184 mp_irq.type = MP_IOAPIC; 185 mp_irq.irqtype = mp_INT; 186 mp_irq.irqflag = 0; 187 mp_irq.srcbus = 0; 188 mp_irq.srcbusirq = pentry->irq; /* IRQ */ 189 mp_irq.dstapic = MP_APIC_ALL; 190 mp_irq.dstirq = pentry->irq; 191 save_mp_irq(&mp_irq); 192 } 193 return 0; 194} 195 196static unsigned long __init mrst_calibrate_tsc(void) 197{ 198 unsigned long flags, fast_calibrate; 199 200 local_irq_save(flags); 201 fast_calibrate = apbt_quick_calibrate(); 202 local_irq_restore(flags); 203 204 if (fast_calibrate) 205 return fast_calibrate; 206 207 return 0; 208} 209 210void __init mrst_time_init(void) 211{ 212 switch (mrst_timer_options) { 213 case MRST_TIMER_APBT_ONLY: 214 break; 215 case MRST_TIMER_LAPIC_APBT: 216 x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; 217 x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; 218 break; 219 default: 220 if (!boot_cpu_has(X86_FEATURE_ARAT)) 221 break; 222 x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; 223 x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; 224 return; 225 } 226 /* we need at least one APB timer */ 227 sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); 228 pre_init_apic_IRQ0(); 229 apbt_time_init(); 230} 231 232void __init mrst_rtc_init(void) 233{ 234 sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); 235} 236 237void __cpuinit mrst_arch_setup(void) 238{ 239 if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) 240 __mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; 241 else if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x26) 242 __mrst_cpu_chip = MRST_CPU_CHIP_LINCROFT; 243 else { 244 pr_err("Unknown Moorestown CPU (%d:%d), default to Lincroft\n", 245 boot_cpu_data.x86, boot_cpu_data.x86_model); 246 __mrst_cpu_chip = MRST_CPU_CHIP_LINCROFT; 247 } 248 pr_debug("Moorestown CPU %s identified\n", 249 (__mrst_cpu_chip == MRST_CPU_CHIP_LINCROFT) ? 250 "Lincroft" : "Penwell"); 251} 252 253/* MID systems don't have i8042 controller */ 254static int mrst_i8042_detect(void) 255{ 256 return 0; 257} 258 259/* 260 * Moorestown specific x86_init function overrides and early setup 261 * calls. 262 */ 263void __init x86_mrst_early_setup(void) 264{ 265 x86_init.resources.probe_roms = x86_init_noop; 266 x86_init.resources.reserve_resources = x86_init_noop; 267 268 x86_init.timers.timer_init = mrst_time_init; 269 x86_init.timers.setup_percpu_clockev = x86_init_noop; 270 271 x86_init.irqs.pre_vector_init = x86_init_noop; 272 273 x86_init.oem.arch_setup = mrst_arch_setup; 274 275 x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; 276 277 x86_platform.calibrate_tsc = mrst_calibrate_tsc; 278 x86_platform.i8042_detect = mrst_i8042_detect; 279 x86_init.pci.init = pci_mrst_init; 280 x86_init.pci.fixup_irqs = x86_init_noop; 281 282 legacy_pic = &null_legacy_pic; 283 284 /* Avoid searching for BIOS MP tables */ 285 x86_init.mpparse.find_smp_config = x86_init_noop; 286 x86_init.mpparse.get_smp_config = x86_init_uint_noop; 287 288} 289 290/* 291 * if user does not want to use per CPU apb timer, just give it a lower rating 292 * than local apic timer and skip the late per cpu timer init. 293 */ 294static inline int __init setup_x86_mrst_timer(char *arg) 295{ 296 if (!arg) 297 return -EINVAL; 298 299 if (strcmp("apbt_only", arg) == 0) 300 mrst_timer_options = MRST_TIMER_APBT_ONLY; 301 else if (strcmp("lapic_and_apbt", arg) == 0) 302 mrst_timer_options = MRST_TIMER_LAPIC_APBT; 303 else { 304 pr_warning("X86 MRST timer option %s not recognised" 305 " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", 306 arg); 307 return -EINVAL; 308 } 309 return 0; 310} 311__setup("x86_mrst_timer=", setup_x86_mrst_timer); 312