full system SPARC emulation (Blue Swirl)
[qemu] / hw / openpic.c
index bd06618..d193cfe 100644 (file)
@@ -34,7 +34,7 @@
  */
 #include "vl.h"
 
-#define DEBUG_OPENPIC
+//#define DEBUG_OPENPIC
 
 #ifdef DEBUG_OPENPIC
 #define DPRINTF(fmt, args...) do { printf(fmt , ##args); } while (0)
@@ -65,7 +65,7 @@
 
 #define MAX_CPU     2
 #define MAX_IRQ    64
-#define EXT_IRQ    16
+#define EXT_IRQ    48
 #define MAX_DBL     0
 #define MAX_MBX     0
 #define MAX_TMR     4
@@ -139,7 +139,7 @@ typedef struct IRQ_src_t {
     uint32_t ide;   /* IRQ destination register */
     int type;
     int last_cpu;
-    int waited_acks;
+    int pending;    /* TRUE if IRQ is pending */
 } IRQ_src_t;
 
 enum IPVP_bits {
@@ -150,7 +150,7 @@ enum IPVP_bits {
     IPVP_SENSE    = 22,
 };
 #define IPVP_PRIORITY_MASK     (0x1F << 16)
-#define IPVP_PRIORITY(_ipvpr_) (((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16)
+#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16))
 #define IPVP_VECTOR_MASK       ((1 << VECTOR_BITS) - 1)
 #define IPVP_VECTOR(_ipvpr_)   ((_ipvpr_) & IPVP_VECTOR_MASK)
 
@@ -162,8 +162,9 @@ typedef struct IRQ_dst_t {
     CPUState *env; /* Needed if we did SMP */
 } IRQ_dst_t;
 
-typedef struct openpic_t {
+struct openpic_t {
     PCIDevice pci_dev;
+    int mem_index;
     /* Global registers */
     uint32_t frep; /* Feature reporting register */
     uint32_t glbc; /* Global configuration register  */
@@ -194,7 +195,7 @@ typedef struct openpic_t {
        uint32_t mbr;    /* Mailbox register */
     } mailboxes[MAX_MAILBOXES];
 #endif
-} openpic_t;
+};
 
 static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
 {
@@ -220,6 +221,8 @@ static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
     priority = -1;
     for (i = 0; i < MAX_IRQ; i++) {
        if (IRQ_testbit(q, i)) {
+            DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n", 
+                    i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
            if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
                next = i;
                priority = IPVP_PRIORITY(opp->src[i].ipvp);
@@ -233,10 +236,7 @@ static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
 static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q)
 {
     if (q->next == -1) {
-       if (q->queue == 0) {
-           /* No more IRQ */
-           return -1;
-       }
+        /* XXX: optimize */
        IRQ_check(opp, q);
     }
 
@@ -269,13 +269,19 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
     }
 }
 
-void openpic_set_IRQ (openpic_t *opp, int n_IRQ, int level)
+/* update pic state because registers for n_IRQ have changed value */
+static void openpic_update_irq(openpic_t *opp, int n_IRQ)
 {
     IRQ_src_t *src;
     int i;
 
     src = &opp->src[n_IRQ];
-    if (!test_bit(&src->ipvp, IPVP_MASK)) {
+
+    if (!src->pending) {
+        /* no irq pending */
+        return;
+    }
+    if (test_bit(&src->ipvp, IPVP_MASK)) {
        /* Interrupt source is disabled */
        return;
     }
@@ -283,43 +289,57 @@ void openpic_set_IRQ (openpic_t *opp, int n_IRQ, int level)
        /* Priority set to zero */
        return;
     }
+    if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
+        /* IRQ already active */
+        return;
+    }
     if (src->ide == 0x00000000) {
        /* No target */
        return;
     }
-    if (level == 0) {
-       if (test_bit(&src->ipvp, IPVP_ACTIVITY) &&
-           test_bit(&src->ipvp, IPVP_SENSE)) {
-           /* Inactivate a active level-sensitive IRQ */
-           reset_bit(&src->ipvp, IPVP_ACTIVITY);
-       }
+
+    if (!test_bit(&src->ipvp, IPVP_MODE) ||
+        src->ide == (1 << src->last_cpu)) {
+        /* Directed delivery mode */
+        for (i = 0; i < opp->nb_cpus; i++) {
+            if (test_bit(&src->ide, i))
+                IRQ_local_pipe(opp, i, n_IRQ);
+        }
     } else {
-       if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
-           /* Interrupt already pending */
-           return;
-       }
-       if (!test_bit(&src->ipvp, IPVP_MODE) ||
-           src->ide == (1 << src->last_cpu)) {
-           /* Directed delivery mode */
-           for (i = 0; i < opp->nb_cpus; i++) {
-               if (test_bit(&src->ide, i))
-                   IRQ_local_pipe(opp, i, n_IRQ);
-           }
-       } else {
-           /* Distributed delivery mode */
-           for (i = src->last_cpu; i < src->last_cpu; i++) {
-               if (i == MAX_IRQ)
-                   i = 0;
-               if (test_bit(&src->ide, i)) {
-                   IRQ_local_pipe(opp, i, n_IRQ);
-                   src->last_cpu = i;
-                   break;
-               }
-           }
-       }
+        /* Distributed delivery mode */
+        /* XXX: incorrect code */
+        for (i = src->last_cpu; i < src->last_cpu; i++) {
+            if (i == MAX_IRQ)
+                i = 0;
+            if (test_bit(&src->ide, i)) {
+                IRQ_local_pipe(opp, i, n_IRQ);
+                src->last_cpu = i;
+                break;
+            }
+        }
     }
 }
 
+void openpic_set_irq(openpic_t *opp, int n_IRQ, int level)
+{
+    IRQ_src_t *src;
+
+    src = &opp->src[n_IRQ];
+    DPRINTF("openpic: set irq %d = %d ipvp=%08x\n", 
+            n_IRQ, level, src->ipvp);
+    if (test_bit(&src->ipvp, IPVP_SENSE)) {
+        /* level-sensitive irq */
+        src->pending = level;
+        if (!level)
+            reset_bit(&src->ipvp, IPVP_ACTIVITY);
+    } else {
+        /* edge-sensitive irq */
+        if (level)
+            src->pending = 1;
+    }
+    openpic_update_irq(opp, n_IRQ);
+}
+
 static void openpic_reset (openpic_t *opp)
 {
     int i;
@@ -389,18 +409,15 @@ static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
 
     switch (reg) {
     case IRQ_IPVP:
-       tmp = opp->src[n_IRQ].ipvp & 0x40000000;
-       if (tmp == 0) {
-           tmp |= val & 0x80000000;
-           if ((opp->src[n_IRQ].type & IRQ_EXTERNAL) != 0)
-               tmp |= val & 0x40C00000;
-           else if ((opp->src[n_IRQ].type & IRQ_TIMER) != 0)
-               tmp |= val & 0x00F00000;
-       } else {
-           tmp |= val & 0x80000000;
-       }
-       opp->src[n_IRQ].ipvp = tmp | (val & 0x000F00FF);
-        DPRINTF("Set IPVP %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ipvp);
+        /* NOTE: not fully accurate for special IRQs, but simple and
+           sufficient */
+        /* ACTIVITY bit is read-only */
+       opp->src[n_IRQ].ipvp = 
+            (opp->src[n_IRQ].ipvp & 0x40000000) |
+            (val & 0x800F00FF);
+        openpic_update_irq(opp, n_IRQ);
+        DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", 
+                n_IRQ, val, opp->src[n_IRQ].ipvp);
        break;
     case IRQ_IDE:
        tmp = val & 0xC0000000;
@@ -736,8 +753,8 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val)
     case 0x70:
         idx = (addr - 0x40) >> 4;
         write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE, val);
-        openpic_set_IRQ(opp, IRQ_IPI0 + idx, 1);
-        openpic_set_IRQ(opp, IRQ_IPI0 + idx, 0);
+        openpic_set_irq(opp, IRQ_IPI0 + idx, 1);
+        openpic_set_irq(opp, IRQ_IPI0 + idx, 0);
         break;
 #endif
     case 0x80: /* PCTP */
@@ -818,8 +835,11 @@ static uint32_t openpic_cpu_read (void *opaque, uint32_t addr)
            }
            IRQ_resetbit(&dst->raised, n_IRQ);
            dst->raised.next = -1;
-           if (!test_bit(&src->ipvp, IPVP_SENSE))
+           if (!test_bit(&src->ipvp, IPVP_SENSE)) {
+                /* edge-sensitive IRQ */
                reset_bit(&src->ipvp, IPVP_ACTIVITY);
+                src->pending = 0;
+            }
        }
        break;
     case 0xB0: /* PEOI */
@@ -862,7 +882,7 @@ static void openpic_writel (void *opaque,
     openpic_t *opp = opaque;
 
     addr &= 0x3FFFF;
-    DPRINTF("%s: offset %08lx val: %08x\n", __func__, addr, val);
+    DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
     if (addr < 0x1100) {
         /* Global registers */
         openpic_gbl_write(opp, addr, val);
@@ -884,7 +904,7 @@ static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr)
     uint32_t retval;
 
     addr &= 0x3FFFF;
-    DPRINTF("%s: offset %08lx\n", __func__, addr);
+    DPRINTF("%s: offset %08x\n", __func__, (int)addr);
     if (addr < 0x1100) {
         /* Global registers */
         retval = openpic_gbl_read(opp, addr);
@@ -918,7 +938,6 @@ static void openpic_map(PCIDevice *pci_dev, int region_num,
                         uint32_t addr, uint32_t size, int type)
 {
     openpic_t *opp;
-    int opp_io_memory;
 
     DPRINTF("Map OpenPIC\n");
     opp = (openpic_t *)pci_dev;
@@ -934,9 +953,7 @@ static void openpic_map(PCIDevice *pci_dev, int region_num,
     /* Per CPU registers */
     DPRINTF("Register OPENPIC dst   %08x => %08x\n",
             addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU);
-    opp_io_memory = cpu_register_io_memory(0, openpic_read,
-                                           openpic_write, opp);
-    cpu_register_physical_memory(addr, 0x40000, opp_io_memory);
+    cpu_register_physical_memory(addr, 0x40000, opp->mem_index);
 #if 0 // Don't implement ISU for now
     opp_io_memory = cpu_register_io_memory(0, openpic_src_read,
                                            openpic_src_write);
@@ -945,7 +962,7 @@ static void openpic_map(PCIDevice *pci_dev, int region_num,
 #endif
 }
 
-openpic_t *openpic_init (uint32_t isu_base, uint32_t idu_base, int nb_cpus)
+openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus)
 {
     openpic_t *opp;
     uint8_t *pci_conf;
@@ -954,25 +971,32 @@ openpic_t *openpic_init (uint32_t isu_base, uint32_t idu_base, int nb_cpus)
     /* XXX: for now, only one CPU is supported */
     if (nb_cpus != 1)
         return NULL;
-    opp = (openpic_t *)pci_register_device("OpenPIC", sizeof(openpic_t),
-                                           0, -1, NULL, NULL);
-    if (opp == NULL)
-        return NULL;
-    pci_conf = opp->pci_dev.config;
-    pci_conf[0x00] = 0x14; // IBM MPIC2
-    pci_conf[0x01] = 0x10;
-    pci_conf[0x02] = 0xFF;
-    pci_conf[0x03] = 0xFF;
-    pci_conf[0x0a] = 0x80; // PIC
-    pci_conf[0x0b] = 0x08;
-    pci_conf[0x0e] = 0x00; // header_type
-    pci_conf[0x3d] = 0x00; // no interrupt pin
-
-    /* Register I/O spaces */
-    pci_register_io_region((PCIDevice *)opp, 0, 0x40000,
-                           PCI_ADDRESS_SPACE_MEM, &openpic_map);
-
-    isu_base &= 0xFFFC0000;
+    if (bus) {
+        opp = (openpic_t *)pci_register_device(bus, "OpenPIC", sizeof(openpic_t),
+                                               -1, NULL, NULL);
+        if (opp == NULL)
+            return NULL;
+        pci_conf = opp->pci_dev.config;
+        pci_conf[0x00] = 0x14; // IBM MPIC2
+        pci_conf[0x01] = 0x10;
+        pci_conf[0x02] = 0xFF;
+        pci_conf[0x03] = 0xFF;
+        pci_conf[0x0a] = 0x80; // PIC
+        pci_conf[0x0b] = 0x08;
+        pci_conf[0x0e] = 0x00; // header_type
+        pci_conf[0x3d] = 0x00; // no interrupt pin
+        
+        /* Register I/O spaces */
+        pci_register_io_region((PCIDevice *)opp, 0, 0x40000,
+                               PCI_ADDRESS_SPACE_MEM, &openpic_map);
+    } else {
+        opp = qemu_mallocz(sizeof(openpic_t));
+    }
+
+    opp->mem_index = cpu_register_io_memory(0, openpic_read,
+                                            openpic_write, opp);
+    
+    //    isu_base &= 0xFFFC0000;
     opp->nb_cpus = nb_cpus;
     /* Set IRQ types */
     for (i = 0; i < EXT_IRQ; i++) {
@@ -993,6 +1017,7 @@ openpic_t *openpic_init (uint32_t isu_base, uint32_t idu_base, int nb_cpus)
         opp->src[i].type = IRQ_INTERNAL;
     }
     openpic_reset(opp);
-
+    if (pmem_index)
+        *pmem_index = opp->mem_index;
     return opp;
 }