| /* |
| * Heathrow PIC support (OldWorld PowerMac) |
| * |
| * Copyright (c) 2005-2007 Fabrice Bellard |
| * Copyright (c) 2007 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 |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| #include "hw.h" |
| #include "ppc_mac.h" |
| |
| /* debug PIC */ |
| //#define DEBUG_PIC |
| |
| #ifdef DEBUG_PIC |
| #define PIC_DPRINTF(fmt, ...) \ |
| do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0) |
| #else |
| #define PIC_DPRINTF(fmt, ...) |
| #endif |
| |
| typedef struct HeathrowPIC { |
| uint32_t events; |
| uint32_t mask; |
| uint32_t levels; |
| uint32_t level_triggered; |
| } HeathrowPIC; |
| |
| typedef struct HeathrowPICS { |
| HeathrowPIC pics[2]; |
| qemu_irq *irqs; |
| } HeathrowPICS; |
| |
| static inline int check_irq(HeathrowPIC *pic) |
| { |
| return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask; |
| } |
| |
| /* update the CPU irq state */ |
| static void heathrow_pic_update(HeathrowPICS *s) |
| { |
| if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) { |
| qemu_irq_raise(s->irqs[0]); |
| } else { |
| qemu_irq_lower(s->irqs[0]); |
| } |
| } |
| |
| static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
| { |
| HeathrowPICS *s = opaque; |
| HeathrowPIC *pic; |
| unsigned int n; |
| |
| n = ((addr & 0xfff) - 0x10) >> 4; |
| PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); |
| if (n >= 2) |
| return; |
| pic = &s->pics[n]; |
| switch(addr & 0xf) { |
| case 0x04: |
| pic->mask = value; |
| heathrow_pic_update(s); |
| break; |
| case 0x08: |
| /* do not reset level triggered IRQs */ |
| value &= ~pic->level_triggered; |
| pic->events &= ~value; |
| heathrow_pic_update(s); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static uint32_t pic_readl (void *opaque, target_phys_addr_t addr) |
| { |
| HeathrowPICS *s = opaque; |
| HeathrowPIC *pic; |
| unsigned int n; |
| uint32_t value; |
| |
| n = ((addr & 0xfff) - 0x10) >> 4; |
| if (n >= 2) { |
| value = 0; |
| } else { |
| pic = &s->pics[n]; |
| switch(addr & 0xf) { |
| case 0x0: |
| value = pic->events; |
| break; |
| case 0x4: |
| value = pic->mask; |
| break; |
| case 0xc: |
| value = pic->levels; |
| break; |
| default: |
| value = 0; |
| break; |
| } |
| } |
| PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); |
| return value; |
| } |
| |
| static CPUWriteMemoryFunc * const pic_write[] = { |
| &pic_writel, |
| &pic_writel, |
| &pic_writel, |
| }; |
| |
| static CPUReadMemoryFunc * const pic_read[] = { |
| &pic_readl, |
| &pic_readl, |
| &pic_readl, |
| }; |
| |
| |
| static void heathrow_pic_set_irq(void *opaque, int num, int level) |
| { |
| HeathrowPICS *s = opaque; |
| HeathrowPIC *pic; |
| unsigned int irq_bit; |
| |
| #if defined(DEBUG) |
| { |
| static int last_level[64]; |
| if (last_level[num] != level) { |
| PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level); |
| last_level[num] = level; |
| } |
| } |
| #endif |
| pic = &s->pics[1 - (num >> 5)]; |
| irq_bit = 1 << (num & 0x1f); |
| if (level) { |
| pic->events |= irq_bit & ~pic->level_triggered; |
| pic->levels |= irq_bit; |
| } else { |
| pic->levels &= ~irq_bit; |
| } |
| heathrow_pic_update(s); |
| } |
| |
| static void heathrow_pic_save_one(QEMUFile *f, HeathrowPIC *s) |
| { |
| qemu_put_be32s(f, &s->events); |
| qemu_put_be32s(f, &s->mask); |
| qemu_put_be32s(f, &s->levels); |
| qemu_put_be32s(f, &s->level_triggered); |
| } |
| |
| static void heathrow_pic_save(QEMUFile *f, void *opaque) |
| { |
| HeathrowPICS *s = (HeathrowPICS *)opaque; |
| |
| heathrow_pic_save_one(f, &s->pics[0]); |
| heathrow_pic_save_one(f, &s->pics[1]); |
| } |
| |
| static void heathrow_pic_load_one(QEMUFile *f, HeathrowPIC *s) |
| { |
| qemu_get_be32s(f, &s->events); |
| qemu_get_be32s(f, &s->mask); |
| qemu_get_be32s(f, &s->levels); |
| qemu_get_be32s(f, &s->level_triggered); |
| } |
| |
| static int heathrow_pic_load(QEMUFile *f, void *opaque, int version_id) |
| { |
| HeathrowPICS *s = (HeathrowPICS *)opaque; |
| |
| if (version_id != 1) |
| return -EINVAL; |
| |
| heathrow_pic_load_one(f, &s->pics[0]); |
| heathrow_pic_load_one(f, &s->pics[1]); |
| |
| return 0; |
| } |
| |
| static void heathrow_pic_reset_one(HeathrowPIC *s) |
| { |
| memset(s, '\0', sizeof(HeathrowPIC)); |
| } |
| |
| static void heathrow_pic_reset(void *opaque) |
| { |
| HeathrowPICS *s = opaque; |
| |
| heathrow_pic_reset_one(&s->pics[0]); |
| heathrow_pic_reset_one(&s->pics[1]); |
| |
| s->pics[0].level_triggered = 0; |
| s->pics[1].level_triggered = 0x1ff00000; |
| } |
| |
| qemu_irq *heathrow_pic_init(int *pmem_index, |
| int nb_cpus, qemu_irq **irqs) |
| { |
| HeathrowPICS *s; |
| |
| s = qemu_mallocz(sizeof(HeathrowPICS)); |
| /* only 1 CPU */ |
| s->irqs = irqs[0]; |
| *pmem_index = cpu_register_io_memory(pic_read, pic_write, s, |
| DEVICE_LITTLE_ENDIAN); |
| |
| register_savevm(NULL, "heathrow_pic", -1, 1, heathrow_pic_save, |
| heathrow_pic_load, s); |
| qemu_register_reset(heathrow_pic_reset, s); |
| return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64); |
| } |