* [PATCH v2 01/53] MIPS: PCI: xtalk-bridge: refuse device numbers past the last slot
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 02/53] MIPS: PCI: xtalk-bridge: assign slots the PROM leaves unconfigured Imre Kaloz
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, stable, linux-pci
Config slots 8 and up alias the type 1 window, so a root bus scan finds
phantom copies of whatever device b_pci_cfg selects. Refuse root bus
accesses past the ASIC's last slot.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/pci/pci-xtalk-bridge.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index cf115abb54e0..9451b7546068 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -92,6 +92,11 @@ static int ioc3_cfg_wr(void *addr, int where, int size, u32 value)
return PCIBIOS_SUCCESSFUL;
}
+static int bridge_nslots(struct bridge_controller *bc)
+{
+ return BRIDGE_DEV_CNT;
+}
+
static void bridge_disable_swapping(struct pci_dev *dev)
{
struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
@@ -127,6 +132,13 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn,
u32 cf;
int res;
+ /*
+ * Only bridge_nslots() slots exist. Config slots 8 and up alias the
+ * type 1 window and would answer for whatever b_pci_cfg selects.
+ */
+ if (slot >= bridge_nslots(bc))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID];
if (get_dbe(cf, (u32 *)addr))
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -212,6 +224,9 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn,
u32 cf;
int res;
+ if (slot >= bridge_nslots(bc))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID];
if (get_dbe(cf, (u32 *)addr))
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -695,7 +710,7 @@ static int bridge_probe(struct platform_device *pdev)
bridge_write(bc, b_dir_map, (bd->masterwid << 20)); /* DMA */
bridge_write(bc, b_int_enable, 0);
- for (slot = 0; slot < 8; slot++) {
+ for (slot = 0; slot < bridge_nslots(bc); slot++) {
bridge_set(bc, b_device[slot].reg, BRIDGE_DEV_SWAP_DIR);
bc->pci_int[slot][0] = -1;
bc->pci_int[slot][1] = -1;
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 02/53] MIPS: PCI: xtalk-bridge: assign slots the PROM leaves unconfigured
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 01/53] MIPS: PCI: xtalk-bridge: refuse device numbers past the last slot Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 03/53] MIPS: PCI: xtalk-bridge: write a type 1 IOC3 through the type 1 window Imre Kaloz
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-pci
The PROM leaves some slots unassigned, such as an LSI SAS1064 or the bus
behind a PCI-to-PCI bridge, so their drivers cannot bind. Map their devio
windows, borrowing an empty slot's for I/O when memory takes their own.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/pci/pci-xtalk-bridge.c | 192 +++++++++++++++++++++++++++++++
1 file changed, 192 insertions(+)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 9451b7546068..2fa9ac8d0a1c 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -619,6 +619,192 @@ static int bridge_get_partnum(u64 baddr, char *partnum)
return 0;
}
+static unsigned long bridge_dev_space(struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ struct resource *res;
+ int i;
+
+ pci_dev_for_each_resource(pdev, res, i)
+ if (i < PCI_ROM_RESOURCE && resource_size(res))
+ flags |= res->flags & (IORESOURCE_IO | IORESOURCE_MEM);
+
+ return flags;
+}
+
+static bool bridge_ppb_window(struct pci_dev *ppb, int idx,
+ struct resource *win)
+{
+ struct resource *res = &ppb->resource[idx];
+
+ if (!win)
+ return false;
+ res->start = win->start;
+ res->end = win->end;
+ res->flags = win->flags;
+ if (request_resource(win, res))
+ return false;
+ pci_info(ppb, "bridge window %pR: assigned\n", res);
+ return true;
+}
+
+static void bridge_setup_ppb(struct pci_dev *ppb,
+ struct xtalk_bridge_platform_data *bd,
+ struct resource *io, struct resource *mem)
+{
+ u32 start, end;
+ u8 io_type;
+
+ pci_read_config_byte(ppb, PCI_IO_BASE, &io_type);
+ if (io && (io_type & PCI_IO_RANGE_TYPE_MASK) != PCI_IO_RANGE_TYPE_32) {
+ pci_warn(ppb, "no 32-bit I/O window\n");
+ io = NULL;
+ }
+ if (!bridge_ppb_window(ppb, PCI_BRIDGE_IO_WINDOW, io))
+ io = NULL;
+ if (!bridge_ppb_window(ppb, PCI_BRIDGE_MEM_WINDOW, mem))
+ mem = NULL;
+ if (!io && !mem)
+ return;
+
+ if (io) {
+ start = io->start - bd->io_offset;
+ end = io->end - bd->io_offset;
+ pci_write_config_dword(ppb, PCI_IO_BASE_UPPER16,
+ (end & 0xffff0000) | (start >> 16));
+ pci_write_config_word(ppb, PCI_IO_BASE,
+ (end & 0xf000) | (start >> 8 & 0xf0));
+ } else {
+ pci_write_config_dword(ppb, PCI_IO_BASE_UPPER16, 0);
+ pci_write_config_word(ppb, PCI_IO_BASE, 0x00f0);
+ }
+ if (mem) {
+ start = mem->start - bd->mem_offset;
+ end = mem->end - bd->mem_offset;
+ pci_write_config_dword(ppb, PCI_MEMORY_BASE,
+ (end & 0xfff00000) | (start >> 16));
+ } else {
+ pci_write_config_dword(ppb, PCI_MEMORY_BASE, 0x0000fff0);
+ }
+ pci_write_config_dword(ppb, PCI_PREF_MEMORY_BASE, 0x0000fff0);
+ pci_write_config_dword(ppb, PCI_PREF_BASE_UPPER32, 0);
+ pci_write_config_dword(ppb, PCI_PREF_LIMIT_UPPER32, 0);
+
+ pci_bus_assign_resources(ppb->subordinate);
+}
+
+/* Map devio window @win, at its own address, in memory or I/O space. */
+static struct resource *bridge_map_devio(struct bridge_controller *bc,
+ struct xtalk_bridge_platform_data *bd,
+ struct device *dev, int win, bool mem)
+{
+ struct resource *root = mem ? &bd->mem : &bd->io;
+ resource_size_t offset = mem ? bd->mem_offset : bd->io_offset;
+ struct resource *res;
+ u32 devreg;
+
+ res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return NULL;
+ res->name = "Bridge PCI slot";
+ res->start = root->start - BRIDGE_DEVIO0 + BRIDGE_DEVIO(win);
+ res->end = res->start + (win <= 1 ? BRIDGE_DEVIO_2MB :
+ BRIDGE_DEVIO_1MB) - 1;
+ res->flags = root->flags;
+ if (devm_request_resource(dev, root, res))
+ return NULL;
+
+ devreg = bridge_read(bc, b_device[win].reg);
+ devreg &= ~(BRIDGE_DEV_OFF_MASK | BRIDGE_DEV_DEV_IO_MEM);
+ devreg |= (res->start - offset) >> BRIDGE_DEV_OFF_ADDR_SHFT;
+ if (mem)
+ devreg |= BRIDGE_DEV_DEV_IO_MEM;
+ bridge_write(bc, b_device[win].reg, devreg);
+ bridge_read(bc, b_wid_tflush);
+
+ return res;
+}
+
+/*
+ * A slot's BARs are reached only through a devio window, which Device(x)
+ * points at either PCI I/O or PCI memory space. The PROM leaves some slots
+ * without resources, the secondary bus of a PCI-to-PCI bridge among them.
+ * Give such a slot its own window, in memory space if anything in the slot
+ * needs memory, and the window of a slot with no device for I/O space if
+ * it needs both: PIO through any devio window reaches every device on the
+ * bus.
+ */
+static void bridge_setup_slot(struct bridge_controller *bc,
+ struct xtalk_bridge_platform_data *bd,
+ struct device *dev, struct pci_bus *bus, int slot,
+ unsigned long *busy)
+{
+ struct resource *res, *win, *io = NULL, *mem = NULL;
+ struct pci_dev *pdev, *child;
+ unsigned long flags = 0;
+ int i;
+
+ list_for_each_entry(pdev, &bus->devices, bus_list) {
+ if (PCI_SLOT(pdev->devfn) != slot)
+ continue;
+ pci_dev_for_each_resource(pdev, res)
+ if (res->parent)
+ return;
+ if (!pdev->subordinate)
+ flags |= bridge_dev_space(pdev);
+ else
+ list_for_each_entry(child, &pdev->subordinate->devices,
+ bus_list)
+ flags |= bridge_dev_space(child);
+ }
+ if (!flags)
+ return;
+
+ if (flags & IORESOURCE_MEM)
+ mem = bridge_map_devio(bc, bd, dev, slot, true);
+ else
+ io = bridge_map_devio(bc, bd, dev, slot, false);
+ if (!io && !mem)
+ return;
+
+ if ((flags & IORESOURCE_IO) && !io) {
+ i = find_first_zero_bit(busy, bridge_nslots(bc));
+ if (i < bridge_nslots(bc)) {
+ __set_bit(i, busy);
+ io = bridge_map_devio(bc, bd, dev, i, false);
+ }
+ if (!io)
+ dev_warn(dev, "slot %d: no devio window for I/O space\n",
+ slot);
+ }
+
+ list_for_each_entry(pdev, &bus->devices, bus_list) {
+ if (PCI_SLOT(pdev->devfn) != slot)
+ continue;
+ if (pdev->subordinate) {
+ bridge_setup_ppb(pdev, bd, io, mem);
+ continue;
+ }
+ pci_dev_for_each_resource(pdev, res, i) {
+ win = res->flags & IORESOURCE_MEM ? mem : io;
+ if (i >= PCI_ROM_RESOURCE || !win ||
+ !(res->flags & win->flags) || !resource_size(res))
+ continue;
+ if (allocate_resource(win, res, resource_size(res),
+ win->start, win->end,
+ resource_alignment(res),
+ NULL, NULL)) {
+ pci_warn(pdev, "BAR %d %pR: no space in slot\n",
+ i, res);
+ continue;
+ }
+ res->flags &= ~IORESOURCE_UNSET;
+ pci_update_resource(pdev, i);
+ pci_info(pdev, "BAR %d %pR: assigned\n", i, res);
+ }
+ }
+}
+
static int bridge_probe(struct platform_device *pdev)
{
struct xtalk_bridge_platform_data *bd = dev_get_platdata(&pdev->dev);
@@ -627,6 +813,8 @@ static int bridge_probe(struct platform_device *pdev)
struct pci_host_bridge *host;
struct irq_domain *domain, *parent;
struct fwnode_handle *fn;
+ struct pci_dev *pcidev;
+ unsigned long busy = 0;
char partnum[26];
int slot;
int err;
@@ -734,6 +922,10 @@ static int bridge_probe(struct platform_device *pdev)
goto err_free_resource;
pci_bus_claim_resources(host->bus);
+ list_for_each_entry(pcidev, &host->bus->devices, bus_list)
+ __set_bit(PCI_SLOT(pcidev->devfn), &busy);
+ for (slot = 0; slot < bridge_nslots(bc); slot++)
+ bridge_setup_slot(bc, bd, dev, host->bus, slot, &busy);
pci_bus_add_devices(host->bus);
platform_set_drvdata(pdev, host->bus);
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 03/53] MIPS: PCI: xtalk-bridge: write a type 1 IOC3 through the type 1 window
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 01/53] MIPS: PCI: xtalk-bridge: refuse device numbers past the last slot Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 02/53] MIPS: PCI: xtalk-bridge: assign slots the PROM leaves unconfigured Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 04/53] MIPS: PCI: xtalk-bridge: flush config space writes Imre Kaloz
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, stable, linux-pci
The IOC3 case of the type 1 config write goes through the root bus
slot's type 0 window and so writes a different device. Use the type 1
window, as the matching read does.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/pci/pci-xtalk-bridge.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 2fa9ac8d0a1c..18251a68fa06 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -277,7 +277,7 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn,
* generic PCI code a chance to look at it for real ...
*/
if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) {
- addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2];
+ addr = &bridge->b_type1_cfg.c[(fn << 8) | (where & ~3)];
return ioc3_cfg_wr(addr, where, size, value);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 04/53] MIPS: PCI: xtalk-bridge: flush config space writes
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (2 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 03/53] MIPS: PCI: xtalk-bridge: write a type 1 IOC3 through the type 1 window Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 05/53] MIPS: PCI: xtalk-bridge: reassign slots whose devio window maps elsewhere Imre Kaloz
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-pci
PIC hangs on a config access issued behind a config write that is still
posted, as when probing an SGI PCI digital audio board. Read the widget
flush register after each config write other than an IOC3's.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/pci/pci-xtalk-bridge.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 18251a68fa06..16c1ac2c2551 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -252,6 +252,9 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn,
if (res)
return PCIBIOS_DEVICE_NOT_FOUND;
+ /* PIC hangs on a config access behind an unflushed config write. */
+ bridge_read(bc, b_wid_tflush);
+
return PCIBIOS_SUCCESSFUL;
}
@@ -293,6 +296,8 @@ static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn,
if (res)
return PCIBIOS_DEVICE_NOT_FOUND;
+ bridge_read(bc, b_wid_tflush);
+
return PCIBIOS_SUCCESSFUL;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 05/53] MIPS: PCI: xtalk-bridge: reassign slots whose devio window maps elsewhere
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (3 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 04/53] MIPS: PCI: xtalk-bridge: flush config space writes Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 26/53] MIPS: SGI-IP27: pick the XBridge and PIC DMA address form by mask Imre Kaloz
2026-10-13 13:35 ` [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Niklas Cassel
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-pci
The PROM may point the devio window covering a BAR elsewhere. On an
Octane, window 2 maps the IOC3 while the RAD1 BAR sits in it, so RAD1
writes land in the IOC3. Reassign such slots like unconfigured ones.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/pci/pci-xtalk-bridge.c | 58 ++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 16c1ac2c2551..8b56c754245d 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -730,6 +730,63 @@ static struct resource *bridge_map_devio(struct bridge_controller *bc,
return res;
}
+/*
+ * Linux reaches a BAR through the devio window at the BAR's own address, but
+ * the PROM may point that window elsewhere: an Octane's window 2 maps the IOC3
+ * at 0x500000 while the RAD1 BAR sits at 0x600000. Only the offset matters;
+ * the PROM puts a slot's I/O and memory BARs at one address and maps the
+ * window for memory.
+ */
+static bool bridge_devio_reaches(struct bridge_controller *bc,
+ struct xtalk_bridge_platform_data *bd,
+ struct resource *res)
+{
+ resource_size_t base = bd->mem.start - BRIDGE_DEVIO0;
+ resource_size_t offset = res->flags & IORESOURCE_IO ? bd->io_offset :
+ bd->mem_offset;
+ resource_size_t addr = res->start;
+ u32 off;
+ int win;
+
+ while (addr <= res->end) {
+ addr -= base;
+ win = addr < BRIDGE_DEVIO2 ?
+ (addr - BRIDGE_DEVIO0) / BRIDGE_DEVIO_2MB :
+ 2 + (addr - BRIDGE_DEVIO2) / BRIDGE_DEVIO_1MB;
+ if (win >= bridge_nslots(bc))
+ return false;
+ off = bridge_read(bc, b_device[win].reg) & BRIDGE_DEV_OFF_MASK;
+ if (off != (base + BRIDGE_DEVIO(win) - offset) >>
+ BRIDGE_DEV_OFF_ADDR_SHFT)
+ return false;
+ addr = base + BRIDGE_DEVIO(win + 1);
+ }
+
+ return true;
+}
+
+/* Release slots with a BAR Linux cannot reach, for bridge_setup_slot(). */
+static void bridge_release_unreached(struct bridge_controller *bc,
+ struct xtalk_bridge_platform_data *bd,
+ struct pci_bus *bus)
+{
+ unsigned long unreached = 0;
+ struct pci_dev *pdev;
+ struct resource *res;
+ int i;
+
+ list_for_each_entry(pdev, &bus->devices, bus_list)
+ pci_dev_for_each_resource(pdev, res)
+ if (res->parent && !pdev->subordinate &&
+ !bridge_devio_reaches(bc, bd, res))
+ __set_bit(PCI_SLOT(pdev->devfn), &unreached);
+
+ list_for_each_entry(pdev, &bus->devices, bus_list)
+ if (test_bit(PCI_SLOT(pdev->devfn), &unreached))
+ pci_dev_for_each_resource(pdev, res, i)
+ pci_release_resource(pdev, i);
+}
+
/*
* A slot's BARs are reached only through a devio window, which Device(x)
* points at either PCI I/O or PCI memory space. The PROM leaves some slots
@@ -929,6 +986,7 @@ static int bridge_probe(struct platform_device *pdev)
pci_bus_claim_resources(host->bus);
list_for_each_entry(pcidev, &host->bus->devices, bus_list)
__set_bit(PCI_SLOT(pcidev->devfn), &busy);
+ bridge_release_unreached(bc, bd, host->bus);
for (slot = 0; slot < bridge_nslots(bc); slot++)
bridge_setup_slot(bc, bd, dev, host->bus, slot, &busy);
pci_bus_add_devices(host->bus);
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 26/53] MIPS: SGI-IP27: pick the XBridge and PIC DMA address form by mask
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (4 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 05/53] MIPS: PCI: xtalk-bridge: reassign slots whose devio window maps elsewhere Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:35 ` [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Niklas Cassel
6 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Bjorn Helgaas, linux-pci,
Christoph Hellwig
XBridge and PIC need bit 55 set in a 64-bit direct-mapped DMA address;
without it no data moves. arch_dma_set_mask() gives that form to a
device with a 64-bit mask. PIC in PCI-X mode decodes only that form.
PIC's PCI-X bit 33 lies outside b_wid_stat. Narrower devices use the
2GB 32-bit window, with SWIOTLB bouncing streaming buffers above it
and ZONE_DMA32 serving coherent ones. The IOC3 and IOC4 take DMA
unswapped and bit 55 swaps, so they stay on the window with swapping
off in their slots. An MFD cell maps through its PCI parent.
The 64MB bounce pool comes from node 0 whenever DRAM passes 2GB (any
multi-node Hub). A later patch flags PIC.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/Kconfig | 3 +
arch/mips/include/asm/pci/bridge.h | 7 ++
arch/mips/pci/pci-xtalk-bridge.c | 98 +++++++++++++++++++++-
arch/mips/sgi-ip27/ip27-memory.c | 18 ++++
arch/mips/sgi-ip27/ip27-xtalk.c | 15 +++-
include/linux/pci_ids.h | 1 +
include/linux/platform_data/xtalk-bridge.h | 2 +
7 files changed, 139 insertions(+), 5 deletions(-)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b64f1a8ebf41..85f1c4bd6549 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -771,6 +771,7 @@ config SGI_IP27
select NR_CPUS_DEFAULT_64
select PCI_DRIVERS_GENERIC
select PCI_XTALK_BRIDGE
+ select SWIOTLB
select SYS_HAS_CPU_R10000
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
@@ -779,6 +780,7 @@ config SGI_IP27
select WAR_R10000_LLSC
select MIPS_L1_CACHE_SHIFT_7
select NUMA
+ select ZONE_DMA32
help
These are the SGI Origin 200, Origin 2000 and Onyx 2 Graphics
workstations, and the IP35 machines built around the Bedrock hub
@@ -1254,6 +1256,7 @@ config PCI_GT64XXX_PCI0
config PCI_XTALK_BRIDGE
bool
+ select ARCH_HAS_DMA_SET_MASK
config NO_EXCEPT_FILL
bool
diff --git a/arch/mips/include/asm/pci/bridge.h b/arch/mips/include/asm/pci/bridge.h
index eaeafccd82c7..647ea5c9e34b 100644
--- a/arch/mips/include/asm/pci/bridge.h
+++ b/arch/mips/include/asm/pci/bridge.h
@@ -413,6 +413,9 @@ struct bridge_err_cmdword {
#define BRIDGE_STAT_PCI_GIO_N (0x1 << 5)
#define BRIDGE_STAT_PENDING (0x1F << 0)
+/* PIC-only widget status bit, upper 32 bits of the register. */
+#define PIC_STAT_PCIX_ACTIVE (0x1ull << 33)
+
/* Bridge widget control register bits definition */
#define BRIDGE_CTRL_FLASH_WR_EN (0x1ul << 31)
#define BRIDGE_CTRL_EN_CLK50 (0x1 << 30)
@@ -797,6 +800,7 @@ struct bridge_err_cmdword {
#define PCI64_ATTR_PREC 0x0400000000000000
#define PCI64_ATTR_VIRTUAL 0x0200000000000000
#define PCI64_ATTR_BAR 0x0100000000000000
+#define PCI64_ATTR_SWAP 0x0080000000000000 /* XBridge, PIC */
#define PCI64_ATTR_RMF_MASK 0x00ff000000000000
#define PCI64_ATTR_RMF_SHFT 48
@@ -810,6 +814,9 @@ struct bridge_controller {
unsigned int int_mapping[8][2];
u32 ioc3_sid[8];
nasid_t nasid;
+ bool is_xbridge;
+ bool is_pic;
+ bool is_pcix;
};
#define BRIDGE_CONTROLLER(bus) \
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 8b56c754245d..03ccb82a6189 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -23,22 +23,106 @@
#define CRC16_INIT 0
#define CRC16_VALID 0xb001
+/* An IOC3/IOC4 MFD cell DMAs through its PCI parent's bridge. */
+static struct pci_dev *bridge_dma_pdev(struct device *dev)
+{
+ while (dev && !dev_is_pci(dev))
+ dev = dev->parent;
+
+ return dev ? to_pci_dev(dev) : NULL;
+}
+
/*
* Common phys<->dma mapping for platforms using pci xtalk bridge
*/
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus);
+ struct pci_dev *pdev = bridge_dma_pdev(dev);
+ struct bridge_controller *bc;
+
+ /* A crosstalk widget addresses memory by physical address. */
+ if (!pdev)
+ return paddr;
+
+ bc = BRIDGE_CONTROLLER(pdev->bus);
+
+ /*
+ * XBridge and PIC need bit 55 set in a 64-bit direct-mapped address.
+ * Outside PCI-X mode, a device with a narrower mask uses the 2GB
+ * 32-bit window instead; its bus_dma_limit stays at the window's top,
+ * so SWIOTLB bounces any buffer above it.
+ */
+ if (bc->is_xbridge || bc->is_pic) {
+ if (!pdev->dev.bus_dma_limit)
+ return bc->baddr | PCI64_ATTR_SWAP | paddr;
+ return BRIDGE_DMA_DIRECT_BASE + paddr;
+ }
return bc->baddr + paddr;
}
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)
{
+ struct pci_dev *pdev = bridge_dma_pdev(dev);
+ struct bridge_controller *bc;
+
+ if (!pdev)
+ return dma_addr;
+
+ bc = BRIDGE_CONTROLLER(pdev->bus);
+
+ if (bc->is_xbridge || bc->is_pic) {
+ if (!(dma_addr & PCI64_ATTR_BAR))
+ return dma_addr - BRIDGE_DMA_DIRECT_BASE;
+ return dma_addr & ~(0x1ffUL << 55);
+ }
+
return dma_addr & ~(0xffUL << 56);
}
+void pcibios_bus_add_device(struct pci_dev *pdev)
+{
+ struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus);
+
+ if ((bc->is_xbridge || bc->is_pic) && !bc->is_pcix)
+ pdev->dev.bus_dma_limit = DMA_BIT_MASK(32);
+}
+
+/*
+ * Only a 64-bit mask covers the target widget bits 63:60 of a direct address.
+ * The IOC3 and IOC4 stay on the 32-bit window: they take DMA unswapped, and
+ * bit 55 byte-swaps the 64-bit direct form. A PIC bus in PCI-X mode decodes
+ * only the 64-bit direct form, so its devices never use the window.
+ */
+void arch_dma_set_mask(struct device *dev, u64 mask)
+{
+ struct pci_dev *pdev;
+ struct bridge_controller *bc;
+
+ if (!dev_is_pci(dev))
+ return;
+
+ pdev = to_pci_dev(dev);
+ bc = BRIDGE_CONTROLLER(pdev->bus);
+ if (!bc->is_xbridge && !bc->is_pic)
+ return;
+
+ if (bc->is_pcix) {
+ if (mask != DMA_BIT_MASK(64))
+ dev_warn(dev, "DMA mask %#llx unusable, PIC bus is in PCI-X mode\n",
+ mask);
+ return;
+ }
+
+ if (mask == DMA_BIT_MASK(64) &&
+ !(pdev->vendor == PCI_VENDOR_ID_SGI &&
+ (pdev->device == PCI_DEVICE_ID_SGI_IOC3 ||
+ pdev->device == PCI_DEVICE_ID_SGI_IOC4)))
+ dev->bus_dma_limit = 0;
+ else
+ dev->bus_dma_limit = DMA_BIT_MASK(32);
+}
+
/*
* Most of the IOC3 PCI config register aren't present
* we emulate what is needed for a normal PCI enumeration
@@ -109,6 +193,8 @@ static void bridge_disable_swapping(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
bridge_disable_swapping);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4,
+ bridge_disable_swapping);
/*
@@ -928,6 +1014,14 @@ static int bridge_probe(struct platform_device *pdev)
bc->baddr = (u64)bd->masterwid << 60 | PCI64_ATTR_BAR;
bc->base = (struct bridge_regs *)bd->bridge_addr;
bc->intr_addr = bd->intr_addr;
+ bc->is_xbridge = bd->is_xbridge;
+ bc->is_pic = bd->is_pic;
+ if (bc->is_pic) {
+ unsigned long stat = (unsigned long)&bc->base->b_wid_stat;
+
+ bc->is_pcix = __raw_readq((void *)(stat & ~7UL)) &
+ PIC_STAT_PCIX_ACTIVE;
+ }
/*
* Clear all pending interrupts.
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c
index 98184dfcd8c5..a497bf0cbf37 100644
--- a/arch/mips/sgi-ip27/ip27-memory.c
+++ b/arch/mips/sgi-ip27/ip27-memory.c
@@ -19,8 +19,10 @@
#include <linux/nodemask.h>
#include <linux/sizes.h>
#include <linux/swap.h>
+#include <linux/swiotlb.h>
#include <linux/pfn.h>
#include <linux/highmem.h>
+#include <asm/bootinfo.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
@@ -435,5 +437,21 @@ void __init prom_meminit(void)
void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
{
+ /* The 32-bit DMA window of XBridge and PIC reaches only the first 2GB. */
+ max_zone_pfns[ZONE_DMA32] = min(max_low_pfn, PFN_DOWN(SZ_2G));
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
}
+
+/*
+ * XBridge and PIC devices on the 2GB 32-bit DMA window bounce through
+ * SWIOTLB, so cap the pool there; which widgets exist isn't known this
+ * early, so reserve it whenever node memory reaches past the window.
+ */
+void __init plat_swiotlb_setup(void)
+{
+ phys_addr_t old_limit = memblock_get_current_limit();
+
+ memblock_set_current_limit(min_t(phys_addr_t, old_limit, SZ_2G));
+ swiotlb_init(max_low_pfn > PFN_DOWN(SZ_2G), SWIOTLB_VERBOSE);
+ memblock_set_current_limit(old_limit);
+}
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index baa937b474a8..bd2ac24452f5 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -23,7 +23,13 @@
#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */
#define BASE_XBOW_PORT 8 /* Lowest external port */
-static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
+enum xtalk_bridge_type {
+ XTALK_BRIDGE,
+ XTALK_XBRIDGE,
+};
+
+static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
+ enum xtalk_bridge_type type)
{
struct xtalk_bridge_platform_data *bd;
struct sgi_w1_platform_data *wd;
@@ -85,6 +91,7 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid)
bd->intr_addr = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
bd->nasid = nasid;
bd->masterwid = masterwid;
+ bd->is_xbridge = type == XTALK_XBRIDGE;
bd->mem.name = "Bridge PCI MEM";
bd->mem.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
@@ -136,8 +143,10 @@ static int probe_one_port(nasid_t nasid, int widget, int masterwid)
switch (partnum) {
case BRIDGE_WIDGET_PART_NUM:
+ bridge_platform_create(nasid, widget, masterwid, XTALK_BRIDGE);
+ break;
case XBRIDGE_WIDGET_PART_NUM:
- bridge_platform_create(nasid, widget, masterwid);
+ bridge_platform_create(nasid, widget, masterwid, XTALK_XBRIDGE);
break;
default:
pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
@@ -228,7 +237,7 @@ static void xtalk_probe_node(nasid_t nasid)
switch (partnum) {
case BRIDGE_WIDGET_PART_NUM:
- bridge_platform_create(nasid, 0x8, 0xa);
+ bridge_platform_create(nasid, 0x8, 0xa, XTALK_BRIDGE);
break;
case XBOW_WIDGET_PART_NUM:
case XXBOW_WIDGET_PART_NUM:
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 63774ae2bf71..fc92822b1509 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1113,6 +1113,7 @@
#define PCI_VENDOR_ID_SGI 0x10a9
#define PCI_DEVICE_ID_SGI_IOC3 0x0003
#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002
+#define PCI_DEVICE_ID_SGI_IOC4 0x100a
#define PCI_VENDOR_ID_WINBOND 0x10ad
#define PCI_DEVICE_ID_WINBOND_82C105 0x0105
diff --git a/include/linux/platform_data/xtalk-bridge.h b/include/linux/platform_data/xtalk-bridge.h
index 51e5001f2c05..171b5bcf56e4 100644
--- a/include/linux/platform_data/xtalk-bridge.h
+++ b/include/linux/platform_data/xtalk-bridge.h
@@ -17,6 +17,8 @@ struct xtalk_bridge_platform_data {
unsigned long io_offset;
nasid_t nasid;
int masterwid;
+ bool is_xbridge;
+ bool is_pic;
};
#endif /* PLATFORM_DATA_XTALK_BRIDGE_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (5 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 26/53] MIPS: SGI-IP27: pick the XBridge and PIC DMA address form by mask Imre Kaloz
@ 2026-10-13 13:35 ` Niklas Cassel
2026-10-13 14:25 ` Imre Kaloz
6 siblings, 1 reply; 9+ messages in thread
From: Niklas Cassel @ 2026-10-13 13:35 UTC (permalink / raw)
To: Imre Kaloz
Cc: Thomas Bogendoerfer, linux-mips, linux-kernel, Lee Jones,
Greg Kroah-Hartman, Jiri Slaby, mfd, linux-serial,
Alexandre Belloni, linux-rtc, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, Damien Le Moal,
linux-ide, Pavel Machek, linux-leds, Bjorn Helgaas, linux-pci,
Christoph Hellwig
Hello Imre,
This series is rather large.
I would recommend you to submit a minimal patch series
to get the basic support for your new board, such that
it can boot with a ramdisk and serial console.
Then in the next cycle, send separate series for ata,
net, leds, mfc, rtc, etc.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines
2026-10-13 13:35 ` [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Niklas Cassel
@ 2026-10-13 14:25 ` Imre Kaloz
0 siblings, 0 replies; 9+ messages in thread
From: Imre Kaloz @ 2026-10-13 14:25 UTC (permalink / raw)
To: Niklas Cassel
Cc: Thomas Bogendoerfer, linux-mips, linux-kernel, Lee Jones,
Greg Kroah-Hartman, Jiri Slaby, mfd, linux-serial,
Alexandre Belloni, linux-rtc, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, Damien Le Moal,
linux-ide, Pavel Machek, linux-leds, Bjorn Helgaas, linux-pci,
Christoph Hellwig
[-- Attachment #1: Type: text/plain, Size: 1013 bytes --]
Hello Niklas,
On Tue, 13 Oct 2026, Niklas Cassel wrote:
> Hello Imre,
>
> This series is rather large.
>
> I would recommend you to submit a minimal patch series
> to get the basic support for your new board, such that
> it can boot with a ramdisk and serial console.
>
> Then in the next cycle, send separate series for ata,
> net, leds, mfc, rtc, etc.
Hi Niklas,
Thanks for having a look. I checked what the minimum is: 31 of the 53
patches (1, 6-24, 26-29, 31-36 and 39) boot a Tezro to a ramdisk with a
serial console. Two mfd/ioc3 patches have to be part of that, as they
provide the console UARTs (31 for IOC4 on the Tezro, 36 for the IOC3 on
the Fuel). The rest separates cleanly into follow-ups: the remaining
Bridge fixes (2-5), ioc3-eth (25, 38), rtc (30), the IO8 serial IRQs (37),
machine identification and the Odyssey widget (40-44), ata (45-47, 49),
the defconfig refresh (48) and the LEDs (50-53).
I’ll wait for Thomas to say whether he prefers it split that way for v3.
Thanks,
Imre
^ permalink raw reply [flat|nested] 9+ messages in thread