This patch fixes up a regression with virtio not working under the latest OpenBIOS SPARC64.
The error was introduced by the new interrupt routing code which needs to generate interrupt-map properties based upon the current active PCI slot (previously the OS would determine this automatically as the properties weren't present in the device tree).
In its current form, the code works by hard-coding the slots for the network and disk devices; however this fails to map the interrupt for devices added at QEMU runtime via device such as virtio.
This patchset fixes this problem in the following way:
1) Move the PCI host interrupt setup from out of the per-device host bridge callback (called generally towards the start of the PCI bus scan) and into a separate function called after the entire PCI bus has been scanned
2) Add code to dynamically generate the interrupt-map property for slots containing devices with an interrupt pin once the PCI bus scan is complete
3) As a bonus: tidy up the openpic interrupt mapping into the same routine
I believe there is a strong case for getting this patchset into the upcoming QEMU 2.2 release, so please let me know if there are any great objections.
Mark Cave-Ayland (4): pci.c: remove unneeded CONFIG_PPC define pci.c: rework pci_host_set_interrupt_map() to use phandle_t rather than pci_config_t macio.c: move openpic interrupt map initialisation to after PCI bus scan pci.c: move openpic interrupt mapping to post-PCI-bus scan
openbios-devel/drivers/macio.c | 40 --------- openbios-devel/drivers/pci.c | 185 +++++++++++++++++++++++++--------------- 2 files changed, 116 insertions(+), 109 deletions(-)
This is already covered by the surrounding define.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- openbios-devel/drivers/pci.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c index eca322d..333cea8 100644 --- a/openbios-devel/drivers/pci.c +++ b/openbios-devel/drivers/pci.c @@ -436,11 +436,9 @@ static void pci_host_set_interrupt_map(const pci_config_t *config) u32 props[7 * 8]; int i;
-#if defined(CONFIG_PPC) /* Oldworld macs do interrupt maps differently */ if(!is_newworld()) return; -#endif
for (i = 0; i < (7*8); i+=7) { props[i+PCI_INT_MAP_PCI0] = 0;
This is in preparation for some further work to set the interrupt map after the PCI bus has been enumerated.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- openbios-devel/drivers/pci.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c index 333cea8..c5148e9 100644 --- a/openbios-devel/drivers/pci.c +++ b/openbios-devel/drivers/pci.c @@ -421,7 +421,7 @@ static void pci_set_bus_range(const pci_config_t *config) #define SUN4U_INTERRUPT(dev, irq_pin) \ ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f)
-static void pci_host_set_interrupt_map(const pci_config_t *config) +static void pci_host_set_interrupt_map(phandle_t dev) { /* XXX We currently have a hook in the MPIC init code to fill in its handle. * If you want to have interrupt maps for your PCI host bus, add your @@ -432,7 +432,6 @@ static void pci_host_set_interrupt_map(const pci_config_t *config) * mechanism here. */ #if defined(CONFIG_PPC) - phandle_t dev = get_cur_dev(); u32 props[7 * 8]; int i;
@@ -458,7 +457,6 @@ static void pci_host_set_interrupt_map(const pci_config_t *config)
set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); #elif defined(CONFIG_SPARC64) - phandle_t dev = get_cur_dev(); uint32_t props[12]; int ncells, device, i;
@@ -469,7 +467,7 @@ static void pci_host_set_interrupt_map(const pci_config_t *config)
ncells += pci_encode_phys_addr(props + ncells, 0, 0, device, 0, 0); props[ncells++] = 1; - props[ncells++] = get_cur_dev(); + props[ncells++] = dev; props[ncells++] = SUN4U_INTERRUPT(device, 1); }
@@ -549,7 +547,7 @@ int host_config_cb(const pci_config_t *config) //XXX this overrides "reg" property pci_host_set_reg(get_cur_dev()); pci_host_set_ranges(config); - pci_host_set_interrupt_map(config); + pci_host_set_interrupt_map(get_cur_dev());
return 0; }
Here we move the interrupt initialisation from openpic_init to a new post-bus scan function called ob_pci_host_set_interrupt_map().
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- openbios-devel/drivers/macio.c | 40 ------------------------------ openbios-devel/drivers/pci.c | 53 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 40 deletions(-)
diff --git a/openbios-devel/drivers/macio.c b/openbios-devel/drivers/macio.c index bf71419..f54bc86 100644 --- a/openbios-devel/drivers/macio.c +++ b/openbios-devel/drivers/macio.c @@ -162,7 +162,6 @@ macio_nvram_get(char *buf) static void openpic_init(const char *path, phys_addr_t addr) { - phandle_t target_node; phandle_t dnode; int props[2]; char buf[128]; @@ -187,45 +186,6 @@ openpic_init(const char *path, phys_addr_t addr) set_int_property(dnode, "clock-frequency", 4166666);
fword("finish-device"); - - u32 *interrupt_map; - int len, i; - - /* patch in interrupt parent */ - dnode = find_dev(buf); - - target_node = find_dev("/pci/mac-io"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/escc/ch-a"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/escc/ch-b"); - set_int_property(target_node, "interrupt-parent", dnode); - - /* QEMU only emulates 2 of the 3 ata buses currently */ - /* On a new world Mac these are not numbered but named by the - * ATA version they support. Thus we have: ata-3, ata-3, ata-4 - * On g3beige they all called just ide. - * We take ata-3 and ata-4 which seems to work for both - * at least for clients we care about */ - target_node = find_dev("/pci/mac-io/ata-3"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/ata-4"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/via-cuda"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci"); - set_int_property(target_node, "interrupt-parent", dnode); - - interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len); - for (i = 0; i < 8; i++) { - interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode; - } - set_property(target_node, "interrupt-map", (char *)interrupt_map, len); }
DECLARE_NODE(ob_macio, INSTALL_OPEN, sizeof(int), "Tmac-io"); diff --git a/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c index c5148e9..09e601c 100644 --- a/openbios-devel/drivers/pci.c +++ b/openbios-devel/drivers/pci.c @@ -1436,6 +1436,56 @@ static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigne set_property(host, "available", (char *)props, ncells * sizeof(props[0])); }
+static void ob_pci_host_set_interrupt_map(phandle_t host) +{ +#if defined(CONFIG_PPC) + phandle_t dnode = 0; + phandle_t target_node; + u32 *interrupt_map; + int len, i; + + /* Oldworld macs do interrupt maps differently */ + if (!is_newworld()) + return; + + /* patch in interrupt parent */ + while ((dnode = dt_iterate_type(dnode, "open-pic"))) { + target_node = find_dev("/pci/mac-io"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + + /* QEMU only emulates 2 of the 3 ata buses currently */ + /* On a new world Mac these are not numbered but named by the + * ATA version they support. Thus we have: ata-3, ata-3, ata-4 + * On g3beige they all called just ide. + * We take ata-3 and ata-4 which seems to work for both + * at least for clients we care about */ + target_node = find_dev("/pci/mac-io/ata-3"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/ata-4"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/via-cuda"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci"); + set_int_property(target_node, "interrupt-parent", dnode); + + interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len); + for (i = 0; i < 8; i++) { + interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode; + } + set_property(target_node, "interrupt-map", (char *)interrupt_map, len); + } +#endif +} + int ob_pci_init(void) { int bus, devnum, fn; @@ -1494,6 +1544,9 @@ int ob_pci_init(void) /* create available attributes for the PCI bridge */ ob_pci_set_available(phandle_host, mem_base, io_base);
+ /* configure the host bridge interrupt map */ + ob_pci_host_set_interrupt_map(phandle_host); + device_end();
return 0;
Move the interrupt mapping from the pre-scan pci_host_set_interrupt_map() to the post-scan ob_pci_host_set_interrupt_map() function.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- openbios-devel/drivers/pci.c | 146 +++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 74 deletions(-)
diff --git a/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c index 09e601c..629a652 100644 --- a/openbios-devel/drivers/pci.c +++ b/openbios-devel/drivers/pci.c @@ -417,70 +417,6 @@ static void pci_set_bus_range(const pci_config_t *config) set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0])); }
-/* Convert device/irq pin to interrupt property */ -#define SUN4U_INTERRUPT(dev, irq_pin) \ - ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f) - -static void pci_host_set_interrupt_map(phandle_t dev) -{ -/* XXX We currently have a hook in the MPIC init code to fill in its handle. - * If you want to have interrupt maps for your PCI host bus, add your - * architecture to the #if and make your bridge detect code fill in its - * handle too. - * - * It would be great if someone clever could come up with a more universal - * mechanism here. - */ -#if defined(CONFIG_PPC) - u32 props[7 * 8]; - int i; - - /* Oldworld macs do interrupt maps differently */ - if(!is_newworld()) - return; - - for (i = 0; i < (7*8); i+=7) { - props[i+PCI_INT_MAP_PCI0] = 0; - props[i+PCI_INT_MAP_PCI1] = 0; - props[i+PCI_INT_MAP_PCI2] = 0; - props[i+PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 - props[i+PCI_INT_MAP_PIC_HANDLE] = 0; // gets patched in later - props[i+PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; - props[i+PCI_INT_MAP_PIC_POL] = 3; - } - set_property(dev, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); - - props[PCI_INT_MAP_PCI0] = 0; - props[PCI_INT_MAP_PCI1] = 0; - props[PCI_INT_MAP_PCI2] = 0; - props[PCI_INT_MAP_PCI_INT] = 0x7; - - set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); -#elif defined(CONFIG_SPARC64) - uint32_t props[12]; - int ncells, device, i; - - /* Set interrupt-map for devices 4 (NE2000) and 5 (CMD646) */ - ncells = 0; - for (i = 4; i <= 5; i++) { - device = i << 11; - - ncells += pci_encode_phys_addr(props + ncells, 0, 0, device, 0, 0); - props[ncells++] = 1; - props[ncells++] = dev; - props[ncells++] = SUN4U_INTERRUPT(device, 1); - } - - set_property(dev, "interrupt-map", (char *)props, ncells * sizeof(props[0])); - - props[0] = 0x0000f800; - props[1] = 0x0; - props[2] = 0x0; - props[3] = 7; - set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); -#endif -} - static void pci_host_set_reg(phandle_t phandle) { phandle_t dev = phandle; @@ -547,7 +483,6 @@ int host_config_cb(const pci_config_t *config) //XXX this overrides "reg" property pci_host_set_reg(get_cur_dev()); pci_host_set_ranges(config); - pci_host_set_interrupt_map(get_cur_dev());
return 0; } @@ -1436,20 +1371,26 @@ static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigne set_property(host, "available", (char *)props, ncells * sizeof(props[0])); }
+/* Convert device/irq pin to interrupt property */ +#define SUN4U_INTERRUPT(dev, irq_pin) \ + ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f) + static void ob_pci_host_set_interrupt_map(phandle_t host) { -#if defined(CONFIG_PPC) phandle_t dnode = 0; + u32 props[128]; + int i; + +#if defined(CONFIG_PPC) phandle_t target_node; - u32 *interrupt_map; - int len, i;
/* Oldworld macs do interrupt maps differently */ if (!is_newworld()) return;
- /* patch in interrupt parent */ - while ((dnode = dt_iterate_type(dnode, "open-pic"))) { + dnode = dt_iterate_type(0, "open-pic"); + if (dnode) { + /* patch in openpic interrupt-parent properties */ target_node = find_dev("/pci/mac-io"); set_int_property(target_node, "interrupt-parent", dnode);
@@ -1477,12 +1418,69 @@ static void ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev("/pci"); set_int_property(target_node, "interrupt-parent", dnode);
- interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len); - for (i = 0; i < 8; i++) { - interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode; + /* openpic interrupt mapping */ + for (i = 0; i < (7*8); i += 7) { + props[i + PCI_INT_MAP_PCI0] = 0; + props[i + PCI_INT_MAP_PCI1] = 0; + props[i + PCI_INT_MAP_PCI2] = 0; + props[i + PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 + props[i + PCI_INT_MAP_PIC_HANDLE] = dnode; + props[i + PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; + props[i + PCI_INT_MAP_PIC_POL] = 3; + } + set_property(host, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); + + props[PCI_INT_MAP_PCI0] = 0; + props[PCI_INT_MAP_PCI1] = 0; + props[PCI_INT_MAP_PCI2] = 0; + props[PCI_INT_MAP_PCI_INT] = 0x7; + + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); + } + +#elif defined(CONFIG_SPARC64) + int ncells, len; + u32 *val, addr; + char *reg; + + /* Set interrupt-map for devices 4 (NE2000) and 5 (CMD646) */ + ncells = 0; + + PUSH(host); + fword("child"); + dnode = POP(); + while (dnode) { + if (get_int_property(dnode, "interrupts", &len)) { + reg = get_property(dnode, "reg", &len); + if (reg) { + val = (u32 *)reg; + + for (i = 0; i < (len / sizeof(u32)); i += 5) { + addr = val[i]; + + /* Device address is in 1st 32-bit word of encoded PCI address for config space */ + if (!(addr & 0x03000000)) { + ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0); + props[ncells++] = 1; /* always interrupt pin 1 for QEMU */ + props[ncells++] = host; + props[ncells++] = SUN4U_INTERRUPT(addr, 1); + } + } + } } - set_property(target_node, "interrupt-map", (char *)interrupt_map, len); + + PUSH(dnode); + fword("peer"); + dnode = POP(); } + set_property(host, "interrupt-map", (char *)props, ncells * sizeof(props[0])); + + props[0] = 0x0000f800; + props[1] = 0x0; + props[2] = 0x0; + props[3] = 7; + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); + #endif }
On 11/13/14 13:19, Mark Cave-Ayland wrote:
This patch fixes up a regression with virtio not working under the latest OpenBIOS SPARC64.
The error was introduced by the new interrupt routing code which needs to generate interrupt-map properties based upon the current active PCI slot (previously the OS would determine this automatically as the properties weren't present in the device tree).
In its current form, the code works by hard-coding the slots for the network and disk devices; however this fails to map the interrupt for devices added at QEMU runtime via device such as virtio.
This patchset fixes this problem in the following way:
- Move the PCI host interrupt setup from out of the per-device host
bridge callback (called generally towards the start of the PCI bus scan) and into a separate function called after the entire PCI bus has been scanned
- Add code to dynamically generate the interrupt-map property for
slots containing devices with an interrupt pin once the PCI bus scan is complete
- As a bonus: tidy up the openpic interrupt mapping into the same
routine
I believe there is a strong case for getting this patchset into the upcoming QEMU 2.2 release, so please let me know if there are any great objections.
Acked-by: Alexander Graf agraf@suse.de
Alex
On 13/11/14 18:21, Alexander Graf wrote:
On 11/13/14 13:19, Mark Cave-Ayland wrote:
This patch fixes up a regression with virtio not working under the latest OpenBIOS SPARC64.
The error was introduced by the new interrupt routing code which needs to generate interrupt-map properties based upon the current active PCI slot (previously the OS would determine this automatically as the properties weren't present in the device tree).
In its current form, the code works by hard-coding the slots for the network and disk devices; however this fails to map the interrupt for devices added at QEMU runtime via device such as virtio.
This patchset fixes this problem in the following way:
- Move the PCI host interrupt setup from out of the per-device host
bridge callback (called generally towards the start of the PCI bus scan) and into a separate function called after the entire PCI bus has been scanned
- Add code to dynamically generate the interrupt-map property for
slots containing devices with an interrupt pin once the PCI bus scan is complete
- As a bonus: tidy up the openpic interrupt mapping into the same
routine
I believe there is a strong case for getting this patchset into the upcoming QEMU 2.2 release, so please let me know if there are any great objections.
Acked-by: Alexander Graf agraf@suse.de
Alex
Thanks! I've just committed the slightly-less-squashed version to SVN trunk (which has an identical end result) and will send a pull request for QEMU 2.2 tomorrow.
ATB,
Mark.