X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fpci.c;h=f159dc2cb4c042d3c49db33d4dd4a1f66f714c7b;hb=30ca2aab8e7419d2c8cf597743633a6477840277;hp=93d33d926c65f6332049ad8316ae84169cac138b;hpb=77d4bc349abd61ba2e12327e40f95bfc4069f2a0;p=qemu diff --git a/hw/pci.c b/hw/pci.c index 93d33d9..f159dc2 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -40,37 +40,60 @@ #define PCI_DEVICES_MAX 64 #define PCI_IRQ_WORDS ((PCI_DEVICES_MAX + 31) / 32) -typedef struct PCIBridge { - uint32_t config_reg; - PCIDevice **pci_bus[256]; -} PCIBridge; +struct PCIBus { + int bus_num; + int devfn_min; + void (*set_irq)(PCIDevice *pci_dev, int irq_num, int level); + uint32_t config_reg; /* XXX: suppress */ + openpic_t *openpic; /* XXX: suppress */ + PCIDevice *devices[256]; +}; -static PCIBridge pci_bridge; target_phys_addr_t pci_mem_base; static int pci_irq_index; static uint32_t pci_irq_levels[4][PCI_IRQ_WORDS]; +static PCIBus *first_bus; + +static PCIBus *pci_register_bus(void) +{ + PCIBus *bus; + bus = qemu_mallocz(sizeof(PCIBus)); + first_bus = bus; + return bus; +} + +void generic_pci_save(QEMUFile* f, void *opaque) +{ + PCIDevice* s=(PCIDevice*)opaque; + + qemu_put_buffer(f, s->config, 256); +} + +int generic_pci_load(QEMUFile* f, void *opaque, int version_id) +{ + PCIDevice* s=(PCIDevice*)opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_buffer(f, s->config, 256); + return 0; +} /* -1 for devfn means auto assign */ -PCIDevice *pci_register_device(const char *name, int instance_size, - int bus_num, int devfn, +PCIDevice *pci_register_device(PCIBus *bus, const char *name, + int instance_size, int devfn, PCIConfigReadFunc *config_read, PCIConfigWriteFunc *config_write) { - PCIBridge *s = &pci_bridge; - PCIDevice *pci_dev, **bus; + PCIDevice *pci_dev; if (pci_irq_index >= PCI_DEVICES_MAX) return NULL; - if (!s->pci_bus[bus_num]) { - s->pci_bus[bus_num] = qemu_mallocz(256 * sizeof(PCIDevice *)); - if (!s->pci_bus[bus_num]) - return NULL; - } - bus = s->pci_bus[bus_num]; if (devfn < 0) { - for(devfn = 0 ; devfn < 256; devfn += 8) { - if (!bus[devfn]) + for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) { + if (!bus->devices[devfn]) goto found; } return NULL; @@ -79,7 +102,7 @@ PCIDevice *pci_register_device(const char *name, int instance_size, pci_dev = qemu_mallocz(instance_size); if (!pci_dev) return NULL; - pci_dev->bus_num = bus_num; + pci_dev->bus = bus; pci_dev->devfn = devfn; pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); @@ -90,7 +113,7 @@ PCIDevice *pci_register_device(const char *name, int instance_size, pci_dev->config_read = config_read; pci_dev->config_write = config_write; pci_dev->irq_index = pci_irq_index++; - bus[devfn] = pci_dev; + bus->devices[devfn] = pci_dev; return pci_dev; } @@ -100,7 +123,7 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num, { PCIIORegion *r; - if ((unsigned int)region_num >= 6) + if ((unsigned int)region_num >= PCI_NUM_REGIONS) return; r = &pci_dev->io_regions[region_num]; r->addr = -1; @@ -111,13 +134,13 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num, static void pci_addr_writel(void* opaque, uint32_t addr, uint32_t val) { - PCIBridge *s = opaque; + PCIBus *s = opaque; s->config_reg = val; } static uint32_t pci_addr_readl(void* opaque, uint32_t addr) { - PCIBridge *s = opaque; + PCIBus *s = opaque; return s->config_reg; } @@ -125,16 +148,21 @@ static void pci_update_mappings(PCIDevice *d) { PCIIORegion *r; int cmd, i; - uint32_t last_addr, new_addr; + uint32_t last_addr, new_addr, config_ofs; cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND)); - for(i = 0; i < 6; i++) { + for(i = 0; i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; + if (i == PCI_ROM_SLOT) { + config_ofs = 0x30; + } else { + config_ofs = 0x10 + i * 4; + } if (r->size != 0) { if (r->type & PCI_ADDRESS_SPACE_IO) { if (cmd & PCI_COMMAND_IO) { new_addr = le32_to_cpu(*(uint32_t *)(d->config + - 0x10 + i * 4)); + config_ofs)); new_addr = new_addr & ~(r->size - 1); last_addr = new_addr + r->size - 1; /* NOTE: we have only 64K ioports on PC */ @@ -148,7 +176,10 @@ static void pci_update_mappings(PCIDevice *d) } else { if (cmd & PCI_COMMAND_MEMORY) { new_addr = le32_to_cpu(*(uint32_t *)(d->config + - 0x10 + i * 4)); + config_ofs)); + /* the ROM slot has a specific enable bit */ + if (i == PCI_ROM_SLOT && !(new_addr & 1)) + goto no_mem_map; new_addr = new_addr & ~(r->size - 1); last_addr = new_addr + r->size - 1; /* NOTE: we do not support wrapping */ @@ -160,6 +191,7 @@ static void pci_update_mappings(PCIDevice *d) new_addr = -1; } } else { + no_mem_map: new_addr = -1; } } @@ -216,18 +248,28 @@ void pci_default_write_config(PCIDevice *d, int can_write, i; uint32_t end, addr; - if (len == 4 && (address >= 0x10 && address < 0x10 + 4 * 6)) { + if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) || + (address >= 0x30 && address < 0x34))) { PCIIORegion *r; int reg; - reg = (address - 0x10) >> 2; + if ( address >= 0x30 ) { + reg = PCI_ROM_SLOT; + }else{ + reg = (address - 0x10) >> 2; + } r = &d->io_regions[reg]; if (r->size == 0) goto default_config; /* compute the stored value */ - val &= ~(r->size - 1); - val |= r->type; - *(uint32_t *)(d->config + 0x10 + reg * 4) = cpu_to_le32(val); + if (reg == PCI_ROM_SLOT) { + /* keep ROM enable bit */ + val &= (~(r->size - 1)) | 1; + } else { + val &= ~(r->size - 1); + val |= r->type; + } + *(uint32_t *)(d->config + address) = cpu_to_le32(val); pci_update_mappings(d); return; } @@ -236,21 +278,49 @@ void pci_default_write_config(PCIDevice *d, addr = address; for(i = 0; i < len; i++) { /* default read/write accesses */ - switch(addr) { + switch(d->config[0x0e]) { case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0e: - case 0x3d: - can_write = 0; + case 0x80: + switch(addr) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x10 ... 0x27: /* base */ + case 0x30 ... 0x33: /* rom */ + case 0x3d: + can_write = 0; + break; + default: + can_write = 1; + break; + } break; default: - can_write = 1; + case 0x01: + switch(addr) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x38 ... 0x3b: /* rom */ + case 0x3d: + can_write = 0; + break; + default: + can_write = 1; + break; + } break; } if (can_write) { @@ -270,9 +340,9 @@ void pci_default_write_config(PCIDevice *d, static void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len) { - PCIBridge *s = opaque; - PCIDevice **bus, *pci_dev; - int config_addr; + PCIBus *s = opaque; + PCIDevice *pci_dev; + int config_addr, bus_num; #if defined(DEBUG_PCI) && 0 printf("pci_data_write: addr=%08x val=%08x len=%d\n", @@ -284,10 +354,10 @@ static void pci_data_write(void *opaque, uint32_t addr, if ((s->config_reg & 0x3) != 0) { return; } - bus = s->pci_bus[(s->config_reg >> 16) & 0xff]; - if (!bus) + bus_num = (s->config_reg >> 16) & 0xff; + if (bus_num != 0) return; - pci_dev = bus[(s->config_reg >> 8) & 0xff]; + pci_dev = s->devices[(s->config_reg >> 8) & 0xff]; if (!pci_dev) return; config_addr = (s->config_reg & 0xfc) | (addr & 3); @@ -301,19 +371,19 @@ static void pci_data_write(void *opaque, uint32_t addr, static uint32_t pci_data_read(void *opaque, uint32_t addr, int len) { - PCIBridge *s = opaque; - PCIDevice **bus, *pci_dev; - int config_addr; + PCIBus *s = opaque; + PCIDevice *pci_dev; + int config_addr, bus_num; uint32_t val; if (!(s->config_reg & (1 << 31))) goto fail; if ((s->config_reg & 0x3) != 0) goto fail; - bus = s->pci_bus[(s->config_reg >> 16) & 0xff]; - if (!bus) + bus_num = (s->config_reg >> 16) & 0xff; + if (bus_num != 0) goto fail; - pci_dev = bus[(s->config_reg >> 8) & 0xff]; + pci_dev = s->devices[(s->config_reg >> 8) & 0xff]; if (!pci_dev) { fail: switch(len) { @@ -376,11 +446,16 @@ static uint32_t pci_data_readl(void* opaque, uint32_t addr) /* i440FX PCI bridge */ -void i440fx_init(void) +static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level); + +PCIBus *i440fx_init(void) { - PCIBridge *s = &pci_bridge; + PCIBus *s; PCIDevice *d; + s = pci_register_bus(); + s->set_irq = piix3_set_irq; + register_ioport_write(0xcf8, 4, 4, pci_addr_writel, s); register_ioport_read(0xcf8, 4, 4, pci_addr_readl, s); @@ -391,7 +466,7 @@ void i440fx_init(void) register_ioport_read(0xcfc, 4, 2, pci_data_readw, s); register_ioport_read(0xcfc, 4, 4, pci_data_readl, s); - d = pci_register_device("i440FX", sizeof(PCIDevice), 0, 0, + d = pci_register_device(s, "i440FX", sizeof(PCIDevice), 0, NULL, NULL); d->config[0x00] = 0x86; // vendor_id @@ -399,10 +474,10 @@ void i440fx_init(void) d->config[0x02] = 0x37; // device_id d->config[0x03] = 0x12; d->config[0x08] = 0x02; // revision - d->config[0x0a] = 0x04; // class_sub = pci2pci + d->config[0x0a] = 0x00; // class_sub = host2pci d->config[0x0b] = 0x06; // class_base = PCI_bridge - d->config[0x0c] = 0x01; // line_size in 32 bit words - d->config[0x0e] = 0x01; // header_type + d->config[0x0e] = 0x00; // header_type + return s; } /* PIIX3 PCI to ISA bridge */ @@ -413,6 +488,52 @@ typedef struct PIIX3State { PIIX3State *piix3_state; +/* return the global irq number corresponding to a given device irq + pin. We could also use the bus number to have a more precise + mapping. */ +static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + int slot_addend; + slot_addend = (pci_dev->devfn >> 3); + return (irq_num + slot_addend) & 3; +} + +static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level) +{ + int irq_index, shift, pic_irq, pic_level; + uint32_t *p; + + irq_num = pci_slot_get_pirq(pci_dev, irq_num); + irq_index = pci_dev->irq_index; + p = &pci_irq_levels[irq_num][irq_index >> 5]; + shift = (irq_index & 0x1f); + *p = (*p & ~(1 << shift)) | (level << shift); + + /* now we change the pic irq level according to the piix irq mappings */ + pic_irq = piix3_state->dev.config[0x60 + irq_num]; + if (pic_irq < 16) { + /* the pic level is the logical OR of all the PCI irqs mapped + to it */ + pic_level = 0; +#if (PCI_IRQ_WORDS == 2) + pic_level = ((pci_irq_levels[irq_num][0] | + pci_irq_levels[irq_num][1]) != 0); +#else + { + int i; + pic_level = 0; + for(i = 0; i < PCI_IRQ_WORDS; i++) { + if (pci_irq_levels[irq_num][i]) { + pic_level = 1; + break; + } + } + } +#endif + pic_set_irq(pic_irq, pic_level); + } +} + static void piix3_reset(PIIX3State *d) { uint8_t *pci_conf = d->dev.config; @@ -448,14 +569,15 @@ static void piix3_reset(PIIX3State *d) pci_conf[0xae] = 0x00; } -void piix3_init(void) +void piix3_init(PCIBus *bus) { PIIX3State *d; uint8_t *pci_conf; - d = (PIIX3State *)pci_register_device("PIIX3", sizeof(PIIX3State), - 0, -1, - NULL, NULL); + d = (PIIX3State *)pci_register_device(bus, "PIIX3", sizeof(PIIX3State), + -1, NULL, NULL); + register_savevm("PIIX3", 0, 1, generic_pci_save, generic_pci_load, d); + piix3_state = d; pci_conf = d->dev.config; @@ -472,7 +594,7 @@ void piix3_init(void) /* PREP pci init */ -static inline void set_config(PCIBridge *s, target_phys_addr_t addr) +static inline void set_config(PCIBus *s, target_phys_addr_t addr) { int devfn, i; @@ -484,16 +606,16 @@ static inline void set_config(PCIBridge *s, target_phys_addr_t addr) s->config_reg = 0x80000000 | (addr & 0xfc) | (devfn << 8); } -static void PPC_PCIIO_writeb (target_phys_addr_t addr, uint32_t val) +static void PPC_PCIIO_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; set_config(s, addr); pci_data_write(s, addr, val, 1); } -static void PPC_PCIIO_writew (target_phys_addr_t addr, uint32_t val) +static void PPC_PCIIO_writew (void *opaque, target_phys_addr_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; set_config(s, addr); #ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); @@ -501,9 +623,9 @@ static void PPC_PCIIO_writew (target_phys_addr_t addr, uint32_t val) pci_data_write(s, addr, val, 2); } -static void PPC_PCIIO_writel (target_phys_addr_t addr, uint32_t val) +static void PPC_PCIIO_writel (void *opaque, target_phys_addr_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; set_config(s, addr); #ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); @@ -511,18 +633,18 @@ static void PPC_PCIIO_writel (target_phys_addr_t addr, uint32_t val) pci_data_write(s, addr, val, 4); } -static uint32_t PPC_PCIIO_readb (target_phys_addr_t addr) +static uint32_t PPC_PCIIO_readb (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; set_config(s, addr); val = pci_data_read(s, addr, 1); return val; } -static uint32_t PPC_PCIIO_readw (target_phys_addr_t addr) +static uint32_t PPC_PCIIO_readw (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; set_config(s, addr); val = pci_data_read(s, addr, 2); @@ -532,9 +654,9 @@ static uint32_t PPC_PCIIO_readw (target_phys_addr_t addr) return val; } -static uint32_t PPC_PCIIO_readl (target_phys_addr_t addr) +static uint32_t PPC_PCIIO_readl (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; set_config(s, addr); val = pci_data_read(s, addr, 4); @@ -556,15 +678,27 @@ static CPUReadMemoryFunc *PPC_PCIIO_read[] = { &PPC_PCIIO_readl, }; -void pci_prep_init(void) +static void prep_set_irq(PCIDevice *d, int irq_num, int level) { + /* XXX: we do not simulate the hardware - we rely on the BIOS to + set correctly for irq line field */ + pic_set_irq(d->config[PCI_INTERRUPT_LINE], level); +} + +PCIBus *pci_prep_init(void) +{ + PCIBus *s; PCIDevice *d; int PPC_io_memory; - PPC_io_memory = cpu_register_io_memory(0, PPC_PCIIO_read, PPC_PCIIO_write); + s = pci_register_bus(); + s->set_irq = prep_set_irq; + + PPC_io_memory = cpu_register_io_memory(0, PPC_PCIIO_read, + PPC_PCIIO_write, s); cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory); - d = pci_register_device("PREP PCI Bridge", sizeof(PCIDevice), 0, 0, + d = pci_register_device(s, "PREP PCI Bridge", sizeof(PCIDevice), 0, NULL, NULL); /* XXX: put correct IDs */ @@ -576,23 +710,27 @@ void pci_prep_init(void) d->config[0x0a] = 0x04; // class_sub = pci2pci d->config[0x0b] = 0x06; // class_base = PCI_bridge d->config[0x0e] = 0x01; // header_type + return s; } /* pmac pci init */ -static void pci_pmac_config_writel (target_phys_addr_t addr, uint32_t val) +#if 0 +/* Grackle PCI host */ +static void pci_grackle_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; #ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); #endif s->config_reg = val; } -static uint32_t pci_pmac_config_readl (target_phys_addr_t addr) +static uint32_t pci_grackle_config_readl (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; val = s->config_reg; @@ -602,53 +740,56 @@ static uint32_t pci_pmac_config_readl (target_phys_addr_t addr) return val; } -static CPUWriteMemoryFunc *pci_pmac_config_write[] = { - &pci_pmac_config_writel, - &pci_pmac_config_writel, - &pci_pmac_config_writel, +static CPUWriteMemoryFunc *pci_grackle_config_write[] = { + &pci_grackle_config_writel, + &pci_grackle_config_writel, + &pci_grackle_config_writel, }; -static CPUReadMemoryFunc *pci_pmac_config_read[] = { - &pci_pmac_config_readl, - &pci_pmac_config_readl, - &pci_pmac_config_readl, +static CPUReadMemoryFunc *pci_grackle_config_read[] = { + &pci_grackle_config_readl, + &pci_grackle_config_readl, + &pci_grackle_config_readl, }; -static void pci_pmac_writeb (target_phys_addr_t addr, uint32_t val) +static void pci_grackle_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; pci_data_write(s, addr, val, 1); } -static void pci_pmac_writew (target_phys_addr_t addr, uint32_t val) +static void pci_grackle_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; #ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); #endif pci_data_write(s, addr, val, 2); } -static void pci_pmac_writel (target_phys_addr_t addr, uint32_t val) +static void pci_grackle_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; #ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); #endif pci_data_write(s, addr, val, 4); } -static uint32_t pci_pmac_readb (target_phys_addr_t addr) +static uint32_t pci_grackle_readb (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; val = pci_data_read(s, addr, 1); return val; } -static uint32_t pci_pmac_readw (target_phys_addr_t addr) +static uint32_t pci_grackle_readw (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; val = pci_data_read(s, addr, 2); #ifdef TARGET_WORDS_BIGENDIAN @@ -657,9 +798,9 @@ static uint32_t pci_pmac_readw (target_phys_addr_t addr) return val; } -static uint32_t pci_pmac_readl (target_phys_addr_t addr) +static uint32_t pci_grackle_readl (void *opaque, target_phys_addr_t addr) { - PCIBridge *s = &pci_bridge; + PCIBus *s = opaque; uint32_t val; val = pci_data_read(s, addr, 4); @@ -669,33 +810,390 @@ static uint32_t pci_pmac_readl (target_phys_addr_t addr) return val; } -static CPUWriteMemoryFunc *pci_pmac_write[] = { - &pci_pmac_writeb, - &pci_pmac_writew, - &pci_pmac_writel, +static CPUWriteMemoryFunc *pci_grackle_write[] = { + &pci_grackle_writeb, + &pci_grackle_writew, + &pci_grackle_writel, }; -static CPUReadMemoryFunc *pci_pmac_read[] = { - &pci_pmac_readb, - &pci_pmac_readw, - &pci_pmac_readl, +static CPUReadMemoryFunc *pci_grackle_read[] = { + &pci_grackle_readb, + &pci_grackle_readw, + &pci_grackle_readl, }; +#endif -void pci_pmac_init(void) +/* Uninorth PCI host (for all Mac99 and newer machines */ +static void pci_unin_main_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) { - PCIDevice *d; - int pci_mem_config, pci_mem_data; + PCIBus *s = opaque; + int i; + +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + for (i = 11; i < 32; i++) { + if ((val & (1 << i)) != 0) + break; + } +#if 0 + s->config_reg = 0x80000000 | (1 << 16) | (val & 0x7FC) | (i << 11); +#else + s->config_reg = 0x80000000 | (0 << 16) | (val & 0x7FC) | (i << 11); +#endif +} + +static uint32_t pci_unin_main_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + int devfn; + + devfn = (s->config_reg >> 8) & 0xFF; + val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_main_config_write[] = { + &pci_unin_main_config_writel, + &pci_unin_main_config_writel, + &pci_unin_main_config_writel, +}; + +static CPUReadMemoryFunc *pci_unin_main_config_read[] = { + &pci_unin_main_config_readl, + &pci_unin_main_config_readl, + &pci_unin_main_config_readl, +}; + +static void pci_unin_main_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + pci_data_write(s, addr & 7, val, 1); +} + +static void pci_unin_main_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr & 7, val, 2); +} + +static void pci_unin_main_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr & 7, val, 4); +} + +static uint32_t pci_unin_main_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 1); + + return val; +} + +static uint32_t pci_unin_main_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + + return val; +} + +static uint32_t pci_unin_main_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_main_write[] = { + &pci_unin_main_writeb, + &pci_unin_main_writew, + &pci_unin_main_writel, +}; + +static CPUReadMemoryFunc *pci_unin_main_read[] = { + &pci_unin_main_readb, + &pci_unin_main_readw, + &pci_unin_main_readl, +}; + +#if 0 + +static void pci_unin_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + s->config_reg = 0x80000000 | (val & ~0x00000001); +} + +static uint32_t pci_unin_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = (s->config_reg | 0x00000001) & ~0x80000000; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_config_write[] = { + &pci_unin_config_writel, + &pci_unin_config_writel, + &pci_unin_config_writel, +}; + +static CPUReadMemoryFunc *pci_unin_config_read[] = { + &pci_unin_config_readl, + &pci_unin_config_readl, + &pci_unin_config_readl, +}; + +static void pci_unin_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + pci_data_write(s, addr & 3, val, 1); +} + +static void pci_unin_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr & 3, val, 2); +} + +static void pci_unin_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr & 3, val, 4); +} + +static uint32_t pci_unin_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 3, 1); + + return val; +} + +static uint32_t pci_unin_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 3, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif - pci_mem_config = cpu_register_io_memory(0, pci_pmac_config_read, - pci_pmac_config_write); - pci_mem_data = cpu_register_io_memory(0, pci_pmac_read, pci_pmac_write); + return val; +} - cpu_register_physical_memory(0xfec00000, 0x1000, pci_mem_config); - cpu_register_physical_memory(0xfee00000, 0x1000, pci_mem_data); +static uint32_t pci_unin_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; - d = pci_register_device("MPC106", sizeof(PCIDevice), 0, 0, + val = pci_data_read(s, addr & 3, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_write[] = { + &pci_unin_writeb, + &pci_unin_writew, + &pci_unin_writel, +}; + +static CPUReadMemoryFunc *pci_unin_read[] = { + &pci_unin_readb, + &pci_unin_readw, + &pci_unin_readl, +}; +#endif + +static void pmac_set_irq(PCIDevice *d, int irq_num, int level) +{ + openpic_t *openpic; + /* XXX: we do not simulate the hardware - we rely on the BIOS to + set correctly for irq line field */ + openpic = d->bus->openpic; +#ifdef TARGET_PPC + if (openpic) + openpic_set_irq(openpic, d->config[PCI_INTERRUPT_LINE], level); +#endif +} + +void pci_pmac_set_openpic(PCIBus *bus, openpic_t *openpic) +{ + bus->openpic = openpic; +} + +PCIBus *pci_pmac_init(void) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data; + + /* Use values found on a real PowerMac */ + /* Uninorth main bus */ + s = pci_register_bus(); + s->set_irq = pmac_set_irq; + + pci_mem_config = cpu_register_io_memory(0, pci_unin_main_config_read, + pci_unin_main_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_main_read, + pci_unin_main_write, s); + cpu_register_physical_memory(0xf2800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf2c00000, 0x1000, pci_mem_data); + s->devfn_min = 11 << 3; + d = pci_register_device(s, "Uni-north main", sizeof(PCIDevice), + 11 << 3, NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x1F; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer + +#if 0 // XXX: not activated as PPC BIOS doesn't handle mutiple buses properly + /* pci-to-pci bridge */ + d = pci_register_device("Uni-north bridge", sizeof(PCIDevice), 0, 13 << 3, NULL, NULL); + d->config[0x00] = 0x11; // vendor_id : TI + d->config[0x01] = 0x10; + d->config[0x02] = 0x26; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x05; // revision + d->config[0x0A] = 0x04; // class_sub = pci2pci + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x20; // latency_timer + d->config[0x0E] = 0x01; // header_type + + d->config[0x18] = 0x01; // primary_bus + d->config[0x19] = 0x02; // secondary_bus + d->config[0x1A] = 0x02; // subordinate_bus + d->config[0x1B] = 0x20; // secondary_latency_timer + d->config[0x1C] = 0x11; // io_base + d->config[0x1D] = 0x01; // io_limit + d->config[0x20] = 0x00; // memory_base + d->config[0x21] = 0x80; + d->config[0x22] = 0x00; // memory_limit + d->config[0x23] = 0x80; + d->config[0x24] = 0x01; // prefetchable_memory_base + d->config[0x25] = 0x80; + d->config[0x26] = 0xF1; // prefectchable_memory_limit + d->config[0x27] = 0x7F; + // d->config[0x34] = 0xdc // capabilities_pointer +#endif +#if 0 // XXX: not needed for now + /* Uninorth AGP bus */ + s = &pci_bridge[1]; + pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read, + pci_unin_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_read, + pci_unin_write, s); + cpu_register_physical_memory(0xf0800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf0c00000, 0x1000, pci_mem_data); + + d = pci_register_device("Uni-north AGP", sizeof(PCIDevice), 0, 11 << 3, + NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x20; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + // d->config[0x34] = 0x80; // capabilities_pointer +#endif +#if 0 // XXX: not needed for now + /* Uninorth internal bus */ + s = &pci_bridge[2]; + pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read, + pci_unin_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_read, + pci_unin_write, s); + cpu_register_physical_memory(0xf4800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf4c00000, 0x1000, pci_mem_data); + + d = pci_register_device("Uni-north internal", sizeof(PCIDevice), + 3, 11 << 3, NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x1E; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer +#endif + +#if 0 // Grackle ? /* same values as PearPC - check this */ d->config[0x00] = 0x11; // vendor_id d->config[0x01] = 0x10; @@ -721,63 +1219,19 @@ void pci_pmac_init(void) d->config[0x25] = 0x84; d->config[0x26] = 0x00; // prefetchable_memory_limit d->config[0x27] = 0x85; +#endif + return s; } /***********************************************************/ /* generic PCI irq support */ -/* return the global irq number corresponding to a given device irq - pin. We could also use the bus number to have a more precise - mapping. */ -static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) -{ - int slot_addend; - slot_addend = (pci_dev->devfn >> 3); - return (irq_num + slot_addend) & 3; -} - /* 0 <= irq_num <= 3. level must be 0 or 1 */ -#ifdef TARGET_PPC void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level) { + PCIBus *bus = pci_dev->bus; + bus->set_irq(pci_dev, irq_num, level); } -#else -void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level) -{ - int irq_index, shift, pic_irq, pic_level; - uint32_t *p; - - irq_num = pci_slot_get_pirq(pci_dev, irq_num); - irq_index = pci_dev->irq_index; - p = &pci_irq_levels[irq_num][irq_index >> 5]; - shift = (irq_index & 0x1f); - *p = (*p & ~(1 << shift)) | (level << shift); - - /* now we change the pic irq level according to the piix irq mappings */ - pic_irq = piix3_state->dev.config[0x60 + irq_num]; - if (pic_irq < 16) { - /* the pic level is the logical OR of all the PCI irqs mapped - to it */ - pic_level = 0; -#if (PCI_IRQ_WORDS == 2) - pic_level = ((pci_irq_levels[irq_num][0] | - pci_irq_levels[irq_num][1]) != 0); -#else - { - int i; - pic_level = 0; - for(i = 0; i < PCI_IRQ_WORDS; i++) { - if (pci_irq_levels[irq_num][i]) { - pic_level = 1; - break; - } - } - } -#endif - pic_set_irq(pic_irq, pic_level); - } -} -#endif /***********************************************************/ /* monitor info on PCI */ @@ -788,7 +1242,7 @@ static void pci_info_device(PCIDevice *d) PCIIORegion *r; printf(" Bus %2d, device %3d, function %d:\n", - d->bus_num, d->devfn >> 3, d->devfn & 7); + d->bus->bus_num, d->devfn >> 3, d->devfn & 7); class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE))); printf(" "); switch(class) { @@ -812,7 +1266,7 @@ static void pci_info_device(PCIDevice *d) if (d->config[PCI_INTERRUPT_PIN] != 0) { printf(" IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]); } - for(i = 0;i < 6; i++) { + for(i = 0;i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; if (r->size != 0) { printf(" BAR%d: ", i); @@ -829,17 +1283,15 @@ static void pci_info_device(PCIDevice *d) void pci_info(void) { - PCIBridge *s = &pci_bridge; - PCIDevice **bus; - int bus_num, devfn; + PCIBus *bus = first_bus; + PCIDevice *d; + int devfn; - for(bus_num = 0; bus_num < 256; bus_num++) { - bus = s->pci_bus[bus_num]; - if (bus) { - for(devfn = 0; devfn < 256; devfn++) { - if (bus[devfn]) - pci_info_device(bus[devfn]); - } + if (bus) { + for(devfn = 0; devfn < 256; devfn++) { + d = bus->devices[devfn]; + if (d) + pci_info_device(d); } } } @@ -847,7 +1299,7 @@ void pci_info(void) /***********************************************************/ /* XXX: the following should be moved to the PC BIOS */ -static uint32_t isa_inb(uint32_t addr) +static __attribute__((unused)) uint32_t isa_inb(uint32_t addr) { return cpu_inb(cpu_single_env, addr); } @@ -857,70 +1309,70 @@ static void isa_outb(uint32_t val, uint32_t addr) cpu_outb(cpu_single_env, addr, val); } -static uint32_t isa_inw(uint32_t addr) +static __attribute__((unused)) uint32_t isa_inw(uint32_t addr) { return cpu_inw(cpu_single_env, addr); } -static void isa_outw(uint32_t val, uint32_t addr) +static __attribute__((unused)) void isa_outw(uint32_t val, uint32_t addr) { cpu_outw(cpu_single_env, addr, val); } -static uint32_t isa_inl(uint32_t addr) +static __attribute__((unused)) uint32_t isa_inl(uint32_t addr) { return cpu_inl(cpu_single_env, addr); } -static void isa_outl(uint32_t val, uint32_t addr) +static __attribute__((unused)) void isa_outl(uint32_t val, uint32_t addr) { cpu_outl(cpu_single_env, addr, val); } static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | addr; pci_data_write(s, 0, val, 4); } static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | (addr & ~3); pci_data_write(s, addr & 3, val, 2); } static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | (addr & ~3); pci_data_write(s, addr & 3, val, 1); } -static uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) +static __attribute__((unused)) uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | addr; return pci_data_read(s, 0, 4); } static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | (addr & ~3); return pci_data_read(s, addr & 3, 2); } static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) { - PCIBridge *s = &pci_bridge; - s->config_reg = 0x80000000 | (d->bus_num << 16) | + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | (d->devfn << 8) | (addr & ~3); return pci_data_read(s, addr & 3, 1); } @@ -934,13 +1386,22 @@ static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) { PCIIORegion *r; uint16_t cmd; + uint32_t ofs; + + if ( region_num == PCI_ROM_SLOT ) { + ofs = 0x30; + }else{ + ofs = 0x10 + region_num * 4; + } - pci_config_writel(d, 0x10 + region_num * 4, addr); + pci_config_writel(d, ofs, addr); r = &d->io_regions[region_num]; /* enable memory mappings */ cmd = pci_config_readw(d, PCI_COMMAND); - if (r->type & PCI_ADDRESS_SPACE_IO) + if ( region_num == PCI_ROM_SLOT ) + cmd |= 2; + else if (r->type & PCI_ADDRESS_SPACE_IO) cmd |= 1; else cmd |= 2; @@ -955,14 +1416,15 @@ static void pci_bios_init_device(PCIDevice *d) int i, pin, pic_irq, vendor_id, device_id; class = pci_config_readw(d, PCI_CLASS_DEVICE); + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); switch(class) { case 0x0101: - vendor_id = pci_config_readw(d, PCI_VENDOR_ID); - device_id = pci_config_readw(d, PCI_DEVICE_ID); if (vendor_id == 0x8086 && device_id == 0x7010) { /* PIIX3 IDE */ - pci_config_writew(d, PCI_COMMAND, PCI_COMMAND_IO); pci_config_writew(d, 0x40, 0x8000); // enable IDE0 + pci_config_writew(d, 0x42, 0x8000); // enable IDE1 + goto default_map; } else { /* IDE: we map it as in ISA mode */ pci_set_io_region_addr(d, 0, 0x1f0); @@ -972,12 +1434,34 @@ static void pci_bios_init_device(PCIDevice *d) } break; case 0x0300: + if (vendor_id != 0x1234) + goto default_map; /* VGA: map frame buffer to default Bochs VBE address */ pci_set_io_region_addr(d, 0, 0xE0000000); break; + case 0x0800: + /* PIC */ + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + if (vendor_id == 0x1014) { + /* IBM */ + if (device_id == 0x0046 || device_id == 0xFFFF) { + /* MPIC & MPIC2 */ + pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000); + } + } + break; + case 0xff00: + if (vendor_id == 0x0106b && + (device_id == 0x0017 || device_id == 0x0022)) { + /* macio bridge */ + pci_set_io_region_addr(d, 0, 0x80800000); + } + break; default: + default_map: /* default memory mappings */ - for(i = 0; i < 6; i++) { + for(i = 0; i < PCI_NUM_REGIONS; i++) { r = &d->io_regions[i]; if (r->size) { if (r->type & PCI_ADDRESS_SPACE_IO) @@ -1008,9 +1492,9 @@ static void pci_bios_init_device(PCIDevice *d) */ void pci_bios_init(void) { - PCIBridge *s = &pci_bridge; - PCIDevice **bus; - int bus_num, devfn, i, irq; + PCIBus *bus; + PCIDevice *d; + int devfn, i, irq; uint8_t elcr[2]; pci_bios_io_addr = 0xc000; @@ -1029,54 +1513,12 @@ void pci_bios_init(void) isa_outb(elcr[0], 0x4d0); isa_outb(elcr[1], 0x4d1); - for(bus_num = 0; bus_num < 256; bus_num++) { - bus = s->pci_bus[bus_num]; - if (bus) { - for(devfn = 0; devfn < 256; devfn++) { - if (bus[devfn]) - pci_bios_init_device(bus[devfn]); - } - } - } -} - -/* - * This function initializes the PCI devices as a normal PCI BIOS - * would do. It is provided just in case the BIOS has no support for - * PCI. - */ -void pci_ppc_bios_init(void) -{ - PCIBridge *s = &pci_bridge; - PCIDevice **bus; - int bus_num, devfn, i, irq; - uint8_t elcr[2]; - - pci_bios_io_addr = 0xc000; - pci_bios_mem_addr = 0xc0000000; - -#if 0 - /* activate IRQ mappings */ - elcr[0] = 0x00; - elcr[1] = 0x00; - for(i = 0; i < 4; i++) { - irq = pci_irqs[i]; - /* set to trigger level */ - elcr[irq >> 3] |= (1 << (irq & 7)); - /* activate irq remapping in PIIX */ - pci_config_writeb((PCIDevice *)piix3_state, 0x60 + i, irq); - } - isa_outb(elcr[0], 0x4d0); - isa_outb(elcr[1], 0x4d1); -#endif - - for(bus_num = 0; bus_num < 256; bus_num++) { - bus = s->pci_bus[bus_num]; - if (bus) { - for(devfn = 0; devfn < 256; devfn++) { - if (bus[devfn]) - pci_bios_init_device(bus[devfn]); - } + bus = first_bus; + if (bus) { + for(devfn = 0; devfn < 256; devfn++) { + d = bus->devices[devfn]; + if (d) + pci_bios_init_device(d); } } }