blob: f08d44fabef4d991e640b79bcef78ea566bc607b [file] [log] [blame]
Jiang Liu74afab72014-10-27 16:12:00 +08001/*
2 * Local APIC related interfaces to support IOAPIC, MSI, HT_IRQ etc.
3 *
4 * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
5 * Moved from arch/x86/kernel/apic/io_apic.c.
Jiang Liub5dc8e62015-04-13 14:11:24 +08006 * Jiang Liu <jiang.liu@linux.intel.com>
7 * Enable support of hierarchical irqdomains
Jiang Liu74afab72014-10-27 16:12:00 +08008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/interrupt.h>
Thomas Gleixner65d7ed52017-09-13 23:29:39 +020014#include <linux/seq_file.h>
Jiang Liu74afab72014-10-27 16:12:00 +080015#include <linux/init.h>
16#include <linux/compiler.h>
Jiang Liu74afab72014-10-27 16:12:00 +080017#include <linux/slab.h>
Jiang Liud746d1e2015-04-14 10:30:09 +080018#include <asm/irqdomain.h>
Jiang Liu74afab72014-10-27 16:12:00 +080019#include <asm/hw_irq.h>
20#include <asm/apic.h>
21#include <asm/i8259.h>
22#include <asm/desc.h>
23#include <asm/irq_remapping.h>
24
Thomas Gleixner8d1e3dc2017-09-13 23:29:41 +020025#include <asm/trace/irq_vectors.h>
26
Jiang Liu7f3262e2015-04-14 10:30:03 +080027struct apic_chip_data {
Thomas Gleixnerba224fe2017-09-13 23:29:45 +020028 struct irq_cfg hw_irq_cfg;
29 unsigned int vector;
30 unsigned int prev_vector;
Thomas Gleixner029c6e12017-09-13 23:29:31 +020031 unsigned int cpu;
32 unsigned int prev_cpu;
Thomas Gleixner69cde002017-09-13 23:29:42 +020033 unsigned int irq;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +020034 struct hlist_node clist;
Jiang Liu7f3262e2015-04-14 10:30:03 +080035 u8 move_in_progress : 1;
36};
37
Jiang Liub5dc8e62015-04-13 14:11:24 +080038struct irq_domain *x86_vector_domain;
Jake Oshinsc8f3e512015-12-10 17:52:59 +000039EXPORT_SYMBOL_GPL(x86_vector_domain);
Jiang Liu74afab72014-10-27 16:12:00 +080040static DEFINE_RAW_SPINLOCK(vector_lock);
Thomas Gleixner69cde002017-09-13 23:29:42 +020041static cpumask_var_t vector_searchmask;
Jiang Liub5dc8e62015-04-13 14:11:24 +080042static struct irq_chip lapic_controller;
Thomas Gleixner0fa115d2017-09-13 23:29:38 +020043static struct irq_matrix *vector_matrix;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +020044#ifdef CONFIG_SMP
45static DEFINE_PER_CPU(struct hlist_head, cleanup_list);
46#endif
Jiang Liu74afab72014-10-27 16:12:00 +080047
48void lock_vector_lock(void)
49{
50 /* Used to the online set of cpus does not change
51 * during assign_irq_vector.
52 */
53 raw_spin_lock(&vector_lock);
54}
55
56void unlock_vector_lock(void)
57{
58 raw_spin_unlock(&vector_lock);
59}
60
Thomas Gleixner99a14822017-09-13 23:29:36 +020061void init_irq_alloc_info(struct irq_alloc_info *info,
62 const struct cpumask *mask)
63{
64 memset(info, 0, sizeof(*info));
65 info->mask = mask;
66}
67
68void copy_irq_alloc_info(struct irq_alloc_info *dst, struct irq_alloc_info *src)
69{
70 if (src)
71 *dst = *src;
72 else
73 memset(dst, 0, sizeof(*dst));
74}
75
Thomas Gleixner86ba6552017-09-13 23:29:30 +020076static struct apic_chip_data *apic_chip_data(struct irq_data *irqd)
Jiang Liu74afab72014-10-27 16:12:00 +080077{
Thomas Gleixner86ba6552017-09-13 23:29:30 +020078 if (!irqd)
Jiang Liub5dc8e62015-04-13 14:11:24 +080079 return NULL;
80
Thomas Gleixner86ba6552017-09-13 23:29:30 +020081 while (irqd->parent_data)
82 irqd = irqd->parent_data;
Jiang Liub5dc8e62015-04-13 14:11:24 +080083
Thomas Gleixner86ba6552017-09-13 23:29:30 +020084 return irqd->chip_data;
Jiang Liu74afab72014-10-27 16:12:00 +080085}
86
Thomas Gleixner86ba6552017-09-13 23:29:30 +020087struct irq_cfg *irqd_cfg(struct irq_data *irqd)
Jiang Liu74afab72014-10-27 16:12:00 +080088{
Thomas Gleixner86ba6552017-09-13 23:29:30 +020089 struct apic_chip_data *apicd = apic_chip_data(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +080090
Thomas Gleixnerba224fe2017-09-13 23:29:45 +020091 return apicd ? &apicd->hw_irq_cfg : NULL;
Jiang Liu7f3262e2015-04-14 10:30:03 +080092}
Jake Oshinsc8f3e512015-12-10 17:52:59 +000093EXPORT_SYMBOL_GPL(irqd_cfg);
Jiang Liu7f3262e2015-04-14 10:30:03 +080094
95struct irq_cfg *irq_cfg(unsigned int irq)
96{
97 return irqd_cfg(irq_get_irq_data(irq));
98}
99
100static struct apic_chip_data *alloc_apic_chip_data(int node)
101{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200102 struct apic_chip_data *apicd;
Jiang Liu7f3262e2015-04-14 10:30:03 +0800103
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200104 apicd = kzalloc_node(sizeof(*apicd), GFP_KERNEL, node);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200105 if (apicd)
106 INIT_HLIST_NODE(&apicd->clist);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200107 return apicd;
Jiang Liu74afab72014-10-27 16:12:00 +0800108}
109
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200110static void free_apic_chip_data(struct apic_chip_data *apicd)
Jiang Liu74afab72014-10-27 16:12:00 +0800111{
Thomas Gleixner69cde002017-09-13 23:29:42 +0200112 kfree(apicd);
Jiang Liu74afab72014-10-27 16:12:00 +0800113}
114
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200115static void apic_update_irq_cfg(struct irq_data *irqd, unsigned int vector,
116 unsigned int cpu)
Jiang Liu74afab72014-10-27 16:12:00 +0800117{
Thomas Gleixner69cde002017-09-13 23:29:42 +0200118 struct apic_chip_data *apicd = apic_chip_data(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +0800119
Thomas Gleixner69cde002017-09-13 23:29:42 +0200120 lockdep_assert_held(&vector_lock);
Jiang Liu74afab72014-10-27 16:12:00 +0800121
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200122 apicd->hw_irq_cfg.vector = vector;
123 apicd->hw_irq_cfg.dest_apicid = apic->calc_dest_apicid(cpu);
124 irq_data_update_effective_affinity(irqd, cpumask_of(cpu));
125 trace_vector_config(irqd->irq, vector, cpu,
126 apicd->hw_irq_cfg.dest_apicid);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200127}
Jiang Liu74afab72014-10-27 16:12:00 +0800128
Thomas Gleixner69cde002017-09-13 23:29:42 +0200129static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,
130 unsigned int newcpu)
131{
132 struct apic_chip_data *apicd = apic_chip_data(irqd);
133 struct irq_desc *desc = irq_data_to_desc(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +0800134
Thomas Gleixner69cde002017-09-13 23:29:42 +0200135 lockdep_assert_held(&vector_lock);
Thomas Gleixner3716fd22015-12-31 16:30:48 +0000136
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200137 trace_vector_update(irqd->irq, newvec, newcpu, apicd->vector,
Thomas Gleixner69cde002017-09-13 23:29:42 +0200138 apicd->cpu);
Jiang Liu74afab72014-10-27 16:12:00 +0800139
Thomas Gleixner69cde002017-09-13 23:29:42 +0200140 /* Setup the vector move, if required */
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200141 if (apicd->vector && cpu_online(apicd->cpu)) {
Thomas Gleixner69cde002017-09-13 23:29:42 +0200142 apicd->move_in_progress = true;
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200143 apicd->prev_vector = apicd->vector;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200144 apicd->prev_cpu = apicd->cpu;
145 } else {
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200146 apicd->prev_vector = 0;
Jiang Liu74afab72014-10-27 16:12:00 +0800147 }
Jiang Liu74afab72014-10-27 16:12:00 +0800148
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200149 apicd->vector = newvec;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200150 apicd->cpu = newcpu;
151 BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec]));
152 per_cpu(vector_irq, newcpu)[newvec] = desc;
153}
154
155static int allocate_vector(struct irq_data *irqd, const struct cpumask *dest)
156{
157 struct apic_chip_data *apicd = apic_chip_data(irqd);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200158 unsigned int cpu = apicd->cpu;
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200159 int vector = apicd->vector;
160
161 lockdep_assert_held(&vector_lock);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200162
Thomas Gleixner847667e2015-12-31 16:30:50 +0000163 /*
Thomas Gleixner69cde002017-09-13 23:29:42 +0200164 * If the current target CPU is online and in the new requested
165 * affinity mask, there is no point in moving the interrupt from
166 * one CPU to another.
Thomas Gleixner847667e2015-12-31 16:30:50 +0000167 */
Thomas Gleixner69cde002017-09-13 23:29:42 +0200168 if (vector && cpu_online(cpu) && cpumask_test_cpu(cpu, dest))
169 return 0;
170
171 vector = irq_matrix_alloc(vector_matrix, dest, false, &cpu);
172 if (vector > 0)
173 apic_update_vector(irqd, vector, cpu);
174 trace_vector_alloc(irqd->irq, vector, false, vector);
175 return vector;
176}
177
178static int assign_vector_locked(struct irq_data *irqd,
179 const struct cpumask *dest)
180{
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200181 struct apic_chip_data *apicd = apic_chip_data(irqd);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200182 int vector = allocate_vector(irqd, dest);
183
184 if (vector < 0)
185 return vector;
186
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200187 apic_update_irq_cfg(irqd, apicd->vector, apicd->cpu);
Thomas Gleixner3716fd22015-12-31 16:30:48 +0000188 return 0;
Jiang Liu74afab72014-10-27 16:12:00 +0800189}
190
Thomas Gleixner69cde002017-09-13 23:29:42 +0200191static int assign_irq_vector(struct irq_data *irqd, const struct cpumask *dest)
Jiang Liu74afab72014-10-27 16:12:00 +0800192{
Jiang Liu74afab72014-10-27 16:12:00 +0800193 unsigned long flags;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200194 int ret;
Jiang Liu74afab72014-10-27 16:12:00 +0800195
196 raw_spin_lock_irqsave(&vector_lock, flags);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200197 cpumask_and(vector_searchmask, dest, cpu_online_mask);
198 ret = assign_vector_locked(irqd, vector_searchmask);
Jiang Liu74afab72014-10-27 16:12:00 +0800199 raw_spin_unlock_irqrestore(&vector_lock, flags);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200200 return ret;
Jiang Liu74afab72014-10-27 16:12:00 +0800201}
202
Thomas Gleixner69cde002017-09-13 23:29:42 +0200203static int assign_irq_vector_policy(struct irq_data *irqd,
204 struct irq_alloc_info *info, int node)
Jiang Liu486ca532015-05-07 10:53:56 +0800205{
Thomas Gleixner258d86e2017-09-13 23:29:35 +0200206 if (info->mask)
Thomas Gleixner69cde002017-09-13 23:29:42 +0200207 return assign_irq_vector(irqd, info->mask);
Jiang Liu486ca532015-05-07 10:53:56 +0800208 if (node != NUMA_NO_NODE &&
Thomas Gleixner69cde002017-09-13 23:29:42 +0200209 !assign_irq_vector(irqd, cpumask_of_node(node)))
Jiang Liu486ca532015-05-07 10:53:56 +0800210 return 0;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200211 return assign_irq_vector(irqd, cpu_online_mask);
Jiang Liu486ca532015-05-07 10:53:56 +0800212}
213
Thomas Gleixner69cde002017-09-13 23:29:42 +0200214static void clear_irq_vector(struct irq_data *irqd)
Jiang Liu74afab72014-10-27 16:12:00 +0800215{
Thomas Gleixner69cde002017-09-13 23:29:42 +0200216 struct apic_chip_data *apicd = apic_chip_data(irqd);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200217 unsigned int vector = apicd->vector;
Jiang Liu74afab72014-10-27 16:12:00 +0800218
Thomas Gleixner69cde002017-09-13 23:29:42 +0200219 lockdep_assert_held(&vector_lock);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200220
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200221 if (!vector)
Keith Busch1bdb8972016-04-27 14:22:32 -0600222 return;
Jiang Liu74afab72014-10-27 16:12:00 +0800223
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200224 trace_vector_clear(irqd->irq, vector, apicd->cpu, apicd->prev_vector,
Thomas Gleixner69cde002017-09-13 23:29:42 +0200225 apicd->prev_cpu);
226
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200227 per_cpu(vector_irq, apicd->cpu)[vector] = VECTOR_UNUSED;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200228 irq_matrix_free(vector_matrix, apicd->cpu, vector, false);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200229 apicd->vector = 0;
Jiang Liu74afab72014-10-27 16:12:00 +0800230
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200231 /* Clean up move in progress */
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200232 vector = apicd->prev_vector;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200233 if (!vector)
Jiang Liu74afab72014-10-27 16:12:00 +0800234 return;
Jiang Liu74afab72014-10-27 16:12:00 +0800235
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200236 per_cpu(vector_irq, apicd->prev_cpu)[vector] = VECTOR_UNUSED;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200237 irq_matrix_free(vector_matrix, apicd->prev_cpu, vector, false);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200238 apicd->prev_vector = 0;
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200239 apicd->move_in_progress = 0;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200240 hlist_del_init(&apicd->clist);
Jiang Liu74afab72014-10-27 16:12:00 +0800241}
242
Jiang Liub5dc8e62015-04-13 14:11:24 +0800243static void x86_vector_free_irqs(struct irq_domain *domain,
244 unsigned int virq, unsigned int nr_irqs)
245{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200246 struct apic_chip_data *apicd;
247 struct irq_data *irqd;
Jiang Liu111abeb2015-12-31 16:30:44 +0000248 unsigned long flags;
Jiang Liub5dc8e62015-04-13 14:11:24 +0800249 int i;
250
251 for (i = 0; i < nr_irqs; i++) {
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200252 irqd = irq_domain_get_irq_data(x86_vector_domain, virq + i);
253 if (irqd && irqd->chip_data) {
Jiang Liu111abeb2015-12-31 16:30:44 +0000254 raw_spin_lock_irqsave(&vector_lock, flags);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200255 clear_irq_vector(irqd);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200256 apicd = irqd->chip_data;
257 irq_domain_reset_irq_data(irqd);
Jiang Liu111abeb2015-12-31 16:30:44 +0000258 raw_spin_unlock_irqrestore(&vector_lock, flags);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200259 free_apic_chip_data(apicd);
Jiang Liub5dc8e62015-04-13 14:11:24 +0800260 }
261 }
262}
263
264static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
265 unsigned int nr_irqs, void *arg)
266{
267 struct irq_alloc_info *info = arg;
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200268 struct apic_chip_data *apicd;
269 struct irq_data *irqd;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200270 unsigned long flags;
Jiang Liu5f2dbbc2015-06-01 16:05:14 +0800271 int i, err, node;
Jiang Liub5dc8e62015-04-13 14:11:24 +0800272
273 if (disable_apic)
274 return -ENXIO;
275
276 /* Currently vector allocator can't guarantee contiguous allocations */
277 if ((info->flags & X86_IRQ_ALLOC_CONTIGUOUS_VECTORS) && nr_irqs > 1)
278 return -ENOSYS;
279
Jiang Liub5dc8e62015-04-13 14:11:24 +0800280 for (i = 0; i < nr_irqs; i++) {
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200281 irqd = irq_domain_get_irq_data(domain, virq + i);
282 BUG_ON(!irqd);
283 node = irq_data_get_node(irqd);
Thomas Gleixner4ef76eb2017-09-13 23:29:34 +0200284 WARN_ON_ONCE(irqd->chip_data);
285 apicd = alloc_apic_chip_data(node);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200286 if (!apicd) {
Jiang Liub5dc8e62015-04-13 14:11:24 +0800287 err = -ENOMEM;
288 goto error;
289 }
290
Thomas Gleixner69cde002017-09-13 23:29:42 +0200291 apicd->irq = virq + i;
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200292 irqd->chip = &lapic_controller;
293 irqd->chip_data = apicd;
294 irqd->hwirq = virq + i;
295 irqd_set_single_target(irqd);
Thomas Gleixner4ef76eb2017-09-13 23:29:34 +0200296 /*
Thomas Gleixner69cde002017-09-13 23:29:42 +0200297 * Legacy vectors are already assigned when the IOAPIC
298 * takes them over. They stay on the same vector. This is
299 * required for check_timer() to work correctly as it might
300 * switch back to legacy mode. Only update the hardware
301 * config.
Thomas Gleixner4ef76eb2017-09-13 23:29:34 +0200302 */
303 if (info->flags & X86_IRQ_ALLOC_LEGACY) {
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200304 apicd->vector = ISA_IRQ_VECTOR(virq + i);
Thomas Gleixner4ef76eb2017-09-13 23:29:34 +0200305 apicd->cpu = 0;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200306 trace_vector_setup(virq + i, true, 0);
307 raw_spin_lock_irqsave(&vector_lock, flags);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200308 apic_update_irq_cfg(irqd, apicd->vector, apicd->cpu);
Thomas Gleixner69cde002017-09-13 23:29:42 +0200309 raw_spin_unlock_irqrestore(&vector_lock, flags);
310 continue;
Thomas Gleixner4ef76eb2017-09-13 23:29:34 +0200311 }
312
Thomas Gleixner69cde002017-09-13 23:29:42 +0200313 err = assign_irq_vector_policy(irqd, info, node);
314 trace_vector_setup(virq + i, false, err);
Jiang Liub5dc8e62015-04-13 14:11:24 +0800315 if (err)
316 goto error;
317 }
318
319 return 0;
320
321error:
322 x86_vector_free_irqs(domain, virq, i + 1);
323 return err;
324}
325
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200326#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
327void x86_vector_debug_show(struct seq_file *m, struct irq_domain *d,
328 struct irq_data *irqd, int ind)
329{
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200330 unsigned int cpu, vector, prev_cpu, prev_vector;
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200331 struct apic_chip_data *apicd;
332 unsigned long flags;
333 int irq;
334
335 if (!irqd) {
336 irq_matrix_debug_show(m, vector_matrix, ind);
337 return;
338 }
339
340 irq = irqd->irq;
341 if (irq < nr_legacy_irqs() && !test_bit(irq, &io_apic_irqs)) {
342 seq_printf(m, "%*sVector: %5d\n", ind, "", ISA_IRQ_VECTOR(irq));
343 seq_printf(m, "%*sTarget: Legacy PIC all CPUs\n", ind, "");
344 return;
345 }
346
347 apicd = irqd->chip_data;
348 if (!apicd) {
349 seq_printf(m, "%*sVector: Not assigned\n", ind, "");
350 return;
351 }
352
353 raw_spin_lock_irqsave(&vector_lock, flags);
354 cpu = apicd->cpu;
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200355 vector = apicd->vector;
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200356 prev_cpu = apicd->prev_cpu;
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200357 prev_vector = apicd->prev_vector;
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200358 raw_spin_unlock_irqrestore(&vector_lock, flags);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200359 seq_printf(m, "%*sVector: %5u\n", ind, "", vector);
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200360 seq_printf(m, "%*sTarget: %5u\n", ind, "", cpu);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200361 if (prev_vector) {
362 seq_printf(m, "%*sPrevious vector: %5u\n", ind, "", prev_vector);
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200363 seq_printf(m, "%*sPrevious target: %5u\n", ind, "", prev_cpu);
364 }
365}
366#endif
367
Thomas Gleixnereb18cf52015-05-05 11:10:11 +0200368static const struct irq_domain_ops x86_vector_domain_ops = {
Thomas Gleixner65d7ed52017-09-13 23:29:39 +0200369 .alloc = x86_vector_alloc_irqs,
370 .free = x86_vector_free_irqs,
371#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
372 .debug_show = x86_vector_debug_show,
373#endif
Jiang Liub5dc8e62015-04-13 14:11:24 +0800374};
375
Jiang Liu11d686e2014-10-27 16:12:05 +0800376int __init arch_probe_nr_irqs(void)
377{
378 int nr;
379
380 if (nr_irqs > (NR_VECTORS * nr_cpu_ids))
381 nr_irqs = NR_VECTORS * nr_cpu_ids;
382
383 nr = (gsi_top + nr_legacy_irqs()) + 8 * nr_cpu_ids;
384#if defined(CONFIG_PCI_MSI) || defined(CONFIG_HT_IRQ)
385 /*
386 * for MSI and HT dyn irq
387 */
388 if (gsi_top <= NR_IRQS_LEGACY)
389 nr += 8 * nr_cpu_ids;
390 else
391 nr += gsi_top * 16;
392#endif
393 if (nr < nr_irqs)
394 nr_irqs = nr;
395
Vitaly Kuznetsov8c058b02015-11-03 10:40:14 +0100396 /*
397 * We don't know if PIC is present at this point so we need to do
398 * probe() to get the right number of legacy IRQs.
399 */
400 return legacy_pic->probe();
Jiang Liu11d686e2014-10-27 16:12:05 +0800401}
402
Thomas Gleixner0fa115d2017-09-13 23:29:38 +0200403void lapic_assign_legacy_vector(unsigned int irq, bool replace)
404{
405 /*
406 * Use assign system here so it wont get accounted as allocated
407 * and moveable in the cpu hotplug check and it prevents managed
408 * irq reservation from touching it.
409 */
410 irq_matrix_assign_system(vector_matrix, ISA_IRQ_VECTOR(irq), replace);
411}
412
413void __init lapic_assign_system_vectors(void)
414{
415 unsigned int i, vector = 0;
416
417 for_each_set_bit_from(vector, system_vectors, NR_VECTORS)
418 irq_matrix_assign_system(vector_matrix, vector, false);
419
420 if (nr_legacy_irqs() > 1)
421 lapic_assign_legacy_vector(PIC_CASCADE_IR, false);
422
423 /* System vectors are reserved, online it */
424 irq_matrix_online(vector_matrix);
425
426 /* Mark the preallocated legacy interrupts */
427 for (i = 0; i < nr_legacy_irqs(); i++) {
428 if (i != PIC_CASCADE_IR)
429 irq_matrix_assign(vector_matrix, ISA_IRQ_VECTOR(i));
430 }
431}
432
Jiang Liu11d686e2014-10-27 16:12:05 +0800433int __init arch_early_irq_init(void)
434{
Thomas Gleixner9d35f852017-06-20 01:37:06 +0200435 struct fwnode_handle *fn;
436
Thomas Gleixner9d35f852017-06-20 01:37:06 +0200437 fn = irq_domain_alloc_named_fwnode("VECTOR");
438 BUG_ON(!fn);
439 x86_vector_domain = irq_domain_create_tree(fn, &x86_vector_domain_ops,
440 NULL);
Jiang Liub5dc8e62015-04-13 14:11:24 +0800441 BUG_ON(x86_vector_domain == NULL);
Thomas Gleixner9d35f852017-06-20 01:37:06 +0200442 irq_domain_free_fwnode(fn);
Jiang Liub5dc8e62015-04-13 14:11:24 +0800443 irq_set_default_host(x86_vector_domain);
444
Jiang Liu52f518a2015-04-13 14:11:35 +0800445 arch_init_msi_domain(x86_vector_domain);
Jiang Liu49e07d82015-04-13 14:11:43 +0800446 arch_init_htirq_domain(x86_vector_domain);
Jiang Liu52f518a2015-04-13 14:11:35 +0800447
Thomas Gleixner3716fd22015-12-31 16:30:48 +0000448 BUG_ON(!alloc_cpumask_var(&vector_searchmask, GFP_KERNEL));
Jiang Liuf7fa7ae2015-04-14 10:30:10 +0800449
Thomas Gleixner0fa115d2017-09-13 23:29:38 +0200450 /*
451 * Allocate the vector matrix allocator data structure and limit the
452 * search area.
453 */
454 vector_matrix = irq_alloc_matrix(NR_VECTORS, FIRST_EXTERNAL_VECTOR,
455 FIRST_SYSTEM_VECTOR);
456 BUG_ON(!vector_matrix);
457
Jiang Liu11d686e2014-10-27 16:12:05 +0800458 return arch_early_ioapic_init();
459}
460
Thomas Gleixnerba801642017-09-13 23:29:44 +0200461#ifdef CONFIG_SMP
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200462/* Temporary hack to keep things working */
463static void vector_update_shutdown_irqs(void)
Jiang Liu74afab72014-10-27 16:12:00 +0800464{
Thomas Gleixnera782a7e2015-08-02 20:38:27 +0000465 struct irq_desc *desc;
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200466 int irq;
Jiang Liu74afab72014-10-27 16:12:00 +0800467
Thomas Gleixnera782a7e2015-08-02 20:38:27 +0000468 for_each_irq_desc(irq, desc) {
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200469 struct irq_data *irqd = irq_desc_get_irq_data(desc);
470 struct apic_chip_data *ad = apic_chip_data(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +0800471
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200472 if (!ad || !ad->vector || ad->cpu != smp_processor_id())
Thomas Gleixner69cde002017-09-13 23:29:42 +0200473 continue;
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200474 this_cpu_write(vector_irq[ad->vector], desc);
475 irq_matrix_assign(vector_matrix, ad->vector);
Jiang Liu74afab72014-10-27 16:12:00 +0800476 }
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200477}
Jiang Liu74afab72014-10-27 16:12:00 +0800478
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200479static struct irq_desc *__setup_vector_irq(int vector)
480{
481 int isairq = vector - ISA_IRQ_VECTOR(0);
482
483 /* Check whether the irq is in the legacy space */
484 if (isairq < 0 || isairq >= nr_legacy_irqs())
485 return VECTOR_UNUSED;
486 /* Check whether the irq is handled by the IOAPIC */
487 if (test_bit(isairq, &io_apic_irqs))
488 return VECTOR_UNUSED;
489 return irq_to_desc(isairq);
Jiang Liu74afab72014-10-27 16:12:00 +0800490}
491
Thomas Gleixner0fa115d2017-09-13 23:29:38 +0200492/* Online the local APIC infrastructure and initialize the vectors */
493void lapic_online(void)
Jiang Liu74afab72014-10-27 16:12:00 +0800494{
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200495 unsigned int vector;
Jiang Liu74afab72014-10-27 16:12:00 +0800496
Thomas Gleixner5a3f75e2015-07-05 17:12:32 +0000497 lockdep_assert_held(&vector_lock);
Thomas Gleixner0fa115d2017-09-13 23:29:38 +0200498
499 /* Online the vector matrix array for this CPU */
500 irq_matrix_online(vector_matrix);
501
Jiang Liu74afab72014-10-27 16:12:00 +0800502 /*
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200503 * The interrupt affinity logic never targets interrupts to offline
504 * CPUs. The exception are the legacy PIC interrupts. In general
505 * they are only targeted to CPU0, but depending on the platform
506 * they can be distributed to any online CPU in hardware. The
507 * kernel has no influence on that. So all active legacy vectors
508 * must be installed on all CPUs. All non legacy interrupts can be
509 * cleared.
Jiang Liu74afab72014-10-27 16:12:00 +0800510 */
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200511 for (vector = 0; vector < NR_VECTORS; vector++)
512 this_cpu_write(vector_irq[vector], __setup_vector_irq(vector));
Jiang Liu74afab72014-10-27 16:12:00 +0800513
Thomas Gleixnerf0cc6cc2017-09-13 23:29:29 +0200514 /*
515 * Until the rewrite of the managed interrupt management is in
516 * place it's necessary to walk the irq descriptors and check for
517 * interrupts which are targeted at this CPU.
518 */
519 vector_update_shutdown_irqs();
Jiang Liu74afab72014-10-27 16:12:00 +0800520}
521
Thomas Gleixner0fa115d2017-09-13 23:29:38 +0200522void lapic_offline(void)
523{
524 lock_vector_lock();
525 irq_matrix_offline(vector_matrix);
526 unlock_vector_lock();
527}
528
Thomas Gleixnerba801642017-09-13 23:29:44 +0200529static int apic_set_affinity(struct irq_data *irqd,
530 const struct cpumask *dest, bool force)
531{
532 int err;
533
534 if (!IS_ENABLED(CONFIG_SMP))
535 return -EPERM;
536
537 if (!cpumask_intersects(dest, cpu_online_mask))
538 return -EINVAL;
539
540 err = assign_irq_vector(irqd, dest);
541 return err ? err : IRQ_SET_MASK_OK;
542}
543
544#else
545# define apic_set_affinity NULL
546#endif
547
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200548static int apic_retrigger_irq(struct irq_data *irqd)
Jiang Liu74afab72014-10-27 16:12:00 +0800549{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200550 struct apic_chip_data *apicd = apic_chip_data(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +0800551 unsigned long flags;
Jiang Liu74afab72014-10-27 16:12:00 +0800552
553 raw_spin_lock_irqsave(&vector_lock, flags);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200554 apic->send_IPI(apicd->cpu, apicd->vector);
Jiang Liu74afab72014-10-27 16:12:00 +0800555 raw_spin_unlock_irqrestore(&vector_lock, flags);
556
557 return 1;
558}
559
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200560void apic_ack_edge(struct irq_data *irqd)
Jiang Liu74afab72014-10-27 16:12:00 +0800561{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200562 irq_complete_move(irqd_cfg(irqd));
563 irq_move_irq(irqd);
Jiang Liu74afab72014-10-27 16:12:00 +0800564 ack_APIC_irq();
565}
566
Jiang Liub5dc8e62015-04-13 14:11:24 +0800567static struct irq_chip lapic_controller = {
Thomas Gleixner8947dfb2017-06-20 01:37:01 +0200568 .name = "APIC",
Jiang Liub5dc8e62015-04-13 14:11:24 +0800569 .irq_ack = apic_ack_edge,
Jiang Liu68f9f442015-04-14 10:30:01 +0800570 .irq_set_affinity = apic_set_affinity,
Jiang Liub5dc8e62015-04-13 14:11:24 +0800571 .irq_retrigger = apic_retrigger_irq,
572};
573
Jiang Liu74afab72014-10-27 16:12:00 +0800574#ifdef CONFIG_SMP
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200575
Thomas Gleixner69cde002017-09-13 23:29:42 +0200576static void free_moved_vector(struct apic_chip_data *apicd)
577{
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200578 unsigned int vector = apicd->prev_vector;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200579 unsigned int cpu = apicd->prev_cpu;
580
581 trace_vector_free_moved(apicd->irq, vector, false);
582 irq_matrix_free(vector_matrix, cpu, vector, false);
583 __this_cpu_write(vector_irq[vector], VECTOR_UNUSED);
584 hlist_del_init(&apicd->clist);
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200585 apicd->prev_vector = 0;
Thomas Gleixner69cde002017-09-13 23:29:42 +0200586 apicd->move_in_progress = 0;
587}
588
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200589asmlinkage __visible void __irq_entry smp_irq_move_cleanup_interrupt(void)
590{
591 struct hlist_head *clhead = this_cpu_ptr(&cleanup_list);
592 struct apic_chip_data *apicd;
593 struct hlist_node *tmp;
594
595 entering_ack_irq();
596 /* Prevent vectors vanishing under us */
597 raw_spin_lock(&vector_lock);
598
599 hlist_for_each_entry_safe(apicd, tmp, clhead, clist) {
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200600 unsigned int irr, vector = apicd->prev_vector;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200601
602 /*
603 * Paranoia: Check if the vector that needs to be cleaned
604 * up is registered at the APICs IRR. If so, then this is
605 * not the best time to clean it up. Clean it up in the
606 * next attempt by sending another IRQ_MOVE_CLEANUP_VECTOR
607 * to this CPU. IRQ_MOVE_CLEANUP_VECTOR is the lowest
608 * priority external vector, so on return from this
609 * interrupt the device interrupt will happen first.
610 */
611 irr = apic_read(APIC_IRR + (vector / 32 * 0x10));
612 if (irr & (1U << (vector % 32))) {
613 apic->send_IPI_self(IRQ_MOVE_CLEANUP_VECTOR);
614 continue;
615 }
Thomas Gleixner69cde002017-09-13 23:29:42 +0200616 free_moved_vector(apicd);
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200617 }
618
619 raw_spin_unlock(&vector_lock);
620 exiting_irq();
621}
622
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200623static void __send_cleanup_vector(struct apic_chip_data *apicd)
Jiang Liu74afab72014-10-27 16:12:00 +0800624{
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200625 unsigned int cpu;
626
Thomas Gleixnerc1684f52015-12-31 16:30:51 +0000627 raw_spin_lock(&vector_lock);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200628 apicd->move_in_progress = 0;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200629 cpu = apicd->prev_cpu;
630 if (cpu_online(cpu)) {
631 hlist_add_head(&apicd->clist, per_cpu_ptr(&cleanup_list, cpu));
632 apic->send_IPI(cpu, IRQ_MOVE_CLEANUP_VECTOR);
633 } else {
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200634 apicd->prev_vector = 0;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200635 }
Thomas Gleixnerc1684f52015-12-31 16:30:51 +0000636 raw_spin_unlock(&vector_lock);
Jiang Liu74afab72014-10-27 16:12:00 +0800637}
638
Jiang Liuc6c20022015-04-14 10:30:02 +0800639void send_cleanup_vector(struct irq_cfg *cfg)
640{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200641 struct apic_chip_data *apicd;
Jiang Liu7f3262e2015-04-14 10:30:03 +0800642
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200643 apicd = container_of(cfg, struct apic_chip_data, hw_irq_cfg);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200644 if (apicd->move_in_progress)
645 __send_cleanup_vector(apicd);
Jiang Liuc6c20022015-04-14 10:30:02 +0800646}
647
Jiang Liu74afab72014-10-27 16:12:00 +0800648static void __irq_complete_move(struct irq_cfg *cfg, unsigned vector)
649{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200650 struct apic_chip_data *apicd;
Jiang Liu74afab72014-10-27 16:12:00 +0800651
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200652 apicd = container_of(cfg, struct apic_chip_data, hw_irq_cfg);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200653 if (likely(!apicd->move_in_progress))
Jiang Liu74afab72014-10-27 16:12:00 +0800654 return;
655
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200656 if (vector == apicd->vector && apicd->cpu == smp_processor_id())
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200657 __send_cleanup_vector(apicd);
Jiang Liu74afab72014-10-27 16:12:00 +0800658}
659
660void irq_complete_move(struct irq_cfg *cfg)
661{
662 __irq_complete_move(cfg, ~get_irq_regs()->orig_ax);
663}
664
Thomas Gleixner90a22822015-12-31 16:30:53 +0000665/*
Thomas Gleixner551adc62016-03-14 09:40:46 +0100666 * Called from fixup_irqs() with @desc->lock held and interrupts disabled.
Thomas Gleixner90a22822015-12-31 16:30:53 +0000667 */
668void irq_force_complete_move(struct irq_desc *desc)
Jiang Liu74afab72014-10-27 16:12:00 +0800669{
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200670 struct apic_chip_data *apicd;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200671 struct irq_data *irqd;
672 unsigned int vector;
Jiang Liu74afab72014-10-27 16:12:00 +0800673
Mika Westerbergdb91aa72016-10-03 13:17:08 +0300674 /*
675 * The function is called for all descriptors regardless of which
676 * irqdomain they belong to. For example if an IRQ is provided by
677 * an irq_chip as part of a GPIO driver, the chip data for that
678 * descriptor is specific to the irq_chip in question.
679 *
680 * Check first that the chip_data is what we expect
681 * (apic_chip_data) before touching it any further.
682 */
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200683 irqd = irq_domain_get_irq_data(x86_vector_domain,
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200684 irq_desc_get_irq(desc));
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200685 if (!irqd)
Mika Westerbergdb91aa72016-10-03 13:17:08 +0300686 return;
687
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200688 raw_spin_lock(&vector_lock);
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200689 apicd = apic_chip_data(irqd);
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200690 if (!apicd)
691 goto unlock;
Thomas Gleixner56d7d2f2015-12-31 16:30:52 +0000692
Thomas Gleixner56d7d2f2015-12-31 16:30:52 +0000693 /*
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200694 * If prev_vector is empty, no action required.
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200695 */
Thomas Gleixnerba224fe2017-09-13 23:29:45 +0200696 vector = apicd->prev_vector;
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200697 if (!vector)
698 goto unlock;
699
700 /*
701 * This is tricky. If the cleanup of the old vector has not been
Thomas Gleixner98229aa2015-12-31 16:30:54 +0000702 * done yet, then the following setaffinity call will fail with
703 * -EBUSY. This can leave the interrupt in a stale state.
704 *
Thomas Gleixner551adc62016-03-14 09:40:46 +0100705 * All CPUs are stuck in stop machine with interrupts disabled so
706 * calling __irq_complete_move() would be completely pointless.
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200707 *
Thomas Gleixner551adc62016-03-14 09:40:46 +0100708 * 1) The interrupt is in move_in_progress state. That means that we
709 * have not seen an interrupt since the io_apic was reprogrammed to
710 * the new vector.
711 *
712 * 2) The interrupt has fired on the new vector, but the cleanup IPIs
713 * have not been processed yet.
714 */
Thomas Gleixner86ba6552017-09-13 23:29:30 +0200715 if (apicd->move_in_progress) {
Thomas Gleixner551adc62016-03-14 09:40:46 +0100716 /*
717 * In theory there is a race:
718 *
719 * set_ioapic(new_vector) <-- Interrupt is raised before update
720 * is effective, i.e. it's raised on
721 * the old vector.
722 *
723 * So if the target cpu cannot handle that interrupt before
724 * the old vector is cleaned up, we get a spurious interrupt
725 * and in the worst case the ioapic irq line becomes stale.
726 *
727 * But in case of cpu hotplug this should be a non issue
728 * because if the affinity update happens right before all
729 * cpus rendevouz in stop machine, there is no way that the
730 * interrupt can be blocked on the target cpu because all cpus
731 * loops first with interrupts enabled in stop machine, so the
732 * old vector is not yet cleaned up when the interrupt fires.
733 *
734 * So the only way to run into this issue is if the delivery
735 * of the interrupt on the apic/system bus would be delayed
736 * beyond the point where the target cpu disables interrupts
737 * in stop machine. I doubt that it can happen, but at least
738 * there is a theroretical chance. Virtualization might be
739 * able to expose this, but AFAICT the IOAPIC emulation is not
740 * as stupid as the real hardware.
741 *
742 * Anyway, there is nothing we can do about that at this point
743 * w/o refactoring the whole fixup_irq() business completely.
744 * We print at least the irq number and the old vector number,
745 * so we have the necessary information when a problem in that
746 * area arises.
747 */
748 pr_warn("IRQ fixup: irq %d move in progress, old vector %d\n",
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200749 irqd->irq, vector);
Thomas Gleixner551adc62016-03-14 09:40:46 +0100750 }
Thomas Gleixner69cde002017-09-13 23:29:42 +0200751 free_moved_vector(apicd);
Thomas Gleixnerdccfe312017-09-13 23:29:32 +0200752unlock:
Thomas Gleixner56d7d2f2015-12-31 16:30:52 +0000753 raw_spin_unlock(&vector_lock);
Jiang Liu74afab72014-10-27 16:12:00 +0800754}
Jiang Liu74afab72014-10-27 16:12:00 +0800755#endif
756
Jiang Liu74afab72014-10-27 16:12:00 +0800757static void __init print_APIC_field(int base)
758{
759 int i;
760
761 printk(KERN_DEBUG);
762
763 for (i = 0; i < 8; i++)
764 pr_cont("%08x", apic_read(base + i*0x10));
765
766 pr_cont("\n");
767}
768
769static void __init print_local_APIC(void *dummy)
770{
771 unsigned int i, v, ver, maxlvt;
772 u64 icr;
773
Jiang Liu849d3562014-10-27 16:12:01 +0800774 pr_debug("printing local APIC contents on CPU#%d/%d:\n",
775 smp_processor_id(), hard_smp_processor_id());
Jiang Liu74afab72014-10-27 16:12:00 +0800776 v = apic_read(APIC_ID);
Jiang Liu849d3562014-10-27 16:12:01 +0800777 pr_info("... APIC ID: %08x (%01x)\n", v, read_apic_id());
Jiang Liu74afab72014-10-27 16:12:00 +0800778 v = apic_read(APIC_LVR);
Jiang Liu849d3562014-10-27 16:12:01 +0800779 pr_info("... APIC VERSION: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800780 ver = GET_APIC_VERSION(v);
781 maxlvt = lapic_get_maxlvt();
782
783 v = apic_read(APIC_TASKPRI);
Jiang Liu849d3562014-10-27 16:12:01 +0800784 pr_debug("... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK);
Jiang Liu74afab72014-10-27 16:12:00 +0800785
786 /* !82489DX */
787 if (APIC_INTEGRATED(ver)) {
788 if (!APIC_XAPIC(ver)) {
789 v = apic_read(APIC_ARBPRI);
Jiang Liu849d3562014-10-27 16:12:01 +0800790 pr_debug("... APIC ARBPRI: %08x (%02x)\n",
791 v, v & APIC_ARBPRI_MASK);
Jiang Liu74afab72014-10-27 16:12:00 +0800792 }
793 v = apic_read(APIC_PROCPRI);
Jiang Liu849d3562014-10-27 16:12:01 +0800794 pr_debug("... APIC PROCPRI: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800795 }
796
797 /*
798 * Remote read supported only in the 82489DX and local APIC for
799 * Pentium processors.
800 */
801 if (!APIC_INTEGRATED(ver) || maxlvt == 3) {
802 v = apic_read(APIC_RRR);
Jiang Liu849d3562014-10-27 16:12:01 +0800803 pr_debug("... APIC RRR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800804 }
805
806 v = apic_read(APIC_LDR);
Jiang Liu849d3562014-10-27 16:12:01 +0800807 pr_debug("... APIC LDR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800808 if (!x2apic_enabled()) {
809 v = apic_read(APIC_DFR);
Jiang Liu849d3562014-10-27 16:12:01 +0800810 pr_debug("... APIC DFR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800811 }
812 v = apic_read(APIC_SPIV);
Jiang Liu849d3562014-10-27 16:12:01 +0800813 pr_debug("... APIC SPIV: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800814
Jiang Liu849d3562014-10-27 16:12:01 +0800815 pr_debug("... APIC ISR field:\n");
Jiang Liu74afab72014-10-27 16:12:00 +0800816 print_APIC_field(APIC_ISR);
Jiang Liu849d3562014-10-27 16:12:01 +0800817 pr_debug("... APIC TMR field:\n");
Jiang Liu74afab72014-10-27 16:12:00 +0800818 print_APIC_field(APIC_TMR);
Jiang Liu849d3562014-10-27 16:12:01 +0800819 pr_debug("... APIC IRR field:\n");
Jiang Liu74afab72014-10-27 16:12:00 +0800820 print_APIC_field(APIC_IRR);
821
822 /* !82489DX */
823 if (APIC_INTEGRATED(ver)) {
824 /* Due to the Pentium erratum 3AP. */
825 if (maxlvt > 3)
826 apic_write(APIC_ESR, 0);
827
828 v = apic_read(APIC_ESR);
Jiang Liu849d3562014-10-27 16:12:01 +0800829 pr_debug("... APIC ESR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800830 }
831
832 icr = apic_icr_read();
Jiang Liu849d3562014-10-27 16:12:01 +0800833 pr_debug("... APIC ICR: %08x\n", (u32)icr);
834 pr_debug("... APIC ICR2: %08x\n", (u32)(icr >> 32));
Jiang Liu74afab72014-10-27 16:12:00 +0800835
836 v = apic_read(APIC_LVTT);
Jiang Liu849d3562014-10-27 16:12:01 +0800837 pr_debug("... APIC LVTT: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800838
839 if (maxlvt > 3) {
840 /* PC is LVT#4. */
841 v = apic_read(APIC_LVTPC);
Jiang Liu849d3562014-10-27 16:12:01 +0800842 pr_debug("... APIC LVTPC: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800843 }
844 v = apic_read(APIC_LVT0);
Jiang Liu849d3562014-10-27 16:12:01 +0800845 pr_debug("... APIC LVT0: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800846 v = apic_read(APIC_LVT1);
Jiang Liu849d3562014-10-27 16:12:01 +0800847 pr_debug("... APIC LVT1: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800848
849 if (maxlvt > 2) {
850 /* ERR is LVT#3. */
851 v = apic_read(APIC_LVTERR);
Jiang Liu849d3562014-10-27 16:12:01 +0800852 pr_debug("... APIC LVTERR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800853 }
854
855 v = apic_read(APIC_TMICT);
Jiang Liu849d3562014-10-27 16:12:01 +0800856 pr_debug("... APIC TMICT: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800857 v = apic_read(APIC_TMCCT);
Jiang Liu849d3562014-10-27 16:12:01 +0800858 pr_debug("... APIC TMCCT: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800859 v = apic_read(APIC_TDCR);
Jiang Liu849d3562014-10-27 16:12:01 +0800860 pr_debug("... APIC TDCR: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800861
862 if (boot_cpu_has(X86_FEATURE_EXTAPIC)) {
863 v = apic_read(APIC_EFEAT);
864 maxlvt = (v >> 16) & 0xff;
Jiang Liu849d3562014-10-27 16:12:01 +0800865 pr_debug("... APIC EFEAT: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800866 v = apic_read(APIC_ECTRL);
Jiang Liu849d3562014-10-27 16:12:01 +0800867 pr_debug("... APIC ECTRL: %08x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800868 for (i = 0; i < maxlvt; i++) {
869 v = apic_read(APIC_EILVTn(i));
Jiang Liu849d3562014-10-27 16:12:01 +0800870 pr_debug("... APIC EILVT%d: %08x\n", i, v);
Jiang Liu74afab72014-10-27 16:12:00 +0800871 }
872 }
873 pr_cont("\n");
874}
875
876static void __init print_local_APICs(int maxcpu)
877{
878 int cpu;
879
880 if (!maxcpu)
881 return;
882
883 preempt_disable();
884 for_each_online_cpu(cpu) {
885 if (cpu >= maxcpu)
886 break;
887 smp_call_function_single(cpu, print_local_APIC, NULL, 1);
888 }
889 preempt_enable();
890}
891
892static void __init print_PIC(void)
893{
894 unsigned int v;
895 unsigned long flags;
896
897 if (!nr_legacy_irqs())
898 return;
899
Jiang Liu849d3562014-10-27 16:12:01 +0800900 pr_debug("\nprinting PIC contents\n");
Jiang Liu74afab72014-10-27 16:12:00 +0800901
902 raw_spin_lock_irqsave(&i8259A_lock, flags);
903
904 v = inb(0xa1) << 8 | inb(0x21);
Jiang Liu849d3562014-10-27 16:12:01 +0800905 pr_debug("... PIC IMR: %04x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800906
907 v = inb(0xa0) << 8 | inb(0x20);
Jiang Liu849d3562014-10-27 16:12:01 +0800908 pr_debug("... PIC IRR: %04x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800909
910 outb(0x0b, 0xa0);
911 outb(0x0b, 0x20);
912 v = inb(0xa0) << 8 | inb(0x20);
913 outb(0x0a, 0xa0);
914 outb(0x0a, 0x20);
915
916 raw_spin_unlock_irqrestore(&i8259A_lock, flags);
917
Jiang Liu849d3562014-10-27 16:12:01 +0800918 pr_debug("... PIC ISR: %04x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800919
920 v = inb(0x4d1) << 8 | inb(0x4d0);
Jiang Liu849d3562014-10-27 16:12:01 +0800921 pr_debug("... PIC ELCR: %04x\n", v);
Jiang Liu74afab72014-10-27 16:12:00 +0800922}
923
924static int show_lapic __initdata = 1;
925static __init int setup_show_lapic(char *arg)
926{
927 int num = -1;
928
929 if (strcmp(arg, "all") == 0) {
930 show_lapic = CONFIG_NR_CPUS;
931 } else {
932 get_option(&arg, &num);
933 if (num >= 0)
934 show_lapic = num;
935 }
936
937 return 1;
938}
939__setup("show_lapic=", setup_show_lapic);
940
941static int __init print_ICs(void)
942{
943 if (apic_verbosity == APIC_QUIET)
944 return 0;
945
946 print_PIC();
947
948 /* don't print out if apic is not there */
Borislav Petkov93984fb2016-04-04 22:25:00 +0200949 if (!boot_cpu_has(X86_FEATURE_APIC) && !apic_from_smp_config())
Jiang Liu74afab72014-10-27 16:12:00 +0800950 return 0;
951
952 print_local_APICs(show_lapic);
953 print_IO_APICs();
954
955 return 0;
956}
957
958late_initcall(print_ICs);