blob: 7f0c7aaaec15084d30586d423e932a3ea0858a92 [file] [log] [blame]
Kevin O'Connor84ad59a2008-07-04 05:47:26 -04001// CPU count detection
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2006 Fabrice Bellard
5//
6// This file may be distributed under the terms of the GNU GPLv3 license.
7
8#include "util.h" // dprintf
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "config.h" // CONFIG_*
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040010
11#define CPUID_APIC (1 << 9)
12
Kevin O'Connorf5c11612008-12-14 10:11:45 -050013#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300)
14#define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0)
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040015
16#define APIC_ENABLED 0x0100
17
18static inline void writel(void *addr, u32 val)
19{
20 *(volatile u32 *)addr = val;
21}
22
23static inline void writew(void *addr, u16 val)
24{
25 *(volatile u16 *)addr = val;
26}
27
28static inline void writeb(void *addr, u8 val)
29{
30 *(volatile u8 *)addr = val;
31}
32
33static inline u32 readl(const void *addr)
34{
35 return *(volatile const u32 *)addr;
36}
37
38static inline u16 readw(const void *addr)
39{
40 return *(volatile const u16 *)addr;
41}
42
43static inline u8 readb(const void *addr)
44{
45 return *(volatile const u8 *)addr;
46}
47
Kevin O'Connora06bfb62008-12-06 19:37:56 -050048extern void smp_ap_boot_code();
49extern u32 smp_cpus;
50#if MODE16
51u32 smp_cpus VISIBLE16;
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040052asm(
Kevin O'Connora06bfb62008-12-06 19:37:56 -050053 " .global smp_ap_boot_code\n"
54 "smp_ap_boot_code:\n"
Kevin O'Connor9649a962008-12-10 20:53:35 -050055 // Increment the cpu counter
Kevin O'Connora06bfb62008-12-06 19:37:56 -050056 " movw $" __stringify(SEG_BIOS) ", %ax\n"
Kevin O'Connor484270d2008-08-17 10:50:57 -040057 " movw %ax, %ds\n"
Kevin O'Connora06bfb62008-12-06 19:37:56 -050058 " lock incl smp_cpus\n"
Kevin O'Connor484270d2008-08-17 10:50:57 -040059 // Halt the processor.
Kevin O'Connora06bfb62008-12-06 19:37:56 -050060 " jmp permanent_halt\n"
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040061 );
Kevin O'Connora06bfb62008-12-06 19:37:56 -050062#endif
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040063
64/* find the number of CPUs by launching a SIPI to them */
65int
66smp_probe(void)
67{
68 if (smp_cpus)
69 return smp_cpus;
70
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040071 u32 eax, ebx, ecx, cpuid_features;
72 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
Kevin O'Connora06bfb62008-12-06 19:37:56 -050073 if (! (cpuid_features & CPUID_APIC)) {
74 // No apic - only the main cpu is present.
75 smp_cpus = 1;
76 return 1;
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040077 }
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040078
Kevin O'Connora06bfb62008-12-06 19:37:56 -050079 // Init the counter.
80 writel(&smp_cpus, 1);
81
82 // Setup jump trampoline to counter code.
83 u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
84 // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR)
85 u64 new = (0xea | ((u64)SEG_BIOS<<24)
86 | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8));
87 *(u64*)BUILD_AP_BOOT_ADDR = new;
88
89 // enable local APIC
Kevin O'Connorf5c11612008-12-14 10:11:45 -050090 u32 val = readl(APIC_SVR);
91 writel(APIC_SVR, val | APIC_ENABLED);
Kevin O'Connora06bfb62008-12-06 19:37:56 -050092
93 // broadcast SIPI
Kevin O'Connorf5c11612008-12-14 10:11:45 -050094 writel(APIC_ICR_LOW, 0x000C4500);
Kevin O'Connora06bfb62008-12-06 19:37:56 -050095 u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
Kevin O'Connorf5c11612008-12-14 10:11:45 -050096 writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
Kevin O'Connora06bfb62008-12-06 19:37:56 -050097
98 // Wait for other CPUs to process the SIPI.
99 mdelay(10);
100
101 // Restore memory.
Kevin O'Connorf5c11612008-12-14 10:11:45 -0500102 writel(APIC_SVR, val);
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500103 *(u64*)BUILD_AP_BOOT_ADDR = old;
104
105 u32 count = readl(&smp_cpus);
106 dprintf(1, "Found %d cpu(s)\n", count);
107 return count;
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400108}
Kevin O'Connoracf13742008-11-29 11:19:19 -0500109
110// Reset smp_cpus to zero (forces a recheck on reboots).
111void
112smp_probe_setup(void)
113{
114 smp_cpus = 0;
115}