X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fopenpic.c;h=54830c31bfe3fec321d93a74de825c073c672cf8;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=bd06618588b58874b25939439d22c22aa81a3758;hpb=dbda808a4ad5744fded19bf6a750e7aecbf993d8;p=qemu diff --git a/hw/openpic.c b/hw/openpic.c index bd06618..54830c3 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -1,8 +1,8 @@ /* * OpenPIC emulation - * + * * Copyright (c) 2004 Jocelyn Mayer - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -30,11 +30,11 @@ * - Motorola Harrier programmer manuel * * Serial interrupts, as implemented in Raven chipset are not supported yet. - * + * */ #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) @@ -159,16 +159,18 @@ typedef struct IRQ_dst_t { uint32_t pcsr; /* CPU sensitivity register */ IRQ_queue_t raised; IRQ_queue_t servicing; - CPUState *env; /* Needed if we did SMP */ + qemu_irq *irqs; } IRQ_dst_t; typedef struct openpic_t { PCIDevice pci_dev; + int mem_index; /* Global registers */ uint32_t frep; /* Feature reporting register */ uint32_t glbc; /* Global configuration register */ uint32_t micr; /* MPIC interrupt configuration register */ uint32_t veni; /* Vendor identification register */ + uint32_t pint; /* Processor initialization register */ uint32_t spve; /* Spurious vector register */ uint32_t tifr; /* Timer frequency reporting register */ /* Source registers */ @@ -194,6 +196,8 @@ typedef struct openpic_t { uint32_t mbr; /* Mailbox register */ } mailboxes[MAX_MAILBOXES]; #endif + /* IRQ out is used when in bypass mode (not implemented) */ + qemu_irq irq_out; } openpic_t; static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ) @@ -220,6 +224,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 +239,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); } @@ -254,70 +257,112 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ) priority = IPVP_PRIORITY(src->ipvp); if (priority <= dst->pctp) { /* Too low priority */ + DPRINTF("%s: IRQ %d has too low priority on CPU %d\n", + __func__, n_IRQ, n_CPU); return; } if (IRQ_testbit(&dst->raised, n_IRQ)) { /* Interrupt miss */ + DPRINTF("%s: IRQ %d was missed on CPU %d\n", + __func__, n_IRQ, n_CPU); return; } set_bit(&src->ipvp, IPVP_ACTIVITY); IRQ_setbit(&dst->raised, n_IRQ); - if (priority > dst->raised.priority) { - IRQ_get_next(opp, &dst->raised); - DPRINTF("Raise CPU IRQ\n"); - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + if (priority < dst->raised.priority) { + /* An higher priority IRQ is already raised */ + DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n", + __func__, n_IRQ, dst->raised.next, n_CPU); + return; + } + IRQ_get_next(opp, &dst->raised); + if (IRQ_get_next(opp, &dst->servicing) != -1 && + priority < dst->servicing.priority) { + DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n", + __func__, n_IRQ, dst->servicing.next, n_CPU); + /* Already servicing a higher priority IRQ */ + return; } + DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ); + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); } -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 */ + DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ); + return; + } + if (test_bit(&src->ipvp, IPVP_MASK)) { /* Interrupt source is disabled */ + DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); return; } if (IPVP_PRIORITY(src->ipvp) == 0) { /* Priority set to zero */ + DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ); return; } + if (test_bit(&src->ipvp, IPVP_ACTIVITY)) { + /* IRQ already active */ + DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ); + return; + } if (src->ide == 0x00000000) { /* No target */ + DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); 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 (src->ide == (1 << src->last_cpu)) { + /* Only one CPU is allowed to receive this IRQ */ + IRQ_local_pipe(opp, src->last_cpu, n_IRQ); + } else if (!test_bit(&src->ipvp, IPVP_MODE)) { + /* 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 */ + for (i = src->last_cpu + 1; i != src->last_cpu; i++) { + if (i == opp->nb_cpus) + i = 0; + if (test_bit(&src->ide, i)) { + IRQ_local_pipe(opp, i, n_IRQ); + src->last_cpu = i; + break; + } + } + } +} + +static void openpic_set_irq(void *opaque, int n_IRQ, int level) +{ + openpic_t *opp = opaque; + 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) @@ -325,9 +370,10 @@ static void openpic_reset (openpic_t *opp) int i; opp->glbc = 0x80000000; - /* Initialise controler registers */ + /* Initialise controller registers */ opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID; opp->veni = VENI; + opp->pint = 0x00000000; opp->spve = 0x000000FF; opp->tifr = 0x003F7A00; /* ? */ @@ -338,7 +384,7 @@ static void openpic_reset (openpic_t *opp) opp->src[i].ide = 0x00000000; } /* Initialise IRQ destinations */ - for (i = 0; i < opp->nb_cpus; i++) { + for (i = 0; i < MAX_CPU; i++) { opp->dst[i].pctp = 0x0000000F; opp->dst[i].pcsr = 0x00000000; memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); @@ -389,18 +435,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; @@ -432,7 +475,7 @@ static uint32_t read_doorbell_register (openpic_t *opp, return retval; } - + static void write_doorbell_register (penpic_t *opp, int n_dbl, uint32_t offset, uint32_t value) { @@ -492,6 +535,8 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx, static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) { openpic_t *opp = opaque; + IRQ_dst_t *dst; + int idx; DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) @@ -511,11 +556,18 @@ static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) case 0x80: /* VENI */ break; case 0x90: /* PINT */ - /* XXX: Should be able to reset any CPU */ - if (val & 1) { - DPRINTF("Reset CPU IRQ\n"); - // cpu_interrupt(cpu_single_env, CPU_INTERRUPT_RESET); + for (idx = 0; idx < opp->nb_cpus; idx++) { + if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { + DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); + dst = &opp->dst[idx]; + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); + } else if (!(val & (1 << idx)) && (opp->pint & (1 << idx))) { + DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); + dst = &opp->dst[idx]; + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); + } } + opp->pint = val; break; #if MAX_IPI > 0 case 0xA0: /* IPI_IPVP */ @@ -612,7 +664,7 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) break; case 0x10: /* TIBC */ if ((opp->timers[idx].ticc & 0x80000000) != 0 && - (val & 0x800000000) == 0 && + (val & 0x80000000) == 0 && (opp->timers[idx].tibc & 0x80000000) != 0) opp->timers[idx].ticc &= ~0x80000000; opp->timers[idx].tibc = val; @@ -716,7 +768,7 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) openpic_t *opp = opaque; IRQ_src_t *src; IRQ_dst_t *dst; - int idx, n_IRQ; + int idx, s_IRQ, n_IRQ; DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) @@ -736,8 +788,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 */ @@ -751,21 +803,21 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) break; case 0xB0: /* PEOI */ DPRINTF("PEOI\n"); - n_IRQ = IRQ_get_next(opp, &dst->servicing); - IRQ_resetbit(&dst->servicing, n_IRQ); + s_IRQ = IRQ_get_next(opp, &dst->servicing); + IRQ_resetbit(&dst->servicing, s_IRQ); dst->servicing.next = -1; - src = &opp->src[n_IRQ]; /* Set up next servicing IRQ */ - IRQ_get_next(opp, &dst->servicing); - /* Check queued interrupts. */ - n_IRQ = IRQ_get_next(opp, &dst->raised); - if (n_IRQ != -1) { - src = &opp->src[n_IRQ]; - if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) { - DPRINTF("Raise CPU IRQ\n"); - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); - } - } + s_IRQ = IRQ_get_next(opp, &dst->servicing); + /* Check queued interrupts. */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + src = &opp->src[n_IRQ]; + if (n_IRQ != -1 && + (s_IRQ == -1 || + IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) { + DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", + idx, n_IRQ); + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); + } break; default: break; @@ -779,7 +831,7 @@ static uint32_t openpic_cpu_read (void *opaque, uint32_t addr) IRQ_dst_t *dst; uint32_t retval; int idx, n_IRQ; - + DPRINTF("%s: addr %08x\n", __func__, addr); retval = 0xFFFFFFFF; if (addr & 0xF) @@ -796,11 +848,13 @@ static uint32_t openpic_cpu_read (void *opaque, uint32_t addr) retval = idx; break; case 0xA0: /* PIAC */ + DPRINTF("Lower OpenPIC INT output\n"); + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); n_IRQ = IRQ_get_next(opp, &dst->raised); DPRINTF("PIAC: irq=%d\n", n_IRQ); if (n_IRQ == -1) { /* No more interrupt pending */ - retval = opp->spve; + retval = IPVP_VECTOR(opp->spve); } else { src = &opp->src[n_IRQ]; if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || @@ -818,8 +872,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 +919,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 +941,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); @@ -914,11 +971,10 @@ static CPUReadMemoryFunc *openpic_read[] = { &openpic_readl, }; -static void openpic_map(PCIDevice *pci_dev, int region_num, +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 +990,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,34 +999,41 @@ 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) +qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, + qemu_irq **irqs, qemu_irq irq_out) { openpic_t *opp; uint8_t *pci_conf; int i, m; - + /* 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++) { @@ -992,7 +1053,12 @@ openpic_t *openpic_init (uint32_t isu_base, uint32_t idu_base, int nb_cpus) for (; i < MAX_IRQ; i++) { opp->src[i].type = IRQ_INTERNAL; } + for (i = 0; i < nb_cpus; i++) + opp->dst[i].irqs = irqs[i]; + opp->irq_out = irq_out; openpic_reset(opp); + if (pmem_index) + *pmem_index = opp->mem_index; - return opp; + return qemu_allocate_irqs(openpic_set_irq, opp, MAX_IRQ); }