Message ID | c5935a9c12aa282e39bb5960a5db3a8e5d95e446.1276755023.git.yamahata@valinux.co.jp |
---|---|
State | New |
Headers | show |
On Thu, Jun 17, 2010 at 03:15:46PM +0900, Isaku Yamahata wrote: > introduce pci bridge layer. > export pci_bridge_write_config() for generic use. > support device reset and bus reset of bridge control. > convert apb bridge and dec p2p bridge to use new pci bridge layer. > save/restore is supported as a side effect. > > This might be a bit over engineering, but this is also preparation > for pci express root/upstream/downstream port. > > Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> Well, preparations are easier to judge with patches that use them. > --- > hw/apb_pci.c | 38 +++++++++----- > hw/dec_pci.c | 28 +++++++--- > hw/pci_bridge.c | 146 +++++++++++++++++++++++++++++++++++++------------------ > hw/pci_bridge.h | 35 ++++++++++++- > qemu-common.h | 1 + > 5 files changed, 177 insertions(+), 71 deletions(-) > > diff --git a/hw/apb_pci.c b/hw/apb_pci.c > index c11d9b5..cb9051b 100644 > --- a/hw/apb_pci.c > +++ b/hw/apb_pci.c > @@ -31,6 +31,7 @@ > #include "pci_host.h" > #include "pci_bridge.h" > #include "rwhandler.h" > +#include "pci_bridge.h" > #include "apb_pci.h" > #include "sysemu.h" > > @@ -294,9 +295,12 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) > } > } > > -static void apb_pci_bridge_init(PCIBus *b) > +static int apb_pci_bridge_init(PCIBridge *br) > { > - PCIDevice *dev = pci_bridge_get_device(b); > + PCIDevice *dev = &br->dev; > + > + pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN); > + pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA); > > /* > * command register: > @@ -316,6 +320,8 @@ static void apb_pci_bridge_init(PCIBus *b) > pci_set_byte(dev->config + PCI_HEADER_TYPE, > pci_get_byte(dev->config + PCI_HEADER_TYPE) | > PCI_HEADER_TYPE_MULTI_FUNCTION); > + > + return 0; > } > > PCIBus *pci_apb_init(target_phys_addr_t special_base, > @@ -326,6 +332,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, > SysBusDevice *s; > APBState *d; > unsigned int i; > + PCIBridge *br; > > /* Ultrasparc PBM main bus */ > dev = qdev_create(NULL, "pbm"); > @@ -351,17 +358,13 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, > pci_create_simple(d->bus, 0, "pbm"); > > /* APB secondary busses */ > - *bus2 = pci_bridge_init(d->bus, PCI_DEVFN(1, 0), > - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, > - pci_apb_map_irq, > - "Advanced PCI Bus secondary bridge 1"); > - apb_pci_bridge_init(*bus2); > - > - *bus3 = pci_bridge_init(d->bus, PCI_DEVFN(1, 1), > - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, > - pci_apb_map_irq, > - "Advanced PCI Bus secondary bridge 2"); > - apb_pci_bridge_init(*bus3); > + br = pci_bridge_create_simple(d->bus, PCI_DEVFN(1, 0), "pbm-bridge", > + "Advanced PCI Bus secondary bridge 1"); > + *bus2 = pci_bridge_get_sec_bus(br); > + > + br = pci_bridge_create_simple(d->bus, PCI_DEVFN(1, 1), "pbm-bridge", > + "Advanced PCI Bus secondary bridge 2"); > + *bus3 = pci_bridge_get_sec_bus(br); > > return d->bus; > } > @@ -446,10 +449,19 @@ static SysBusDeviceInfo pbm_host_info = { > .qdev.reset = pci_pbm_reset, > .init = pci_pbm_init_device, > }; > + > +static PCIBridgeInfo pbm_pci_bridge_info = { > + .pci.qdev.name = "pbm-bridge", > + .pci.qdev.vmsd = &vmstate_pci_device, > + .init = apb_pci_bridge_init, > + .map_irq = pci_apb_map_irq, > +}; > + > static void pbm_register_devices(void) > { > sysbus_register_withprop(&pbm_host_info); > pci_qdev_register(&pbm_pci_host_info); > + pci_bridge_qdev_register(&pbm_pci_bridge_info); > } > > device_init(pbm_register_devices) > diff --git a/hw/dec_pci.c b/hw/dec_pci.c > index b2759dd..45b5c28 100644 > --- a/hw/dec_pci.c > +++ b/hw/dec_pci.c > @@ -49,18 +49,27 @@ static int dec_map_irq(PCIDevice *pci_dev, int irq_num) > return irq_num; > } > > -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) > +static int dec_21154_initfn(PCIBridge *br) > { > - DeviceState *dev; > - PCIBus *ret; > + pci_config_set_vendor_id(br->dev.config, PCI_VENDOR_ID_DEC); > + pci_config_set_device_id(br->dev.config, PCI_DEVICE_ID_DEC_21154); > + return 0; > +} > > - dev = qdev_create(NULL, "dec-21154"); > - qdev_init_nofail(dev); > - ret = pci_bridge_init(parent_bus, devfn, > - PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21154, > - dec_map_irq, "DEC 21154 PCI-PCI bridge"); > +static PCIBridgeInfo dec_21154_pci_bridge_info = { > + .pci.qdev.name = "dec-21154-p2p-bridge", > + .pci.qdev.desc = "DEC 21154 PCI-PCI bridge", > + .pci.qdev.vmsd = &vmstate_pci_device, > + .init = dec_21154_initfn, > + .map_irq = dec_map_irq, > +}; > > - return ret; > +PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) > +{ > + PCIBridge *br; > + br = pci_bridge_create_simple(parent_bus, devfn, "dec-21154-p2p-bridge", > + "DEC 21154 PCI-PCI bridge"); > + return pci_bridge_get_sec_bus(br); > } > > static int pci_dec_21154_init_device(SysBusDevice *dev) > @@ -99,6 +108,7 @@ static void dec_register_devices(void) > sysbus_register_dev("dec-21154", sizeof(DECState), > pci_dec_21154_init_device); > pci_qdev_register(&dec_21154_pci_host_info); > + pci_bridge_qdev_register(&dec_21154_pci_bridge_info); > } > > device_init(dec_register_devices) Please document the APIs. For example, what is pci_bridge_create_simple and how does it differ from pci_bridge_create? > diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c > index bf746f1..43c21d4 100644 > --- a/hw/pci_bridge.c > +++ b/hw/pci_bridge.c > @@ -29,16 +29,10 @@ > > #include "pci_bridge.h" > > -typedef struct { > - PCIDevice dev; > - PCIBus *bus; > - uint32_t vid; > - uint32_t did; > -} PCIBridge; > - > -static void pci_bridge_write_config(PCIDevice *d, > - uint32_t address, uint32_t val, int len) > +void pci_bridge_write_config(PCIDevice *d, > + uint32_t address, uint32_t val, int len) > { > + uint16_t bridge_control = pci_get_word(d->config + PCI_BRIDGE_CONTROL); > pci_default_write_config(d, address, val, len); > > if (/* io base/limit */ > @@ -49,64 +43,122 @@ static void pci_bridge_write_config(PCIDevice *d, > ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { > pci_bridge_update_mappings(d->bus); > } > + > + if (ranges_overlap(address, len, PCI_BRIDGE_CONTROL, 2)) { > + uint16_t new = pci_get_word(d->config + PCI_BRIDGE_CONTROL); > + if (!(bridge_control & PCI_BRIDGE_CTL_BUS_RESET) && > + (new & PCI_BRIDGE_CTL_BUS_RESET)) { > + /* 0 -> 1 */ > + PCIBridge *br = DO_UPCAST(PCIBridge, dev, d); > + pci_bus_reset(br->bus); > + } > + } > } > > -static int pci_bridge_initfn(PCIDevice *dev) > +void pci_bridge_reset_reg(PCIDevice *dev) > { > - PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev); > + uint8_t *conf = dev->config; > + > + conf[PCI_PRIMARY_BUS] = 0; > + conf[PCI_SECONDARY_BUS] = 0; > + conf[PCI_SUBORDINATE_BUS] = 0; > + conf[PCI_SEC_LATENCY_TIMER] = 0; > > - pci_config_set_vendor_id(s->dev.config, s->vid); > - pci_config_set_device_id(s->dev.config, s->did); > + conf[PCI_IO_BASE] = 0; > + conf[PCI_IO_LIMIT] = 0; > + pci_set_word(conf + PCI_MEMORY_BASE, 0); > + pci_set_word(conf + PCI_MEMORY_LIMIT, 0); > + pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0); > + pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0); > + pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0); > + pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0); > + > + pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); > +} > + > +void pci_bridge_reset(DeviceState *qdev) > +{ > + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); > + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); > + > + pci_bus_reset(br->bus); > + pci_bridge_reset_reg(dev); > + pci_device_reset_default(dev); > +} > + > +static int pci_bridge_initfn(PCIDevice *dev) > +{ > + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); > + PCIDeviceInfo *pci_info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info); > + PCIBridgeInfo *info = DO_UPCAST(PCIBridgeInfo, pci, pci_info); > + int rc = 0; > > pci_set_word(dev->config + PCI_STATUS, > PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); > pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); > - dev->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE; > pci_set_word(dev->config + PCI_SEC_STATUS, > PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); > - return 0; > + > + br->bus = pci_register_secondary_bus(dev->bus, dev, > + info->map_irq, br->bus_name); > + if (!br->bus) { > + return -1; > + } > + if (info->init) { > + rc = info->init(br); > + } > + return rc; > } > > -static int pci_bridge_exitfn(PCIDevice *pci_dev) > +static int pci_bridge_exitfn(PCIDevice *dev) > { > - PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); > - pci_unregister_secondary_bus(s->bus); > + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); > + PCIDeviceInfo *pci_info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info); > + PCIBridgeInfo *info = DO_UPCAST(PCIBridgeInfo, pci, pci_info); > + > + if (info->exit) { > + int rc = info->exit(br); > + if (rc){ > + return rc; > + } > + } > + pci_unregister_secondary_bus(br->bus); > return 0; > } > > -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, > - pci_map_irq_fn map_irq, const char *name) > +void pci_bridge_qdev_register(PCIBridgeInfo *info) > { > - PCIDevice *dev; > - PCIBridge *s; > - > - dev = pci_create(bus, devfn, "pci-bridge"); > - qdev_prop_set_uint32(&dev->qdev, "vendorid", vid); > - qdev_prop_set_uint32(&dev->qdev, "deviceid", did); > - qdev_init_nofail(&dev->qdev); > - > - s = DO_UPCAST(PCIBridge, dev, dev); > - s->bus = pci_register_secondary_bus(bus, &s->dev, map_irq, name); > - return s->bus; > + if (!info->pci.qdev.size) { > + info->pci.qdev.size = sizeof(PCIBridge); > + } > + info->pci.init = pci_bridge_initfn; > + info->pci.exit = pci_bridge_exitfn; > + info->pci.header_type = PCI_HEADER_TYPE_BRIDGE; > + if (!info->pci.config_write) { > + info->pci.config_write = pci_bridge_write_config; > + } > + if (!info->pci.qdev.reset) { > + info->pci.qdev.reset = pci_bridge_reset; > + } > + pci_qdev_register(&info->pci); > } > > -static PCIDeviceInfo bridge_info = { > - .qdev.name = "pci-bridge", > - .qdev.size = sizeof(PCIBridge), > - .init = pci_bridge_initfn, > - .exit = pci_bridge_exitfn, > - .config_write = pci_bridge_write_config, > - .header_type = PCI_HEADER_TYPE_BRIDGE, > - .qdev.props = (Property[]) { > - DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0), > - DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0), > - DEFINE_PROP_END_OF_LIST(), > - } > -}; > +PCIBridge *pci_bridge_create(PCIBus *bus, int devfn, const char *name) > +{ > + PCIDevice *dev = pci_create(bus, devfn, name); > + return DO_UPCAST(PCIBridge, dev, dev); > +} > > -static void pci_register_devices(void) > +void pci_bridge_set_bus_name(PCIBridge *br, const char *bus_name) > { > - pci_qdev_register(&bridge_info); > + br->bus_name = bus_name; > } > > -device_init(pci_register_devices) > +PCIBridge *pci_bridge_create_simple(PCIBus *bus, int devfn, > + const char *name, const char *bus_name) > +{ > + PCIBridge *br = pci_bridge_create(bus, devfn, name); > + pci_bridge_set_bus_name(br, bus_name); > + qdev_init_nofail(&br->dev.qdev); > + return br; > +} > diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h > index 1d46abf..2747e7f 100644 > --- a/hw/pci_bridge.h > +++ b/hw/pci_bridge.h > @@ -23,8 +23,39 @@ > > #include "pci.h" > > -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, > - pci_map_irq_fn map_irq, const char *name); > +struct PCIBridge { > + PCIDevice dev; > + > + /* private member */ > + PCIBus *bus; > + const char *bus_name; > +}; > + > +static inline PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) > +{ > + return br->bus; > +} > + > +void pci_bridge_write_config(PCIDevice *d, > + uint32_t address, uint32_t val, int len); > +void pci_bridge_reset_reg(PCIDevice *dev); > +void pci_bridge_reset(DeviceState *qdev); > + > +typedef int (*pci_bridge_qdev_initfn)(PCIBridge *br); > +typedef int (*pci_bridge_qdev_exitfn)(PCIBridge *br); > +typedef struct struct PCIBridgeInfo, so we can later forward declare if we need to. > +{ > + PCIDeviceInfo pci; > + pci_bridge_qdev_initfn init; > + pci_bridge_qdev_exitfn exit; > + pci_map_irq_fn map_irq; > +} PCIBridgeInfo; > + > +void pci_bridge_qdev_register(PCIBridgeInfo *info); > +PCIBridge *pci_bridge_create(PCIBus *bus, int devfn, const char *name); > +void pci_bridge_set_bus_name(PCIBridge *br, const char *bus_name); > +PCIBridge *pci_bridge_create_simple(PCIBus *bus, int devfn, > + const char *name, const char *bus_name); > > #endif /* QEMU_PCI_BRIDGE_H */ > /* > diff --git a/qemu-common.h b/qemu-common.h > index d133f35..8257311 100644 > --- a/qemu-common.h > +++ b/qemu-common.h > @@ -221,6 +221,7 @@ typedef struct PCIHostState PCIHostState; > typedef struct PCIExpressHost PCIExpressHost; > typedef struct PCIBus PCIBus; > typedef struct PCIDevice PCIDevice; > +typedef struct PCIBridge PCIBridge; > typedef struct SerialState SerialState; > typedef struct IRQState *qemu_irq; > typedef struct PCMCIACardState PCMCIACardState; > -- > 1.6.6.1
diff --git a/hw/apb_pci.c b/hw/apb_pci.c index c11d9b5..cb9051b 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -31,6 +31,7 @@ #include "pci_host.h" #include "pci_bridge.h" #include "rwhandler.h" +#include "pci_bridge.h" #include "apb_pci.h" #include "sysemu.h" @@ -294,9 +295,12 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) } } -static void apb_pci_bridge_init(PCIBus *b) +static int apb_pci_bridge_init(PCIBridge *br) { - PCIDevice *dev = pci_bridge_get_device(b); + PCIDevice *dev = &br->dev; + + pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN); + pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA); /* * command register: @@ -316,6 +320,8 @@ static void apb_pci_bridge_init(PCIBus *b) pci_set_byte(dev->config + PCI_HEADER_TYPE, pci_get_byte(dev->config + PCI_HEADER_TYPE) | PCI_HEADER_TYPE_MULTI_FUNCTION); + + return 0; } PCIBus *pci_apb_init(target_phys_addr_t special_base, @@ -326,6 +332,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, SysBusDevice *s; APBState *d; unsigned int i; + PCIBridge *br; /* Ultrasparc PBM main bus */ dev = qdev_create(NULL, "pbm"); @@ -351,17 +358,13 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, pci_create_simple(d->bus, 0, "pbm"); /* APB secondary busses */ - *bus2 = pci_bridge_init(d->bus, PCI_DEVFN(1, 0), - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, - pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 1"); - apb_pci_bridge_init(*bus2); - - *bus3 = pci_bridge_init(d->bus, PCI_DEVFN(1, 1), - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, - pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 2"); - apb_pci_bridge_init(*bus3); + br = pci_bridge_create_simple(d->bus, PCI_DEVFN(1, 0), "pbm-bridge", + "Advanced PCI Bus secondary bridge 1"); + *bus2 = pci_bridge_get_sec_bus(br); + + br = pci_bridge_create_simple(d->bus, PCI_DEVFN(1, 1), "pbm-bridge", + "Advanced PCI Bus secondary bridge 2"); + *bus3 = pci_bridge_get_sec_bus(br); return d->bus; } @@ -446,10 +449,19 @@ static SysBusDeviceInfo pbm_host_info = { .qdev.reset = pci_pbm_reset, .init = pci_pbm_init_device, }; + +static PCIBridgeInfo pbm_pci_bridge_info = { + .pci.qdev.name = "pbm-bridge", + .pci.qdev.vmsd = &vmstate_pci_device, + .init = apb_pci_bridge_init, + .map_irq = pci_apb_map_irq, +}; + static void pbm_register_devices(void) { sysbus_register_withprop(&pbm_host_info); pci_qdev_register(&pbm_pci_host_info); + pci_bridge_qdev_register(&pbm_pci_bridge_info); } device_init(pbm_register_devices) diff --git a/hw/dec_pci.c b/hw/dec_pci.c index b2759dd..45b5c28 100644 --- a/hw/dec_pci.c +++ b/hw/dec_pci.c @@ -49,18 +49,27 @@ static int dec_map_irq(PCIDevice *pci_dev, int irq_num) return irq_num; } -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) +static int dec_21154_initfn(PCIBridge *br) { - DeviceState *dev; - PCIBus *ret; + pci_config_set_vendor_id(br->dev.config, PCI_VENDOR_ID_DEC); + pci_config_set_device_id(br->dev.config, PCI_DEVICE_ID_DEC_21154); + return 0; +} - dev = qdev_create(NULL, "dec-21154"); - qdev_init_nofail(dev); - ret = pci_bridge_init(parent_bus, devfn, - PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21154, - dec_map_irq, "DEC 21154 PCI-PCI bridge"); +static PCIBridgeInfo dec_21154_pci_bridge_info = { + .pci.qdev.name = "dec-21154-p2p-bridge", + .pci.qdev.desc = "DEC 21154 PCI-PCI bridge", + .pci.qdev.vmsd = &vmstate_pci_device, + .init = dec_21154_initfn, + .map_irq = dec_map_irq, +}; - return ret; +PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) +{ + PCIBridge *br; + br = pci_bridge_create_simple(parent_bus, devfn, "dec-21154-p2p-bridge", + "DEC 21154 PCI-PCI bridge"); + return pci_bridge_get_sec_bus(br); } static int pci_dec_21154_init_device(SysBusDevice *dev) @@ -99,6 +108,7 @@ static void dec_register_devices(void) sysbus_register_dev("dec-21154", sizeof(DECState), pci_dec_21154_init_device); pci_qdev_register(&dec_21154_pci_host_info); + pci_bridge_qdev_register(&dec_21154_pci_bridge_info); } device_init(dec_register_devices) diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c index bf746f1..43c21d4 100644 --- a/hw/pci_bridge.c +++ b/hw/pci_bridge.c @@ -29,16 +29,10 @@ #include "pci_bridge.h" -typedef struct { - PCIDevice dev; - PCIBus *bus; - uint32_t vid; - uint32_t did; -} PCIBridge; - -static void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) { + uint16_t bridge_control = pci_get_word(d->config + PCI_BRIDGE_CONTROL); pci_default_write_config(d, address, val, len); if (/* io base/limit */ @@ -49,64 +43,122 @@ static void pci_bridge_write_config(PCIDevice *d, ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { pci_bridge_update_mappings(d->bus); } + + if (ranges_overlap(address, len, PCI_BRIDGE_CONTROL, 2)) { + uint16_t new = pci_get_word(d->config + PCI_BRIDGE_CONTROL); + if (!(bridge_control & PCI_BRIDGE_CTL_BUS_RESET) && + (new & PCI_BRIDGE_CTL_BUS_RESET)) { + /* 0 -> 1 */ + PCIBridge *br = DO_UPCAST(PCIBridge, dev, d); + pci_bus_reset(br->bus); + } + } } -static int pci_bridge_initfn(PCIDevice *dev) +void pci_bridge_reset_reg(PCIDevice *dev) { - PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev); + uint8_t *conf = dev->config; + + conf[PCI_PRIMARY_BUS] = 0; + conf[PCI_SECONDARY_BUS] = 0; + conf[PCI_SUBORDINATE_BUS] = 0; + conf[PCI_SEC_LATENCY_TIMER] = 0; - pci_config_set_vendor_id(s->dev.config, s->vid); - pci_config_set_device_id(s->dev.config, s->did); + conf[PCI_IO_BASE] = 0; + conf[PCI_IO_LIMIT] = 0; + pci_set_word(conf + PCI_MEMORY_BASE, 0); + pci_set_word(conf + PCI_MEMORY_LIMIT, 0); + pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0); + pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0); + pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0); + pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0); + + pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); +} + +void pci_bridge_reset(DeviceState *qdev) +{ + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + + pci_bus_reset(br->bus); + pci_bridge_reset_reg(dev); + pci_device_reset_default(dev); +} + +static int pci_bridge_initfn(PCIDevice *dev) +{ + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIDeviceInfo *pci_info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info); + PCIBridgeInfo *info = DO_UPCAST(PCIBridgeInfo, pci, pci_info); + int rc = 0; pci_set_word(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); - dev->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE; pci_set_word(dev->config + PCI_SEC_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - return 0; + + br->bus = pci_register_secondary_bus(dev->bus, dev, + info->map_irq, br->bus_name); + if (!br->bus) { + return -1; + } + if (info->init) { + rc = info->init(br); + } + return rc; } -static int pci_bridge_exitfn(PCIDevice *pci_dev) +static int pci_bridge_exitfn(PCIDevice *dev) { - PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); - pci_unregister_secondary_bus(s->bus); + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIDeviceInfo *pci_info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info); + PCIBridgeInfo *info = DO_UPCAST(PCIBridgeInfo, pci, pci_info); + + if (info->exit) { + int rc = info->exit(br); + if (rc){ + return rc; + } + } + pci_unregister_secondary_bus(br->bus); return 0; } -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name) +void pci_bridge_qdev_register(PCIBridgeInfo *info) { - PCIDevice *dev; - PCIBridge *s; - - dev = pci_create(bus, devfn, "pci-bridge"); - qdev_prop_set_uint32(&dev->qdev, "vendorid", vid); - qdev_prop_set_uint32(&dev->qdev, "deviceid", did); - qdev_init_nofail(&dev->qdev); - - s = DO_UPCAST(PCIBridge, dev, dev); - s->bus = pci_register_secondary_bus(bus, &s->dev, map_irq, name); - return s->bus; + if (!info->pci.qdev.size) { + info->pci.qdev.size = sizeof(PCIBridge); + } + info->pci.init = pci_bridge_initfn; + info->pci.exit = pci_bridge_exitfn; + info->pci.header_type = PCI_HEADER_TYPE_BRIDGE; + if (!info->pci.config_write) { + info->pci.config_write = pci_bridge_write_config; + } + if (!info->pci.qdev.reset) { + info->pci.qdev.reset = pci_bridge_reset; + } + pci_qdev_register(&info->pci); } -static PCIDeviceInfo bridge_info = { - .qdev.name = "pci-bridge", - .qdev.size = sizeof(PCIBridge), - .init = pci_bridge_initfn, - .exit = pci_bridge_exitfn, - .config_write = pci_bridge_write_config, - .header_type = PCI_HEADER_TYPE_BRIDGE, - .qdev.props = (Property[]) { - DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0), - DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0), - DEFINE_PROP_END_OF_LIST(), - } -}; +PCIBridge *pci_bridge_create(PCIBus *bus, int devfn, const char *name) +{ + PCIDevice *dev = pci_create(bus, devfn, name); + return DO_UPCAST(PCIBridge, dev, dev); +} -static void pci_register_devices(void) +void pci_bridge_set_bus_name(PCIBridge *br, const char *bus_name) { - pci_qdev_register(&bridge_info); + br->bus_name = bus_name; } -device_init(pci_register_devices) +PCIBridge *pci_bridge_create_simple(PCIBus *bus, int devfn, + const char *name, const char *bus_name) +{ + PCIBridge *br = pci_bridge_create(bus, devfn, name); + pci_bridge_set_bus_name(br, bus_name); + qdev_init_nofail(&br->dev.qdev); + return br; +} diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h index 1d46abf..2747e7f 100644 --- a/hw/pci_bridge.h +++ b/hw/pci_bridge.h @@ -23,8 +23,39 @@ #include "pci.h" -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name); +struct PCIBridge { + PCIDevice dev; + + /* private member */ + PCIBus *bus; + const char *bus_name; +}; + +static inline PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) +{ + return br->bus; +} + +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_bridge_reset_reg(PCIDevice *dev); +void pci_bridge_reset(DeviceState *qdev); + +typedef int (*pci_bridge_qdev_initfn)(PCIBridge *br); +typedef int (*pci_bridge_qdev_exitfn)(PCIBridge *br); +typedef struct +{ + PCIDeviceInfo pci; + pci_bridge_qdev_initfn init; + pci_bridge_qdev_exitfn exit; + pci_map_irq_fn map_irq; +} PCIBridgeInfo; + +void pci_bridge_qdev_register(PCIBridgeInfo *info); +PCIBridge *pci_bridge_create(PCIBus *bus, int devfn, const char *name); +void pci_bridge_set_bus_name(PCIBridge *br, const char *bus_name); +PCIBridge *pci_bridge_create_simple(PCIBus *bus, int devfn, + const char *name, const char *bus_name); #endif /* QEMU_PCI_BRIDGE_H */ /* diff --git a/qemu-common.h b/qemu-common.h index d133f35..8257311 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -221,6 +221,7 @@ typedef struct PCIHostState PCIHostState; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; +typedef struct PCIBridge PCIBridge; typedef struct SerialState SerialState; typedef struct IRQState *qemu_irq; typedef struct PCMCIACardState PCMCIACardState;
introduce pci bridge layer. export pci_bridge_write_config() for generic use. support device reset and bus reset of bridge control. convert apb bridge and dec p2p bridge to use new pci bridge layer. save/restore is supported as a side effect. This might be a bit over engineering, but this is also preparation for pci express root/upstream/downstream port. Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> --- hw/apb_pci.c | 38 +++++++++----- hw/dec_pci.c | 28 +++++++--- hw/pci_bridge.c | 146 +++++++++++++++++++++++++++++++++++++------------------ hw/pci_bridge.h | 35 ++++++++++++- qemu-common.h | 1 + 5 files changed, 177 insertions(+), 71 deletions(-)