Patchwork [04/10] pci_bridge: introduce pci bridge layer.

login
register
mail settings
Submitter Isaku Yamahata
Date June 17, 2010, 6:15 a.m.
Message ID <c5935a9c12aa282e39bb5960a5db3a8e5d95e446.1276755023.git.yamahata@valinux.co.jp>
Download mbox | patch
Permalink /patch/55969/
State New
Headers show

Comments

Isaku Yamahata - June 17, 2010, 6:15 a.m.
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(-)
Michael S. Tsirkin - June 17, 2010, 9:52 a.m.
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

Patch

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;