summaryrefslogtreecommitdiffstats
path: root/hw/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci.c')
-rw-r--r--hw/pci.c145
1 files changed, 29 insertions, 116 deletions
diff --git a/hw/pci.c b/hw/pci.c
index f2d7daf..f8f5716 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -255,6 +255,17 @@ static PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
return pci_find_bus(bus);
}
+static void pci_init_wmask(PCIDevice *dev)
+{
+ int i;
+ dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff;
+ dev->wmask[PCI_INTERRUPT_LINE] = 0xff;
+ dev->wmask[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+ | PCI_COMMAND_MASTER;
+ for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i)
+ dev->wmask[i] = 0xff;
+}
+
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
const char *name, int devfn,
@@ -276,6 +287,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state));
pci_set_default_subsystem_id(pci_dev);
+ pci_init_wmask(pci_dev);
if (!config_read)
config_read = pci_default_read_config;
@@ -347,6 +359,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
{
PCIIORegion *r;
uint32_t addr;
+ uint32_t wmask;
if ((unsigned int)region_num >= PCI_NUM_REGIONS)
return;
@@ -362,12 +375,17 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
r->size = size;
r->type = type;
r->map_func = map_func;
+
+ wmask = ~(size - 1);
if (region_num == PCI_ROM_SLOT) {
addr = 0x30;
+ /* ROM enable bit is writeable */
+ wmask |= 1;
} else {
addr = 0x10 + region_num * 4;
}
*(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
+ *(uint32_t *)(pci_dev->wmask + addr) = cpu_to_le32(wmask);
}
static void pci_update_mappings(PCIDevice *d)
@@ -476,118 +494,21 @@ uint32_t pci_default_read_config(PCIDevice *d,
return val;
}
-void pci_default_write_config(PCIDevice *d,
- uint32_t address, uint32_t val, int len)
+void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
{
- int can_write, i;
- uint32_t end, addr;
-
- if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) ||
- (address >= 0x30 && address < 0x34))) {
- PCIIORegion *r;
- int reg;
+ uint8_t orig[PCI_CONFIG_SPACE_SIZE];
+ int i;
- 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 */
- 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;
- }
- default_config:
/* not efficient, but simple */
- addr = address;
- for(i = 0; i < len; i++) {
- /* default read/write accesses */
- switch(d->config[0x0e]) {
- case 0x00:
- case 0x80:
- switch(addr) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0e:
- case 0x10 ... 0x27: /* base */
- case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */
- case 0x30 ... 0x33: /* rom */
- case 0x3d:
- can_write = 0;
- break;
- default:
- can_write = 1;
- break;
- }
- break;
- default:
- case 0x01:
- switch(addr) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x06:
- case 0x07:
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0e:
- case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */
- case 0x38 ... 0x3b: /* rom */
- case 0x3d:
- can_write = 0;
- break;
- default:
- can_write = 1;
- break;
- }
- break;
- }
- if (can_write) {
- /* Mask out writes to reserved bits in registers */
- switch (addr) {
- case 0x05:
- val &= ~PCI_COMMAND_RESERVED_MASK_HI;
- break;
- case 0x06:
- val &= ~PCI_STATUS_RESERVED_MASK_LO;
- break;
- case 0x07:
- val &= ~PCI_STATUS_RESERVED_MASK_HI;
- break;
- }
- d->config[addr] = val;
- }
- if (++addr > 0xff)
- break;
- val >>= 8;
+ memcpy(orig, d->config, PCI_CONFIG_SPACE_SIZE);
+ for(i = 0; i < l && addr < PCI_CONFIG_SPACE_SIZE; val >>= 8, ++i, ++addr) {
+ uint8_t wmask = d->wmask[addr];
+ d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask);
}
-
- end = address + len;
- if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) {
- /* if the command register is modified, we must modify the mappings */
+ if (memcmp(orig + PCI_BASE_ADDRESS_0, d->config + PCI_BASE_ADDRESS_0, 24)
+ || ((orig[PCI_COMMAND] ^ d->config[PCI_COMMAND])
+ & (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)))
pci_update_mappings(d);
- }
}
void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
@@ -880,16 +801,8 @@ static void pci_bridge_write_config(PCIDevice *d,
{
PCIBridge *s = (PCIBridge *)d;
- if (address == 0x19 || (address == 0x18 && len > 1)) {
- if (address == 0x19)
- s->bus->bus_num = val & 0xff;
- else
- s->bus->bus_num = (val >> 8) & 0xff;
-#if defined(DEBUG_PCI)
- printf ("pci-bridge: %s: Assigned bus %d\n", d->name, s->bus->bus_num);
-#endif
- }
pci_default_write_config(d, address, val, len);
+ s->bus->bus_num = d->config[PCI_SECONDARY_BUS];
}
PCIBus *pci_find_bus(int bus_num)
OpenPOWER on IntegriCloud