diff mbox

[3/3] sparc64: implement PCI and ISA irqs

Message ID CAAu8pHvu1kp02kz-dvBNFYdV5Fnatj8b5t+s6xTYeYCTxxKVXg@mail.gmail.com
State New
Headers show

Commit Message

Blue Swirl March 11, 2012, 11:02 a.m. UTC
Generate correct trap for external interrupts. Map PCI and ISA IRQs to
RIC/UltraSPARC-IIi interrupt vectors.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
---
 hw/apb_pci.c               |   48 +++++++++++++++++++++++++++----------
 hw/apb_pci.h               |    3 +-
 hw/sun4u.c                 |   57 ++++++++++++++++++++++++++++++--------------
 target-sparc/cpu.h         |    3 ++
 target-sparc/ldst_helper.c |   20 ++++++++++----
 5 files changed, 93 insertions(+), 38 deletions(-)

     case 0x4b: /* E-cache error enable */
@@ -1540,11 +1553,6 @@ uint64_t helper_ld_asi(target_ulong addr, int
asi, int size, int sign)
     case 0x7e: /* E-cache tag */
         break;
     case 0x5b: /* D-MMU data pointer */
-    case 0x48: /* Interrupt dispatch, RO */
-    case 0x49: /* Interrupt data receive */
-    case 0x7f: /* Incoming interrupt vector, RO */
-        /* XXX */
-        break;
     case 0x54: /* I-MMU data in, WO */
     case 0x57: /* I-MMU demap, WO */
     case 0x5c: /* D-MMU data in, WO */
@@ -1954,7 +1962,7 @@ void helper_st_asi(target_ulong addr,
target_ulong val, int asi, int size)
         demap_tlb(env->dtlb, addr, "dmmu", env);
         return;
     case 0x49: /* Interrupt data receive */
-        /* XXX */
+        env->ivec_status = val & 0x20;
         return;
     case 0x46: /* D-cache data */
     case 0x47: /* D-cache tag access */

Comments

Artyom Tarasenko March 27, 2012, 6:19 p.m. UTC | #1
On Sun, Mar 11, 2012 at 12:02 PM, Blue Swirl <blauwirbel@gmail.com> wrote:
> Generate correct trap for external interrupts. Map PCI and ISA IRQs to
> RIC/UltraSPARC-IIi interrupt vectors.
>
> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
> ---
>  hw/apb_pci.c               |   48 +++++++++++++++++++++++++++----------
>  hw/apb_pci.h               |    3 +-
>  hw/sun4u.c                 |   57 ++++++++++++++++++++++++++++++--------------
>  target-sparc/cpu.h         |    3 ++
>  target-sparc/ldst_helper.c |   20 ++++++++++----
>  5 files changed, 93 insertions(+), 38 deletions(-)
>
> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
> index b10f31e..7e28808 100644
> --- a/hw/apb_pci.c
> +++ b/hw/apb_pci.c
> @@ -66,6 +66,8 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
>  #define RESET_WCMASK 0x98000000
>  #define RESET_WMASK  0x60000000
>
> +#define MAX_IVEC 0x30

Shouldn't that be 0x40 (0x20 for OBIO and 0x20 for PCI)? I mean in
theory, obviously not all of them are used.

> +
>  typedef struct APBState {
>     SysBusDevice busdev;
>     PCIBus      *bus;
> @@ -77,7 +79,8 @@ typedef struct APBState {
>     uint32_t pci_control[16];
>     uint32_t pci_irq_map[8];
>     uint32_t obio_irq_map[32];
> -    qemu_irq pci_irqs[32];
> +    qemu_irq *pbm_irqs;
> +    qemu_irq *ivec_irqs;
>     uint32_t reset_control;
>     unsigned int nr_resets;
>  } APBState;
> @@ -87,7 +90,7 @@ static void apb_config_writel (void *opaque,
> target_phys_addr_t addr,
>  {
>     APBState *s = opaque;
>
> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
> __func__, addr, val);
>
>     switch (addr & 0xffff) {
>     case 0x30 ... 0x4f: /* DMA error registers */
> @@ -104,6 +107,12 @@ static void apb_config_writel (void *opaque,
> target_phys_addr_t addr,
>             s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
>         }
>         break;
> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
> +        if (addr & 4) {
> +            s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK;
> +            s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK;
> +        }
> +        break;
>     case 0x2000 ... 0x202f: /* PCI control */
>         s->pci_control[(addr & 0x3f) >> 2] = val;
>         break;
> @@ -154,6 +163,13 @@ static uint64_t apb_config_readl (void *opaque,
>             val = 0;
>         }
>         break;
> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
> +        if (addr & 4) {
> +            val = s->obio_irq_map[(addr & 0xff) >> 3];
> +        } else {
> +            val = 0;
> +        }
> +        break;
>     case 0x2000 ... 0x202f: /* PCI control */
>         val = s->pci_control[(addr & 0x3f) >> 2];
>         break;
> @@ -190,7 +206,7 @@ static void apb_pci_config_write(void *opaque,
> target_phys_addr_t addr,
>     APBState *s = opaque;
>
>     val = qemu_bswap_len(val, size);
> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
> __func__, addr, val);
>     pci_data_write(s->bus, addr, val, size);
>  }
>
> @@ -280,10 +296,19 @@ static void pci_apb_set_irq(void *opaque, int
> irq_num, int level)
>     if (irq_num < 32) {
>         if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
>             APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
> -            qemu_set_irq(s->pci_irqs[irq_num], level);
> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
> +        } else {
> +            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
> +        }
> +    } else {
> +        /* OBIO IRQ map onto the next 16 INO.  */
> +        if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) {
> +            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
>         } else {
>             APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
> -            qemu_irq_lower(s->pci_irqs[irq_num]);
> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
>         }
>     }
>  }
> @@ -316,12 +341,12 @@ static int apb_pci_bridge_initfn(PCIDevice *dev)
>
>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>                      target_phys_addr_t mem_base,
> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
> +                     qemu_irq **pbm_irqs)
>  {
>     DeviceState *dev;
>     SysBusDevice *s;
>     APBState *d;
> -    unsigned int i;
>     PCIDevice *pci_dev;
>     PCIBridge *br;
>
> @@ -346,9 +371,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>                               get_system_io(),
>                               0, 32);
>
> -    for (i = 0; i < 32; i++) {
> -        sysbus_connect_irq(s, i, pic[i]);
> -    }
> +    *pbm_irqs = d->pbm_irqs;
> +    d->ivec_irqs = ivec_irqs;
>
>     pci_create_simple(d->bus, 0, "pbm-pci");
>
> @@ -402,9 +426,7 @@ static int pci_pbm_init_device(SysBusDevice *dev)
>     for (i = 0; i < 8; i++) {
>         s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
>     }
> -    for (i = 0; i < 32; i++) {
> -        sysbus_init_irq(dev, &s->pci_irqs[i]);
> -    }
> +    s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC);
>
>     /* apb_config */
>     memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config",
> diff --git a/hw/apb_pci.h b/hw/apb_pci.h
> index 8869f9d..55f7c4c 100644
> --- a/hw/apb_pci.h
> +++ b/hw/apb_pci.h
> @@ -5,5 +5,6 @@
>
>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>                      target_phys_addr_t mem_base,
> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
> +                     qemu_irq **pbm_irqs);
>  #endif
> diff --git a/hw/sun4u.c b/hw/sun4u.c
> index 423108f..e0adb9e 100644
> --- a/hw/sun4u.c
> +++ b/hw/sun4u.c
> @@ -81,7 +81,7 @@
>  #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
>  #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
>
> -#define MAX_PILS 16
> +#define IVEC_MAX             0x30
>
>  #define TICK_MAX             0x7fffffffffffffffULL
>
> @@ -304,18 +304,24 @@ static void cpu_kick_irq(CPUState *env)
>     qemu_cpu_kick(env);
>  }
>
> -static void cpu_set_irq(void *opaque, int irq, int level)
> +static void cpu_set_ivec_irq(void *opaque, int irq, int level)
>  {
>     CPUState *env = opaque;
>
>     if (level) {
> -        CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
> -        env->pil_in |= 1 << irq;
> -        cpu_kick_irq(env);
> -    } else {
> -        CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
> -        env->pil_in &= ~(1 << irq);
> -        cpu_check_irqs(env);
> +        CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
> +        env->interrupt_index = TT_IVEC;
> +        env->pil_in |= 1 << 5;

Err. Spurious irq 5?

> +        env->ivec_status |= 0x20;
> +        env->ivec_data[0] = (0x1f << 6) | irq;
> +        env->ivec_data[1] = 0;
> +        env->ivec_data[2] = 0;
> +        cpu_interrupt(env, CPU_INTERRUPT_HARD);

Shouldn't there be a cpu_interrupts_enabled(env) check before?

> +      } else {
> +        CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
> +        env->pil_in &= ~(1 << 5);
> +        env->ivec_status &= ~0x20;
> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>     }
>  }
>
> @@ -521,13 +527,29 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
>     }
>  }
>
> -static void dummy_isa_irq_handler(void *opaque, int n, int level)
> +static void isa_irq_handler(void *opaque, int n, int level)
>  {
> +    static const int isa_irq_to_ivec[16] = {
> +        [1] = 0x29, /* keyboard */
> +        [4] = 0x2b, /* serial */
> +        [6] = 0x27, /* floppy */
> +        [7] = 0x22, /* parallel */
> +        [12] = 0x2a, /* mouse */
> +    };
> +    qemu_irq *irqs = opaque;
> +    int ivec;
> +
> +    assert(n < 16);
> +    ivec = isa_irq_to_ivec[n];
> +    EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec);
> +    if (ivec) {
> +        qemu_set_irq(irqs[ivec], level);
> +    }
>  }
>
>  /* EBUS (Eight bit bus) bridge */
>  static ISABus *
> -pci_ebus_init(PCIBus *bus, int devfn)
> +pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
>  {
>     qemu_irq *isa_irq;
>     PCIDevice *pci_dev;
> @@ -536,7 +558,7 @@ pci_ebus_init(PCIBus *bus, int devfn)
>     pci_dev = pci_create_simple(bus, devfn, "ebus");
>     isa_bus = DO_UPCAST(ISABus, qbus,
>                         qdev_get_child_bus(&pci_dev->qdev, "isa.0"));
> -    isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
> +    isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16);
>     isa_bus_irqs(isa_bus, isa_irq);
>     return isa_bus;
>  }
> @@ -761,7 +783,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
>     long initrd_size, kernel_size;
>     PCIBus *pci_bus, *pci_bus2, *pci_bus3;
>     ISABus *isa_bus;
> -    qemu_irq *irq;
> +    qemu_irq *ivec_irqs, *pbm_irqs;
>     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
>     DriveInfo *fd[MAX_FD];
>     void *fw_cfg;
> @@ -774,14 +796,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
>
>     prom_init(hwdef->prom_addr, bios_name);
>
> -
> -    irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
> -    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2,
> -                           &pci_bus3);
> +    ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX);
> +    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs,
> &pci_bus2,
> +                           &pci_bus3, &pbm_irqs);
>     pci_vga_init(pci_bus);
>
>     // XXX Should be pci_bus3
> -    isa_bus = pci_ebus_init(pci_bus, -1);
> +    isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs);
>
>     i = 0;
>     if (hwdef->console_serial_base) {
> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
> index b81779b..8994000 100644
> --- a/target-sparc/cpu.h
> +++ b/target-sparc/cpu.h
> @@ -491,6 +491,9 @@ typedef struct CPUSPARCState {
>     /* UA 2005 hyperprivileged registers */
>     uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
>     CPUTimer *hstick; // UA 2005
> +    /* Interrupt vector registers */
> +    uint64_t ivec_status;
> +    uint64_t ivec_data[3];
>     uint32_t softint;
>  #define SOFTINT_TIMER   1
>  #define SOFTINT_STIMER  (1 << 16)
> diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
> index b59707e..4c34346 100644
> --- a/target-sparc/ldst_helper.c
> +++ b/target-sparc/ldst_helper.c
> @@ -1526,6 +1526,19 @@ uint64_t helper_ld_asi(target_ulong addr, int
> asi, int size, int sign)
>             ret = env->dtlb[reg].tag;
>             break;
>         }
> +    case 0x48: /* Interrupt dispatch, RO */
> +        break;
> +    case 0x49: /* Interrupt data receive */
> +        ret = env->ivec_status;
> +        break;
> +    case 0x7f: /* Incoming interrupt vector, RO */
> +        {
> +            int reg = (addr >> 4) & 0x3;
> +            if (reg < 3) {
> +                ret = env->ivec_data[reg];
> +            }
> +            break;
> +        }
>     case 0x46: /* D-cache data */
>     case 0x47: /* D-cache tag access */
>     case 0x4b: /* E-cache error enable */
> @@ -1540,11 +1553,6 @@ uint64_t helper_ld_asi(target_ulong addr, int
> asi, int size, int sign)
>     case 0x7e: /* E-cache tag */
>         break;
>     case 0x5b: /* D-MMU data pointer */
> -    case 0x48: /* Interrupt dispatch, RO */
> -    case 0x49: /* Interrupt data receive */
> -    case 0x7f: /* Incoming interrupt vector, RO */
> -        /* XXX */
> -        break;
>     case 0x54: /* I-MMU data in, WO */
>     case 0x57: /* I-MMU demap, WO */
>     case 0x5c: /* D-MMU data in, WO */
> @@ -1954,7 +1962,7 @@ void helper_st_asi(target_ulong addr,
> target_ulong val, int asi, int size)
>         demap_tlb(env->dtlb, addr, "dmmu", env);
>         return;
>     case 0x49: /* Interrupt data receive */
> -        /* XXX */
> +        env->ivec_status = val & 0x20;
>         return;
>     case 0x46: /* D-cache data */
>     case 0x47: /* D-cache tag access */
> --
> 1.7.9
Blue Swirl March 27, 2012, 7:26 p.m. UTC | #2
On Tue, Mar 27, 2012 at 18:19, Artyom Tarasenko <atar4qemu@gmail.com> wrote:
> On Sun, Mar 11, 2012 at 12:02 PM, Blue Swirl <blauwirbel@gmail.com> wrote:
>> Generate correct trap for external interrupts. Map PCI and ISA IRQs to
>> RIC/UltraSPARC-IIi interrupt vectors.
>>
>> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
>> ---
>>  hw/apb_pci.c               |   48 +++++++++++++++++++++++++++----------
>>  hw/apb_pci.h               |    3 +-
>>  hw/sun4u.c                 |   57 ++++++++++++++++++++++++++++++--------------
>>  target-sparc/cpu.h         |    3 ++
>>  target-sparc/ldst_helper.c |   20 ++++++++++----
>>  5 files changed, 93 insertions(+), 38 deletions(-)
>>
>> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
>> index b10f31e..7e28808 100644
>> --- a/hw/apb_pci.c
>> +++ b/hw/apb_pci.c
>> @@ -66,6 +66,8 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
>>  #define RESET_WCMASK 0x98000000
>>  #define RESET_WMASK  0x60000000
>>
>> +#define MAX_IVEC 0x30
>
> Shouldn't that be 0x40 (0x20 for OBIO and 0x20 for PCI)? I mean in
> theory, obviously not all of them are used.

This is the maximum number (0x30 is PCI Bus Error), which actually
means that number of IRQ lines allocated should be MAX_IVEC + 1. 0x40
wouldn't hurt much.

>> +
>>  typedef struct APBState {
>>     SysBusDevice busdev;
>>     PCIBus      *bus;
>> @@ -77,7 +79,8 @@ typedef struct APBState {
>>     uint32_t pci_control[16];
>>     uint32_t pci_irq_map[8];
>>     uint32_t obio_irq_map[32];
>> -    qemu_irq pci_irqs[32];
>> +    qemu_irq *pbm_irqs;
>> +    qemu_irq *ivec_irqs;
>>     uint32_t reset_control;
>>     unsigned int nr_resets;
>>  } APBState;
>> @@ -87,7 +90,7 @@ static void apb_config_writel (void *opaque,
>> target_phys_addr_t addr,
>>  {
>>     APBState *s = opaque;
>>
>> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
>> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
>> __func__, addr, val);
>>
>>     switch (addr & 0xffff) {
>>     case 0x30 ... 0x4f: /* DMA error registers */
>> @@ -104,6 +107,12 @@ static void apb_config_writel (void *opaque,
>> target_phys_addr_t addr,
>>             s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
>>         }
>>         break;
>> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
>> +        if (addr & 4) {
>> +            s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK;
>> +            s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK;
>> +        }
>> +        break;
>>     case 0x2000 ... 0x202f: /* PCI control */
>>         s->pci_control[(addr & 0x3f) >> 2] = val;
>>         break;
>> @@ -154,6 +163,13 @@ static uint64_t apb_config_readl (void *opaque,
>>             val = 0;
>>         }
>>         break;
>> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
>> +        if (addr & 4) {
>> +            val = s->obio_irq_map[(addr & 0xff) >> 3];
>> +        } else {
>> +            val = 0;
>> +        }
>> +        break;
>>     case 0x2000 ... 0x202f: /* PCI control */
>>         val = s->pci_control[(addr & 0x3f) >> 2];
>>         break;
>> @@ -190,7 +206,7 @@ static void apb_pci_config_write(void *opaque,
>> target_phys_addr_t addr,
>>     APBState *s = opaque;
>>
>>     val = qemu_bswap_len(val, size);
>> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
>> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
>> __func__, addr, val);
>>     pci_data_write(s->bus, addr, val, size);
>>  }
>>
>> @@ -280,10 +296,19 @@ static void pci_apb_set_irq(void *opaque, int
>> irq_num, int level)
>>     if (irq_num < 32) {
>>         if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
>>             APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
>> -            qemu_set_irq(s->pci_irqs[irq_num], level);
>> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
>> +        } else {
>> +            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
>> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
>> +        }
>> +    } else {
>> +        /* OBIO IRQ map onto the next 16 INO.  */
>> +        if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) {
>> +            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
>> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
>>         } else {
>>             APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
>> -            qemu_irq_lower(s->pci_irqs[irq_num]);
>> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
>>         }
>>     }
>>  }
>> @@ -316,12 +341,12 @@ static int apb_pci_bridge_initfn(PCIDevice *dev)
>>
>>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>                      target_phys_addr_t mem_base,
>> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
>> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
>> +                     qemu_irq **pbm_irqs)
>>  {
>>     DeviceState *dev;
>>     SysBusDevice *s;
>>     APBState *d;
>> -    unsigned int i;
>>     PCIDevice *pci_dev;
>>     PCIBridge *br;
>>
>> @@ -346,9 +371,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>                               get_system_io(),
>>                               0, 32);
>>
>> -    for (i = 0; i < 32; i++) {
>> -        sysbus_connect_irq(s, i, pic[i]);
>> -    }
>> +    *pbm_irqs = d->pbm_irqs;
>> +    d->ivec_irqs = ivec_irqs;
>>
>>     pci_create_simple(d->bus, 0, "pbm-pci");
>>
>> @@ -402,9 +426,7 @@ static int pci_pbm_init_device(SysBusDevice *dev)
>>     for (i = 0; i < 8; i++) {
>>         s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
>>     }
>> -    for (i = 0; i < 32; i++) {
>> -        sysbus_init_irq(dev, &s->pci_irqs[i]);
>> -    }
>> +    s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC);
>>
>>     /* apb_config */
>>     memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config",
>> diff --git a/hw/apb_pci.h b/hw/apb_pci.h
>> index 8869f9d..55f7c4c 100644
>> --- a/hw/apb_pci.h
>> +++ b/hw/apb_pci.h
>> @@ -5,5 +5,6 @@
>>
>>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>                      target_phys_addr_t mem_base,
>> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
>> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
>> +                     qemu_irq **pbm_irqs);
>>  #endif
>> diff --git a/hw/sun4u.c b/hw/sun4u.c
>> index 423108f..e0adb9e 100644
>> --- a/hw/sun4u.c
>> +++ b/hw/sun4u.c
>> @@ -81,7 +81,7 @@
>>  #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
>>  #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
>>
>> -#define MAX_PILS 16
>> +#define IVEC_MAX             0x30
>>
>>  #define TICK_MAX             0x7fffffffffffffffULL
>>
>> @@ -304,18 +304,24 @@ static void cpu_kick_irq(CPUState *env)
>>     qemu_cpu_kick(env);
>>  }
>>
>> -static void cpu_set_irq(void *opaque, int irq, int level)
>> +static void cpu_set_ivec_irq(void *opaque, int irq, int level)
>>  {
>>     CPUState *env = opaque;
>>
>>     if (level) {
>> -        CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
>> -        env->pil_in |= 1 << irq;
>> -        cpu_kick_irq(env);
>> -    } else {
>> -        CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
>> -        env->pil_in &= ~(1 << irq);
>> -        cpu_check_irqs(env);
>> +        CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
>> +        env->interrupt_index = TT_IVEC;
>> +        env->pil_in |= 1 << 5;
>
> Err. Spurious irq 5?

Maybe, I can't find it in the manual and I can't remember why I did
that either. :-(

>
>> +        env->ivec_status |= 0x20;
>> +        env->ivec_data[0] = (0x1f << 6) | irq;
>> +        env->ivec_data[1] = 0;
>> +        env->ivec_data[2] = 0;
>> +        cpu_interrupt(env, CPU_INTERRUPT_HARD);
>
> Shouldn't there be a cpu_interrupts_enabled(env) check before?

This is checked in the CPU loop, but the code there is not OK for
Sparc64 because of the assumptions with TT_EXTINT. I thought I fixed
that, but it looks like I didn't use that version for some reason.

>> +      } else {
>> +        CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
>> +        env->pil_in &= ~(1 << 5);
>> +        env->ivec_status &= ~0x20;
>> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>>     }
>>  }
>>
>> @@ -521,13 +527,29 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
>>     }
>>  }
>>
>> -static void dummy_isa_irq_handler(void *opaque, int n, int level)
>> +static void isa_irq_handler(void *opaque, int n, int level)
>>  {
>> +    static const int isa_irq_to_ivec[16] = {
>> +        [1] = 0x29, /* keyboard */
>> +        [4] = 0x2b, /* serial */
>> +        [6] = 0x27, /* floppy */
>> +        [7] = 0x22, /* parallel */
>> +        [12] = 0x2a, /* mouse */
>> +    };
>> +    qemu_irq *irqs = opaque;
>> +    int ivec;
>> +
>> +    assert(n < 16);
>> +    ivec = isa_irq_to_ivec[n];
>> +    EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec);
>> +    if (ivec) {
>> +        qemu_set_irq(irqs[ivec], level);
>> +    }
>>  }
>>
>>  /* EBUS (Eight bit bus) bridge */
>>  static ISABus *
>> -pci_ebus_init(PCIBus *bus, int devfn)
>> +pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
>>  {
>>     qemu_irq *isa_irq;
>>     PCIDevice *pci_dev;
>> @@ -536,7 +558,7 @@ pci_ebus_init(PCIBus *bus, int devfn)
>>     pci_dev = pci_create_simple(bus, devfn, "ebus");
>>     isa_bus = DO_UPCAST(ISABus, qbus,
>>                         qdev_get_child_bus(&pci_dev->qdev, "isa.0"));
>> -    isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
>> +    isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16);
>>     isa_bus_irqs(isa_bus, isa_irq);
>>     return isa_bus;
>>  }
>> @@ -761,7 +783,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
>>     long initrd_size, kernel_size;
>>     PCIBus *pci_bus, *pci_bus2, *pci_bus3;
>>     ISABus *isa_bus;
>> -    qemu_irq *irq;
>> +    qemu_irq *ivec_irqs, *pbm_irqs;
>>     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
>>     DriveInfo *fd[MAX_FD];
>>     void *fw_cfg;
>> @@ -774,14 +796,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
>>
>>     prom_init(hwdef->prom_addr, bios_name);
>>
>> -
>> -    irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
>> -    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2,
>> -                           &pci_bus3);
>> +    ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX);
>> +    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs,
>> &pci_bus2,
>> +                           &pci_bus3, &pbm_irqs);
>>     pci_vga_init(pci_bus);
>>
>>     // XXX Should be pci_bus3
>> -    isa_bus = pci_ebus_init(pci_bus, -1);
>> +    isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs);
>>
>>     i = 0;
>>     if (hwdef->console_serial_base) {
>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>> index b81779b..8994000 100644
>> --- a/target-sparc/cpu.h
>> +++ b/target-sparc/cpu.h
>> @@ -491,6 +491,9 @@ typedef struct CPUSPARCState {
>>     /* UA 2005 hyperprivileged registers */
>>     uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
>>     CPUTimer *hstick; // UA 2005
>> +    /* Interrupt vector registers */
>> +    uint64_t ivec_status;
>> +    uint64_t ivec_data[3];
>>     uint32_t softint;
>>  #define SOFTINT_TIMER   1
>>  #define SOFTINT_STIMER  (1 << 16)
>> diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
>> index b59707e..4c34346 100644
>> --- a/target-sparc/ldst_helper.c
>> +++ b/target-sparc/ldst_helper.c
>> @@ -1526,6 +1526,19 @@ uint64_t helper_ld_asi(target_ulong addr, int
>> asi, int size, int sign)
>>             ret = env->dtlb[reg].tag;
>>             break;
>>         }
>> +    case 0x48: /* Interrupt dispatch, RO */
>> +        break;
>> +    case 0x49: /* Interrupt data receive */
>> +        ret = env->ivec_status;
>> +        break;
>> +    case 0x7f: /* Incoming interrupt vector, RO */
>> +        {
>> +            int reg = (addr >> 4) & 0x3;
>> +            if (reg < 3) {
>> +                ret = env->ivec_data[reg];
>> +            }
>> +            break;
>> +        }
>>     case 0x46: /* D-cache data */
>>     case 0x47: /* D-cache tag access */
>>     case 0x4b: /* E-cache error enable */
>> @@ -1540,11 +1553,6 @@ uint64_t helper_ld_asi(target_ulong addr, int
>> asi, int size, int sign)
>>     case 0x7e: /* E-cache tag */
>>         break;
>>     case 0x5b: /* D-MMU data pointer */
>> -    case 0x48: /* Interrupt dispatch, RO */
>> -    case 0x49: /* Interrupt data receive */
>> -    case 0x7f: /* Incoming interrupt vector, RO */
>> -        /* XXX */
>> -        break;
>>     case 0x54: /* I-MMU data in, WO */
>>     case 0x57: /* I-MMU demap, WO */
>>     case 0x5c: /* D-MMU data in, WO */
>> @@ -1954,7 +1962,7 @@ void helper_st_asi(target_ulong addr,
>> target_ulong val, int asi, int size)
>>         demap_tlb(env->dtlb, addr, "dmmu", env);
>>         return;
>>     case 0x49: /* Interrupt data receive */
>> -        /* XXX */
>> +        env->ivec_status = val & 0x20;
>>         return;
>>     case 0x46: /* D-cache data */
>>     case 0x47: /* D-cache tag access */
>> --
>> 1.7.9
>
>
>
> --
> Regards,
> Artyom Tarasenko
>
> solaris/sparc under qemu blog: http://tyom.blogspot.com/search/label/qemu
Artyom Tarasenko March 28, 2012, 5:55 p.m. UTC | #3
On 3/27/12, Blue Swirl <blauwirbel@gmail.com> wrote:
> On Tue, Mar 27, 2012 at 18:19, Artyom Tarasenko <atar4qemu@gmail.com> wrote:
>> On Sun, Mar 11, 2012 at 12:02 PM, Blue Swirl <blauwirbel@gmail.com> wrote:
>>> Generate correct trap for external interrupts. Map PCI and ISA IRQs to
>>> RIC/UltraSPARC-IIi interrupt vectors.
>>>
>>> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
>>> ---
>>>  hw/apb_pci.c               |   48 +++++++++++++++++++++++++++----------
>>>  hw/apb_pci.h               |    3 +-
>>>  hw/sun4u.c                 |   57
>>> ++++++++++++++++++++++++++++++--------------
>>>  target-sparc/cpu.h         |    3 ++
>>>  target-sparc/ldst_helper.c |   20 ++++++++++----
>>>  5 files changed, 93 insertions(+), 38 deletions(-)
>>>
>>> diff --git a/hw/apb_pci.c b/hw/apb_pci.c
>>> index b10f31e..7e28808 100644
>>> --- a/hw/apb_pci.c
>>> +++ b/hw/apb_pci.c
>>> @@ -66,6 +66,8 @@ do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
>>>  #define RESET_WCMASK 0x98000000
>>>  #define RESET_WMASK  0x60000000
>>>
>>> +#define MAX_IVEC 0x30
>>
>> Shouldn't that be 0x40 (0x20 for OBIO and 0x20 for PCI)? I mean in
>> theory, obviously not all of them are used.
>
> This is the maximum number (0x30 is PCI Bus Error), which actually
> means that number of IRQ lines allocated should be MAX_IVEC + 1.

MAX_IVEC + 3 ;-).   0x32 is power management wakeup
(http://fxr.watson.org/fxr/source/sun4u/io/pci/pci_intr.c?v=OPENSOLARIS)

> 0x40 wouldn't hurt much.
>
>>> +
>>>  typedef struct APBState {
>>>     SysBusDevice busdev;
>>>     PCIBus      *bus;
>>> @@ -77,7 +79,8 @@ typedef struct APBState {
>>>     uint32_t pci_control[16];
>>>     uint32_t pci_irq_map[8];
>>>     uint32_t obio_irq_map[32];
>>> -    qemu_irq pci_irqs[32];
>>> +    qemu_irq *pbm_irqs;
>>> +    qemu_irq *ivec_irqs;
>>>     uint32_t reset_control;
>>>     unsigned int nr_resets;
>>>  } APBState;
>>> @@ -87,7 +90,7 @@ static void apb_config_writel (void *opaque,
>>> target_phys_addr_t addr,
>>>  {
>>>     APBState *s = opaque;
>>>
>>> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr,
>>> val);
>>> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
>>> __func__, addr, val);
>>>
>>>     switch (addr & 0xffff) {
>>>     case 0x30 ... 0x4f: /* DMA error registers */
>>> @@ -104,6 +107,12 @@ static void apb_config_writel (void *opaque,
>>> target_phys_addr_t addr,
>>>             s->pci_irq_map[(addr & 0x3f) >> 3] |= val &
>>> ~PBM_PCI_IMR_MASK;
>>>         }
>>>         break;
>>> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
>>> +        if (addr & 4) {
>>> +            s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK;
>>> +            s->obio_irq_map[(addr & 0xff) >> 3] |= val &
>>> ~PBM_PCI_IMR_MASK;
>>> +        }
>>> +        break;
>>>     case 0x2000 ... 0x202f: /* PCI control */
>>>         s->pci_control[(addr & 0x3f) >> 2] = val;
>>>         break;
>>> @@ -154,6 +163,13 @@ static uint64_t apb_config_readl (void *opaque,
>>>             val = 0;
>>>         }
>>>         break;
>>> +    case 0x1000 ... 0x1080: /* OBIO interrupt control */
>>> +        if (addr & 4) {
>>> +            val = s->obio_irq_map[(addr & 0xff) >> 3];
>>> +        } else {
>>> +            val = 0;
>>> +        }
>>> +        break;
>>>     case 0x2000 ... 0x202f: /* PCI control */
>>>         val = s->pci_control[(addr & 0x3f) >> 2];
>>>         break;
>>> @@ -190,7 +206,7 @@ static void apb_pci_config_write(void *opaque,
>>> target_phys_addr_t addr,
>>>     APBState *s = opaque;
>>>
>>>     val = qemu_bswap_len(val, size);
>>> -    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr,
>>> val);
>>> +    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n",
>>> __func__, addr, val);
>>>     pci_data_write(s->bus, addr, val, size);
>>>  }
>>>
>>> @@ -280,10 +296,19 @@ static void pci_apb_set_irq(void *opaque, int
>>> irq_num, int level)
>>>     if (irq_num < 32) {
>>>         if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
>>>             APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num,
>>> level);
>>> -            qemu_set_irq(s->pci_irqs[irq_num], level);
>>> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
>>> +        } else {
>>> +            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__,
>>> irq_num);
>>> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
>>> +        }
>>> +    } else {
>>> +        /* OBIO IRQ map onto the next 16 INO.  */
>>> +        if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) {
>>> +            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num,
>>> level);
>>> +            qemu_set_irq(s->ivec_irqs[irq_num], level);
>>>         } else {
>>>             APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__,
>>> irq_num);
>>> -            qemu_irq_lower(s->pci_irqs[irq_num]);
>>> +            qemu_irq_lower(s->ivec_irqs[irq_num]);
>>>         }
>>>     }
>>>  }
>>> @@ -316,12 +341,12 @@ static int apb_pci_bridge_initfn(PCIDevice *dev)
>>>
>>>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>>                      target_phys_addr_t mem_base,
>>> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
>>> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
>>> +                     qemu_irq **pbm_irqs)
>>>  {
>>>     DeviceState *dev;
>>>     SysBusDevice *s;
>>>     APBState *d;
>>> -    unsigned int i;
>>>     PCIDevice *pci_dev;
>>>     PCIBridge *br;
>>>
>>> @@ -346,9 +371,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>>                               get_system_io(),
>>>                               0, 32);
>>>
>>> -    for (i = 0; i < 32; i++) {
>>> -        sysbus_connect_irq(s, i, pic[i]);
>>> -    }
>>> +    *pbm_irqs = d->pbm_irqs;
>>> +    d->ivec_irqs = ivec_irqs;
>>>
>>>     pci_create_simple(d->bus, 0, "pbm-pci");
>>>
>>> @@ -402,9 +426,7 @@ static int pci_pbm_init_device(SysBusDevice *dev)
>>>     for (i = 0; i < 8; i++) {
>>>         s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
>>>     }
>>> -    for (i = 0; i < 32; i++) {
>>> -        sysbus_init_irq(dev, &s->pci_irqs[i]);
>>> -    }
>>> +    s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC);
>>>
>>>     /* apb_config */
>>>     memory_region_init_io(&s->apb_config, &apb_config_ops, s,
>>> "apb-config",
>>> diff --git a/hw/apb_pci.h b/hw/apb_pci.h
>>> index 8869f9d..55f7c4c 100644
>>> --- a/hw/apb_pci.h
>>> +++ b/hw/apb_pci.h
>>> @@ -5,5 +5,6 @@
>>>
>>>  PCIBus *pci_apb_init(target_phys_addr_t special_base,
>>>                      target_phys_addr_t mem_base,
>>> -                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
>>> +                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
>>> +                     qemu_irq **pbm_irqs);
>>>  #endif
>>> diff --git a/hw/sun4u.c b/hw/sun4u.c
>>> index 423108f..e0adb9e 100644
>>> --- a/hw/sun4u.c
>>> +++ b/hw/sun4u.c
>>> @@ -81,7 +81,7 @@
>>>  #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
>>>  #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
>>>
>>> -#define MAX_PILS 16
>>> +#define IVEC_MAX             0x30
>>>
>>>  #define TICK_MAX             0x7fffffffffffffffULL
>>>
>>> @@ -304,18 +304,24 @@ static void cpu_kick_irq(CPUState *env)
>>>     qemu_cpu_kick(env);
>>>  }
>>>
>>> -static void cpu_set_irq(void *opaque, int irq, int level)
>>> +static void cpu_set_ivec_irq(void *opaque, int irq, int level)
>>>  {
>>>     CPUState *env = opaque;
>>>
>>>     if (level) {
>>> -        CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
>>> -        env->pil_in |= 1 << irq;
>>> -        cpu_kick_irq(env);
>>> -    } else {
>>> -        CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
>>> -        env->pil_in &= ~(1 << irq);
>>> -        cpu_check_irqs(env);
>>> +        CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
>>> +        env->interrupt_index = TT_IVEC;
>>> +        env->pil_in |= 1 << 5;
>>
>> Err. Spurious irq 5?
>
> Maybe, I can't find it in the manual and I can't remember why I did
> that either. :-(
>
>>
>>> +        env->ivec_status |= 0x20;
>>> +        env->ivec_data[0] = (0x1f << 6) | irq;
>>> +        env->ivec_data[1] = 0;
>>> +        env->ivec_data[2] = 0;
>>> +        cpu_interrupt(env, CPU_INTERRUPT_HARD);
>>
>> Shouldn't there be a cpu_interrupts_enabled(env) check before?
>
> This is checked in the CPU loop, but the code there is not OK for
> Sparc64 because of the assumptions with TT_EXTINT. I thought I fixed
> that, but it looks like I didn't use that version for some reason.

And btw, what happens if multiple vector interrupts are triggered? I
think ivec_data shan't be overwritten if ivec_status!=0.

>>> +      } else {
>>> +        CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
>>> +        env->pil_in &= ~(1 << 5);
>>> +        env->ivec_status &= ~0x20;
>>> +        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
>>>     }
>>>  }
>>>
>>> @@ -521,13 +527,29 @@ void cpu_tick_set_limit(CPUTimer *timer, uint64_t
>>> limit)
>>>     }
>>>  }
>>>
>>> -static void dummy_isa_irq_handler(void *opaque, int n, int level)
>>> +static void isa_irq_handler(void *opaque, int n, int level)
>>>  {
>>> +    static const int isa_irq_to_ivec[16] = {
>>> +        [1] = 0x29, /* keyboard */
>>> +        [4] = 0x2b, /* serial */
>>> +        [6] = 0x27, /* floppy */
>>> +        [7] = 0x22, /* parallel */
>>> +        [12] = 0x2a, /* mouse */
>>> +    };
>>> +    qemu_irq *irqs = opaque;
>>> +    int ivec;
>>> +
>>> +    assert(n < 16);
>>> +    ivec = isa_irq_to_ivec[n];
>>> +    EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level,
>>> ivec);
>>> +    if (ivec) {
>>> +        qemu_set_irq(irqs[ivec], level);
>>> +    }
>>>  }
>>>
>>>  /* EBUS (Eight bit bus) bridge */
>>>  static ISABus *
>>> -pci_ebus_init(PCIBus *bus, int devfn)
>>> +pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
>>>  {
>>>     qemu_irq *isa_irq;
>>>     PCIDevice *pci_dev;
>>> @@ -536,7 +558,7 @@ pci_ebus_init(PCIBus *bus, int devfn)
>>>     pci_dev = pci_create_simple(bus, devfn, "ebus");
>>>     isa_bus = DO_UPCAST(ISABus, qbus,
>>>                         qdev_get_child_bus(&pci_dev->qdev, "isa.0"));
>>> -    isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
>>> +    isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16);
>>>     isa_bus_irqs(isa_bus, isa_irq);
>>>     return isa_bus;
>>>  }
>>> @@ -761,7 +783,7 @@ static void sun4uv_init(MemoryRegion
>>> *address_space_mem,
>>>     long initrd_size, kernel_size;
>>>     PCIBus *pci_bus, *pci_bus2, *pci_bus3;
>>>     ISABus *isa_bus;
>>> -    qemu_irq *irq;
>>> +    qemu_irq *ivec_irqs, *pbm_irqs;
>>>     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
>>>     DriveInfo *fd[MAX_FD];
>>>     void *fw_cfg;
>>> @@ -774,14 +796,13 @@ static void sun4uv_init(MemoryRegion
>>> *address_space_mem,
>>>
>>>     prom_init(hwdef->prom_addr, bios_name);
>>>
>>> -
>>> -    irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
>>> -    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq,
>>> &pci_bus2,
>>> -                           &pci_bus3);
>>> +    ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX);
>>> +    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs,
>>> &pci_bus2,
>>> +                           &pci_bus3, &pbm_irqs);
>>>     pci_vga_init(pci_bus);
>>>
>>>     // XXX Should be pci_bus3
>>> -    isa_bus = pci_ebus_init(pci_bus, -1);
>>> +    isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs);
>>>
>>>     i = 0;
>>>     if (hwdef->console_serial_base) {
>>> diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
>>> index b81779b..8994000 100644
>>> --- a/target-sparc/cpu.h
>>> +++ b/target-sparc/cpu.h
>>> @@ -491,6 +491,9 @@ typedef struct CPUSPARCState {
>>>     /* UA 2005 hyperprivileged registers */
>>>     uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr,
>>> ssr;
>>>     CPUTimer *hstick; // UA 2005
>>> +    /* Interrupt vector registers */
>>> +    uint64_t ivec_status;
>>> +    uint64_t ivec_data[3];
>>>     uint32_t softint;
>>>  #define SOFTINT_TIMER   1
>>>  #define SOFTINT_STIMER  (1 << 16)
>>> diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
>>> index b59707e..4c34346 100644
>>> --- a/target-sparc/ldst_helper.c
>>> +++ b/target-sparc/ldst_helper.c
>>> @@ -1526,6 +1526,19 @@ uint64_t helper_ld_asi(target_ulong addr, int
>>> asi, int size, int sign)
>>>             ret = env->dtlb[reg].tag;
>>>             break;
>>>         }
>>> +    case 0x48: /* Interrupt dispatch, RO */
>>> +        break;
>>> +    case 0x49: /* Interrupt data receive */
>>> +        ret = env->ivec_status;
>>> +        break;
>>> +    case 0x7f: /* Incoming interrupt vector, RO */
>>> +        {
>>> +            int reg = (addr >> 4) & 0x3;
>>> +            if (reg < 3) {
>>> +                ret = env->ivec_data[reg];
>>> +            }
>>> +            break;
>>> +        }
>>>     case 0x46: /* D-cache data */
>>>     case 0x47: /* D-cache tag access */
>>>     case 0x4b: /* E-cache error enable */
>>> @@ -1540,11 +1553,6 @@ uint64_t helper_ld_asi(target_ulong addr, int
>>> asi, int size, int sign)
>>>     case 0x7e: /* E-cache tag */
>>>         break;
>>>     case 0x5b: /* D-MMU data pointer */
>>> -    case 0x48: /* Interrupt dispatch, RO */
>>> -    case 0x49: /* Interrupt data receive */
>>> -    case 0x7f: /* Incoming interrupt vector, RO */
>>> -        /* XXX */
>>> -        break;
>>>     case 0x54: /* I-MMU data in, WO */
>>>     case 0x57: /* I-MMU demap, WO */
>>>     case 0x5c: /* D-MMU data in, WO */
>>> @@ -1954,7 +1962,7 @@ void helper_st_asi(target_ulong addr,
>>> target_ulong val, int asi, int size)
>>>         demap_tlb(env->dtlb, addr, "dmmu", env);
>>>         return;
>>>     case 0x49: /* Interrupt data receive */
>>> -        /* XXX */
>>> +        env->ivec_status = val & 0x20;
>>>         return;
>>>     case 0x46: /* D-cache data */
>>>     case 0x47: /* D-cache tag access */
>>> --
>>> 1.7.9
>>
>>
>>
>> --
>> Regards,
>> Artyom Tarasenko
>>
>> solaris/sparc under qemu blog: http://tyom.blogspot.com/search/label/qemu
>
diff mbox

Patch

From d184e51d012650cf730cb9bc431315e1a8ad5a9a Mon Sep 17 00:00:00 2001
Message-Id: <d184e51d012650cf730cb9bc431315e1a8ad5a9a.1331463238.git.blauwirbel@gmail.com>
In-Reply-To: <4bd3c025d124cc8ce66346143d5ec906e565c47a.1331463238.git.blauwirbel@gmail.com>
References: <4bd3c025d124cc8ce66346143d5ec906e565c47a.1331463238.git.blauwirbel@gmail.com>
From: Blue Swirl <blauwirbel@gmail.com>
Date: Sat, 10 Mar 2012 20:37:00 +0000
Subject: [PATCH 3/3] sparc64: implement PCI and ISA irqs

Generate correct trap for external interrupts. Map PCI and ISA IRQs to
RIC/UltraSPARC-IIi interrupt vectors.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
---
 hw/apb_pci.c               |   48 +++++++++++++++++++++++++++----------
 hw/apb_pci.h               |    3 +-
 hw/sun4u.c                 |   57 ++++++++++++++++++++++++++++++--------------
 target-sparc/cpu.h         |    3 ++
 target-sparc/ldst_helper.c |   20 ++++++++++----
 5 files changed, 93 insertions(+), 38 deletions(-)

diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index b10f31e..7e28808 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -66,6 +66,8 @@  do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
 #define RESET_WCMASK 0x98000000
 #define RESET_WMASK  0x60000000
 
+#define MAX_IVEC 0x30
+
 typedef struct APBState {
     SysBusDevice busdev;
     PCIBus      *bus;
@@ -77,7 +79,8 @@  typedef struct APBState {
     uint32_t pci_control[16];
     uint32_t pci_irq_map[8];
     uint32_t obio_irq_map[32];
-    qemu_irq pci_irqs[32];
+    qemu_irq *pbm_irqs;
+    qemu_irq *ivec_irqs;
     uint32_t reset_control;
     unsigned int nr_resets;
 } APBState;
@@ -87,7 +90,7 @@  static void apb_config_writel (void *opaque, target_phys_addr_t addr,
 {
     APBState *s = opaque;
 
-    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val);
 
     switch (addr & 0xffff) {
     case 0x30 ... 0x4f: /* DMA error registers */
@@ -104,6 +107,12 @@  static void apb_config_writel (void *opaque, target_phys_addr_t addr,
             s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
         }
         break;
+    case 0x1000 ... 0x1080: /* OBIO interrupt control */
+        if (addr & 4) {
+            s->obio_irq_map[(addr & 0xff) >> 3] &= PBM_PCI_IMR_MASK;
+            s->obio_irq_map[(addr & 0xff) >> 3] |= val & ~PBM_PCI_IMR_MASK;
+        }
+        break;
     case 0x2000 ... 0x202f: /* PCI control */
         s->pci_control[(addr & 0x3f) >> 2] = val;
         break;
@@ -154,6 +163,13 @@  static uint64_t apb_config_readl (void *opaque,
             val = 0;
         }
         break;
+    case 0x1000 ... 0x1080: /* OBIO interrupt control */
+        if (addr & 4) {
+            val = s->obio_irq_map[(addr & 0xff) >> 3];
+        } else {
+            val = 0;
+        }
+        break;
     case 0x2000 ... 0x202f: /* PCI control */
         val = s->pci_control[(addr & 0x3f) >> 2];
         break;
@@ -190,7 +206,7 @@  static void apb_pci_config_write(void *opaque, target_phys_addr_t addr,
     APBState *s = opaque;
 
     val = qemu_bswap_len(val, size);
-    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %" PRIx64 "\n", __func__, addr, val);
     pci_data_write(s->bus, addr, val, size);
 }
 
@@ -280,10 +296,19 @@  static void pci_apb_set_irq(void *opaque, int irq_num, int level)
     if (irq_num < 32) {
         if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
             APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
-            qemu_set_irq(s->pci_irqs[irq_num], level);
+            qemu_set_irq(s->ivec_irqs[irq_num], level);
+        } else {
+            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
+            qemu_irq_lower(s->ivec_irqs[irq_num]);
+        }
+    } else {
+        /* OBIO IRQ map onto the next 16 INO.  */
+        if (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED) {
+            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
+            qemu_set_irq(s->ivec_irqs[irq_num], level);
         } else {
             APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
-            qemu_irq_lower(s->pci_irqs[irq_num]);
+            qemu_irq_lower(s->ivec_irqs[irq_num]);
         }
     }
 }
@@ -316,12 +341,12 @@  static int apb_pci_bridge_initfn(PCIDevice *dev)
 
 PCIBus *pci_apb_init(target_phys_addr_t special_base,
                      target_phys_addr_t mem_base,
-                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
+                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
+                     qemu_irq **pbm_irqs)
 {
     DeviceState *dev;
     SysBusDevice *s;
     APBState *d;
-    unsigned int i;
     PCIDevice *pci_dev;
     PCIBridge *br;
 
@@ -346,9 +371,8 @@  PCIBus *pci_apb_init(target_phys_addr_t special_base,
                               get_system_io(),
                               0, 32);
 
-    for (i = 0; i < 32; i++) {
-        sysbus_connect_irq(s, i, pic[i]);
-    }
+    *pbm_irqs = d->pbm_irqs;
+    d->ivec_irqs = ivec_irqs;
 
     pci_create_simple(d->bus, 0, "pbm-pci");
 
@@ -402,9 +426,7 @@  static int pci_pbm_init_device(SysBusDevice *dev)
     for (i = 0; i < 8; i++) {
         s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
     }
-    for (i = 0; i < 32; i++) {
-        sysbus_init_irq(dev, &s->pci_irqs[i]);
-    }
+    s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC);
 
     /* apb_config */
     memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config",
diff --git a/hw/apb_pci.h b/hw/apb_pci.h
index 8869f9d..55f7c4c 100644
--- a/hw/apb_pci.h
+++ b/hw/apb_pci.h
@@ -5,5 +5,6 @@ 
 
 PCIBus *pci_apb_init(target_phys_addr_t special_base,
                      target_phys_addr_t mem_base,
-                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
+                     qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3,
+                     qemu_irq **pbm_irqs);
 #endif
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 423108f..e0adb9e 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -81,7 +81,7 @@ 
 #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
 #define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
 
-#define MAX_PILS 16
+#define IVEC_MAX             0x30
 
 #define TICK_MAX             0x7fffffffffffffffULL
 
@@ -304,18 +304,24 @@  static void cpu_kick_irq(CPUState *env)
     qemu_cpu_kick(env);
 }
 
-static void cpu_set_irq(void *opaque, int irq, int level)
+static void cpu_set_ivec_irq(void *opaque, int irq, int level)
 {
     CPUState *env = opaque;
 
     if (level) {
-        CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
-        env->pil_in |= 1 << irq;
-        cpu_kick_irq(env);
-    } else {
-        CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
-        env->pil_in &= ~(1 << irq);
-        cpu_check_irqs(env);
+        CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq);
+        env->interrupt_index = TT_IVEC;
+        env->pil_in |= 1 << 5;
+        env->ivec_status |= 0x20;
+        env->ivec_data[0] = (0x1f << 6) | irq;
+        env->ivec_data[1] = 0;
+        env->ivec_data[2] = 0;
+        cpu_interrupt(env, CPU_INTERRUPT_HARD);
+      } else {
+        CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq);
+        env->pil_in &= ~(1 << 5);
+        env->ivec_status &= ~0x20;
+        cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
     }
 }
 
@@ -521,13 +527,29 @@  void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
     }
 }
 
-static void dummy_isa_irq_handler(void *opaque, int n, int level)
+static void isa_irq_handler(void *opaque, int n, int level)
 {
+    static const int isa_irq_to_ivec[16] = {
+        [1] = 0x29, /* keyboard */
+        [4] = 0x2b, /* serial */
+        [6] = 0x27, /* floppy */
+        [7] = 0x22, /* parallel */
+        [12] = 0x2a, /* mouse */
+    };
+    qemu_irq *irqs = opaque;
+    int ivec;
+
+    assert(n < 16);
+    ivec = isa_irq_to_ivec[n];
+    EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec);
+    if (ivec) {
+        qemu_set_irq(irqs[ivec], level);
+    }
 }
 
 /* EBUS (Eight bit bus) bridge */
 static ISABus *
-pci_ebus_init(PCIBus *bus, int devfn)
+pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs)
 {
     qemu_irq *isa_irq;
     PCIDevice *pci_dev;
@@ -536,7 +558,7 @@  pci_ebus_init(PCIBus *bus, int devfn)
     pci_dev = pci_create_simple(bus, devfn, "ebus");
     isa_bus = DO_UPCAST(ISABus, qbus,
                         qdev_get_child_bus(&pci_dev->qdev, "isa.0"));
-    isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
+    isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16);
     isa_bus_irqs(isa_bus, isa_irq);
     return isa_bus;
 }
@@ -761,7 +783,7 @@  static void sun4uv_init(MemoryRegion *address_space_mem,
     long initrd_size, kernel_size;
     PCIBus *pci_bus, *pci_bus2, *pci_bus3;
     ISABus *isa_bus;
-    qemu_irq *irq;
+    qemu_irq *ivec_irqs, *pbm_irqs;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     DriveInfo *fd[MAX_FD];
     void *fw_cfg;
@@ -774,14 +796,13 @@  static void sun4uv_init(MemoryRegion *address_space_mem,
 
     prom_init(hwdef->prom_addr, bios_name);
 
-
-    irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
-    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2,
-                           &pci_bus3);
+    ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, env, IVEC_MAX);
+    pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2,
+                           &pci_bus3, &pbm_irqs);
     pci_vga_init(pci_bus);
 
     // XXX Should be pci_bus3
-    isa_bus = pci_ebus_init(pci_bus, -1);
+    isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs);
 
     i = 0;
     if (hwdef->console_serial_base) {
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index b81779b..8994000 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -491,6 +491,9 @@  typedef struct CPUSPARCState {
     /* UA 2005 hyperprivileged registers */
     uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
     CPUTimer *hstick; // UA 2005
+    /* Interrupt vector registers */
+    uint64_t ivec_status;
+    uint64_t ivec_data[3];
     uint32_t softint;
 #define SOFTINT_TIMER   1
 #define SOFTINT_STIMER  (1 << 16)
diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
index b59707e..4c34346 100644
--- a/target-sparc/ldst_helper.c
+++ b/target-sparc/ldst_helper.c
@@ -1526,6 +1526,19 @@  uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
             ret = env->dtlb[reg].tag;
             break;
         }
+    case 0x48: /* Interrupt dispatch, RO */
+        break;
+    case 0x49: /* Interrupt data receive */
+        ret = env->ivec_status;
+        break;
+    case 0x7f: /* Incoming interrupt vector, RO */
+        {
+            int reg = (addr >> 4) & 0x3;
+            if (reg < 3) {
+                ret = env->ivec_data[reg];
+            }
+            break;
+        }
     case 0x46: /* D-cache data */
     case 0x47: /* D-cache tag access */
     case 0x4b: /* E-cache error enable */
@@ -1540,11 +1553,6 @@  uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
     case 0x7e: /* E-cache tag */
         break;
     case 0x5b: /* D-MMU data pointer */
-    case 0x48: /* Interrupt dispatch, RO */
-    case 0x49: /* Interrupt data receive */
-    case 0x7f: /* Incoming interrupt vector, RO */
-        /* XXX */
-        break;
     case 0x54: /* I-MMU data in, WO */
     case 0x57: /* I-MMU demap, WO */
     case 0x5c: /* D-MMU data in, WO */
@@ -1954,7 +1962,7 @@  void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
         demap_tlb(env->dtlb, addr, "dmmu", env);
         return;
     case 0x49: /* Interrupt data receive */
-        /* XXX */
+        env->ivec_status = val & 0x20;
         return;
     case 0x46: /* D-cache data */
     case 0x47: /* D-cache tag access */
-- 
1.7.2.5