/*
    Copyright 2007 (c) by S. Ryffel,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*

    File:                  $Source$
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision$
    Last Date of Change:   $Date$  by: $Author$

*/

#ifndef TM_HAL_APIC_H
#define TM_HAL_APIC_H

/*
 * Local APIC
 */

#define APIC_ISR_NR              0x8
#define APIC_ISR_REG             (address_t)((unsigned long)apic_base + 0x100)
#define APIC_IRR_NR              0x8
#define APIC_IRR_REG             (address_t)((unsigned long)apic_base + 0x200)

/* Local Vector Table Registers */
#define APIC_LVT_TIMER_REG       (address_t)((unsigned long)apic_base + 0x320)
#define APIC_LVT_THERM_REG       (address_t)((unsigned long)apic_base + 0x330)
#define APIC_LVT_PERF_REG        (address_t)((unsigned long)apic_base + 0x340)
#define APIC_LVT_LINT0_REG       (address_t)((unsigned long)apic_base + 0x350)
#define APIC_LVT_LINT1_REG       (address_t)((unsigned long)apic_base + 0x360)
#define APIC_LVT_ERROR_REG       (address_t)((unsigned long)apic_base + 0x370)

/* Local Vector Table Register Flags */
#define APIC_LVT_TIMER_PERIODIC  0x20000
#define APIC_LVT_MASKED          0x10000
#define APIC_LVT_MODE_FIXED      0x00000
#define APIC_LVT_MODE_SMI        0x00200
#define APIC_LVT_MODE_NMI        0x00400
#define APIC_LVT_MODE_INIT       0x00500
#define APIC_LVT_MODE_EXTINT     0x00700
#define APIC_LVT_TRIG_LEVEL      0x08000
#define APIC_LVT_PENDING         0x01000
#define APIC_LVT_ACTIVE_LOW      0x02000

#define APIC_TPR_REG             (address_t)((unsigned long)apic_base + 0x080)
#define APIC_PPR_REG             (address_t)((unsigned long)apic_base + 0x0A0)
#define APIC_ESR_REG             (address_t)((unsigned long)apic_base + 0x280)
#define APIC_SPURIOUS_IV_REG     (address_t)((unsigned long)apic_base + 0x0F0)

#define APIC_SPIV_APIC_ENABLED   (1<<8)

#define APIC_TIMER_ICOUNT_REG    (address_t)((unsigned long)apic_base + 0x380)
#define APIC_TIMER_CCOUNT_REG    (address_t)((unsigned long)apic_base + 0x390)
#define APIC_TIMER_DIV_REG       (address_t)((unsigned long)apic_base + 0x3E0)

#define APIC_TIMER_DIV_BY64      0x9
#define APIC_TIMER_DIV           64

/* Flags of the Error Status Register */
#define APIC_IS_ERROR(esr)       (esr & 0xEF) 
#define APIC_ESR_SEND_CHKSUM     0x01
#define APIC_ESR_RECV_CHKSUM     0x02
#define APIC_ESR_SEND_ACK        0x04
#define APIC_ESR_RECV_ACK        0x08
#define APIC_ESR_SEND_ILLVEC     0x20
#define APIC_ESR_RECV_ILLVEC     0x40
#define APIC_ESR_ILLREG          0x80

/* Task Priority: priority threshold for interrupting the processor
 *  priority = Interrupt nr / 16; 2 <= priority <= 15
 */
#define APIC_TP                  0x0      /* allways interrupt */

/* Spurious Vector
 */
#define APIC_IR_SPIV             T_IR_0F  /* 15 */
#define APIC_SPIV_HANDLING_IRET           /* just do an iret */ 
//#define APIC_SPIV_HANDLING_ISR          /* call an Interrupt Service Routine */

/* Error Vector
 */
#define APIC_IR_ERRIV            T_IR_0E  /* 14 */


#ifndef __SASM__

enum cpu_type_e  { BeforeIntel486,
                    Pentium, 
                    PentiumPro, 
                    PentiumII, 
                    IntelCeleron, 
                    PentiumIII, 
                    Pentium4, 
                    IntelXeon, 
                    PentiumM, 
                    Pentium4HT, 
                    PentiumD, 
                    IntelCoreDuo, 
                    IntelCoreDuo2, 
                    IntelXeon3000 };
typedef enum cpu_type_e cpu_type; 

void ApicInit(void);
void ApicTimerInit(void);
void ApicTimerISR(void);
void ApicSpivISR(void* arg);
void ApicErrorISR(void* arg);

void apic_print_timer_status(void);

inline void apic_write(address_t addr, unsigned long v);
inline unsigned long apic_read(address_t addr);
inline void apic_write_lvt(address_t addr, int v);

#endif

/*
 * IO-APIC
 */

#define IOAPIC_MAXRED     24    /* entries in the redirection table */

#define IOAPIC_ID         0x00
#define IOAPIC_VER        0x01
#define IOAPIC_ARB        0x02
#define IOAPIC_REDTLB     0x10

#define IOAPIC_DEST_MASK                  ((unsigned long long)0xFF << 56)
#define IOAPIC_MASKED                     0x10000
#define IOAPIC_TRIG_LEVEL                 0x08000
#define IOAPIC_ACTIVE_LOW                 0x02000
#define IOAPIC_PENDING                    0x01000
#define IOAPIC_DEST_LOGICAL               0x00800
#define IOAPIC_MODE_MASK                  0x00700
#define IOAPIC_MODE_FIXED                 0x00000
#define IOAPIC_MODE_SMI                   0x00200
#define IOAPIC_MODE_NMI                   0x00400
#define IOAPIC_MODE_INIT                  0x00500
#define IOAPIC_MODE_EXTINT                0x00700
#define IOAPIC_VEC_MASK                   0x000FF

#ifndef __SASM__

#define IOAPIC_IOREGSEL   ((char*)ioapic_base + 0x00)
#define IOAPIC_IOWIN      ((char*)ioapic_base + 0x10)

#define IOAPIC_REGSEL(reg)        do { *((volatile unsigned int *)(IOAPIC_IOREGSEL)) = reg; } while(0)
#define IOAPIC_READWIN            *((volatile unsigned int *)(IOAPIC_IOWIN)) 
#define IOAPIC_WRITEWIN(reg)      do { *((volatile unsigned int *)(IOAPIC_IOWIN)) = reg; } while(0)


#define IOAPIC_SET_DESTINATION(reg,id)    do { reg = (((reg) & ~IOAPIC_DEST_MASK) | ((unsigned long long)(id)) << 56 ); } while(0)
#define IOAPIC_SET_VECTOR(reg,vec)        do { reg = (((reg) & ~IOAPIC_VEC_MASK ) | (vec)        ); } while(0)
#define IOAPIC_SET_MODE(reg,mode)         do { reg = (((reg) & ~IOAPIC_MODE_MASK) | (mode)       ); } while(0)


void ioapic_write_redtlb(address_t ioapic_base, int i, unsigned long long value);
unsigned long long ioapic_read_redtlb(address_t ioapic_base, int i);

void ioapic_enable_IRQ(int irq);
void ioapic_mask_IRQ(int irq);

#endif

#endif

