#include "types.h"
#include "mp.h"
#include "defs.h"
#include "x86.h"
#include "traps.h"
#include "ioapic.h"

struct ioapic {
  uint ioregsel;  uint p01; uint p02; uint p03;
  uint iowin;     uint p11; uint p12; uint p13;
};


#define IOAPIC_REDTBL_LO(i)  (IOAPIC_REDTBL + (i) * 2)
#define IOAPIC_REDTBL_HI(i)  (IOAPIC_REDTBL_LO(i) + 1)

static uint
ioapic_read(struct ioapic *io, int reg)
{
  io->ioregsel = reg;
  return io->iowin;
}

static void
ioapic_write(struct ioapic *io, int reg, uint val)
{
  io->ioregsel = reg;
  io->iowin = val;
}

void
ioapic_init(void)
{
  struct ioapic *io;
  uint l, h;
  int nintr;
  uchar id;
  int i;

  io = (struct ioapic*) IO_APIC_BASE;
  l = ioapic_read(io, IOAPIC_VER);
  nintr =  ((l & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1;
  id = ioapic_read(io, IOAPIC_ID) >> APIC_ID_SHIFT;
  if(id != ioapic_id)
    panic("ioapic_init: id isn't equal to ioapic_id\n");
  for(i = 0; i < nintr; i++) {
    // active-hi and edge-triggered for ISA interrupts
    // Assume that pin 0 on the first I/O APIC is an ExtINT pin.
    // Assume that pins 1-15 are ISA interrupts
    l = ioapic_read(io, IOAPIC_REDTBL_LO(i));
    l = l & ~IOART_INTMASK;  // allow INTs
    l |= IOART_INTMSET;
    l = l & ~IOART_INTPOL;   // active hi
    l = l & ~IOART_TRGRMOD;  // edgee triggered
    l = l & ~IOART_DELMOD;   // fixed
    l = l & ~IOART_DESTMOD;  // physical mode
    l = l | (IRQ_OFFSET + i); // vector
    ioapic_write(io, IOAPIC_REDTBL_LO(i), l);
    h = ioapic_read(io, IOAPIC_REDTBL_HI(i));
    h &= ~IOART_DEST;
    ioapic_write(io, IOAPIC_REDTBL_HI(i), h);
  }
}

void
ioapic_enable (int irq, int cpunum)
{
  uint l, h;
  struct ioapic *io;

  io = (struct ioapic*) IO_APIC_BASE;
  l = ioapic_read(io, IOAPIC_REDTBL_LO(irq));
  l = l & ~IOART_INTMASK;  // allow INTs
  ioapic_write(io, IOAPIC_REDTBL_LO(irq), l);
  h = ioapic_read(io, IOAPIC_REDTBL_HI(irq));
  h &= ~IOART_DEST;
  h |= (cpunum << APIC_ID_SHIFT);
  ioapic_write(io, IOAPIC_REDTBL_HI(irq), h);
}