source: mainline/kernel/arch/ia32/src/smp/apic.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 15.4 KB
RevLine 
[f761f1eb]1/*
[df4ed85]2 * Copyright (c) 2001-2004 Jakub Jermar
[f761f1eb]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[84afc7b]29/** @addtogroup ia32
[b45c443]30 * @{
31 */
32/** @file
33 */
34
[d99c1d2]35#include <typedefs.h>
[397c77f]36#include <arch/smp/apic.h>
37#include <arch/smp/ap.h>
[ed0dd65]38#include <arch/smp/mps.h>
[66def8d]39#include <arch/boot/boot.h>
[63e27ef]40#include <assert.h>
[f761f1eb]41#include <mm/page.h>
42#include <time/delay.h>
[fcfac420]43#include <interrupt.h>
[f761f1eb]44#include <arch/interrupt.h>
[b2fa1204]45#include <log.h>
[f761f1eb]46#include <arch/asm.h>
47#include <arch.h>
[3e35fd7]48#include <ddi/irq.h>
[f761f1eb]49
[5f85c91]50#ifdef CONFIG_SMP
[8262010]51
[f761f1eb]52/*
[a83a802]53 * Advanced Programmable Interrupt Controller for SMP systems.
[f761f1eb]54 * Tested on:
[da1bafb]55 * Bochs 2.0.2 - Bochs 2.2.6 with 2-8 CPUs
56 * Simics 2.0.28 - Simics 2.2.19 2-15 CPUs
57 * VMware Workstation 5.5 with 2 CPUs
58 * QEMU 0.8.0 with 2-15 CPUs
59 * ASUS P/I-P65UP5 + ASUS C-P55T2D REV. 1.41 with 2x 200Mhz Pentium CPUs
60 * ASUS PCH-DL with 2x 3000Mhz Pentium 4 Xeon (HT) CPUs
61 * MSI K7D Master-L with 2x 2100MHz Athlon MP CPUs
62 *
[f761f1eb]63 */
64
65/*
66 * These variables either stay configured as initilalized, or are changed by
67 * the MP configuration code.
68 *
69 * Pay special attention to the volatile keyword. Without it, gcc -O2 would
70 * optimize the code too much and accesses to l_apic and io_apic, that must
71 * always be 32-bit, would use byte oriented instructions.
[da1bafb]72 *
[f761f1eb]73 */
[0f17bff]74volatile uint32_t *l_apic = (uint32_t *) L_APIC_BASE;
75volatile uint32_t *io_apic = (uint32_t *) IO_APIC_BASE;
[f761f1eb]76
[7f1c620]77uint32_t apic_id_mask = 0;
[99718a2e]78uint8_t bsp_l_apic = 0;
79
[3e35fd7]80static irq_t l_apic_timer_irq;
[f761f1eb]81
[f701b236]82static int apic_poll_errors(void);
83
[9149135]84#ifdef LAPIC_VERBOSE
[da1bafb]85static const char *delmod_str[] = {
[f701b236]86 "Fixed",
87 "Lowest Priority",
88 "SMI",
89 "Reserved",
90 "NMI",
91 "INIT",
92 "STARTUP",
93 "ExtInt"
94};
95
[da1bafb]96static const char *destmod_str[] = {
[f701b236]97 "Physical",
98 "Logical"
99};
100
[da1bafb]101static const char *trigmod_str[] = {
[f701b236]102 "Edge",
103 "Level"
104};
105
[da1bafb]106static const char *mask_str[] = {
[f701b236]107 "Unmasked",
108 "Masked"
109};
110
[da1bafb]111static const char *delivs_str[] = {
[f701b236]112 "Idle",
113 "Send Pending"
114};
115
[da1bafb]116static const char *tm_mode_str[] = {
[f701b236]117 "One-shot",
118 "Periodic"
119};
120
[da1bafb]121static const char *intpol_str[] = {
[f701b236]122 "Polarity High",
123 "Polarity Low"
124};
[9149135]125#endif /* LAPIC_VERBOSE */
[f761f1eb]126
[3e35fd7]127/** APIC spurious interrupt handler.
128 *
[da1bafb]129 * @param n Interrupt vector.
[3e35fd7]130 * @param istate Interrupted state.
[da1bafb]131 *
[3e35fd7]132 */
[214ec25c]133static void apic_spurious(unsigned int n __attribute__((unused)),
[da1bafb]134 istate_t *istate __attribute__((unused)))
[3e35fd7]135{
136#ifdef CONFIG_DEBUG
[b2fa1204]137 log(LF_ARCH, LVL_DEBUG, "cpu%u: APIC spurious interrupt", CPU->id);
[3e35fd7]138#endif
139}
[fcfac420]140
[c9b550b]141static irq_ownership_t l_apic_timer_claim(irq_t *irq)
[3e35fd7]142{
143 return IRQ_ACCEPT;
144}
145
[6cd9aa6]146static void l_apic_timer_irq_handler(irq_t *irq)
[3e35fd7]147{
[7e58979]148 /*
149 * Holding a spinlock could prevent clock() from preempting
150 * the current thread. In this case, we don't need to hold the
151 * irq->lock so we just unlock it and then lock it again.
152 */
[da1bafb]153 irq_spinlock_unlock(&irq->lock, false);
[3e35fd7]154 clock();
[da1bafb]155 irq_spinlock_lock(&irq->lock, false);
[3e35fd7]156}
[fcfac420]157
[99718a2e]158/** Get Local APIC ID.
159 *
160 * @return Local APIC ID.
161 *
162 */
163static uint8_t l_apic_id(void)
164{
165 l_apic_id_t idreg;
[a35b458]166
[99718a2e]167 idreg.value = l_apic[L_APIC_ID];
168 return idreg.apic_id;
169}
170
[8418c7d]171/** Initialize APIC on BSP. */
[f761f1eb]172void apic_init(void)
173{
[b3b7e14a]174 exc_register(VECTOR_APIC_SPUR, "apic_spurious", false,
175 (iroutine_t) apic_spurious);
[a35b458]176
[f761f1eb]177 enable_irqs_function = io_apic_enable_irqs;
178 disable_irqs_function = io_apic_disable_irqs;
179 eoi_function = l_apic_eoi;
[acc7ce4]180 irqs_info = "apic";
[a35b458]181
[f761f1eb]182 /*
183 * Configure interrupt routing.
184 * IRQ 0 remains masked as the time signal is generated by l_apic's themselves.
185 * Other interrupts will be forwarded to the lowest priority CPU.
186 */
[dc0b964]187 io_apic_disable_irqs(0xffffU);
[a35b458]188
[3e35fd7]189 irq_initialize(&l_apic_timer_irq);
[7bcfbbc]190 l_apic_timer_irq.preack = true;
[3e35fd7]191 l_apic_timer_irq.inr = IRQ_CLK;
192 l_apic_timer_irq.claim = l_apic_timer_claim;
193 l_apic_timer_irq.handler = l_apic_timer_irq_handler;
194 irq_register(&l_apic_timer_irq);
[a35b458]195
[7f043c0]196 uint8_t i;
[9149135]197 for (i = 0; i < IRQ_COUNT; i++) {
[f761f1eb]198 int pin;
[a35b458]199
[3e35fd7]200 if ((pin = smp_irq_to_pin(i)) != -1)
[7f043c0]201 io_apic_change_ioredtbl((uint8_t) pin, DEST_ALL, (uint8_t) (IVT_IRQBASE + i), LOPRI);
[f761f1eb]202 }
[a35b458]203
[f761f1eb]204 /*
205 * Ensure that io_apic has unique ID.
206 */
[da1bafb]207 io_apic_id_t idreg;
[a35b458]208
[9149135]209 idreg.value = io_apic_read(IOAPICID);
[da1bafb]210 if ((1 << idreg.apic_id) & apic_id_mask) { /* See if IO APIC ID is used already */
[9149135]211 for (i = 0; i < APIC_ID_COUNT; i++) {
[3e35fd7]212 if (!((1 << i) & apic_id_mask)) {
[9149135]213 idreg.apic_id = i;
214 io_apic_write(IOAPICID, idreg.value);
[f761f1eb]215 break;
216 }
217 }
218 }
[a35b458]219
[f761f1eb]220 /*
221 * Configure the BSP's lapic.
222 */
223 l_apic_init();
[da1bafb]224 l_apic_debug();
[a35b458]225
[99718a2e]226 bsp_l_apic = l_apic_id();
[f761f1eb]227}
228
[f701b236]229/** Poll for APIC errors.
230 *
231 * Examine Error Status Register and report all errors found.
232 *
233 * @return 0 on error, 1 on success.
[da1bafb]234 *
[f701b236]235 */
[f761f1eb]236int apic_poll_errors(void)
237{
[f701b236]238 esr_t esr;
[a35b458]239
[f701b236]240 esr.value = l_apic[ESR];
[a35b458]241
[b2fa1204]242 if (esr.err_bitmap) {
243 log_begin(LF_ARCH, LVL_ERROR);
244 log_printf("APIC errors detected:");
245 if (esr.send_checksum_error)
246 log_printf("\nSend Checksum Error");
247 if (esr.receive_checksum_error)
248 log_printf("\nReceive Checksum Error");
249 if (esr.send_accept_error)
250 log_printf("\nSend Accept Error");
251 if (esr.receive_accept_error)
252 log_printf("\nReceive Accept Error");
253 if (esr.send_illegal_vector)
254 log_printf("\nSend Illegal Vector");
255 if (esr.received_illegal_vector)
256 log_printf("\nReceived Illegal Vector");
257 if (esr.illegal_register_address)
258 log_printf("\nIllegal Register Address");
259 log_end();
260 }
[a35b458]261
[f701b236]262 return !esr.err_bitmap;
[f761f1eb]263}
264
[da68871a]265/* Waits for the destination cpu to accept the previous ipi. */
[5e4f22b]266static void l_apic_wait_for_delivery(void)
267{
268 icr_t icr;
[a35b458]269
[5e4f22b]270 do {
271 icr.lo = l_apic[ICRlo];
[49e6c6b4]272 } while (icr.delivs != DELIVS_IDLE);
273}
274
275/** Send one CPU an IPI vector.
276 *
277 * @param apicid Physical APIC ID of the destination CPU.
278 * @param vector Interrupt vector to be sent.
279 *
280 * @return 0 on failure, 1 on success.
281 */
282int l_apic_send_custom_ipi(uint8_t apicid, uint8_t vector)
283{
284 icr_t icr;
285
286 /* Wait for a destination cpu to accept our previous ipi. */
[da68871a]287 l_apic_wait_for_delivery();
[a35b458]288
[49e6c6b4]289 icr.lo = l_apic[ICRlo];
290 icr.hi = l_apic[ICRhi];
[a35b458]291
[49e6c6b4]292 icr.delmod = DELMOD_FIXED;
293 icr.destmod = DESTMOD_PHYS;
294 icr.level = LEVEL_ASSERT;
295 icr.shorthand = SHORTHAND_NONE;
296 icr.trigger_mode = TRIGMOD_LEVEL;
297 icr.vector = vector;
298 icr.dest = apicid;
299
300 /* Send the IPI by writing to l_apic[ICRlo]. */
301 l_apic[ICRhi] = icr.hi;
302 l_apic[ICRlo] = icr.lo;
[a35b458]303
[49e6c6b4]304 return apic_poll_errors();
[5e4f22b]305}
306
[f701b236]307/** Send all CPUs excluding CPU IPI vector.
308 *
309 * @param vector Interrupt vector to be sent.
310 *
311 * @return 0 on failure, 1 on success.
[da1bafb]312 *
[169587a]313 */
[7f1c620]314int l_apic_broadcast_custom_ipi(uint8_t vector)
[169587a]315{
[8418c7d]316 icr_t icr;
[49e6c6b4]317
318 /* Wait for a destination cpu to accept our previous ipi. */
[da68871a]319 l_apic_wait_for_delivery();
[a35b458]320
[8418c7d]321 icr.lo = l_apic[ICRlo];
322 icr.delmod = DELMOD_FIXED;
323 icr.destmod = DESTMOD_LOGIC;
324 icr.level = LEVEL_ASSERT;
325 icr.shorthand = SHORTHAND_ALL_EXCL;
326 icr.trigger_mode = TRIGMOD_LEVEL;
327 icr.vector = vector;
[a35b458]328
[8418c7d]329 l_apic[ICRlo] = icr.lo;
[a35b458]330
[169587a]331 return apic_poll_errors();
332}
333
[f701b236]334/** Universal Start-up Algorithm for bringing up the AP processors.
335 *
336 * @param apicid APIC ID of the processor to be brought up.
337 *
338 * @return 0 on failure, 1 on success.
[da1bafb]339 *
[f761f1eb]340 */
[7f1c620]341int l_apic_send_init_ipi(uint8_t apicid)
[f761f1eb]342{
343 /*
344 * Read the ICR register in and zero all non-reserved fields.
345 */
[da1bafb]346 icr_t icr;
[a35b458]347
[8418c7d]348 icr.lo = l_apic[ICRlo];
349 icr.hi = l_apic[ICRhi];
[a35b458]350
[8418c7d]351 icr.delmod = DELMOD_INIT;
352 icr.destmod = DESTMOD_PHYS;
353 icr.level = LEVEL_ASSERT;
354 icr.trigger_mode = TRIGMOD_LEVEL;
355 icr.shorthand = SHORTHAND_NONE;
356 icr.vector = 0;
357 icr.dest = apicid;
[a35b458]358
[8418c7d]359 l_apic[ICRhi] = icr.hi;
360 l_apic[ICRlo] = icr.lo;
[a35b458]361
[f761f1eb]362 /*
363 * According to MP Specification, 20us should be enough to
364 * deliver the IPI.
365 */
366 delay(20);
[a35b458]367
[88636f68]368 if (!apic_poll_errors())
369 return 0;
[a35b458]370
[5e4f22b]371 l_apic_wait_for_delivery();
372
[8418c7d]373 icr.lo = l_apic[ICRlo];
374 icr.delmod = DELMOD_INIT;
375 icr.destmod = DESTMOD_PHYS;
376 icr.level = LEVEL_DEASSERT;
377 icr.shorthand = SHORTHAND_NONE;
378 icr.trigger_mode = TRIGMOD_LEVEL;
379 icr.vector = 0;
380 l_apic[ICRlo] = icr.lo;
[a35b458]381
[f761f1eb]382 /*
383 * Wait 10ms as MP Specification specifies.
384 */
385 delay(10000);
[a35b458]386
[c9b8c5c]387 if (!is_82489DX_apic(l_apic[LAVR])) {
388 /*
389 * If this is not 82489DX-based l_apic we must send two STARTUP IPI's.
390 */
[da1bafb]391 unsigned int i;
392 for (i = 0; i < 2; i++) {
[8418c7d]393 icr.lo = l_apic[ICRlo];
[7f043c0]394 icr.vector = (uint8_t) (((uintptr_t) ap_boot) >> 12); /* calculate the reset vector */
[8418c7d]395 icr.delmod = DELMOD_STARTUP;
396 icr.destmod = DESTMOD_PHYS;
397 icr.level = LEVEL_ASSERT;
398 icr.shorthand = SHORTHAND_NONE;
399 icr.trigger_mode = TRIGMOD_LEVEL;
400 l_apic[ICRlo] = icr.lo;
[c9b8c5c]401 delay(200);
402 }
[f761f1eb]403 }
[a35b458]404
[f761f1eb]405 return apic_poll_errors();
406}
407
[f701b236]408/** Initialize Local APIC. */
[f761f1eb]409void l_apic_init(void)
410{
[8418c7d]411 /* Initialize LVT Error register. */
[da1bafb]412 lvt_error_t error;
[a35b458]413
[8418c7d]414 error.value = l_apic[LVT_Err];
415 error.masked = true;
416 l_apic[LVT_Err] = error.value;
[a35b458]417
[8418c7d]418 /* Initialize LVT LINT0 register. */
[da1bafb]419 lvt_lint_t lint;
[a35b458]420
[8418c7d]421 lint.value = l_apic[LVT_LINT0];
422 lint.masked = true;
423 l_apic[LVT_LINT0] = lint.value;
[a35b458]424
[8418c7d]425 /* Initialize LVT LINT1 register. */
426 lint.value = l_apic[LVT_LINT1];
427 lint.masked = true;
428 l_apic[LVT_LINT1] = lint.value;
[a35b458]429
[d0780b4c]430 /* Task Priority Register initialization. */
[da1bafb]431 tpr_t tpr;
[a35b458]432
[d0780b4c]433 tpr.value = l_apic[TPR];
434 tpr.pri_sc = 0;
435 tpr.pri = 0;
436 l_apic[TPR] = tpr.value;
[a35b458]437
[8418c7d]438 /* Spurious-Interrupt Vector Register initialization. */
[da1bafb]439 svr_t svr;
[a35b458]440
[8418c7d]441 svr.value = l_apic[SVR];
442 svr.vector = VECTOR_APIC_SPUR;
443 svr.lapic_enabled = true;
[d0780b4c]444 svr.focus_checking = true;
[8418c7d]445 l_apic[SVR] = svr.value;
[a35b458]446
[434f700]447 if (CPU->arch.family >= 6)
448 enable_l_apic_in_msr();
[a35b458]449
[8418c7d]450 /* Interrupt Command Register initialization. */
[da1bafb]451 icr_t icr;
[a35b458]452
[8418c7d]453 icr.lo = l_apic[ICRlo];
454 icr.delmod = DELMOD_INIT;
455 icr.destmod = DESTMOD_PHYS;
456 icr.level = LEVEL_DEASSERT;
457 icr.shorthand = SHORTHAND_ALL_INCL;
458 icr.trigger_mode = TRIGMOD_LEVEL;
459 l_apic[ICRlo] = icr.lo;
[a35b458]460
[f701b236]461 /* Timer Divide Configuration Register initialization. */
[da1bafb]462 tdcr_t tdcr;
[a35b458]463
[f701b236]464 tdcr.value = l_apic[TDCR];
465 tdcr.div_value = DIVIDE_1;
466 l_apic[TDCR] = tdcr.value;
[a35b458]467
[f701b236]468 /* Program local timer. */
[da1bafb]469 lvt_tm_t tm;
[a35b458]470
[8418c7d]471 tm.value = l_apic[LVT_Tm];
472 tm.vector = VECTOR_CLK;
473 tm.mode = TIMER_PERIODIC;
474 tm.masked = false;
475 l_apic[LVT_Tm] = tm.value;
[a35b458]476
[e20de55]477 /*
478 * Measure and configure the timer to generate timer
479 * interrupt with period 1s/HZ seconds.
480 */
[da1bafb]481 uint32_t t1 = l_apic[CCRT];
[f761f1eb]482 l_apic[ICRT] = 0xffffffff;
[a35b458]483
[da1bafb]484 while (l_apic[CCRT] == t1);
[a35b458]485
[f761f1eb]486 t1 = l_apic[CCRT];
[da1bafb]487 delay(1000000 / HZ);
488 uint32_t t2 = l_apic[CCRT];
[a35b458]489
[da1bafb]490 l_apic[ICRT] = t1 - t2;
[a35b458]491
[93e90c7]492 /* Program Logical Destination Register. */
[63e27ef]493 assert(CPU->id < 8);
[da1bafb]494 ldr_t ldr;
[a35b458]495
[93e90c7]496 ldr.value = l_apic[LDR];
[7f043c0]497 ldr.id = (uint8_t) (1 << CPU->id);
[93e90c7]498 l_apic[LDR] = ldr.value;
[a35b458]499
[93e90c7]500 /* Program Destination Format Register for Flat mode. */
[da1bafb]501 dfr_t dfr;
[a35b458]502
[93e90c7]503 dfr.value = l_apic[DFR];
504 dfr.model = MODEL_FLAT;
505 l_apic[DFR] = dfr.value;
[f761f1eb]506}
507
[f701b236]508/** Local APIC End of Interrupt. */
[f761f1eb]509void l_apic_eoi(void)
510{
511 l_apic[EOI] = 0;
512}
513
[f701b236]514/** Dump content of Local APIC registers. */
[f761f1eb]515void l_apic_debug(void)
516{
517#ifdef LAPIC_VERBOSE
[b2fa1204]518 log_begin(LF_ARCH, LVL_DEBUG);
519 log_printf("LVT on cpu%u, LAPIC ID: %" PRIu8 "\n",
[99718a2e]520 CPU->id, l_apic_id());
[a35b458]521
[da1bafb]522 lvt_tm_t tm;
[f701b236]523 tm.value = l_apic[LVT_Tm];
[b2fa1204]524 log_printf("LVT Tm: vector=%" PRIu8 ", %s, %s, %s\n",
[99718a2e]525 tm.vector, delivs_str[tm.delivs], mask_str[tm.masked],
526 tm_mode_str[tm.mode]);
[a35b458]527
[da1bafb]528 lvt_lint_t lint;
[f701b236]529 lint.value = l_apic[LVT_LINT0];
[b2fa1204]530 log_printf("LVT LINT0: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
[99718a2e]531 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
532 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
533 mask_str[lint.masked]);
[a35b458]534
[99718a2e]535 lint.value = l_apic[LVT_LINT1];
[b2fa1204]536 log_printf("LVT LINT1: vector=%" PRIu8 ", %s, %s, %s, irr=%u, %s, %s\n",
[99718a2e]537 tm.vector, delmod_str[lint.delmod], delivs_str[lint.delivs],
538 intpol_str[lint.intpol], lint.irr, trigmod_str[lint.trigger_mode],
539 mask_str[lint.masked]);
[a35b458]540
[da1bafb]541 lvt_error_t error;
[f701b236]542 error.value = l_apic[LVT_Err];
[b2fa1204]543 log_printf("LVT Err: vector=%" PRIu8 ", %s, %s\n", error.vector,
[99718a2e]544 delivs_str[error.delivs], mask_str[error.masked]);
[b2fa1204]545 log_end();
[f761f1eb]546#endif
547}
548
[f701b236]549/** Read from IO APIC register.
550 *
551 * @param address IO APIC register address.
552 *
553 * @return Content of the addressed IO APIC register.
[da1bafb]554 *
[f701b236]555 */
[7f1c620]556uint32_t io_apic_read(uint8_t address)
[f761f1eb]557{
[f701b236]558 io_regsel_t regsel;
[a35b458]559
[f701b236]560 regsel.value = io_apic[IOREGSEL];
561 regsel.reg_addr = address;
562 io_apic[IOREGSEL] = regsel.value;
[f761f1eb]563 return io_apic[IOWIN];
564}
565
[f701b236]566/** Write to IO APIC register.
567 *
568 * @param address IO APIC register address.
[da1bafb]569 * @param val Content to be written to the addressed IO APIC register.
570 *
[f701b236]571 */
[da1bafb]572void io_apic_write(uint8_t address, uint32_t val)
[f761f1eb]573{
[f701b236]574 io_regsel_t regsel;
[a35b458]575
[f701b236]576 regsel.value = io_apic[IOREGSEL];
577 regsel.reg_addr = address;
578 io_apic[IOREGSEL] = regsel.value;
[da1bafb]579 io_apic[IOWIN] = val;
[f761f1eb]580}
581
[f701b236]582/** Change some attributes of one item in I/O Redirection Table.
583 *
[da1bafb]584 * @param pin IO APIC pin number.
585 * @param dest Interrupt destination address.
586 * @param vec Interrupt vector to trigger.
[f701b236]587 * @param flags Flags.
[da1bafb]588 *
[f701b236]589 */
[da1bafb]590void io_apic_change_ioredtbl(uint8_t pin, uint8_t dest, uint8_t vec,
591 unsigned int flags)
[f761f1eb]592{
[da1bafb]593 unsigned int dlvr;
[a35b458]594
[f761f1eb]595 if (flags & LOPRI)
[a83a802]596 dlvr = DELMOD_LOWPRI;
[da1bafb]597 else
598 dlvr = DELMOD_FIXED;
[a35b458]599
[da1bafb]600 io_redirection_reg_t reg;
[7f043c0]601 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
602 reg.hi = io_apic_read((uint8_t) (IOREDTBL + pin * 2 + 1));
[a35b458]603
[93e90c7]604 reg.dest = dest;
[a83a802]605 reg.destmod = DESTMOD_LOGIC;
606 reg.trigger_mode = TRIGMOD_EDGE;
607 reg.intpol = POLARITY_HIGH;
608 reg.delmod = dlvr;
[da1bafb]609 reg.intvec = vec;
[a35b458]610
[7f043c0]611 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
612 io_apic_write((uint8_t) (IOREDTBL + pin * 2 + 1), reg.hi);
[f761f1eb]613}
614
[f701b236]615/** Mask IRQs in IO APIC.
616 *
617 * @param irqmask Bitmask of IRQs to be masked (0 = do not mask, 1 = mask).
[da1bafb]618 *
[f701b236]619 */
[7f1c620]620void io_apic_disable_irqs(uint16_t irqmask)
[f761f1eb]621{
[623b49f1]622 unsigned int i;
623 for (i = 0; i < 16; i++) {
624 if (irqmask & (1 << i)) {
[f761f1eb]625 /*
626 * Mask the signal input in IO APIC if there is a
627 * mapping for the respective IRQ number.
628 */
[da1bafb]629 int pin = smp_irq_to_pin(i);
[f761f1eb]630 if (pin != -1) {
[da1bafb]631 io_redirection_reg_t reg;
[a35b458]632
[7f043c0]633 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
[a83a802]634 reg.masked = true;
[7f043c0]635 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
[f761f1eb]636 }
[a35b458]637
[f761f1eb]638 }
639 }
640}
641
[f701b236]642/** Unmask IRQs in IO APIC.
643 *
644 * @param irqmask Bitmask of IRQs to be unmasked (0 = do not unmask, 1 = unmask).
[da1bafb]645 *
[f701b236]646 */
[7f1c620]647void io_apic_enable_irqs(uint16_t irqmask)
[f761f1eb]648{
[623b49f1]649 unsigned int i;
[7f043c0]650 for (i = 0; i < 16; i++) {
[623b49f1]651 if (irqmask & (1 << i)) {
[f761f1eb]652 /*
653 * Unmask the signal input in IO APIC if there is a
654 * mapping for the respective IRQ number.
655 */
[da1bafb]656 int pin = smp_irq_to_pin(i);
[f761f1eb]657 if (pin != -1) {
[da1bafb]658 io_redirection_reg_t reg;
[a35b458]659
[7f043c0]660 reg.lo = io_apic_read((uint8_t) (IOREDTBL + pin * 2));
[a83a802]661 reg.masked = false;
[7f043c0]662 io_apic_write((uint8_t) (IOREDTBL + pin * 2), reg.lo);
[f761f1eb]663 }
[a35b458]664
[f761f1eb]665 }
666 }
667}
668
[5f85c91]669#endif /* CONFIG_SMP */
[b45c443]670
[06e1e95]671/** @}
[b45c443]672 */
Note: See TracBrowser for help on using the repository browser.