diff options
Diffstat (limited to 'arch/x86/kernel/acpi')
-rw-r--r-- | arch/x86/kernel/acpi/boot.c | 491 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/processor.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.S | 38 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.h | 5 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/sleep.c | 40 |
5 files changed, 541 insertions, 39 deletions
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 33c5216fd3e1..eb875cdc7367 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <asm/pgtable.h> | 37 | #include <asm/pgtable.h> |
38 | #include <asm/io_apic.h> | 38 | #include <asm/io_apic.h> |
39 | #include <asm/apic.h> | 39 | #include <asm/apic.h> |
40 | #include <asm/genapic.h> | ||
40 | #include <asm/io.h> | 41 | #include <asm/io.h> |
41 | #include <asm/mpspec.h> | 42 | #include <asm/mpspec.h> |
42 | #include <asm/smp.h> | 43 | #include <asm/smp.h> |
@@ -57,7 +58,6 @@ EXPORT_SYMBOL(acpi_disabled); | |||
57 | #ifdef CONFIG_X86_64 | 58 | #ifdef CONFIG_X86_64 |
58 | 59 | ||
59 | #include <asm/proto.h> | 60 | #include <asm/proto.h> |
60 | #include <asm/genapic.h> | ||
61 | 61 | ||
62 | #else /* X86 */ | 62 | #else /* X86 */ |
63 | 63 | ||
@@ -106,21 +106,6 @@ static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; | |||
106 | */ | 106 | */ |
107 | enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC; | 107 | enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC; |
108 | 108 | ||
109 | #ifdef CONFIG_X86_64 | ||
110 | |||
111 | /* rely on all ACPI tables being in the direct mapping */ | ||
112 | char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size) | ||
113 | { | ||
114 | if (!phys_addr || !size) | ||
115 | return NULL; | ||
116 | |||
117 | if (phys_addr+size <= (max_pfn_mapped << PAGE_SHIFT) + PAGE_SIZE) | ||
118 | return __va(phys_addr); | ||
119 | |||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | #else | ||
124 | 109 | ||
125 | /* | 110 | /* |
126 | * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_END, | 111 | * Temporarily use the virtual area starting from FIX_IO_APIC_BASE_END, |
@@ -139,11 +124,15 @@ char *__init __acpi_map_table(unsigned long phys, unsigned long size) | |||
139 | unsigned long base, offset, mapped_size; | 124 | unsigned long base, offset, mapped_size; |
140 | int idx; | 125 | int idx; |
141 | 126 | ||
142 | if (phys + size < 8 * 1024 * 1024) | 127 | if (!phys || !size) |
128 | return NULL; | ||
129 | |||
130 | if (phys+size <= (max_low_pfn_mapped << PAGE_SHIFT)) | ||
143 | return __va(phys); | 131 | return __va(phys); |
144 | 132 | ||
145 | offset = phys & (PAGE_SIZE - 1); | 133 | offset = phys & (PAGE_SIZE - 1); |
146 | mapped_size = PAGE_SIZE - offset; | 134 | mapped_size = PAGE_SIZE - offset; |
135 | clear_fixmap(FIX_ACPI_END); | ||
147 | set_fixmap(FIX_ACPI_END, phys); | 136 | set_fixmap(FIX_ACPI_END, phys); |
148 | base = fix_to_virt(FIX_ACPI_END); | 137 | base = fix_to_virt(FIX_ACPI_END); |
149 | 138 | ||
@@ -155,19 +144,29 @@ char *__init __acpi_map_table(unsigned long phys, unsigned long size) | |||
155 | if (--idx < FIX_ACPI_BEGIN) | 144 | if (--idx < FIX_ACPI_BEGIN) |
156 | return NULL; /* cannot handle this */ | 145 | return NULL; /* cannot handle this */ |
157 | phys += PAGE_SIZE; | 146 | phys += PAGE_SIZE; |
147 | clear_fixmap(idx); | ||
158 | set_fixmap(idx, phys); | 148 | set_fixmap(idx, phys); |
159 | mapped_size += PAGE_SIZE; | 149 | mapped_size += PAGE_SIZE; |
160 | } | 150 | } |
161 | 151 | ||
162 | return ((unsigned char *)base + offset); | 152 | return ((unsigned char *)base + offset); |
163 | } | 153 | } |
164 | #endif | ||
165 | 154 | ||
166 | #ifdef CONFIG_PCI_MMCONFIG | 155 | #ifdef CONFIG_PCI_MMCONFIG |
167 | /* The physical address of the MMCONFIG aperture. Set from ACPI tables. */ | 156 | /* The physical address of the MMCONFIG aperture. Set from ACPI tables. */ |
168 | struct acpi_mcfg_allocation *pci_mmcfg_config; | 157 | struct acpi_mcfg_allocation *pci_mmcfg_config; |
169 | int pci_mmcfg_config_num; | 158 | int pci_mmcfg_config_num; |
170 | 159 | ||
160 | static int acpi_mcfg_64bit_base_addr __initdata = FALSE; | ||
161 | |||
162 | static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg) | ||
163 | { | ||
164 | if (!strcmp(mcfg->header.oem_id, "SGI")) | ||
165 | acpi_mcfg_64bit_base_addr = TRUE; | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
171 | int __init acpi_parse_mcfg(struct acpi_table_header *header) | 170 | int __init acpi_parse_mcfg(struct acpi_table_header *header) |
172 | { | 171 | { |
173 | struct acpi_table_mcfg *mcfg; | 172 | struct acpi_table_mcfg *mcfg; |
@@ -200,8 +199,12 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) | |||
200 | } | 199 | } |
201 | 200 | ||
202 | memcpy(pci_mmcfg_config, &mcfg[1], config_size); | 201 | memcpy(pci_mmcfg_config, &mcfg[1], config_size); |
202 | |||
203 | acpi_mcfg_oem_check(mcfg); | ||
204 | |||
203 | for (i = 0; i < pci_mmcfg_config_num; ++i) { | 205 | for (i = 0; i < pci_mmcfg_config_num; ++i) { |
204 | if (pci_mmcfg_config[i].address > 0xFFFFFFFF) { | 206 | if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) && |
207 | !acpi_mcfg_64bit_base_addr) { | ||
205 | printk(KERN_ERR PREFIX | 208 | printk(KERN_ERR PREFIX |
206 | "MMCONFIG not in low 4GB of memory\n"); | 209 | "MMCONFIG not in low 4GB of memory\n"); |
207 | kfree(pci_mmcfg_config); | 210 | kfree(pci_mmcfg_config); |
@@ -249,10 +252,8 @@ static void __cpuinit acpi_register_lapic(int id, u8 enabled) | |||
249 | return; | 252 | return; |
250 | } | 253 | } |
251 | 254 | ||
252 | #ifdef CONFIG_X86_32 | ||
253 | if (boot_cpu_physical_apicid != -1U) | 255 | if (boot_cpu_physical_apicid != -1U) |
254 | ver = apic_version[boot_cpu_physical_apicid]; | 256 | ver = apic_version[boot_cpu_physical_apicid]; |
255 | #endif | ||
256 | 257 | ||
257 | generic_processor_info(id, ver); | 258 | generic_processor_info(id, ver); |
258 | } | 259 | } |
@@ -338,8 +339,6 @@ acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long e | |||
338 | 339 | ||
339 | #ifdef CONFIG_X86_IO_APIC | 340 | #ifdef CONFIG_X86_IO_APIC |
340 | 341 | ||
341 | struct mp_ioapic_routing mp_ioapic_routing[MAX_IO_APICS]; | ||
342 | |||
343 | static int __init | 342 | static int __init |
344 | acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end) | 343 | acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end) |
345 | { | 344 | { |
@@ -514,8 +513,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity) | |||
514 | * Make sure all (legacy) PCI IRQs are set as level-triggered. | 513 | * Make sure all (legacy) PCI IRQs are set as level-triggered. |
515 | */ | 514 | */ |
516 | if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) { | 515 | if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) { |
517 | extern void eisa_set_level_irq(unsigned int irq); | ||
518 | |||
519 | if (triggering == ACPI_LEVEL_SENSITIVE) | 516 | if (triggering == ACPI_LEVEL_SENSITIVE) |
520 | eisa_set_level_irq(gsi); | 517 | eisa_set_level_irq(gsi); |
521 | } | 518 | } |
@@ -775,11 +772,9 @@ static void __init acpi_register_lapic_address(unsigned long address) | |||
775 | 772 | ||
776 | set_fixmap_nocache(FIX_APIC_BASE, address); | 773 | set_fixmap_nocache(FIX_APIC_BASE, address); |
777 | if (boot_cpu_physical_apicid == -1U) { | 774 | if (boot_cpu_physical_apicid == -1U) { |
778 | boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); | 775 | boot_cpu_physical_apicid = read_apic_id(); |
779 | #ifdef CONFIG_X86_32 | ||
780 | apic_version[boot_cpu_physical_apicid] = | 776 | apic_version[boot_cpu_physical_apicid] = |
781 | GET_APIC_VERSION(apic_read(APIC_LVR)); | 777 | GET_APIC_VERSION(apic_read(APIC_LVR)); |
782 | #endif | ||
783 | } | 778 | } |
784 | } | 779 | } |
785 | 780 | ||
@@ -860,6 +855,364 @@ static int __init acpi_parse_madt_lapic_entries(void) | |||
860 | #endif /* CONFIG_X86_LOCAL_APIC */ | 855 | #endif /* CONFIG_X86_LOCAL_APIC */ |
861 | 856 | ||
862 | #ifdef CONFIG_X86_IO_APIC | 857 | #ifdef CONFIG_X86_IO_APIC |
858 | #define MP_ISA_BUS 0 | ||
859 | |||
860 | #ifdef CONFIG_X86_ES7000 | ||
861 | extern int es7000_plat; | ||
862 | #endif | ||
863 | |||
864 | static struct { | ||
865 | int apic_id; | ||
866 | int gsi_base; | ||
867 | int gsi_end; | ||
868 | DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1); | ||
869 | } mp_ioapic_routing[MAX_IO_APICS]; | ||
870 | |||
871 | static int mp_find_ioapic(int gsi) | ||
872 | { | ||
873 | int i = 0; | ||
874 | |||
875 | /* Find the IOAPIC that manages this GSI. */ | ||
876 | for (i = 0; i < nr_ioapics; i++) { | ||
877 | if ((gsi >= mp_ioapic_routing[i].gsi_base) | ||
878 | && (gsi <= mp_ioapic_routing[i].gsi_end)) | ||
879 | return i; | ||
880 | } | ||
881 | |||
882 | printk(KERN_ERR "ERROR: Unable to locate IOAPIC for GSI %d\n", gsi); | ||
883 | return -1; | ||
884 | } | ||
885 | |||
886 | static u8 __init uniq_ioapic_id(u8 id) | ||
887 | { | ||
888 | #ifdef CONFIG_X86_32 | ||
889 | if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && | ||
890 | !APIC_XAPIC(apic_version[boot_cpu_physical_apicid])) | ||
891 | return io_apic_get_unique_id(nr_ioapics, id); | ||
892 | else | ||
893 | return id; | ||
894 | #else | ||
895 | int i; | ||
896 | DECLARE_BITMAP(used, 256); | ||
897 | bitmap_zero(used, 256); | ||
898 | for (i = 0; i < nr_ioapics; i++) { | ||
899 | struct mp_config_ioapic *ia = &mp_ioapics[i]; | ||
900 | __set_bit(ia->mp_apicid, used); | ||
901 | } | ||
902 | if (!test_bit(id, used)) | ||
903 | return id; | ||
904 | return find_first_zero_bit(used, 256); | ||
905 | #endif | ||
906 | } | ||
907 | |||
908 | static int bad_ioapic(unsigned long address) | ||
909 | { | ||
910 | if (nr_ioapics >= MAX_IO_APICS) { | ||
911 | printk(KERN_ERR "ERROR: Max # of I/O APICs (%d) exceeded " | ||
912 | "(found %d)\n", MAX_IO_APICS, nr_ioapics); | ||
913 | panic("Recompile kernel with bigger MAX_IO_APICS!\n"); | ||
914 | } | ||
915 | if (!address) { | ||
916 | printk(KERN_ERR "WARNING: Bogus (zero) I/O APIC address" | ||
917 | " found in table, skipping!\n"); | ||
918 | return 1; | ||
919 | } | ||
920 | return 0; | ||
921 | } | ||
922 | |||
923 | void __init mp_register_ioapic(int id, u32 address, u32 gsi_base) | ||
924 | { | ||
925 | int idx = 0; | ||
926 | |||
927 | if (bad_ioapic(address)) | ||
928 | return; | ||
929 | |||
930 | idx = nr_ioapics; | ||
931 | |||
932 | mp_ioapics[idx].mp_type = MP_IOAPIC; | ||
933 | mp_ioapics[idx].mp_flags = MPC_APIC_USABLE; | ||
934 | mp_ioapics[idx].mp_apicaddr = address; | ||
935 | |||
936 | set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address); | ||
937 | mp_ioapics[idx].mp_apicid = uniq_ioapic_id(id); | ||
938 | #ifdef CONFIG_X86_32 | ||
939 | mp_ioapics[idx].mp_apicver = io_apic_get_version(idx); | ||
940 | #else | ||
941 | mp_ioapics[idx].mp_apicver = 0; | ||
942 | #endif | ||
943 | /* | ||
944 | * Build basic GSI lookup table to facilitate gsi->io_apic lookups | ||
945 | * and to prevent reprogramming of IOAPIC pins (PCI GSIs). | ||
946 | */ | ||
947 | mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mp_apicid; | ||
948 | mp_ioapic_routing[idx].gsi_base = gsi_base; | ||
949 | mp_ioapic_routing[idx].gsi_end = gsi_base + | ||
950 | io_apic_get_redir_entries(idx); | ||
951 | |||
952 | printk(KERN_INFO "IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, " | ||
953 | "GSI %d-%d\n", idx, mp_ioapics[idx].mp_apicid, | ||
954 | mp_ioapics[idx].mp_apicver, mp_ioapics[idx].mp_apicaddr, | ||
955 | mp_ioapic_routing[idx].gsi_base, mp_ioapic_routing[idx].gsi_end); | ||
956 | |||
957 | nr_ioapics++; | ||
958 | } | ||
959 | |||
960 | static void assign_to_mp_irq(struct mp_config_intsrc *m, | ||
961 | struct mp_config_intsrc *mp_irq) | ||
962 | { | ||
963 | memcpy(mp_irq, m, sizeof(struct mp_config_intsrc)); | ||
964 | } | ||
965 | |||
966 | static int mp_irq_cmp(struct mp_config_intsrc *mp_irq, | ||
967 | struct mp_config_intsrc *m) | ||
968 | { | ||
969 | return memcmp(mp_irq, m, sizeof(struct mp_config_intsrc)); | ||
970 | } | ||
971 | |||
972 | static void save_mp_irq(struct mp_config_intsrc *m) | ||
973 | { | ||
974 | int i; | ||
975 | |||
976 | for (i = 0; i < mp_irq_entries; i++) { | ||
977 | if (!mp_irq_cmp(&mp_irqs[i], m)) | ||
978 | return; | ||
979 | } | ||
980 | |||
981 | assign_to_mp_irq(m, &mp_irqs[mp_irq_entries]); | ||
982 | if (++mp_irq_entries == MAX_IRQ_SOURCES) | ||
983 | panic("Max # of irq sources exceeded!!\n"); | ||
984 | } | ||
985 | |||
986 | void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, u32 gsi) | ||
987 | { | ||
988 | int ioapic; | ||
989 | int pin; | ||
990 | struct mp_config_intsrc mp_irq; | ||
991 | |||
992 | /* | ||
993 | * Convert 'gsi' to 'ioapic.pin'. | ||
994 | */ | ||
995 | ioapic = mp_find_ioapic(gsi); | ||
996 | if (ioapic < 0) | ||
997 | return; | ||
998 | pin = gsi - mp_ioapic_routing[ioapic].gsi_base; | ||
999 | |||
1000 | /* | ||
1001 | * TBD: This check is for faulty timer entries, where the override | ||
1002 | * erroneously sets the trigger to level, resulting in a HUGE | ||
1003 | * increase of timer interrupts! | ||
1004 | */ | ||
1005 | if ((bus_irq == 0) && (trigger == 3)) | ||
1006 | trigger = 1; | ||
1007 | |||
1008 | mp_irq.mp_type = MP_INTSRC; | ||
1009 | mp_irq.mp_irqtype = mp_INT; | ||
1010 | mp_irq.mp_irqflag = (trigger << 2) | polarity; | ||
1011 | mp_irq.mp_srcbus = MP_ISA_BUS; | ||
1012 | mp_irq.mp_srcbusirq = bus_irq; /* IRQ */ | ||
1013 | mp_irq.mp_dstapic = mp_ioapics[ioapic].mp_apicid; /* APIC ID */ | ||
1014 | mp_irq.mp_dstirq = pin; /* INTIN# */ | ||
1015 | |||
1016 | save_mp_irq(&mp_irq); | ||
1017 | } | ||
1018 | |||
1019 | void __init mp_config_acpi_legacy_irqs(void) | ||
1020 | { | ||
1021 | int i; | ||
1022 | int ioapic; | ||
1023 | unsigned int dstapic; | ||
1024 | struct mp_config_intsrc mp_irq; | ||
1025 | |||
1026 | #if defined (CONFIG_MCA) || defined (CONFIG_EISA) | ||
1027 | /* | ||
1028 | * Fabricate the legacy ISA bus (bus #31). | ||
1029 | */ | ||
1030 | mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA; | ||
1031 | #endif | ||
1032 | set_bit(MP_ISA_BUS, mp_bus_not_pci); | ||
1033 | pr_debug("Bus #%d is ISA\n", MP_ISA_BUS); | ||
1034 | |||
1035 | #ifdef CONFIG_X86_ES7000 | ||
1036 | /* | ||
1037 | * Older generations of ES7000 have no legacy identity mappings | ||
1038 | */ | ||
1039 | if (es7000_plat == 1) | ||
1040 | return; | ||
1041 | #endif | ||
1042 | |||
1043 | /* | ||
1044 | * Locate the IOAPIC that manages the ISA IRQs (0-15). | ||
1045 | */ | ||
1046 | ioapic = mp_find_ioapic(0); | ||
1047 | if (ioapic < 0) | ||
1048 | return; | ||
1049 | dstapic = mp_ioapics[ioapic].mp_apicid; | ||
1050 | |||
1051 | /* | ||
1052 | * Use the default configuration for the IRQs 0-15. Unless | ||
1053 | * overridden by (MADT) interrupt source override entries. | ||
1054 | */ | ||
1055 | for (i = 0; i < 16; i++) { | ||
1056 | int idx; | ||
1057 | |||
1058 | for (idx = 0; idx < mp_irq_entries; idx++) { | ||
1059 | struct mp_config_intsrc *irq = mp_irqs + idx; | ||
1060 | |||
1061 | /* Do we already have a mapping for this ISA IRQ? */ | ||
1062 | if (irq->mp_srcbus == MP_ISA_BUS | ||
1063 | && irq->mp_srcbusirq == i) | ||
1064 | break; | ||
1065 | |||
1066 | /* Do we already have a mapping for this IOAPIC pin */ | ||
1067 | if (irq->mp_dstapic == dstapic && | ||
1068 | irq->mp_dstirq == i) | ||
1069 | break; | ||
1070 | } | ||
1071 | |||
1072 | if (idx != mp_irq_entries) { | ||
1073 | printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i); | ||
1074 | continue; /* IRQ already used */ | ||
1075 | } | ||
1076 | |||
1077 | mp_irq.mp_type = MP_INTSRC; | ||
1078 | mp_irq.mp_irqflag = 0; /* Conforming */ | ||
1079 | mp_irq.mp_srcbus = MP_ISA_BUS; | ||
1080 | mp_irq.mp_dstapic = dstapic; | ||
1081 | mp_irq.mp_irqtype = mp_INT; | ||
1082 | mp_irq.mp_srcbusirq = i; /* Identity mapped */ | ||
1083 | mp_irq.mp_dstirq = i; | ||
1084 | |||
1085 | save_mp_irq(&mp_irq); | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | int mp_register_gsi(u32 gsi, int triggering, int polarity) | ||
1090 | { | ||
1091 | int ioapic; | ||
1092 | int ioapic_pin; | ||
1093 | #ifdef CONFIG_X86_32 | ||
1094 | #define MAX_GSI_NUM 4096 | ||
1095 | #define IRQ_COMPRESSION_START 64 | ||
1096 | |||
1097 | static int pci_irq = IRQ_COMPRESSION_START; | ||
1098 | /* | ||
1099 | * Mapping between Global System Interrupts, which | ||
1100 | * represent all possible interrupts, and IRQs | ||
1101 | * assigned to actual devices. | ||
1102 | */ | ||
1103 | static int gsi_to_irq[MAX_GSI_NUM]; | ||
1104 | #else | ||
1105 | |||
1106 | if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC) | ||
1107 | return gsi; | ||
1108 | #endif | ||
1109 | |||
1110 | /* Don't set up the ACPI SCI because it's already set up */ | ||
1111 | if (acpi_gbl_FADT.sci_interrupt == gsi) | ||
1112 | return gsi; | ||
1113 | |||
1114 | ioapic = mp_find_ioapic(gsi); | ||
1115 | if (ioapic < 0) { | ||
1116 | printk(KERN_WARNING "No IOAPIC for GSI %u\n", gsi); | ||
1117 | return gsi; | ||
1118 | } | ||
1119 | |||
1120 | ioapic_pin = gsi - mp_ioapic_routing[ioapic].gsi_base; | ||
1121 | |||
1122 | #ifdef CONFIG_X86_32 | ||
1123 | if (ioapic_renumber_irq) | ||
1124 | gsi = ioapic_renumber_irq(ioapic, gsi); | ||
1125 | #endif | ||
1126 | |||
1127 | /* | ||
1128 | * Avoid pin reprogramming. PRTs typically include entries | ||
1129 | * with redundant pin->gsi mappings (but unique PCI devices); | ||
1130 | * we only program the IOAPIC on the first. | ||
1131 | */ | ||
1132 | if (ioapic_pin > MP_MAX_IOAPIC_PIN) { | ||
1133 | printk(KERN_ERR "Invalid reference to IOAPIC pin " | ||
1134 | "%d-%d\n", mp_ioapic_routing[ioapic].apic_id, | ||
1135 | ioapic_pin); | ||
1136 | return gsi; | ||
1137 | } | ||
1138 | if (test_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed)) { | ||
1139 | pr_debug(KERN_DEBUG "Pin %d-%d already programmed\n", | ||
1140 | mp_ioapic_routing[ioapic].apic_id, ioapic_pin); | ||
1141 | #ifdef CONFIG_X86_32 | ||
1142 | return (gsi < IRQ_COMPRESSION_START ? gsi : gsi_to_irq[gsi]); | ||
1143 | #else | ||
1144 | return gsi; | ||
1145 | #endif | ||
1146 | } | ||
1147 | |||
1148 | set_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed); | ||
1149 | #ifdef CONFIG_X86_32 | ||
1150 | /* | ||
1151 | * For GSI >= 64, use IRQ compression | ||
1152 | */ | ||
1153 | if ((gsi >= IRQ_COMPRESSION_START) | ||
1154 | && (triggering == ACPI_LEVEL_SENSITIVE)) { | ||
1155 | /* | ||
1156 | * For PCI devices assign IRQs in order, avoiding gaps | ||
1157 | * due to unused I/O APIC pins. | ||
1158 | */ | ||
1159 | int irq = gsi; | ||
1160 | if (gsi < MAX_GSI_NUM) { | ||
1161 | /* | ||
1162 | * Retain the VIA chipset work-around (gsi > 15), but | ||
1163 | * avoid a problem where the 8254 timer (IRQ0) is setup | ||
1164 | * via an override (so it's not on pin 0 of the ioapic), | ||
1165 | * and at the same time, the pin 0 interrupt is a PCI | ||
1166 | * type. The gsi > 15 test could cause these two pins | ||
1167 | * to be shared as IRQ0, and they are not shareable. | ||
1168 | * So test for this condition, and if necessary, avoid | ||
1169 | * the pin collision. | ||
1170 | */ | ||
1171 | gsi = pci_irq++; | ||
1172 | /* | ||
1173 | * Don't assign IRQ used by ACPI SCI | ||
1174 | */ | ||
1175 | if (gsi == acpi_gbl_FADT.sci_interrupt) | ||
1176 | gsi = pci_irq++; | ||
1177 | gsi_to_irq[irq] = gsi; | ||
1178 | } else { | ||
1179 | printk(KERN_ERR "GSI %u is too high\n", gsi); | ||
1180 | return gsi; | ||
1181 | } | ||
1182 | } | ||
1183 | #endif | ||
1184 | io_apic_set_pci_routing(ioapic, ioapic_pin, gsi, | ||
1185 | triggering == ACPI_EDGE_SENSITIVE ? 0 : 1, | ||
1186 | polarity == ACPI_ACTIVE_HIGH ? 0 : 1); | ||
1187 | return gsi; | ||
1188 | } | ||
1189 | |||
1190 | int mp_config_acpi_gsi(unsigned char number, unsigned int devfn, u8 pin, | ||
1191 | u32 gsi, int triggering, int polarity) | ||
1192 | { | ||
1193 | #ifdef CONFIG_X86_MPPARSE | ||
1194 | struct mp_config_intsrc mp_irq; | ||
1195 | int ioapic; | ||
1196 | |||
1197 | if (!acpi_ioapic) | ||
1198 | return 0; | ||
1199 | |||
1200 | /* print the entry should happen on mptable identically */ | ||
1201 | mp_irq.mp_type = MP_INTSRC; | ||
1202 | mp_irq.mp_irqtype = mp_INT; | ||
1203 | mp_irq.mp_irqflag = (triggering == ACPI_EDGE_SENSITIVE ? 4 : 0x0c) | | ||
1204 | (polarity == ACPI_ACTIVE_HIGH ? 1 : 3); | ||
1205 | mp_irq.mp_srcbus = number; | ||
1206 | mp_irq.mp_srcbusirq = (((devfn >> 3) & 0x1f) << 2) | ((pin - 1) & 3); | ||
1207 | ioapic = mp_find_ioapic(gsi); | ||
1208 | mp_irq.mp_dstapic = mp_ioapic_routing[ioapic].apic_id; | ||
1209 | mp_irq.mp_dstirq = gsi - mp_ioapic_routing[ioapic].gsi_base; | ||
1210 | |||
1211 | save_mp_irq(&mp_irq); | ||
1212 | #endif | ||
1213 | return 0; | ||
1214 | } | ||
1215 | |||
863 | /* | 1216 | /* |
864 | * Parse IOAPIC related entries in MADT | 1217 | * Parse IOAPIC related entries in MADT |
865 | * returns 0 on success, < 0 on error | 1218 | * returns 0 on success, < 0 on error |
@@ -993,7 +1346,9 @@ static void __init acpi_process_madt(void) | |||
993 | acpi_ioapic = 1; | 1346 | acpi_ioapic = 1; |
994 | 1347 | ||
995 | smp_found_config = 1; | 1348 | smp_found_config = 1; |
1349 | #ifdef CONFIG_X86_32 | ||
996 | setup_apic_routing(); | 1350 | setup_apic_routing(); |
1351 | #endif | ||
997 | } | 1352 | } |
998 | } | 1353 | } |
999 | if (error == -EINVAL) { | 1354 | if (error == -EINVAL) { |
@@ -1009,8 +1364,6 @@ static void __init acpi_process_madt(void) | |||
1009 | return; | 1364 | return; |
1010 | } | 1365 | } |
1011 | 1366 | ||
1012 | #ifdef __i386__ | ||
1013 | |||
1014 | static int __init disable_acpi_irq(const struct dmi_system_id *d) | 1367 | static int __init disable_acpi_irq(const struct dmi_system_id *d) |
1015 | { | 1368 | { |
1016 | if (!acpi_force) { | 1369 | if (!acpi_force) { |
@@ -1061,6 +1414,24 @@ static int __init force_acpi_ht(const struct dmi_system_id *d) | |||
1061 | } | 1414 | } |
1062 | 1415 | ||
1063 | /* | 1416 | /* |
1417 | * Force ignoring BIOS IRQ0 pin2 override | ||
1418 | */ | ||
1419 | static int __init dmi_ignore_irq0_timer_override(const struct dmi_system_id *d) | ||
1420 | { | ||
1421 | /* | ||
1422 | * The ati_ixp4x0_rev() early PCI quirk should have set | ||
1423 | * the acpi_skip_timer_override flag already: | ||
1424 | */ | ||
1425 | if (!acpi_skip_timer_override) { | ||
1426 | WARN(1, KERN_ERR "ati_ixp4x0 quirk not complete.\n"); | ||
1427 | pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", | ||
1428 | d->ident); | ||
1429 | acpi_skip_timer_override = 1; | ||
1430 | } | ||
1431 | return 0; | ||
1432 | } | ||
1433 | |||
1434 | /* | ||
1064 | * If your system is blacklisted here, but you find that acpi=force | 1435 | * If your system is blacklisted here, but you find that acpi=force |
1065 | * works for you, please contact acpi-devel@sourceforge.net | 1436 | * works for you, please contact acpi-devel@sourceforge.net |
1066 | */ | 1437 | */ |
@@ -1227,11 +1598,51 @@ static struct dmi_system_id __initdata acpi_dmi_table[] = { | |||
1227 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), | 1598 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), |
1228 | }, | 1599 | }, |
1229 | }, | 1600 | }, |
1601 | /* | ||
1602 | * HP laptops which use a DSDT reporting as HP/SB400/10000, | ||
1603 | * which includes some code which overrides all temperature | ||
1604 | * trip points to 16C if the INTIN2 input of the I/O APIC | ||
1605 | * is enabled. This input is incorrectly designated the | ||
1606 | * ISA IRQ 0 via an interrupt source override even though | ||
1607 | * it is wired to the output of the master 8259A and INTIN0 | ||
1608 | * is not connected at all. Force ignoring BIOS IRQ0 pin2 | ||
1609 | * override in that cases. | ||
1610 | */ | ||
1611 | { | ||
1612 | .callback = dmi_ignore_irq0_timer_override, | ||
1613 | .ident = "HP nx6115 laptop", | ||
1614 | .matches = { | ||
1615 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
1616 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6115"), | ||
1617 | }, | ||
1618 | }, | ||
1619 | { | ||
1620 | .callback = dmi_ignore_irq0_timer_override, | ||
1621 | .ident = "HP NX6125 laptop", | ||
1622 | .matches = { | ||
1623 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
1624 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6125"), | ||
1625 | }, | ||
1626 | }, | ||
1627 | { | ||
1628 | .callback = dmi_ignore_irq0_timer_override, | ||
1629 | .ident = "HP NX6325 laptop", | ||
1630 | .matches = { | ||
1631 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
1632 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"), | ||
1633 | }, | ||
1634 | }, | ||
1635 | { | ||
1636 | .callback = dmi_ignore_irq0_timer_override, | ||
1637 | .ident = "HP 6715b laptop", | ||
1638 | .matches = { | ||
1639 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
1640 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 6715b"), | ||
1641 | }, | ||
1642 | }, | ||
1230 | {} | 1643 | {} |
1231 | }; | 1644 | }; |
1232 | 1645 | ||
1233 | #endif /* __i386__ */ | ||
1234 | |||
1235 | /* | 1646 | /* |
1236 | * acpi_boot_table_init() and acpi_boot_init() | 1647 | * acpi_boot_table_init() and acpi_boot_init() |
1237 | * called from setup_arch(), always. | 1648 | * called from setup_arch(), always. |
@@ -1259,9 +1670,7 @@ int __init acpi_boot_table_init(void) | |||
1259 | { | 1670 | { |
1260 | int error; | 1671 | int error; |
1261 | 1672 | ||
1262 | #ifdef __i386__ | ||
1263 | dmi_check_system(acpi_dmi_table); | 1673 | dmi_check_system(acpi_dmi_table); |
1264 | #endif | ||
1265 | 1674 | ||
1266 | /* | 1675 | /* |
1267 | * If acpi_disabled, bail out | 1676 | * If acpi_disabled, bail out |
@@ -1386,6 +1795,20 @@ static int __init parse_pci(char *arg) | |||
1386 | } | 1795 | } |
1387 | early_param("pci", parse_pci); | 1796 | early_param("pci", parse_pci); |
1388 | 1797 | ||
1798 | int __init acpi_mps_check(void) | ||
1799 | { | ||
1800 | #if defined(CONFIG_X86_LOCAL_APIC) && !defined(CONFIG_X86_MPPARSE) | ||
1801 | /* mptable code is not built-in*/ | ||
1802 | if (acpi_disabled || acpi_noirq) { | ||
1803 | printk(KERN_WARNING "MPS support code is not built-in.\n" | ||
1804 | "Using acpi=off or acpi=noirq or pci=noacpi " | ||
1805 | "may have problem\n"); | ||
1806 | return 1; | ||
1807 | } | ||
1808 | #endif | ||
1809 | return 0; | ||
1810 | } | ||
1811 | |||
1389 | #ifdef CONFIG_X86_IO_APIC | 1812 | #ifdef CONFIG_X86_IO_APIC |
1390 | static int __init parse_acpi_skip_timer_override(char *arg) | 1813 | static int __init parse_acpi_skip_timer_override(char *arg) |
1391 | { | 1814 | { |
diff --git a/arch/x86/kernel/acpi/processor.c b/arch/x86/kernel/acpi/processor.c index de2d2e4ebad9..7c074eec39fb 100644 --- a/arch/x86/kernel/acpi/processor.c +++ b/arch/x86/kernel/acpi/processor.c | |||
@@ -56,6 +56,12 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) | |||
56 | if (cpu_has(c, X86_FEATURE_ACPI)) | 56 | if (cpu_has(c, X86_FEATURE_ACPI)) |
57 | buf[2] |= ACPI_PDC_T_FFH; | 57 | buf[2] |= ACPI_PDC_T_FFH; |
58 | 58 | ||
59 | /* | ||
60 | * If mwait/monitor is unsupported, C2/C3_FFH will be disabled | ||
61 | */ | ||
62 | if (!cpu_has(c, X86_FEATURE_MWAIT)) | ||
63 | buf[2] &= ~(ACPI_PDC_C_C2C3_FFH); | ||
64 | |||
59 | obj->type = ACPI_TYPE_BUFFER; | 65 | obj->type = ACPI_TYPE_BUFFER; |
60 | obj->buffer.length = 12; | 66 | obj->buffer.length = 12; |
61 | obj->buffer.pointer = (u8 *) buf; | 67 | obj->buffer.pointer = (u8 *) buf; |
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S index f9b77fb37e5b..3355973b12ac 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.S +++ b/arch/x86/kernel/acpi/realmode/wakeup.S | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <asm/msr-index.h> | 5 | #include <asm/msr-index.h> |
6 | #include <asm/page.h> | 6 | #include <asm/page.h> |
7 | #include <asm/pgtable.h> | 7 | #include <asm/pgtable.h> |
8 | #include <asm/processor-flags.h> | ||
8 | 9 | ||
9 | .code16 | 10 | .code16 |
10 | .section ".header", "a" | 11 | .section ".header", "a" |
@@ -24,6 +25,11 @@ pmode_gdt: .quad 0 | |||
24 | realmode_flags: .long 0 | 25 | realmode_flags: .long 0 |
25 | real_magic: .long 0 | 26 | real_magic: .long 0 |
26 | trampoline_segment: .word 0 | 27 | trampoline_segment: .word 0 |
28 | _pad1: .byte 0 | ||
29 | wakeup_jmp: .byte 0xea /* ljmpw */ | ||
30 | wakeup_jmp_off: .word 3f | ||
31 | wakeup_jmp_seg: .word 0 | ||
32 | wakeup_gdt: .quad 0, 0, 0 | ||
27 | signature: .long 0x51ee1111 | 33 | signature: .long 0x51ee1111 |
28 | 34 | ||
29 | .text | 35 | .text |
@@ -34,11 +40,34 @@ _start: | |||
34 | cli | 40 | cli |
35 | cld | 41 | cld |
36 | 42 | ||
43 | /* Apparently some dimwit BIOS programmers don't know how to | ||
44 | program a PM to RM transition, and we might end up here with | ||
45 | junk in the data segment descriptor registers. The only way | ||
46 | to repair that is to go into PM and fix it ourselves... */ | ||
47 | movw $16, %cx | ||
48 | lgdtl %cs:wakeup_gdt | ||
49 | movl %cr0, %eax | ||
50 | orb $X86_CR0_PE, %al | ||
51 | movl %eax, %cr0 | ||
52 | jmp 1f | ||
53 | 1: ljmpw $8, $2f | ||
54 | 2: | ||
55 | movw %cx, %ds | ||
56 | movw %cx, %es | ||
57 | movw %cx, %ss | ||
58 | movw %cx, %fs | ||
59 | movw %cx, %gs | ||
60 | |||
61 | andb $~X86_CR0_PE, %al | ||
62 | movl %eax, %cr0 | ||
63 | jmp wakeup_jmp | ||
64 | 3: | ||
37 | /* Set up segments */ | 65 | /* Set up segments */ |
38 | movw %cs, %ax | 66 | movw %cs, %ax |
39 | movw %ax, %ds | 67 | movw %ax, %ds |
40 | movw %ax, %es | 68 | movw %ax, %es |
41 | movw %ax, %ss | 69 | movw %ax, %ss |
70 | lidtl wakeup_idt | ||
42 | 71 | ||
43 | movl $wakeup_stack_end, %esp | 72 | movl $wakeup_stack_end, %esp |
44 | 73 | ||
@@ -98,7 +127,14 @@ bogus_real_magic: | |||
98 | jmp 1b | 127 | jmp 1b |
99 | 128 | ||
100 | .data | 129 | .data |
101 | .balign 4 | 130 | .balign 8 |
131 | |||
132 | /* This is the standard real-mode IDT */ | ||
133 | wakeup_idt: | ||
134 | .word 0xffff /* limit */ | ||
135 | .long 0 /* address */ | ||
136 | .word 0 | ||
137 | |||
102 | .globl HEAP, heap_end | 138 | .globl HEAP, heap_end |
103 | HEAP: | 139 | HEAP: |
104 | .long wakeup_heap | 140 | .long wakeup_heap |
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.h b/arch/x86/kernel/acpi/realmode/wakeup.h index ef8166fe8020..69d38d0b2b64 100644 --- a/arch/x86/kernel/acpi/realmode/wakeup.h +++ b/arch/x86/kernel/acpi/realmode/wakeup.h | |||
@@ -24,6 +24,11 @@ struct wakeup_header { | |||
24 | u32 realmode_flags; | 24 | u32 realmode_flags; |
25 | u32 real_magic; | 25 | u32 real_magic; |
26 | u16 trampoline_segment; /* segment with trampoline code, 64-bit only */ | 26 | u16 trampoline_segment; /* segment with trampoline code, 64-bit only */ |
27 | u8 _pad1; | ||
28 | u8 wakeup_jmp; | ||
29 | u16 wakeup_jmp_off; | ||
30 | u16 wakeup_jmp_seg; | ||
31 | u64 wakeup_gdt[3]; | ||
27 | u32 signature; /* To check we have correct structure */ | 32 | u32 signature; /* To check we have correct structure */ |
28 | } __attribute__((__packed__)); | 33 | } __attribute__((__packed__)); |
29 | 34 | ||
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index afc25ee9964b..426e5d91b63a 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/bootmem.h> | 9 | #include <linux/bootmem.h> |
10 | #include <linux/dmi.h> | 10 | #include <linux/dmi.h> |
11 | #include <linux/cpumask.h> | 11 | #include <linux/cpumask.h> |
12 | #include <asm/segment.h> | ||
12 | 13 | ||
13 | #include "realmode/wakeup.h" | 14 | #include "realmode/wakeup.h" |
14 | #include "sleep.h" | 15 | #include "sleep.h" |
@@ -19,7 +20,7 @@ unsigned long acpi_realmode_flags; | |||
19 | /* address in low memory of the wakeup routine. */ | 20 | /* address in low memory of the wakeup routine. */ |
20 | static unsigned long acpi_realmode; | 21 | static unsigned long acpi_realmode; |
21 | 22 | ||
22 | #ifdef CONFIG_64BIT | 23 | #if defined(CONFIG_SMP) && defined(CONFIG_64BIT) |
23 | static char temp_stack[10240]; | 24 | static char temp_stack[10240]; |
24 | #endif | 25 | #endif |
25 | 26 | ||
@@ -50,6 +51,29 @@ int acpi_save_state_mem(void) | |||
50 | 51 | ||
51 | header->video_mode = saved_video_mode; | 52 | header->video_mode = saved_video_mode; |
52 | 53 | ||
54 | header->wakeup_jmp_seg = acpi_wakeup_address >> 4; | ||
55 | |||
56 | /* | ||
57 | * Set up the wakeup GDT. We set these up as Big Real Mode, | ||
58 | * that is, with limits set to 4 GB. At least the Lenovo | ||
59 | * Thinkpad X61 is known to need this for the video BIOS | ||
60 | * initialization quirk to work; this is likely to also | ||
61 | * be the case for other laptops or integrated video devices. | ||
62 | */ | ||
63 | |||
64 | /* GDT[0]: GDT self-pointer */ | ||
65 | header->wakeup_gdt[0] = | ||
66 | (u64)(sizeof(header->wakeup_gdt) - 1) + | ||
67 | ((u64)(acpi_wakeup_address + | ||
68 | ((char *)&header->wakeup_gdt - (char *)acpi_realmode)) | ||
69 | << 16); | ||
70 | /* GDT[1]: big real mode-like code segment */ | ||
71 | header->wakeup_gdt[1] = | ||
72 | GDT_ENTRY(0x809b, acpi_wakeup_address, 0xfffff); | ||
73 | /* GDT[2]: big real mode-like data segment */ | ||
74 | header->wakeup_gdt[2] = | ||
75 | GDT_ENTRY(0x8093, acpi_wakeup_address, 0xfffff); | ||
76 | |||
53 | #ifndef CONFIG_64BIT | 77 | #ifndef CONFIG_64BIT |
54 | store_gdt((struct desc_ptr *)&header->pmode_gdt); | 78 | store_gdt((struct desc_ptr *)&header->pmode_gdt); |
55 | 79 | ||
@@ -62,7 +86,7 @@ int acpi_save_state_mem(void) | |||
62 | #endif /* !CONFIG_64BIT */ | 86 | #endif /* !CONFIG_64BIT */ |
63 | 87 | ||
64 | header->pmode_cr0 = read_cr0(); | 88 | header->pmode_cr0 = read_cr0(); |
65 | header->pmode_cr4 = read_cr4(); | 89 | header->pmode_cr4 = read_cr4_safe(); |
66 | header->realmode_flags = acpi_realmode_flags; | 90 | header->realmode_flags = acpi_realmode_flags; |
67 | header->real_magic = 0x12345678; | 91 | header->real_magic = 0x12345678; |
68 | 92 | ||
@@ -72,7 +96,9 @@ int acpi_save_state_mem(void) | |||
72 | saved_magic = 0x12345678; | 96 | saved_magic = 0x12345678; |
73 | #else /* CONFIG_64BIT */ | 97 | #else /* CONFIG_64BIT */ |
74 | header->trampoline_segment = setup_trampoline() >> 4; | 98 | header->trampoline_segment = setup_trampoline() >> 4; |
75 | init_rsp = (unsigned long)temp_stack + 4096; | 99 | #ifdef CONFIG_SMP |
100 | stack_start.sp = temp_stack + 4096; | ||
101 | #endif | ||
76 | initial_code = (unsigned long)wakeup_long64; | 102 | initial_code = (unsigned long)wakeup_long64; |
77 | saved_magic = 0x123456789abcdef0; | 103 | saved_magic = 0x123456789abcdef0; |
78 | #endif /* CONFIG_64BIT */ | 104 | #endif /* CONFIG_64BIT */ |
@@ -111,7 +137,7 @@ void __init acpi_reserve_bootmem(void) | |||
111 | return; | 137 | return; |
112 | } | 138 | } |
113 | 139 | ||
114 | acpi_wakeup_address = acpi_realmode; | 140 | acpi_wakeup_address = virt_to_phys((void *)acpi_realmode); |
115 | } | 141 | } |
116 | 142 | ||
117 | 143 | ||
@@ -124,6 +150,12 @@ static int __init acpi_sleep_setup(char *str) | |||
124 | acpi_realmode_flags |= 2; | 150 | acpi_realmode_flags |= 2; |
125 | if (strncmp(str, "s3_beep", 7) == 0) | 151 | if (strncmp(str, "s3_beep", 7) == 0) |
126 | acpi_realmode_flags |= 4; | 152 | acpi_realmode_flags |= 4; |
153 | #ifdef CONFIG_HIBERNATION | ||
154 | if (strncmp(str, "s4_nohwsig", 10) == 0) | ||
155 | acpi_no_s4_hw_signature(); | ||
156 | #endif | ||
157 | if (strncmp(str, "old_ordering", 12) == 0) | ||
158 | acpi_old_suspend_ordering(); | ||
127 | str = strchr(str, ','); | 159 | str = strchr(str, ','); |
128 | if (str != NULL) | 160 | if (str != NULL) |
129 | str += strspn(str, ", \t"); | 161 | str += strspn(str, ", \t"); |