* [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines
@ 2026-10-13 13:16 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 01/53] MIPS: PCI: xtalk-bridge: refuse device numbers past the last slot Imre Kaloz
` (53 more replies)
0 siblings, 54 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: 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, Niklas Cassel, linux-ide,
Pavel Machek, linux-leds, Bjorn Helgaas, linux-pci,
Christoph Hellwig
This series adds support for the SGI machines built around the Bedrock
hub (IP35), such as Fuel and Tezro, to the IP27 platform code.
Patches 1-5 fix the config space accessors and slot setup of the
Bridge, XBridge and PIC driver: device numbers past the last slot,
slots the PROM leaves unconfigured, the type 1 IOC3 write, posted
config writes and slots whose devio window the PROM maps elsewhere.
Patches 6-22 teach the IP27 code the Bedrock hub: detection, the node
address layout and register windows, the node id, CPU slots, memory
banks, both PIs and the kernel load address.
Patches 23-32 add the crossbar, bridge and I/O parts of these machines:
PXBow, XBridge, PIC and IOC4.
Patches 33-44 add the L1 system controller and what it provides, the
Origin 300 IO8 serial interrupts, the Odyssey graphics widget, the
KLCONFIG console and a topology package id per CPU.
Patches 45-47 add a libata driver for the IOC4 ATA/ATAPI controller,
which carries the DVD-ROM of Tezro and Origin 350 I-bricks. It drives
PIO4 and multiword DMA mode 2.
Patch 48 updates ip27_defconfig to the onboard hardware of these
machines, dropping add-in card drivers and enabling USB and PS/2
input. Patch 49 enables the IOC4 ATA driver in it.
Patches 50-53 add a driver for the Fuel and Tezro front-panel LEDs,
which the L1 sets, as white:status and red:fault. The red LED blinks
on panic, and a Tezro shows the OS state on its L1 display.
The series applies on top of "[PATCH v2] MIPS: SGI-IP27: print the NMI
dump on an nbcon console", which the NMI dump needs to reach the 8250
console.
Tested on SGI Fuel and Tezro, the Fuel with an LSI 53C1030 and an NVMe
drive behind a PCI-X-to-PCIe bridge in slots the PROM leaves
unconfigured, on a 4GB Tezro with an LSI SAS1064 on a PCI-X bus, and
for patch 5 also on an Octane. Patches 50-53 were tested on a Tezro
only, and patch 48 was only built.
On IP27 (Hub) machines:
- patch 22 moves the kernel up by 144KB, and the 144KB below it stays
reserved and unused;
- with patch 26 SWIOTLB takes 64MB of node 0 on a multi-node Origin,
and ZONE_DMA32 spans the first 2GB;
- the XBridge and PIC DMA address forms (patch 26) and the w1 change
(patch 39) do nothing on a Hub with classic Bridges;
- patches 1-5 change the config space handling of its Bridges as well.
Given I have no IP27 boxes, none of this was booted on one.
ip27_defconfig builds at every patch, ip30_defconfig at every patch
that changes code it builds.
An XBridge or PIC on a Bedrock hub takes its board setup from the
first board part number the L1 reports that the setup table knows
(patch 35), so on a machine with several bricks every such bridge gets
that one board's setup.
Patch 42 registers the Odyssey graphics widget as the "sgi-odyssey"
platform device used by the Odyssey DRM driver, a separate series, and
passes it the master widget id as the "sgi,master-widget" property.
checkpatch reports "spaces required around that ':'" in patches 14 and
21 for the GAS numeric local labels 1: and 2: in assembler macros.
Changes in v2:
- New patches 1-5 (Bridge config space and slot fixes), 37 (Origin
300 IO8 serial interrupts), 45-47 and 49 (IOC4 ATA/ATAPI), 48
(ip27_defconfig update) and 50-53 (front-panel LEDs).
- Patch 15 (was 10): slot 0 takes master_nasid as its node, not 0.
- Patch 24 (was 19): also enables the SGI 1-wire master and the DS250x
slave the Bridge probe waits for.
- Patch 25 (was 20): comment and message name the IOC3 on the 32-bit
window behind an XBridge or PIC.
- Patch 26 (was 21), retitled (Christoph Hellwig): an XBridge or PIC
device with a 64-bit DMA mask uses the 64-bit direct form with bit 55
set, other devices the 32-bit window with SWIOTLB and ZONE_DMA32; a
PIC bus in PCI-X mode uses the direct form only; the IOC3 and IOC4
always use the window, with byte swapping off; an IOC3 or IOC4 MFD
cell maps through its PCI parent.
- Patch 29 (was 24): the PIC slot count goes through bridge_nslots()
from patch 1.
- Patch 30 (was 25): probe fails without a platform device id.
- Patch 31 (was 26): probe fails on a PCI id without variant data;
const mfd_cell array; MAINTAINERS covers sgi-ioc3-serial.h; the IOC4
register layout includes the ATA/ATAPI block; the IOC4 firmware
revision is printed, and the serial ports need revision 62 or later;
the PCI id is PCI_DEVICE_ID_SGI_IOC4, which patch 26 adds.
- Patch 36 (was 31): const mfd_cell array.
- Patch 38 (was 32): the random fallback address is set with
eth_hw_addr_random().
- Patch 42 (was 36): passes the "sgi,master-widget" property.
- Patch 43 (was 37): the PROM's console baud rate is printed unsigned,
so 38400 does not come out negative.
- mfd subjects capitalised; rebased onto v7.3-rc7 and the NMI v2 patch.
- v1: https://lore.kernel.org/all/cover.1790857966.git.kaloz@kernel.org/
Imre Kaloz (53):
MIPS: PCI: xtalk-bridge: refuse device numbers past the last slot
MIPS: PCI: xtalk-bridge: assign slots the PROM leaves unconfigured
MIPS: PCI: xtalk-bridge: write a type 1 IOC3 through the type 1 window
MIPS: PCI: xtalk-bridge: flush config space writes
MIPS: PCI: xtalk-bridge: reassign slots whose devio window maps
elsewhere
MIPS: Xtalk: guard the widget identification table from assembly
MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded
MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L()
MIPS: SGI-IP27: detect Bedrock hubs at boot
MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines
MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h
MIPS: SGI-IP27: choose the node address layout at runtime
MIPS: SGI-IP27: add the Bedrock hub register windows
MIPS: SGI-IP27: decode a Bedrock hub's node id and system size
MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0
MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs
MIPS: SGI-IP27: reserve the bottom 64MB of node 0 on Bedrock hubs
MIPS: SGI-IP27: route interrupt masks and slices through both PIs
MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter
MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init()
MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime
MIPS: SGI-IP27: load the kernel at node offset 0x40000
MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW
MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3
net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit
addresses
MIPS: SGI-IP27: pick the XBridge and PIC DMA address form by mask
MIPS: SGI-IP27: recognize the PXBow crossbar
MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only
registers
MIPS: PCI: xtalk-bridge: match PIC's register-level differences
rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base
mfd: ioc3: Add support for IOC4
MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4
MIPS: SGI-IP27: add L1 system controller support
MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address
MIPS: PCI: xtalk-bridge: take the board part number from the L1
mfd: ioc3: Add IP34 system-board entry to ioc3_infos[]
mfd: ioc3: Cross the serial IRQs on the Origin 300 IO8
net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address
MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC
MIPS: SGI-IP27: report a Bedrock machine as SGI IP35
MIPS: SGI-IP27: identify the machine by the L1 brick type
MIPS: SGI-IP27: register the Odyssey graphics widget
MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35
MIPS: SGI-IP27: give each CPU its own topology package id
ata: add pata_sgiioc4 driver for the SGI IOC4 ATA/ATAPI controller
mfd: ioc3: Add IOC4 ATA/ATAPI cell
MAINTAINERS: add an entry for the SGI IOC4 ATA driver
MIPS: ip27_defconfig: update for the onboard hardware
MIPS: ip27_defconfig: enable pata_sgiioc4 and the DVD-ROM path
MIPS: SGI-IP27: add L1 front-panel LED and display requests
leds: Add driver for the SGI Fuel and Tezro front-panel LEDs
MIPS: SGI-IP27: show the OS state on the Fuel and Tezro front panels
MIPS: ip27_defconfig: enable the Fuel and Tezro front-panel LEDs
MAINTAINERS | 7 +
arch/mips/Kconfig | 13 +-
arch/mips/configs/ip27_defconfig | 192 +---
.../include/asm/mach-ip27/kernel-entry-init.h | 33 +-
arch/mips/include/asm/pci/bridge.h | 88 +-
arch/mips/include/asm/sn/addrs.h | 39 +-
arch/mips/include/asm/sn/agent.h | 5 +-
arch/mips/include/asm/sn/arch.h | 8 +
arch/mips/include/asm/sn/intr.h | 14 +-
arch/mips/include/asm/sn/ioc4.h | 143 +++
arch/mips/include/asm/sn/klconfig.h | 19 +-
arch/mips/include/asm/sn/kldir.h | 1 +
arch/mips/include/asm/sn/l1.h | 38 +
arch/mips/include/asm/sn/mapped_kernel.h | 6 +-
arch/mips/include/asm/sn/sn0/addrs.h | 8 +-
arch/mips/include/asm/sn/sn0/hub.h | 37 +-
arch/mips/include/asm/sn/sn0/hubni.h | 3 +
arch/mips/include/asm/sn/sn1/addrs.h | 22 +
arch/mips/include/asm/sn/sn1/hub.h | 40 +
arch/mips/include/asm/sn/sn1/kldir.h | 14 +
arch/mips/include/asm/xtalk/xwidget.h | 11 +-
arch/mips/pci/pci-xtalk-bridge.c | 467 ++++++++-
arch/mips/sgi-ip27/Makefile | 4 +-
arch/mips/sgi-ip27/Platform | 11 +-
arch/mips/sgi-ip27/ip27-berr.c | 4 +-
arch/mips/sgi-ip27/ip27-console.c | 47 +-
arch/mips/sgi-ip27/ip27-init.c | 178 +++-
arch/mips/sgi-ip27/ip27-irq.c | 17 +-
arch/mips/sgi-ip27/ip27-l1.c | 884 ++++++++++++++++++
arch/mips/sgi-ip27/ip27-memory.c | 55 +-
arch/mips/sgi-ip27/ip27-nmi.c | 54 +-
arch/mips/sgi-ip27/ip27-reset.c | 47 +-
arch/mips/sgi-ip27/ip27-smp.c | 41 +-
arch/mips/sgi-ip27/ip27-timer.c | 12 +-
arch/mips/sgi-ip27/ip27-xtalk.c | 138 ++-
drivers/ata/Kconfig | 10 +
drivers/ata/Makefile | 1 +
drivers/ata/pata_sgiioc4.c | 311 ++++++
drivers/leds/Kconfig | 12 +
drivers/leds/Makefile | 1 +
drivers/leds/leds-ip35.c | 144 +++
drivers/mfd/ioc3.c | 455 ++++++++-
drivers/net/ethernet/sgi/ioc3-eth.c | 44 +-
drivers/rtc/rtc-m48t35.c | 33 +-
drivers/tty/serial/8250/8250_ioc3.c | 5 +-
include/linux/pci_ids.h | 1 +
include/linux/platform_data/sgi-ioc3-serial.h | 13 +
include/linux/platform_data/xtalk-bridge.h | 2 +
48 files changed, 3334 insertions(+), 398 deletions(-)
create mode 100644 arch/mips/include/asm/sn/ioc4.h
create mode 100644 arch/mips/include/asm/sn/l1.h
create mode 100644 arch/mips/include/asm/sn/sn1/addrs.h
create mode 100644 arch/mips/include/asm/sn/sn1/hub.h
create mode 100644 arch/mips/include/asm/sn/sn1/kldir.h
create mode 100644 arch/mips/sgi-ip27/ip27-l1.c
create mode 100644 drivers/ata/pata_sgiioc4.c
create mode 100644 drivers/leds/leds-ip35.c
create mode 100644 include/linux/platform_data/sgi-ioc3-serial.h
base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
prerequisite-patch-id: 642054c4c189f29a3f589a119d2311c20e395a23
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [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
` (52 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ 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
` (51 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ 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
` (50 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ 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
` (49 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ 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 06/53] MIPS: Xtalk: guard the widget identification table from assembly Imre Kaloz
` (48 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ messages in thread
* [PATCH v2 06/53] MIPS: Xtalk: guard the widget identification table from assembly
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:16 ` [PATCH v2 07/53] MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded Imre Kaloz
` (47 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Only the constant definitions in this header are usable from assembly;
the widget identification table and the struct it is built from are not.
Put those behind __ASSEMBLER__ so the header can be included from an
assembly file.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/xtalk/xwidget.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/mips/include/asm/xtalk/xwidget.h b/arch/mips/include/asm/xtalk/xwidget.h
index efcfe4494576..6dae5d9628f7 100644
--- a/arch/mips/include/asm/xtalk/xwidget.h
+++ b/arch/mips/include/asm/xtalk/xwidget.h
@@ -112,6 +112,7 @@
#define WIDGET_XBRDG_PART_NUM 0xd002
#define WIDGET_NULL_PART_NUM -1
+#ifndef __ASSEMBLER__
/* For Xtalk Widget identification */
struct widget_ident {
u32 mfgr;
@@ -195,6 +196,7 @@ static const struct widget_ident __initconst widget_idents[] = {
{NULL},
}
};
+#endif /* !__ASSEMBLER__ */
/*
* according to the crosstalk spec, only 32-bits access to the widget
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 07/53] MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded
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 06/53] MIPS: Xtalk: guard the widget identification table from assembly Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 08/53] MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L() Imre Kaloz
` (46 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
nmi_node_eframe_save() walks a fixed two slices per node, so the crash
dump can print a register set and a hub interrupt state for a slice the
node's CPU scan never recorded. Walk the recorded slices instead.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-nmi.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-nmi.c b/arch/mips/sgi-ip27/ip27-nmi.c
index c502982621e6..2cb17f8c7bf3 100644
--- a/arch/mips/sgi-ip27/ip27-nmi.c
+++ b/arch/mips/sgi-ip27/ip27-nmi.c
@@ -5,6 +5,7 @@
#include <linux/nodemask.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
+#include <linux/topology.h>
#include <linux/atomic.h>
#include <asm/sn/types.h>
#include <asm/sn/addrs.h>
@@ -14,12 +15,6 @@
#include "ip27-common.h"
-#if 0
-#define NODE_NUM_CPUS(n) CNODE_NUM_CPUS(n)
-#else
-#define NODE_NUM_CPUS(n) CPUS_PER_NODE
-#endif
-
#define SEND_NMI(_nasid, _slice) \
REMOTE_HUB_S((_nasid), (PI_NMI_A + ((_slice) * PI_NMI_OFFSET)), 1)
@@ -149,13 +144,18 @@ static void nmi_dump_hub_irq(nasid_t nasid, int slice)
*/
static void nmi_node_eframe_save(nasid_t nasid)
{
- int slice;
+ int cpu;
if (nasid == INVALID_NASID)
return;
/* Save the registers into eframe for each cpu */
- for (slice = 0; slice < NODE_NUM_CPUS(slice); slice++) {
+ for_each_possible_cpu(cpu) {
+ int slice = cputoslice(cpu);
+
+ if (cputonasid(cpu) != nasid)
+ continue;
+
nmi_cpu_eframe_save(nasid, slice);
nmi_dump_hub_irq(nasid, slice);
}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 08/53] MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L()
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (6 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 07/53] MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 09/53] MIPS: SGI-IP27: detect Bedrock hubs at boot Imre Kaloz
` (45 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Some hub generations split each hub's PI registers across two
subnode blocks instead of one, so a remote access needs a subnode
selector on top of REMOTE_HUB_ADDR()'s node selector. Passing
subnode 0 reduces the new form to plain REMOTE_HUB_L()/REMOTE_HUB_S().
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/addrs.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/mips/include/asm/sn/addrs.h b/arch/mips/include/asm/sn/addrs.h
index 7c675fecbf9a..d5c7cfea7761 100644
--- a/arch/mips/include/asm/sn/addrs.h
+++ b/arch/mips/include/asm/sn/addrs.h
@@ -267,6 +267,27 @@
#endif /* !__ASSEMBLER__ */
+/*
+ * Some hub generations split each hub's PI registers across two subnode
+ * blocks HUBPI_OFFSET apart; REMOTE_HUB_ADDR() always targets subnode 0.
+ */
+#define HUBPI_OFFSET 0x00200000
+
+#define REMOTE_HUB_PI_ADDR(_n, _sn, _x) \
+ (REMOTE_HUB_ADDR((_n), (_x)) + ((_sn) ? HUBPI_OFFSET : 0))
+
+#ifndef __ASSEMBLER__
+
+#define REMOTE_HUB_PI_PTR(_n, _sn, _x) \
+ ((u64 *)REMOTE_HUB_PI_ADDR((_n), (_sn), (_x)))
+
+#define REMOTE_HUB_PI_L(_n, _sn, _r) \
+ __raw_readq(REMOTE_HUB_PI_PTR((_n), (_sn), (_r)))
+#define REMOTE_HUB_PI_S(_n, _sn, _r, _d) \
+ __raw_writeq((_d), REMOTE_HUB_PI_PTR((_n), (_sn), (_r)))
+
+#endif /* !__ASSEMBLER__ */
+
/*
* Software structure locations -- permanently fixed
* See diagram in kldir.h
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 09/53] MIPS: SGI-IP27: detect Bedrock hubs at boot
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (7 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 08/53] MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L() Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 10/53] MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines Imre Kaloz
` (44 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Read a hub's widget-ID register as the first thing prom_init() does
and record whether it is a Bedrock ASIC, so later code can branch on
that instead of a compile-time select. A local alias read needs no
nasid, so this runs first.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/addrs.h | 6 ++++++
arch/mips/sgi-ip27/ip27-init.c | 9 +++++++++
2 files changed, 15 insertions(+)
diff --git a/arch/mips/include/asm/sn/addrs.h b/arch/mips/include/asm/sn/addrs.h
index d5c7cfea7761..51161fd5130b 100644
--- a/arch/mips/include/asm/sn/addrs.h
+++ b/arch/mips/include/asm/sn/addrs.h
@@ -35,6 +35,12 @@
#endif /* __ASSEMBLER__ */
+#ifndef __ASSEMBLER__
+
+extern bool system_is_ip35;
+
+#endif /* __ASSEMBLER__ */
+
#define NASID_GET_META(_n) ((_n) >> NASID_LOCAL_BITS)
#ifdef CONFIG_SGI_IP27
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index a4daf8ccd16c..6394c2076041 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -31,6 +31,7 @@
#include <asm/thread_info.h>
#include <asm/sn/launch.h>
#include <asm/sn/mapped_kernel.h>
+#include <asm/xtalk/xwidget.h>
#include "ip27-common.h"
@@ -38,6 +39,7 @@
static DECLARE_BITMAP(hub_init_mask, MAX_NUMNODES);
nasid_t master_nasid = INVALID_NASID;
+bool system_is_ip35;
struct cpuinfo_ip27 sn_cpu_info[NR_CPUS];
EXPORT_SYMBOL_GPL(sn_cpu_info);
@@ -139,8 +141,15 @@ const char *get_system_type(void)
return "SGI Origin";
}
+static inline bool hub_is_bedrock(void)
+{
+ return XWIDGET_PART_NUM(LOCAL_HUB_L(IIO_WID)) == WIDGET_BDRCK_PART_NUM;
+}
+
void __init prom_init(void)
{
+ system_is_ip35 = hub_is_bedrock();
+
prom_init_cmdline(fw_arg0, (LONG *)fw_arg1);
prom_meminit();
}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 10/53] MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (8 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 09/53] MIPS: SGI-IP27: detect Bedrock hubs at boot Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 11/53] MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h Imre Kaloz
` (43 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
CONFIG_SGI_IP27 now also covers the Bedrock-based IP35 machines such as
Fuel and Tezro, told apart from Origin 200/2000 at runtime rather than by
a separate config symbol. Update the prompt and help text.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/Kconfig | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index d7c67cebe065..b64f1a8ebf41 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -755,7 +755,7 @@ config SGI_IP22
that runs on these, say Y here.
config SGI_IP27
- bool "SGI IP27 (Origin200/2000)"
+ bool "SGI IP27 (Origin200/2000) and IP35 (Fuel, Tezro)"
select ARCH_HAS_PHYS_TO_DMA
select ARCH_SPARSEMEM_ENABLE
select FW_ARC
@@ -780,9 +780,10 @@ config SGI_IP27
select MIPS_L1_CACHE_SHIFT_7
select NUMA
help
- This are the SGI Origin 200, Origin 2000 and Onyx 2 Graphics
- workstations. To compile a Linux kernel that runs on these, say Y
- here.
+ These are the SGI Origin 200, Origin 2000 and Onyx 2 Graphics
+ workstations, and the IP35 machines built around the Bedrock hub
+ ASIC, such as Fuel and Tezro. To compile a Linux kernel that runs
+ on these, say Y here.
config SGI_IP28
bool "SGI IP28 (Indigo2 R10k)"
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 11/53] MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (9 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 10/53] MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 12/53] MIPS: SGI-IP27: choose the node address layout at runtime Imre Kaloz
` (42 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
klconfig.h's CONFIG_SGI_IP35 branch is unreachable: that symbol is
never selected, and the branch names headers that do not exist in
this tree. Fold its shared includes into the CONFIG_SGI_IP27 branch
and drop the unreachable rest.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/klconfig.h | 19 ++-----------------
1 file changed, 2 insertions(+), 17 deletions(-)
diff --git a/arch/mips/include/asm/sn/klconfig.h b/arch/mips/include/asm/sn/klconfig.h
index 3d1670b3e052..433d2d05aba3 100644
--- a/arch/mips/include/asm/sn/klconfig.h
+++ b/arch/mips/include/asm/sn/klconfig.h
@@ -40,26 +40,11 @@
//#include <sys/graph.h>
//#include <sys/xtalk/xbow.h>
-#elif defined(CONFIG_SGI_IP35)
-
-#include <asm/sn/sn1/addrs.h>
-#include <sys/sn/router.h>
-#include <sys/graph.h>
-#include <asm/xtalk/xbow.h>
-
-#endif /* !CONFIG_SGI_IP27 && !CONFIG_SGI_IP35 */
-
-#if defined(CONFIG_SGI_IP27) || defined(CONFIG_SGI_IP35)
#include <asm/sn/agent.h>
#include <asm/fw/arc/types.h>
#include <asm/fw/arc/hinv.h>
-#if defined(CONFIG_SGI_IP35)
-// The hack file has to be before vector and after sn0_fru....
-#include <asm/hack.h>
-#include <asm/sn/vector.h>
-#include <asm/xtalk/xtalk.h>
-#endif /* CONFIG_SGI_IP35 */
-#endif /* CONFIG_SGI_IP27 || CONFIG_SGI_IP35 */
+
+#endif /* CONFIG_SGI_IP27 */
typedef u64 nic_t;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 12/53] MIPS: SGI-IP27: choose the node address layout at runtime
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (10 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 11/53] MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 13/53] MIPS: SGI-IP27: add the Bedrock hub register windows Imre Kaloz
` (41 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
A Bedrock node owns twice the address space a Hub node does, putting
the nasid one bit higher. Take the shift and the node size from the
generation detected at boot; head.S runs before that and keeps Hub's
values.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/addrs.h | 12 +++++++++++-
arch/mips/include/asm/sn/sn0/addrs.h | 8 ++++----
arch/mips/include/asm/sn/sn1/addrs.h | 22 ++++++++++++++++++++++
3 files changed, 37 insertions(+), 5 deletions(-)
create mode 100644 arch/mips/include/asm/sn/sn1/addrs.h
diff --git a/arch/mips/include/asm/sn/addrs.h b/arch/mips/include/asm/sn/addrs.h
index 51161fd5130b..499615abd501 100644
--- a/arch/mips/include/asm/sn/addrs.h
+++ b/arch/mips/include/asm/sn/addrs.h
@@ -20,7 +20,6 @@
#if defined(CONFIG_SGI_IP27)
#include <asm/sn/sn0/addrs.h>
-#elif defined(CONFIG_SGI_IP35)
#include <asm/sn/sn1/addrs.h>
#endif
@@ -39,6 +38,17 @@
extern bool system_is_ip35;
+#define NASID_SHFT (system_is_ip35 ? SN1_NASID_SHFT : \
+ SN0_NASID_SHFT)
+#define NODE_SIZE_BITS (system_is_ip35 ? SN1_NODE_SIZE_BITS : \
+ SN0_NODE_SIZE_BITS)
+
+#else /* __ASSEMBLER__ */
+
+/* The entry paths run before prom_init() reads the hub's part number. */
+#define NASID_SHFT SN0_NASID_SHFT
+#define NODE_SIZE_BITS SN0_NODE_SIZE_BITS
+
#endif /* __ASSEMBLER__ */
diff --git a/arch/mips/include/asm/sn/sn0/addrs.h b/arch/mips/include/asm/sn/sn0/addrs.h
index a28158a91ecf..f1a161913ae4 100644
--- a/arch/mips/include/asm/sn/sn0/addrs.h
+++ b/arch/mips/include/asm/sn/sn0/addrs.h
@@ -50,12 +50,12 @@
#ifdef CONFIG_SGI_SN_N_MODE
-#define NODE_SIZE_BITS 31
+#define SN0_NODE_SIZE_BITS 31
#define BWIN_SIZE_BITS 28
#define NASID_BITS 9
#define NASID_BITMASK (0x1ffLL)
-#define NASID_SHFT 31
+#define SN0_NASID_SHFT 31
#define NASID_META_BITS 5
#define NASID_LOCAL_BITS 4
@@ -64,12 +64,12 @@
#else /* !defined(CONFIG_SGI_SN_N_MODE), assume that M-mode is desired */
-#define NODE_SIZE_BITS 32
+#define SN0_NODE_SIZE_BITS 32
#define BWIN_SIZE_BITS 29
#define NASID_BITMASK (0xffLL)
#define NASID_BITS 8
-#define NASID_SHFT 32
+#define SN0_NASID_SHFT 32
#define NASID_META_BITS 4
#define NASID_LOCAL_BITS 4
diff --git a/arch/mips/include/asm/sn/sn1/addrs.h b/arch/mips/include/asm/sn/sn1/addrs.h
new file mode 100644
index 000000000000..58878a5b4710
--- /dev/null
+++ b/arch/mips/include/asm/sn/sn1/addrs.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) address space layout.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#ifndef _ASM_SN_SN1_ADDRS_H
+#define _ASM_SN_SN1_ADDRS_H
+
+#ifdef CONFIG_SGI_SN_N_MODE
+
+#define SN1_NODE_SIZE_BITS 32
+#define SN1_NASID_SHFT 32
+
+#else /* !defined(CONFIG_SGI_SN_N_MODE), assume that M-mode is desired */
+
+#define SN1_NODE_SIZE_BITS 33
+#define SN1_NASID_SHFT 33
+
+#endif /* !defined(CONFIG_SGI_SN_N_MODE) */
+
+#endif /* _ASM_SN_SN1_ADDRS_H */
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 13/53] MIPS: SGI-IP27: add the Bedrock hub register windows
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (11 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 12/53] MIPS: SGI-IP27: choose the node address layout at runtime Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 14/53] MIPS: SGI-IP27: decode a Bedrock hub's node id and system size Imre Kaloz
` (40 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Bedrock places its hub register blocks at different offsets than Hub
does. Add a header for them and include it next to the Hub one, since
one kernel has to reach either generation's blocks.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/agent.h | 3 +--
arch/mips/include/asm/sn/sn1/hub.h | 15 +++++++++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/include/asm/sn/sn1/hub.h
diff --git a/arch/mips/include/asm/sn/agent.h b/arch/mips/include/asm/sn/agent.h
index 7e9b3271737a..8873eb34e31b 100644
--- a/arch/mips/include/asm/sn/agent.h
+++ b/arch/mips/include/asm/sn/agent.h
@@ -16,9 +16,8 @@
#if defined(CONFIG_SGI_IP27)
#include <asm/sn/sn0/hub.h>
-#elif defined(CONFIG_SGI_IP35)
#include <asm/sn/sn1/hub.h>
-#endif /* !CONFIG_SGI_IP27 && !CONFIG_SGI_IP35 */
+#endif /* CONFIG_SGI_IP27 */
/*
* NIC register macros
diff --git a/arch/mips/include/asm/sn/sn1/hub.h b/arch/mips/include/asm/sn/sn1/hub.h
new file mode 100644
index 000000000000..db41bcb3d34a
--- /dev/null
+++ b/arch/mips/include/asm/sn/sn1/hub.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) hub register windows.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#ifndef _ASM_SN_SN1_HUB_H
+#define _ASM_SN_SN1_HUB_H
+
+#define HUBLBBASE_IP35 0x00600000
+#define HUBNIBASE_IP35 0x00680000
+#define HUBXBBASE_IP35 0x00700000
+#define HUBMDBASE_IP35 0x00780000
+
+#endif /* _ASM_SN_SN1_HUB_H */
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 14/53] MIPS: SGI-IP27: decode a Bedrock hub's node id and system size
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (12 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 13/53] MIPS: SGI-IP27: add the Bedrock hub register windows Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 15/53] MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0 Imre Kaloz
` (39 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Bedrock's LB_REV_ID holds the node id in bits 39:32 and the system
size, which sets N mode and the region size, in bits 47:46. Decode
both by hub generation; the assembly form, built only for
MAPPED_KERNEL, keys on the widget ID.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
.../include/asm/mach-ip27/kernel-entry-init.h | 8 +++-
arch/mips/include/asm/sn/agent.h | 2 +-
arch/mips/include/asm/sn/sn0/hub.h | 37 +++++++++++++++++--
arch/mips/include/asm/sn/sn0/hubni.h | 3 ++
arch/mips/include/asm/sn/sn1/hub.h | 14 +++++++
arch/mips/include/asm/xtalk/xwidget.h | 2 +-
arch/mips/sgi-ip27/ip27-init.c | 6 ++-
7 files changed, 63 insertions(+), 9 deletions(-)
diff --git a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
index 3e54f605a70b..cd50e6d3d966 100644
--- a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
@@ -65,8 +65,10 @@
* arch/mips/mach-xxx/kernel-entry-init.h when necessary.
*/
.macro kernel_entry_setup
- GET_NASID_ASM t1
+#ifdef CONFIG_MAPPED_KERNEL
+ GET_NASID_ASM t1, t0
move t2, t1 # text and data are here
+#endif
MAPPED_KERNEL_SETUP_TLB
.endm
@@ -74,7 +76,8 @@
* Do SMP slave processor setup necessary before we can safely execute C code.
*/
.macro smp_slave_setup
- GET_NASID_ASM t1
+#ifdef CONFIG_MAPPED_KERNEL
+ GET_NASID_ASM t1, t0
dli t0, KLDIR_OFFSET + (KLI_KERN_VARS * KLDIR_ENT_SIZE) + \
KLDIR_OFF_POINTER + CAC_BASE
dsll t1, NASID_SHFT
@@ -82,6 +85,7 @@
ld t0, 0(t0) # t0 points to kern_vars struct
lh t1, KV_RO_NASID_OFFSET(t0)
lh t2, KV_RW_NASID_OFFSET(t0)
+#endif
MAPPED_KERNEL_SETUP_TLB
/*
diff --git a/arch/mips/include/asm/sn/agent.h b/arch/mips/include/asm/sn/agent.h
index 8873eb34e31b..1d6d0cd5e5fd 100644
--- a/arch/mips/include/asm/sn/agent.h
+++ b/arch/mips/include/asm/sn/agent.h
@@ -15,8 +15,8 @@
#include <asm/sn/arch.h>
#if defined(CONFIG_SGI_IP27)
-#include <asm/sn/sn0/hub.h>
#include <asm/sn/sn1/hub.h>
+#include <asm/sn/sn0/hub.h>
#endif /* CONFIG_SGI_IP27 */
/*
diff --git a/arch/mips/include/asm/sn/sn0/hub.h b/arch/mips/include/asm/sn/sn0/hub.h
index 916394319af5..c5429dbddc7a 100644
--- a/arch/mips/include/asm/sn/sn0/hub.h
+++ b/arch/mips/include/asm/sn/sn0/hub.h
@@ -31,6 +31,8 @@
#include <asm/sn/sn0/hubni.h>
//#include <asm/sn/sn0/hubcore.h>
+#include <asm/xtalk/xwidget.h>
+
/* Translation of uncached attributes */
#define UATTR_HSPEC 0
#define UATTR_IO 1
@@ -39,13 +41,35 @@
#ifdef __ASSEMBLER__
/*
- * Returns the local nasid into res.
+ * Clears tmp on a Bedrock hub. Nothing has read the hub's part number
+ * this early, so take it from the widget ID.
*/
- .macro GET_NASID_ASM res
+ .macro GET_HUB_GEN_ASM tmp
+ dli \tmp, LOCAL_HUB_ADDR(IIO_WID)
+ ld \tmp, (\tmp)
+ dsrl \tmp, WIDGET_PART_NUM_SHFT
+ andi \tmp, (WIDGET_PART_NUM >> WIDGET_PART_NUM_SHFT)
+ xori \tmp, WIDGET_BDRCK_PART_NUM
+ .endm
+
+/*
+ * Returns the local nasid into res, clobbering tmp.
+ */
+ .macro GET_NASID_ASM res, tmp
+ .set push
+ .set reorder
+ GET_HUB_GEN_ASM \tmp
dli \res, LOCAL_HUB_ADDR(NI_STATUS_REV_ID)
ld \res, (\res)
+ beqz \tmp, 1f
and \res, NSRI_NODEID_MASK
dsrl \res, NSRI_NODEID_SHFT
+ b 2f
+1:
+ dsrl \res, LRI_NODEID_SHFT_IP35
+ andi \res, LRI_NODEID_MASK_IP35
+2:
+ .set pop
.endm
#else
@@ -54,8 +78,13 @@
*/
static inline nasid_t get_nasid(void)
{
- return (nasid_t)((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_NODEID_MASK)
- >> NSRI_NODEID_SHFT);
+ u64 id = LOCAL_HUB_L(NI_STATUS_REV_ID);
+
+ if (system_is_ip35)
+ return (nasid_t)((id >> LRI_NODEID_SHFT_IP35) &
+ LRI_NODEID_MASK_IP35);
+
+ return (nasid_t)((id & NSRI_NODEID_MASK) >> NSRI_NODEID_SHFT);
}
#endif
diff --git a/arch/mips/include/asm/sn/sn0/hubni.h b/arch/mips/include/asm/sn/sn0/hubni.h
index 4830bae723e4..ff7df5e27f66 100644
--- a/arch/mips/include/asm/sn/sn0/hubni.h
+++ b/arch/mips/include/asm/sn/sn0/hubni.h
@@ -252,6 +252,9 @@ typedef union hubni_port_error_u {
static inline int get_region_shift(void)
{
+ if (system_is_ip35)
+ return LRI_SYSTEM_SIZE_IP35(LOCAL_HUB_L(NI_STATUS_REV_ID));
+
if (LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_REGIONSIZE_MASK)
return NASID_TO_FINEREG_SHFT;
diff --git a/arch/mips/include/asm/sn/sn1/hub.h b/arch/mips/include/asm/sn/sn1/hub.h
index db41bcb3d34a..71ccd2ba3698 100644
--- a/arch/mips/include/asm/sn/sn1/hub.h
+++ b/arch/mips/include/asm/sn/sn1/hub.h
@@ -12,4 +12,18 @@
#define HUBXBBASE_IP35 0x00700000
#define HUBMDBASE_IP35 0x00780000
+/*
+ * LB_REV_ID, at the base of the LB block and so at Hub's NI_STATUS_REV_ID
+ * offset, holds the node id in bits 39:32.
+ */
+#define LRI_NODEID_SHFT_IP35 32
+#define LRI_NODEID_MASK_IP35 0xff
+
+/*
+ * Bits 47:46 give the system size: 0, 1 or 2 is log2 of the nodes per
+ * region, and 2 is N mode.
+ */
+#define LRI_SYSTEM_SIZE_IP35(id) (((id) >> 46) & 0x3)
+#define LRI_SYSTEM_SIZE_NMODE_IP35 2
+
#endif /* _ASM_SN_SN1_HUB_H */
diff --git a/arch/mips/include/asm/xtalk/xwidget.h b/arch/mips/include/asm/xtalk/xwidget.h
index 6dae5d9628f7..978877e8a090 100644
--- a/arch/mips/include/asm/xtalk/xwidget.h
+++ b/arch/mips/include/asm/xtalk/xwidget.h
@@ -122,7 +122,7 @@ struct widget_ident {
};
/* Known Xtalk Widgets */
-static const struct widget_ident __initconst widget_idents[] = {
+static const struct widget_ident widget_idents[] __initconst __maybe_unused = {
{
WIDGET_XBOW_MFGR_NUM,
WIDGET_XBOW_PART_NUM,
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 6394c2076041..108d90908763 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -121,7 +121,11 @@ void __init plat_mem_setup(void)
* Try to catch kernel missconfigurations and give user an
* indication what option to select.
*/
- n_mode = LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_MORENODES_MASK;
+ if (system_is_ip35)
+ n_mode = LRI_SYSTEM_SIZE_IP35(LOCAL_HUB_L(NI_STATUS_REV_ID)) ==
+ LRI_SYSTEM_SIZE_NMODE_IP35;
+ else
+ n_mode = LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_MORENODES_MASK;
printk("Machine is in %c mode.\n", n_mode ? 'N' : 'M');
#ifdef CONFIG_SGI_SN_N_MODE
if (!n_mode)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 15/53] MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (13 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 14/53] MIPS: SGI-IP27: decode a Bedrock hub's node id and system size Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 16/53] MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs Imre Kaloz
` (38 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
ip27_smp_setup() takes CPU 0 to be the boot CPU. On a Bedrock hub, swap
the boot CPU's sn_cpu_info[] entry, found by nasid and PI_CPU_NUM slice,
into slot 0, and give slot 0 master_nasid. The PROM's virtids run
densely from 0 in board-list order and so match the table's slots.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-smp.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c
index 62733e049570..d49766cffd12 100644
--- a/arch/mips/sgi-ip27/ip27-smp.c
+++ b/arch/mips/sgi-ip27/ip27-smp.c
@@ -80,6 +80,15 @@ void cpu_node_probe(void)
highest = node_scan_cpus(nasid, highest);
}
+ /* The boot CPU must be CPU 0 wherever the board list puts it. */
+ for (i = 1; system_is_ip35 && i <= highest; i++) {
+ if (cputonasid(i) == master_nasid &&
+ cputoslice(i) == LOCAL_HUB_L(PI_CPU_NUM)) {
+ swap(sn_cpu_info[0], sn_cpu_info[i]);
+ break;
+ }
+ }
+
printk("Discovered %d cpus on %d nodes\n", highest + 1, num_online_nodes());
}
@@ -168,9 +177,9 @@ static void __init ip27_smp_setup(void)
replicate_kernel_text();
/*
- * PROM sets up system, that boot cpu is always first CPU on nasid 0
+ * The boot CPU is CPU 0, on the node it booted from
*/
- cputonasid(0) = 0;
+ cputonasid(0) = master_nasid;
cputoslice(0) = LOCAL_HUB_L(PI_CPU_NUM);
}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 16/53] MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (14 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 15/53] MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 17/53] MIPS: SGI-IP27: reserve the bottom 64MB of node 0 " Imre Kaloz
` (37 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Hub describes a node's memory as 128MB slots, four slots to a
klconfig bank entry. Bedrock gives each bank entry a 1GB slot of its
own and sizes it in megabytes. Take the slot shift, the slot count and
the bank size from the hub generation.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-memory.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c
index 4317f5ae1fd1..0204dc8bb1bb 100644
--- a/arch/mips/sgi-ip27/ip27-memory.c
+++ b/arch/mips/sgi-ip27/ip27-memory.c
@@ -31,9 +31,16 @@
#include "ip27-common.h"
-#define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT)
#define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT)
+/* A Bedrock node's memory comes in 1GB banks. */
+#define BEDROCK_SLOT_SHIFT 30
+
+static unsigned int __init slot_shift(void)
+{
+ return system_is_ip35 ? BEDROCK_SLOT_SHIFT : SLOT_SHIFT;
+}
+
struct node_data *__node_data[MAX_NUMNODES];
EXPORT_SYMBOL(__node_data);
@@ -230,7 +237,8 @@ static void __init dump_topology(void)
static unsigned long __init slot_getbasepfn(nasid_t nasid, int slot)
{
- return ((unsigned long)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT);
+ return ((unsigned long)nasid << PFN_NASIDSHFT) |
+ ((unsigned long)slot << (slot_shift() - PAGE_SHIFT));
}
static unsigned long __init slot_psize_compute(nasid_t nasid, int slot)
@@ -249,6 +257,12 @@ static unsigned long __init slot_psize_compute(nasid_t nasid, int slot)
if (!banks)
return 0;
+ /* Bedrock bank entry N sizes the 1GB slot N, in megabytes. */
+ if (system_is_ip35) {
+ size = (unsigned long)banks->membnk_bnksz[slot];
+ return (size << 20) >> PAGE_SHIFT;
+ }
+
/* Size in _Megabytes_ */
size = (unsigned long)banks->membnk_bnksz[slot/4];
@@ -315,12 +329,13 @@ static void __init mlreset(void)
static void __init szmem(void)
{
unsigned long slot_psize, slot0sz = 0, nodebytes; /* Hack to detect problem configs */
+ int slots = system_is_ip35 ? MD_MEM_BANKS : MAX_MEM_SLOTS;
int slot;
nasid_t node;
for_each_online_node(node) {
nodebytes = 0;
- for (slot = 0; slot < MAX_MEM_SLOTS; slot++) {
+ for (slot = 0; slot < slots; slot++) {
slot_psize = slot_psize_compute(node, slot);
if (slot == 0)
slot0sz = slot_psize;
@@ -328,7 +343,7 @@ static void __init szmem(void)
* We need to refine the hack when we have replicated
* kernel text.
*/
- nodebytes += (1LL << SLOT_SHIFT);
+ nodebytes += (1LL << slot_shift());
if (!slot_psize)
continue;
@@ -337,7 +352,7 @@ static void __init szmem(void)
(slot0sz << PAGE_SHIFT)) {
pr_info("Ignoring slot %d onwards on node %d\n",
slot, node);
- slot = MAX_MEM_SLOTS;
+ slot = slots;
continue;
}
memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)),
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 17/53] MIPS: SGI-IP27: reserve the bottom 64MB of node 0 on Bedrock hubs
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (15 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 16/53] MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 18/53] MIPS: SGI-IP27: route interrupt masks and slices through both PIs Imre Kaloz
` (36 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
On Bedrock hubs, reserve the first 64MB of physical memory, the bottom
of node 0, and place node 0's node data above it. node_getfirstfree()
covers only the kernel image, so the area needs a reservation of its
own.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-memory.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c
index 0204dc8bb1bb..98184dfcd8c5 100644
--- a/arch/mips/sgi-ip27/ip27-memory.c
+++ b/arch/mips/sgi-ip27/ip27-memory.c
@@ -17,6 +17,7 @@
#include <linux/mmzone.h>
#include <linux/export.h>
#include <linux/nodemask.h>
+#include <linux/sizes.h>
#include <linux/swap.h>
#include <linux/pfn.h>
#include <linux/highmem.h>
@@ -36,6 +37,9 @@
/* A Bedrock node's memory comes in 1GB banks. */
#define BEDROCK_SLOT_SHIFT 30
+/* Reserved at the bottom of node 0, which starts at physical 0. */
+#define BEDROCK_RSVD_SIZE SZ_64M
+
static unsigned int __init slot_shift(void)
{
return system_is_ip35 ? BEDROCK_SLOT_SHIFT : SLOT_SHIFT;
@@ -370,6 +374,10 @@ static void __init node_mem_init(nasid_t node)
get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
+ /* Keep the node data above the reserved area. */
+ if (system_is_ip35 && node == 0)
+ slot_freepfn = max(slot_freepfn, PFN_UP(BEDROCK_RSVD_SIZE));
+
/*
* Allocate the node data structures on the node first.
*/
@@ -410,6 +418,10 @@ void __init prom_meminit(void)
mlreset();
szmem();
+
+ if (system_is_ip35)
+ memblock_reserve(0, BEDROCK_RSVD_SIZE);
+
max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
for (node = 0; node < MAX_NUMNODES; node++) {
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 18/53] MIPS: SGI-IP27: route interrupt masks and slices through both PIs
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (16 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 17/53] MIPS: SGI-IP27: reserve the bottom 64MB of node 0 " Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 19/53] MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter Imre Kaloz
` (35 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
cputoslice() and PI_CPU_NUM return a physid, 0-3 across a Bedrock hub's
two PIs, where a Hub has a plain A/B bit. Add helpers splitting it into
A/B slot and PI block, and give every remote access the slice's PI
block. Local accesses already reach the running CPU's own PI.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/arch.h | 8 ++++++++
arch/mips/include/asm/sn/intr.h | 10 ++++++----
arch/mips/sgi-ip27/ip27-berr.c | 4 ++--
arch/mips/sgi-ip27/ip27-irq.c | 17 +++++++++--------
arch/mips/sgi-ip27/ip27-smp.c | 16 +++++++++-------
arch/mips/sgi-ip27/ip27-timer.c | 4 ++--
6 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/arch/mips/include/asm/sn/arch.h b/arch/mips/include/asm/sn/arch.h
index 9a9682543e89..602219f98b04 100644
--- a/arch/mips/include/asm/sn/arch.h
+++ b/arch/mips/include/asm/sn/arch.h
@@ -18,7 +18,15 @@
#endif
#define cputonasid(cpu) (sn_cpu_info[(cpu)].p_nasid)
+/*
+ * The slice is the CPU's physid: on a Bedrock hub 0-3 across two PI blocks,
+ * bit 0 selecting a PI's A/B register slot and bit 1 the PI block.
+ */
#define cputoslice(cpu) (sn_cpu_info[(cpu)].p_slice)
+#define slicetoab(slice) ((slice) & 1)
+#define slicetosubnode(slice) (((slice) >> 1) & 1)
+#define cputoab(cpu) slicetoab(cputoslice(cpu))
+#define cputosubnode(cpu) slicetosubnode(cputoslice(cpu))
#define INVALID_NASID (nasid_t)-1
#define INVALID_PNODEID (pnodeid_t)-1
diff --git a/arch/mips/include/asm/sn/intr.h b/arch/mips/include/asm/sn/intr.h
index 3d6954d370dc..90b613f68623 100644
--- a/arch/mips/include/asm/sn/intr.h
+++ b/arch/mips/include/asm/sn/intr.h
@@ -29,13 +29,15 @@ do { \
LOCAL_HUB_L(PI_INT_PEND0); \
} while (0);
-#define REMOTE_HUB_CLR_INTR(hub, level) \
+/* An interrupt is pending in the PI block of the CPU it is routed to. */
+#define REMOTE_HUB_PI_CLR_INTR(hub, sn, level) \
do { \
nasid_t __hub = (hub); \
+ int __sn = (sn); \
\
- REMOTE_HUB_S(__hub, PI_INT_PEND_MOD, (level)); \
- REMOTE_HUB_L(__hub, PI_INT_PEND0); \
-} while (0);
+ REMOTE_HUB_PI_S(__hub, __sn, PI_INT_PEND_MOD, (level)); \
+ REMOTE_HUB_PI_L(__hub, __sn, PI_INT_PEND0); \
+} while (0)
/*
* Hard-coded interrupt levels:
diff --git a/arch/mips/sgi-ip27/ip27-berr.c b/arch/mips/sgi-ip27/ip27-berr.c
index 9eb497cb5d52..b176856aa77c 100644
--- a/arch/mips/sgi-ip27/ip27-berr.c
+++ b/arch/mips/sgi-ip27/ip27-berr.c
@@ -63,7 +63,7 @@ static int ip27_be_handler(struct pt_regs *regs, int is_fixup)
{
unsigned long errst0, errst1;
int data = regs->cp0_cause & 4;
- int cpu = LOCAL_HUB_L(PI_CPU_NUM);
+ int cpu = slicetoab(LOCAL_HUB_L(PI_CPU_NUM));
if (is_fixup)
return MIPS_BE_FIXUP;
@@ -84,7 +84,7 @@ static int ip27_be_handler(struct pt_regs *regs, int is_fixup)
void __init ip27_be_init(void)
{
/* XXX Initialize all the Hub & Bridge error handling here. */
- int cpu = LOCAL_HUB_L(PI_CPU_NUM);
+ int cpu = slicetoab(LOCAL_HUB_L(PI_CPU_NUM));
int cpuoff = cpu << 8;
mips_set_be_handler(ip27_be_handler);
diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c
index 7e5aa04c6695..02a49bb8529d 100644
--- a/arch/mips/sgi-ip27/ip27-irq.c
+++ b/arch/mips/sgi-ip27/ip27-irq.c
@@ -72,7 +72,7 @@ static void disable_hub_irq(struct irq_data *d)
static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask)
{
nasid_t nasid;
- int cpu;
+ int cpu, sn;
cpu = cpumask_first_and(mask, cpu_online_mask);
if (cpu >= nr_cpu_ids)
@@ -80,12 +80,13 @@ static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask)
nasid = cpu_to_node(cpu);
hd->cpu = cpu;
- if (!cputoslice(cpu)) {
- hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_A);
- hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_A);
+ sn = cputosubnode(cpu);
+ if (!cputoab(cpu)) {
+ hd->irq_mask[0] = REMOTE_HUB_PI_PTR(nasid, sn, PI_INT_MASK0_A);
+ hd->irq_mask[1] = REMOTE_HUB_PI_PTR(nasid, sn, PI_INT_MASK1_A);
} else {
- hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_B);
- hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_B);
+ hd->irq_mask[0] = REMOTE_HUB_PI_PTR(nasid, sn, PI_INT_MASK0_B);
+ hd->irq_mask[1] = REMOTE_HUB_PI_PTR(nasid, sn, PI_INT_MASK1_B);
}
}
@@ -147,7 +148,7 @@ static int hub_domain_alloc(struct irq_domain *domain, unsigned int virq,
info->nasid = cpu_to_node(hd->cpu);
/* Make sure it's not already pending when we connect it. */
- REMOTE_HUB_CLR_INTR(info->nasid, swlevel);
+ REMOTE_HUB_PI_CLR_INTR(info->nasid, cputosubnode(hd->cpu), swlevel);
desc = irq_to_desc(virq);
desc->irq_common_data.node = info->nasid;
@@ -253,7 +254,7 @@ void install_ipi(void)
{
int cpu = smp_processor_id();
unsigned long *mask = per_cpu(irq_enable_mask, cpu);
- int slice = LOCAL_HUB_L(PI_CPU_NUM);
+ int slice = slicetoab(LOCAL_HUB_L(PI_CPU_NUM));
int resched, call;
resched = CPU_RESCHED_A_IRQ + slice;
diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c
index d49766cffd12..66c999a8b966 100644
--- a/arch/mips/sgi-ip27/ip27-smp.c
+++ b/arch/mips/sgi-ip27/ip27-smp.c
@@ -94,15 +94,17 @@ void cpu_node_probe(void)
static __init void intr_clear_all(nasid_t nasid)
{
- int i;
+ int i, sn, pis = system_is_ip35 ? 2 : 1;
- REMOTE_HUB_S(nasid, PI_INT_MASK0_A, 0);
- REMOTE_HUB_S(nasid, PI_INT_MASK0_B, 0);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_A, 0);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_B, 0);
+ for (sn = 0; sn < pis; sn++) {
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK0_A, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK0_B, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK1_A, 0);
+ REMOTE_HUB_PI_S(nasid, sn, PI_INT_MASK1_B, 0);
- for (i = 0; i < 128; i++)
- REMOTE_HUB_CLR_INTR(nasid, i);
+ for (i = 0; i < 128; i++)
+ REMOTE_HUB_PI_CLR_INTR(nasid, sn, i);
+ }
}
static void ip27_send_ipi_single(int destid, unsigned int action)
diff --git a/arch/mips/sgi-ip27/ip27-timer.c b/arch/mips/sgi-ip27/ip27-timer.c
index 5f4da05cb2c9..aee14a7aad8a 100644
--- a/arch/mips/sgi-ip27/ip27-timer.c
+++ b/arch/mips/sgi-ip27/ip27-timer.c
@@ -30,7 +30,7 @@
static int rt_next_event(unsigned long delta, struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
- int slice = cputoslice(cpu);
+ int slice = cputoab(cpu);
unsigned long cnt;
cnt = LOCAL_HUB_L(PI_RT_COUNT);
@@ -47,7 +47,7 @@ static irqreturn_t hub_rt_counter_handler(int irq, void *dev_id)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd = &per_cpu(hub_rt_clockevent, cpu);
- int slice = cputoslice(cpu);
+ int slice = cputoab(cpu);
/*
* Ack
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 19/53] MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (17 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 18/53] MIPS: SGI-IP27: route interrupt masks and slices through both PIs Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 20/53] MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init() Imre Kaloz
` (34 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
REMOTE_HUB_SEND_INTR(), SEND_NMI() and nmi_dump_hub_irq() reach the first
PI only; address a CPU's own PI. Call hub_rtc_init() once per PI; its
LOCAL_HUB writes reach the calling CPU's own PI. Read the clocksource from
the boot CPU's PI and enable the Bedrock NI reset for NMI.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/intr.h | 4 +--
arch/mips/include/asm/sn/kldir.h | 1 +
arch/mips/include/asm/sn/sn1/hub.h | 11 ++++++++
arch/mips/include/asm/sn/sn1/kldir.h | 14 ++++++++++
arch/mips/sgi-ip27/ip27-init.c | 14 +++++++---
arch/mips/sgi-ip27/ip27-nmi.c | 38 +++++++++++++++++++---------
arch/mips/sgi-ip27/ip27-smp.c | 4 +--
arch/mips/sgi-ip27/ip27-timer.c | 8 ++++--
8 files changed, 73 insertions(+), 21 deletions(-)
create mode 100644 arch/mips/include/asm/sn/sn1/kldir.h
diff --git a/arch/mips/include/asm/sn/intr.h b/arch/mips/include/asm/sn/intr.h
index 90b613f68623..4927232fafc3 100644
--- a/arch/mips/include/asm/sn/intr.h
+++ b/arch/mips/include/asm/sn/intr.h
@@ -14,8 +14,8 @@
#define LOCAL_HUB_SEND_INTR(level) \
LOCAL_HUB_S(PI_INT_PEND_MOD, (0x100 | (level)))
-#define REMOTE_HUB_SEND_INTR(hub, level) \
- REMOTE_HUB_S((hub), PI_INT_PEND_MOD, (0x100 | (level)))
+#define REMOTE_HUB_PI_SEND_INTR(hub, sn, level) \
+ REMOTE_HUB_PI_S((hub), (sn), PI_INT_PEND_MOD, (0x100 | (level)))
/*
* When clearing the interrupt, make sure this clear does make it
diff --git a/arch/mips/include/asm/sn/kldir.h b/arch/mips/include/asm/sn/kldir.h
index f394b1e0c956..23999dd43eb2 100644
--- a/arch/mips/include/asm/sn/kldir.h
+++ b/arch/mips/include/asm/sn/kldir.h
@@ -31,6 +31,7 @@ typedef struct kldir_ent_s {
#ifdef CONFIG_SGI_IP27
#include <asm/sn/sn0/kldir.h>
+#include <asm/sn/sn1/kldir.h>
#endif
#endif /* _ASM_SN_KLDIR_H */
diff --git a/arch/mips/include/asm/sn/sn1/hub.h b/arch/mips/include/asm/sn/sn1/hub.h
index 71ccd2ba3698..60e0f249852f 100644
--- a/arch/mips/include/asm/sn/sn1/hub.h
+++ b/arch/mips/include/asm/sn/sn1/hub.h
@@ -26,4 +26,15 @@
#define LRI_SYSTEM_SIZE_IP35(id) (((id) >> 46) & 0x3)
#define LRI_SYSTEM_SIZE_NMODE_IP35 2
+/*
+ * Network interface port reset: bit 0 sends a link reset, bit 2 resets
+ * the local Bedrock. Bit 0 of the reset enable register lets a reset
+ * arriving over the link reset this Bedrock.
+ */
+#define NI_PORT_RESET_IP35 (HUBNIBASE_IP35 + 0x000008)
+#define NI_RESET_ACTION_IP35 0x01
+#define NI_RESET_LOCAL_IP35 0x04
+#define NI_RESET_ENABLE_IP35 (HUBNIBASE_IP35 + 0x000010)
+#define NI_RESET_OK_IP35 0x01
+
#endif /* _ASM_SN_SN1_HUB_H */
diff --git a/arch/mips/include/asm/sn/sn1/kldir.h b/arch/mips/include/asm/sn/sn1/kldir.h
new file mode 100644
index 000000000000..c95dccb3ce55
--- /dev/null
+++ b/arch/mips/include/asm/sn/sn1/kldir.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) kldir constants.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#ifndef _ASM_SN_SN1_KLDIR_H
+#define _ASM_SN_SN1_KLDIR_H
+
+/* Save area of kernel NMI regs in the PROM format. */
+#define IP35_NMI_KREGS_OFFSET 0x9000
+#define IP35_NMI_KREGS_CPU_SIZE 0x400
+
+#endif /* _ASM_SN_SN1_KLDIR_H */
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 108d90908763..88d79c50d9a7 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -37,7 +37,11 @@
#define CPU_NONE (cpuid_t)-1
+/* A Bedrock hub has two PI register blocks; a Hub has one. */
+#define PIS_PER_HUB 2
+
static DECLARE_BITMAP(hub_init_mask, MAX_NUMNODES);
+static DECLARE_BITMAP(pi_init_mask, MAX_NUMNODES * PIS_PER_HUB);
nasid_t master_nasid = INVALID_NASID;
bool system_is_ip35;
@@ -46,9 +50,15 @@ EXPORT_SYMBOL_GPL(sn_cpu_info);
static void per_hub_init(nasid_t nasid)
{
+ int cpu = smp_processor_id();
struct hub_data *hub = hub_data(nasid);
+ int subnode = cputosubnode(cpu);
- cpumask_set_cpu(smp_processor_id(), &hub->h_cpus);
+ cpumask_set_cpu(cpu, &hub->h_cpus);
+
+ /* A second pass would zero the counter under a CPU already ticking. */
+ if (!test_and_set_bit(nasid * PIS_PER_HUB + subnode, pi_init_mask))
+ hub_rtc_init(nasid);
if (test_and_set_bit(nasid, hub_init_mask))
return;
@@ -58,8 +68,6 @@ static void per_hub_init(nasid_t nasid)
REMOTE_HUB_S(nasid, IIO_ICTP, 0x800);
REMOTE_HUB_S(nasid, IIO_ICTO, 0xff);
- hub_rtc_init(nasid);
-
if (nasid) {
/* copy exception handlers from first node to current node */
memcpy((void *)NODE_OFFSET_TO_K0(nasid, 0),
diff --git a/arch/mips/sgi-ip27/ip27-nmi.c b/arch/mips/sgi-ip27/ip27-nmi.c
index 2cb17f8c7bf3..627816d5bc6f 100644
--- a/arch/mips/sgi-ip27/ip27-nmi.c
+++ b/arch/mips/sgi-ip27/ip27-nmi.c
@@ -15,8 +15,9 @@
#include "ip27-common.h"
-#define SEND_NMI(_nasid, _slice) \
- REMOTE_HUB_S((_nasid), (PI_NMI_A + ((_slice) * PI_NMI_OFFSET)), 1)
+#define SEND_NMI(_nasid, _slice) \
+ REMOTE_HUB_PI_S((_nasid), slicetosubnode(_slice), \
+ (PI_NMI_A + (slicetoab(_slice) * PI_NMI_OFFSET)), 1)
typedef unsigned long machreg_t;
@@ -45,12 +46,18 @@ void install_cpu_nmi_handler(int slice)
static void nmi_cpu_eframe_save(nasid_t nasid, int slice)
{
struct reg_struct *nr;
+ int kregs_offset, kregs_cpu_size;
int i;
+ kregs_offset = system_is_ip35 ? IP35_NMI_KREGS_OFFSET :
+ IP27_NMI_KREGS_OFFSET;
+ kregs_cpu_size = system_is_ip35 ? IP35_NMI_KREGS_CPU_SIZE :
+ IP27_NMI_KREGS_CPU_SIZE;
+
/* Get the pointer to the current cpu's register set. */
nr = (struct reg_struct *)
- (TO_UNCAC(TO_NODE(nasid, IP27_NMI_KREGS_OFFSET)) +
- slice * IP27_NMI_KREGS_CPU_SIZE);
+ (TO_UNCAC(TO_NODE(nasid, kregs_offset)) +
+ slice * kregs_cpu_size);
pr_emerg("NMI nasid %d: slice %d\n", nasid, slice);
@@ -120,18 +127,19 @@ static void nmi_cpu_eframe_save(nasid_t nasid, int slice)
static void nmi_dump_hub_irq(nasid_t nasid, int slice)
{
+ int subnode = slicetosubnode(slice);
u64 mask0, mask1, pend0, pend1;
- if (slice == 0) { /* Slice A */
- mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_A);
- mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_A);
+ if (!slicetoab(slice)) { /* Slice A */
+ mask0 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_MASK0_A);
+ mask1 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_MASK1_A);
} else { /* Slice B */
- mask0 = REMOTE_HUB_L(nasid, PI_INT_MASK0_B);
- mask1 = REMOTE_HUB_L(nasid, PI_INT_MASK1_B);
+ mask0 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_MASK0_B);
+ mask1 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_MASK1_B);
}
- pend0 = REMOTE_HUB_L(nasid, PI_INT_PEND0);
- pend1 = REMOTE_HUB_L(nasid, PI_INT_PEND1);
+ pend0 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_PEND0);
+ pend1 = REMOTE_HUB_PI_L(nasid, subnode, PI_INT_PEND1);
pr_emerg("PI_INT_MASK0: %16llx PI_INT_MASK1: %16llx\n", mask0, mask1);
pr_emerg("PI_INT_PEND0: %16llx PI_INT_PEND1: %16llx\n", pend0, pend1);
@@ -235,5 +243,11 @@ static void nmi_dump(void)
*/
nmi_eframes_save();
printk_trigger_flush();
- LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+ if (system_is_ip35) {
+ LOCAL_HUB_S(NI_RESET_ENABLE_IP35, NI_RESET_OK_IP35);
+ LOCAL_HUB_S(NI_PORT_RESET_IP35,
+ NI_RESET_ACTION_IP35 | NI_RESET_LOCAL_IP35);
+ } else {
+ LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+ }
}
diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c
index 66c999a8b966..13b85e3ba311 100644
--- a/arch/mips/sgi-ip27/ip27-smp.c
+++ b/arch/mips/sgi-ip27/ip27-smp.c
@@ -122,13 +122,13 @@ static void ip27_send_ipi_single(int destid, unsigned int action)
panic("sendintr");
}
- irq += cputoslice(destid);
+ irq += cputoab(destid);
/*
* Set the interrupt bit associated with the CPU we want to
* send the interrupt to.
*/
- REMOTE_HUB_SEND_INTR(cpu_to_node(destid), irq);
+ REMOTE_HUB_PI_SEND_INTR(cpu_to_node(destid), cputosubnode(destid), irq);
}
static void ip27_send_ipi_mask(const struct cpumask *mask, unsigned int action)
diff --git a/arch/mips/sgi-ip27/ip27-timer.c b/arch/mips/sgi-ip27/ip27-timer.c
index aee14a7aad8a..14df8608b14b 100644
--- a/arch/mips/sgi-ip27/ip27-timer.c
+++ b/arch/mips/sgi-ip27/ip27-timer.c
@@ -100,9 +100,13 @@ static void __init hub_rt_clock_event_global_init(void)
"hub-rt", &hub_rt_clockevent));
}
+/*
+ * Each PI of a Bedrock hub has its own RT counter, zeroed when that PI's
+ * first CPU comes up; the timebase is the boot CPU's.
+ */
static u64 hub_rt_read(struct clocksource *cs)
{
- return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
+ return REMOTE_HUB_PI_L(cputonasid(0), cputosubnode(0), PI_RT_COUNT);
}
struct clocksource hub_rt_clocksource = {
@@ -115,7 +119,7 @@ struct clocksource hub_rt_clocksource = {
static u64 notrace hub_rt_read_sched_clock(void)
{
- return REMOTE_HUB_L(cputonasid(0), PI_RT_COUNT);
+ return REMOTE_HUB_PI_L(cputonasid(0), cputosubnode(0), PI_RT_COUNT);
}
static void __init hub_rt_clocksource_init(void)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 20/53] MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init()
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (18 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 19/53] MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 21/53] MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime Imre Kaloz
` (33 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
cpu_node_probe() records every CPU's node before the first secondary
launches. Take it from there, so a CPU coming up inherits its node from
the boot CPU's scan instead of reading the hub again.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-init.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 88d79c50d9a7..15811fb3cdee 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -81,7 +81,7 @@ static void per_hub_init(nasid_t nasid)
void per_cpu_init(void)
{
int cpu = smp_processor_id();
- nasid_t nasid = get_nasid();
+ nasid_t nasid = cputonasid(cpu);
clear_c0_status(ST0_IM);
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 21/53] MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (19 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 20/53] MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init() Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 22/53] MIPS: SGI-IP27: load the kernel at node offset 0x40000 Imre Kaloz
` (32 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
A Bedrock hub puts the nasid one bit higher in an address than a Hub
does. MAPPED_KERNEL_SETUP_TLB builds its wired entry and smp_slave_setup()
reaches the kernel variables before any C runs, so take the shift from
the hub's Crosstalk widget ID.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
.../include/asm/mach-ip27/kernel-entry-init.h | 23 ++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
index cd50e6d3d966..1bedd3c3bc58 100644
--- a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
@@ -21,6 +21,21 @@
#define PAGE_DIRTY (1 << 8)
#define CACHE_CACHABLE_COW (5 << 9)
+ /*
+ * A Bedrock hub puts the nasid one bit higher in an address than a
+ * Hub does. Returns the shift in \shft, clobbering \tmp.
+ */
+ .macro GET_NASID_SHFT shft, tmp
+ .set push
+ .set reorder
+ GET_HUB_GEN_ASM \tmp
+ li \shft, SN1_NASID_SHFT
+ beqz \tmp, 1f
+ li \shft, SN0_NASID_SHFT
+1:
+ .set pop
+ .endm
+
/*
* inputs are the text nasid in t1, data nasid in t2.
*/
@@ -33,9 +48,10 @@
*/
dli t0, 0xffffffffc0000000
dmtc0 t0, CP0_ENTRYHI
+ GET_NASID_SHFT t3, t0
li t0, 0x1c000 # Offset of text into node memory
- dsll t1, NASID_SHFT # Shift text nasid into place
- dsll t2, NASID_SHFT # Same for data nasid
+ dsllv t1, t1, t3 # Shift text nasid into place
+ dsllv t2, t2, t3 # Same for data nasid
or t1, t1, t0 # Physical load address of kernel text
or t2, t2, t0 # Physical load address of kernel data
dsrl t1, 12 # 4K pfn
@@ -78,9 +94,10 @@
.macro smp_slave_setup
#ifdef CONFIG_MAPPED_KERNEL
GET_NASID_ASM t1, t0
+ GET_NASID_SHFT t3, t0
dli t0, KLDIR_OFFSET + (KLI_KERN_VARS * KLDIR_ENT_SIZE) + \
KLDIR_OFF_POINTER + CAC_BASE
- dsll t1, NASID_SHFT
+ dsllv t1, t1, t3
or t0, t0, t1
ld t0, 0(t0) # t0 points to kern_vars struct
lh t1, KV_RO_NASID_OFFSET(t0)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 22/53] MIPS: SGI-IP27: load the kernel at node offset 0x40000
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (20 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 21/53] MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 23/53] MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW Imre Kaloz
` (31 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
A Bedrock machine's PROM owns node memory below offset 0x40000. Link
both the mapped and the unmapped kernel there instead of at 0x1c000.
On an Origin 200/2000 the kernel moves up by 144KB, and the 144KB
below it stays reserved and unused.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/mach-ip27/kernel-entry-init.h | 2 +-
arch/mips/include/asm/sn/mapped_kernel.h | 6 +++---
arch/mips/sgi-ip27/Platform | 11 ++++++-----
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
index 1bedd3c3bc58..958f4b390df8 100644
--- a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
@@ -49,7 +49,7 @@
dli t0, 0xffffffffc0000000
dmtc0 t0, CP0_ENTRYHI
GET_NASID_SHFT t3, t0
- li t0, 0x1c000 # Offset of text into node memory
+ li t0, 0x40000 # Offset of text into node memory
dsllv t1, t1, t3 # Shift text nasid into place
dsllv t2, t2, t3 # Same for data nasid
or t1, t1, t0 # Physical load address of kernel text
diff --git a/arch/mips/include/asm/sn/mapped_kernel.h b/arch/mips/include/asm/sn/mapped_kernel.h
index 3f1049807018..28227718e391 100644
--- a/arch/mips/include/asm/sn/mapped_kernel.h
+++ b/arch/mips/include/asm/sn/mapped_kernel.h
@@ -10,7 +10,7 @@
/*
* Note on how mapped kernels work: the text and data section is
- * compiled at cksseg segment (LOADADDR = 0xc001c000), and the
+ * compiled at cksseg segment (LOADADDR = 0xc0040000), and the
* init/setup/data section gets a 16M virtual address bump in the
* ld.script file (so that tlblo0 and tlblo1 maps the sections).
* The vmlinux.64 section addresses are put in the xkseg range
@@ -18,8 +18,8 @@
* on IRIX to see where the sections go. The Origin loader loads
* the two sections contiguously in physical memory. The loader
* sets the entry point into kernel_entry using a xkphys address,
- * but instead of using 0xa800000001160000, it uses the address
- * 0xa800000000160000, which is where it physically loaded that
+ * but instead of using 0xa800000001184000, it uses the address
+ * 0xa800000000184000, which is where it physically loaded that
* code. So no jumps can be done before we have switched to using
* cksseg addresses.
*/
diff --git a/arch/mips/sgi-ip27/Platform b/arch/mips/sgi-ip27/Platform
index e734ee6abd44..b312bf69bce1 100644
--- a/arch/mips/sgi-ip27/Platform
+++ b/arch/mips/sgi-ip27/Platform
@@ -1,16 +1,17 @@
#
# SGI-IP27 (Origin200/2000)
#
-# Set the load address to >= 0xc000000000300000 if you want to leave space for
-# symmon, 0xc00000000001c000 for production kernels. Note that the value must
-# be 16kb aligned or the handling of the current variable will break.
+# A Bedrock machine's PROM owns node memory below offset 0x40000, so that is
+# where the kernel loads. Set the load address to >= 0xc000000000300000
+# instead if you want to leave space for symmon. Note that the value must be
+# 16kb aligned or the handling of the current variable will break.
#
cflags-$(CONFIG_SGI_IP27) += -I$(srctree)/arch/mips/include/asm/mach-ip27
ifdef CONFIG_MAPPED_KERNEL
-load-$(CONFIG_SGI_IP27) += 0xc00000004001c000
+load-$(CONFIG_SGI_IP27) += 0xc000000040040000
OBJCOPYFLAGS := --change-addresses=0x3fffffff80000000
dataoffset-$(CONFIG_SGI_IP27) += 0x01000000
else
-load-$(CONFIG_SGI_IP27) += 0xa80000000001c000
+load-$(CONFIG_SGI_IP27) += 0xa800000000040000
OBJCOPYFLAGS := --change-addresses=0x57ffffff80000000
endif
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 23/53] MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (21 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 22/53] MIPS: SGI-IP27: load the kernel at node offset 0x40000 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 24/53] MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3 Imre Kaloz
` (30 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
An IP35-family chassis records its crossbar board as KLTYPE_PBRICK_XBOW
rather than the KLTYPE_MIDPLANE8 an Origin 200/2000 midplane carries, so
xbow_probe() found no board record and never set up the crossbar. Fall
back to the brick type when the midplane lookup misses, and warn when
neither matches.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-xtalk.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index 10834880c261..baa937b474a8 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -159,8 +159,19 @@ static int xbow_probe(nasid_t nasid)
* need to probe xbow
*/
brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_MIDPLANE8);
- if (!brd)
- return -ENODEV;
+ if (!brd) {
+ /*
+ * An IP35-family chassis records its crossbar as
+ * KLTYPE_PBRICK_XBOW instead.
+ */
+ brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLTYPE_PBRICK_XBOW);
+ if (!brd) {
+ pr_warn("xtalk:n%d no midplane/xbow board found\n",
+ nasid);
+ return -ENODEV;
+ }
+ }
xbow_p = (klxbow_t *)find_component(brd, NULL, KLSTRUCT_XBOW);
if (!xbow_p)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 24/53] MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (22 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 23/53] MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 25/53] net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit addresses Imre Kaloz
` (29 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
CONFIG_SGI_MFD_IOC3 was unset, leaving the MFD core out of the built
kernel and the existing CONFIG_SGI_IOC3_ETH=y line unsatisfiable, since
that driver depends on it. Enable it with the 8250 and PS/2 cell
drivers, and enable tg3, which drives the Tezro's Gigabit Ethernet.
On IP27 the IOC3 sits behind a Bridge whose probe defers until its
1-wire PROM is readable, so also enable the SGI 1-wire master and the
DS250x slave.
Fixes: 0ce5ebd24d25 ("mfd: ioc3: Add driver for SGI IOC3 chip")
Fixes: 5dc76a96e95a ("MIPS: PCI: use information from 1-wire PROM for IOC3 detection")
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/configs/ip27_defconfig | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index ac885ff5b712..4c8abe4611b8 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -140,6 +140,7 @@ CONFIG_ATL2=m
CONFIG_ATL1E=m
CONFIG_ATL1C=m
CONFIG_B44=m
+CONFIG_TIGON3=y
CONFIG_BNX2X=m
CONFIG_ENIC=m
CONFIG_BE2NET=m
@@ -219,6 +220,7 @@ CONFIG_RTL8180=m
CONFIG_WL1251=m
CONFIG_WL12XX=m
# CONFIG_INPUT is not set
+CONFIG_SERIO_SGI_IOC3=m
CONFIG_SERIO_LIBPS2=m
CONFIG_SERIO_RAW=m
CONFIG_SERIO_ALTERA_PS2=m
@@ -229,6 +231,7 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_IOC3=y
CONFIG_HW_RANDOM_TIMERIOMEM=m
CONFIG_I2C_CHARDEV=m
CONFIG_I2C_ALI1535=m
@@ -250,6 +253,9 @@ CONFIG_I2C_PCA_PLATFORM=m
CONFIG_I2C_SIMTEC=m
CONFIG_I2C_TAOS_EVM=m
CONFIG_I2C_STUB=m
+CONFIG_W1=y
+CONFIG_W1_MASTER_SGI=y
+CONFIG_W1_SLAVE_DS250X=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
# CONFIG_VGA_ARB is not set
@@ -260,6 +266,7 @@ CONFIG_LEDS_TRIGGER_TIMER=m
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
CONFIG_LEDS_TRIGGER_BACKLIGHT=m
CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
+CONFIG_SGI_MFD_IOC3=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_M48T35=y
CONFIG_UIO=y
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 25/53] net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit addresses
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (23 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 24/53] MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3 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
` (28 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev
A Bridge 64-bit direct-mapped DMA address carries the target widget and
the DMA attributes. An address in a bridge's 2GB 32-bit direct window
carries neither, so attributes OR-ed onto one form a 64-bit address
aimed at widget 0, and every receive raises a PCI error.
Apply the attributes, and the RX barrier's upper address bits, only to
64-bit addresses, so the IOC3 can use the 32-bit window behind an XBridge
or PIC.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/net/ethernet/sgi/ioc3-eth.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c
index 009f37105eaf..f86dd192a3d3 100644
--- a/drivers/net/ethernet/sgi/ioc3-eth.c
+++ b/drivers/net/ethernet/sgi/ioc3-eth.c
@@ -152,19 +152,38 @@ static inline int ioc3_alloc_skb(struct ioc3_private *ip, struct sk_buff **skb,
}
#ifdef CONFIG_PCI_XTALK_BRIDGE
+/* A Bridge 64-bit direct-mapped DMA address carries the target widget in
+ * bits 63:60 and the DMA attributes below it. An address in the 32-bit
+ * direct window, which the IOC3 uses behind an XBridge or PIC, carries
+ * neither, so attributes OR-ed onto one form a 64-bit address aimed at
+ * widget 0.
+ */
static inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr)
{
+ if (!(addr & PCI64_ATTR_TARG_MASK))
+ return addr;
+
return (addr & ~PCI64_ATTR_BAR) | attr;
}
-#define ERBAR_VAL (ERBAR_BARRIER_BIT << ERBAR_RXBARR_SHIFT)
+/* The RX barrier's upper address bits follow the same form. */
+static inline u32 ioc3_erbar(dma_addr_t addr)
+{
+ if (!(addr & PCI64_ATTR_TARG_MASK))
+ return 0;
+
+ return ERBAR_BARRIER_BIT << ERBAR_RXBARR_SHIFT;
+}
#else
static inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr)
{
return addr;
}
-#define ERBAR_VAL 0
+static inline u32 ioc3_erbar(dma_addr_t addr)
+{
+ return 0;
+}
#endif
static int ioc3eth_nvmem_match(struct device *dev, const void *data)
@@ -727,7 +746,7 @@ static void ioc3_init(struct net_device *dev)
readl(®s->emcr);
/* Misc registers */
- writel(ERBAR_VAL, ®s->erbar);
+ writel(ioc3_erbar(ip->rxr_dma), ®s->erbar);
readl(®s->etcdc); /* Clear on read */
writel(15, ®s->ercsr); /* RX low watermark */
writel(0, ®s->ertr); /* Interrupt immediately */
--
2.47.3
^ permalink raw reply [flat|nested] 60+ 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
` (24 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 25/53] net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit addresses Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 27/53] MIPS: SGI-IP27: recognize the PXBow crossbar Imre Kaloz
` (27 subsequent siblings)
53 siblings, 0 replies; 60+ 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 [flat|nested] 60+ messages in thread
* [PATCH v2 27/53] MIPS: SGI-IP27: recognize the PXBow crossbar
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (25 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:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 28/53] MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only registers Imre Kaloz
` (26 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
A Tezro's crossbar is a PXBow, widget part number 0xd100. Without
recognizing it, xtalk_probe_node() logs the widget as unknown and never
calls xbow_probe(), leaving every IO widget behind it unenumerated.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/xtalk/xwidget.h | 7 +++++++
arch/mips/sgi-ip27/ip27-xtalk.c | 2 ++
2 files changed, 9 insertions(+)
diff --git a/arch/mips/include/asm/xtalk/xwidget.h b/arch/mips/include/asm/xtalk/xwidget.h
index 978877e8a090..6a98e87b4711 100644
--- a/arch/mips/include/asm/xtalk/xwidget.h
+++ b/arch/mips/include/asm/xtalk/xwidget.h
@@ -109,6 +109,7 @@
#define WIDGET_BDRCK_PART_NUM 0xc110
#define WIDGET_TPU_PART_NUM 0xc202
#define WIDGET_XXBOW_PART_NUM 0xd000
+#define WIDGET_PXBOW_PART_NUM 0xd100
#define WIDGET_XBRDG_PART_NUM 0xd002
#define WIDGET_NULL_PART_NUM -1
@@ -183,6 +184,12 @@ static const struct widget_ident widget_idents[] __initconst __maybe_unused = {
"xxbow",
{NULL, "1.0", "2.0", NULL},
},
+ {
+ WIDGET_XXBOW_MFGR_NUM,
+ WIDGET_PXBOW_PART_NUM,
+ "pxbow",
+ {NULL, "1.0", NULL},
+ },
{
WIDGET_XBRDG_MFGR_NUM,
WIDGET_XBRDG_PART_NUM,
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index bd2ac24452f5..105977316391 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -21,6 +21,7 @@
#define XBOW_WIDGET_PART_NUM 0x0
#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbow in Xbridge */
+#define PXBOW_WIDGET_PART_NUM 0xd100 /* PXbow */
#define BASE_XBOW_PORT 8 /* Lowest external port */
enum xtalk_bridge_type {
@@ -241,6 +242,7 @@ static void xtalk_probe_node(nasid_t nasid)
break;
case XBOW_WIDGET_PART_NUM:
case XXBOW_WIDGET_PART_NUM:
+ case PXBOW_WIDGET_PART_NUM:
pr_info("xtalk:n%d/0 xbow widget\n", nasid);
xbow_probe(nasid);
break;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 28/53] MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only registers
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (26 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 27/53] MIPS: SGI-IP27: recognize the PXBow crossbar Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 29/53] MIPS: PCI: xtalk-bridge: match PIC's register-level differences Imre Kaloz
` (25 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
PIC replicates the Bridge/XBridge register layout, twice in one widget
window for its two PCI buses, and does not accept a 32-bit write to half
of a 64-bit register. Flag it, widen the register accessors to the
double word, and spawn a controller per bus.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/pci/bridge.h | 70 +++++++++++++++++++++++++++---
arch/mips/sgi-ip27/ip27-xtalk.c | 30 ++++++++++---
2 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/arch/mips/include/asm/pci/bridge.h b/arch/mips/include/asm/pci/bridge.h
index 647ea5c9e34b..cbf36bcbe0b7 100644
--- a/arch/mips/include/asm/pci/bridge.h
+++ b/arch/mips/include/asm/pci/bridge.h
@@ -12,6 +12,8 @@
#ifndef _ASM_PCI_BRIDGE_H
#define _ASM_PCI_BRIDGE_H
+#include <linux/build_bug.h>
+#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/xtalk/xwidget.h> /* generic widget header */
@@ -39,6 +41,9 @@
#define BRIDGE_SSRAM_64K 0x00010000 /* 64kB */
#define BRIDGE_SSRAM_0K 0x00000000 /* 0kB */
+/* PIC replicates its register block twice within one widget's 16MB window. */
+#define PIC_BUS1_OFFSET 0x800000
+
/* ========================================================================
* Bridge address map
*/
@@ -264,6 +269,15 @@ struct bridge_regs {
} b_external_flash; /* 0xC00000 */
};
+/*
+ * Pin b_device[]/b_rrb_map[]'s low-half placement: bridge_readl() and
+ * bridge_writel() rely on it.
+ */
+static_assert(offsetof(struct bridge_regs, b_device[0].reg) == 0x204);
+static_assert(offsetof(struct bridge_regs, b_device[1].reg) == 0x20c);
+static_assert(offsetof(struct bridge_regs, b_rrb_map[0].reg) == 0x284);
+static_assert(offsetof(struct bridge_regs, b_rrb_map[1].reg) == 0x28c);
+
/*
* Field formats for Error Command Word and Auxiliary Error Command Word
* of bridge.
@@ -394,6 +408,7 @@ struct bridge_err_cmdword {
/* Widget part number of bridge */
#define BRIDGE_WIDGET_PART_NUM 0xc002
#define XBRIDGE_WIDGET_PART_NUM 0xd002
+#define PIC_WIDGET_PART_NUM 0xd102
/* Manufacturer of bridge */
#define BRIDGE_WIDGET_MFGR_NUM 0x036
@@ -822,11 +837,54 @@ struct bridge_controller {
#define BRIDGE_CONTROLLER(bus) \
((struct bridge_controller *)((bus)->sysdata))
-#define bridge_read(bc, reg) __raw_readl(&bc->base->reg)
-#define bridge_write(bc, reg, val) __raw_writel(val, &bc->base->reg)
-#define bridge_set(bc, reg, val) \
- __raw_writel(__raw_readl(&bc->base->reg) | (val), &bc->base->reg)
-#define bridge_clr(bc, reg, val) \
- __raw_writel(__raw_readl(&bc->base->reg) & ~(val), &bc->base->reg)
+/*
+ * PIC does not accept a 32-bit write to half of a 64-bit register, so
+ * widen each access to the 8-byte-aligned double word and extract or
+ * insert the 32-bit value at its byte position. The pointer is
+ * volatile-qualified because widget_cfg_t is, and b_widget sits in
+ * every bridge_regs.
+ */
+static inline u32 bridge_readl(struct bridge_controller *bc, volatile u32 *reg)
+{
+ void *dw;
+ unsigned int shift;
+
+ if (likely(!bc->is_pic))
+ return __raw_readl(reg);
+
+ dw = (void *)((unsigned long)reg & ~7UL);
+ shift = ((unsigned long)reg & 4) ? 0 : 32;
+
+ return __raw_readq(dw) >> shift;
+}
+
+static inline void bridge_writel(struct bridge_controller *bc,
+ volatile u32 *reg, u32 val)
+{
+ void *dw;
+ unsigned int shift;
+ u64 tmp;
+
+ if (likely(!bc->is_pic)) {
+ __raw_writel(val, reg);
+ return;
+ }
+
+ dw = (void *)((unsigned long)reg & ~7UL);
+ shift = ((unsigned long)reg & 4) ? 0 : 32;
+
+ tmp = __raw_readq(dw);
+ tmp = (tmp & ~(0xffffffffULL << shift)) | ((u64)val << shift);
+ __raw_writeq(tmp, dw);
+}
+
+#define bridge_read(bc, reg) bridge_readl(bc, &bc->base->reg)
+#define bridge_write(bc, reg, val) bridge_writel(bc, &bc->base->reg, val)
+#define bridge_set(bc, reg, val) \
+ bridge_writel(bc, &bc->base->reg, \
+ bridge_readl(bc, &bc->base->reg) | (val))
+#define bridge_clr(bc, reg, val) \
+ bridge_writel(bc, &bc->base->reg, \
+ bridge_readl(bc, &bc->base->reg) & ~(val))
#endif /* _ASM_PCI_BRIDGE_H */
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index 105977316391..73b2eef96bdb 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -27,6 +27,8 @@
enum xtalk_bridge_type {
XTALK_BRIDGE,
XTALK_XBRIDGE,
+ XTALK_PIC_BUS0,
+ XTALK_PIC_BUS1,
};
static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
@@ -88,21 +90,32 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
}
- bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget);
+ bd->bridge_addr = RAW_NODE_SWIN_BASE(nasid, widget) +
+ (type == XTALK_PIC_BUS1 ? PIC_BUS1_OFFSET : 0);
bd->intr_addr = BIT_ULL(47) + 0x01800000 + PI_INT_PEND_MOD;
bd->nasid = nasid;
bd->masterwid = masterwid;
bd->is_xbridge = type == XTALK_XBRIDGE;
+ bd->is_pic = type == XTALK_PIC_BUS0 || type == XTALK_PIC_BUS1;
+ /* Split a PIC's window at bus 1 or its resource request fails. */
bd->mem.name = "Bridge PCI MEM";
- bd->mem.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
- bd->mem.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
+ bd->mem.start = offset + (widget << SWIN_SIZE_BITS) +
+ (type == XTALK_PIC_BUS1 ? PIC_BUS1_OFFSET : 0) +
+ BRIDGE_DEVIO0;
+ bd->mem.end = offset + (widget << SWIN_SIZE_BITS) +
+ (type == XTALK_PIC_BUS0 ? PIC_BUS1_OFFSET :
+ SWIN_SIZE) - 1;
bd->mem.flags = IORESOURCE_MEM;
bd->mem_offset = offset;
bd->io.name = "Bridge PCI IO";
- bd->io.start = offset + (widget << SWIN_SIZE_BITS) + BRIDGE_DEVIO0;
- bd->io.end = offset + (widget << SWIN_SIZE_BITS) + SWIN_SIZE - 1;
+ bd->io.start = offset + (widget << SWIN_SIZE_BITS) +
+ (type == XTALK_PIC_BUS1 ? PIC_BUS1_OFFSET : 0) +
+ BRIDGE_DEVIO0;
+ bd->io.end = offset + (widget << SWIN_SIZE_BITS) +
+ (type == XTALK_PIC_BUS0 ? PIC_BUS1_OFFSET :
+ SWIN_SIZE) - 1;
bd->io.flags = IORESOURCE_IO;
bd->io_offset = offset;
@@ -149,6 +162,13 @@ static int probe_one_port(nasid_t nasid, int widget, int masterwid)
case XBRIDGE_WIDGET_PART_NUM:
bridge_platform_create(nasid, widget, masterwid, XTALK_XBRIDGE);
break;
+ case PIC_WIDGET_PART_NUM:
+ /* PIC always has both buses in silicon: probe each. */
+ bridge_platform_create(nasid, widget, masterwid,
+ XTALK_PIC_BUS0);
+ bridge_platform_create(nasid, widget, masterwid,
+ XTALK_PIC_BUS1);
+ break;
default:
pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
nasid, widget, partnum);
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 29/53] MIPS: PCI: xtalk-bridge: match PIC's register-level differences
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (27 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 28/53] MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only registers Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base Imre Kaloz
` (24 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
PIC's config space puts the ASIC at device 0 and the slots at 1-4, its
write-request RAM needs clearing, and b_int_addr[] takes a full XIO
address. Handle each, and clear PIC's NO_SNOOP and RELAX_ORDER bits.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/pci/bridge.h | 11 ++++++
arch/mips/pci/pci-xtalk-bridge.c | 60 +++++++++++++++++++++++-------
2 files changed, 58 insertions(+), 13 deletions(-)
diff --git a/arch/mips/include/asm/pci/bridge.h b/arch/mips/include/asm/pci/bridge.h
index cbf36bcbe0b7..ec17f4bcc6e8 100644
--- a/arch/mips/include/asm/pci/bridge.h
+++ b/arch/mips/include/asm/pci/bridge.h
@@ -372,6 +372,12 @@ struct bridge_err_cmdword {
#define BRIDGE_ATE_RAM 0x00010000 /* Internal Addr Xlat Ram */
+/* PIC Write Request RAM: three 256-entry, 64-bit arrays. */
+#define PIC_WR_REQ_BUFSIZE 256
+#define PIC_WR_REQ_LOWER 0x00018000
+#define PIC_WR_REQ_UPPER 0x00018800
+#define PIC_WR_REQ_PARITY 0x00019000
+
#define BRIDGE_TYPE0_CFG_DEV0 0x00020000 /* Type 0 Cfg, Device 0 */
#define BRIDGE_TYPE0_CFG_SLOT_OFF 0x00001000 /* Type 0 Cfg Slot Offset (1..7) */
#define BRIDGE_TYPE0_CFG_FUNC_OFF 0x00000100 /* Type 0 Cfg Func Offset (1..7) */
@@ -389,6 +395,7 @@ struct bridge_err_cmdword {
/* Byte offset macros for Bridge device IO spaces */
#define BRIDGE_DEV_CNT 8 /* Up to 8 devices per bridge */
+#define PIC_NSLOTS 4 /* PIC: 4 devices per bus */
#define BRIDGE_DEVIO0 0x00200000 /* Device IO 0 Addr */
#define BRIDGE_DEVIO1 0x00400000 /* Device IO 1 Addr */
#define BRIDGE_DEVIO2 0x00600000 /* Device IO 2 Addr */
@@ -461,6 +468,10 @@ struct bridge_err_cmdword {
#define BRIDGE_CTRL_WIDGET_ID(n) ((n) << 0)
#define BRIDGE_CTRL_WIDGET_ID_MASK (BRIDGE_CTRL_WIDGET_ID(0xf))
+/* PIC-only widget control bits, upper 32 bits of the register. */
+#define PIC_CTRL_NO_SNOOP (0x1ull << 62)
+#define PIC_CTRL_RELAX_ORDER (0x1ull << 61)
+
/* Bridge Response buffer Error Upper Register bit fields definition */
#define BRIDGE_RESP_ERRUPPR_DEVNUM_SHFT (20)
#define BRIDGE_RESP_ERRUPPR_DEVNUM_MASK (0x7 << BRIDGE_RESP_ERRUPPR_DEVNUM_SHFT)
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index 03ccb82a6189..b2c3a8b207da 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -178,7 +178,7 @@ static int ioc3_cfg_wr(void *addr, int where, int size, u32 value)
static int bridge_nslots(struct bridge_controller *bc)
{
- return BRIDGE_DEV_CNT;
+ return bc->is_pic ? PIC_NSLOTS : BRIDGE_DEV_CNT;
}
static void bridge_disable_swapping(struct pci_dev *dev)
@@ -213,6 +213,8 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn,
struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
struct bridge_regs *bridge = bc->base;
int slot = PCI_SLOT(devfn);
+ /* PIC config-space device 0 is the PIC ASIC; devices start at 1. */
+ int cfgslot = bc->is_pic ? slot + 1 : slot;
int fn = PCI_FUNC(devfn);
void *addr;
u32 cf;
@@ -225,7 +227,7 @@ static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn,
if (slot >= bridge_nslots(bc))
return PCIBIOS_DEVICE_NOT_FOUND;
- addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID];
+ addr = &bridge->b_type0_cfg_dev[cfgslot].f[fn].c[PCI_VENDOR_ID];
if (get_dbe(cf, (u32 *)addr))
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -234,12 +236,12 @@ static int pci_conf0_read_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_type0_cfg_dev[cfgslot].f[fn].l[where >> 2];
return ioc3_cfg_rd(addr, where, size, value,
bc->ioc3_sid[slot]);
}
- addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)];
+ addr = &bridge->b_type0_cfg_dev[cfgslot].f[fn].c[where ^ (4 - size)];
if (size == 1)
res = get_dbe(*value, (u8 *)addr);
@@ -305,6 +307,8 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn,
struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
struct bridge_regs *bridge = bc->base;
int slot = PCI_SLOT(devfn);
+ /* See the matching comment in pci_conf0_read_config(). */
+ int cfgslot = bc->is_pic ? slot + 1 : slot;
int fn = PCI_FUNC(devfn);
void *addr;
u32 cf;
@@ -313,7 +317,7 @@ static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn,
if (slot >= bridge_nslots(bc))
return PCIBIOS_DEVICE_NOT_FOUND;
- addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID];
+ addr = &bridge->b_type0_cfg_dev[cfgslot].f[fn].c[PCI_VENDOR_ID];
if (get_dbe(cf, (u32 *)addr))
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -322,11 +326,11 @@ static int pci_conf0_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_type0_cfg_dev[cfgslot].f[fn].l[where >> 2];
return ioc3_cfg_wr(addr, where, size, value);
}
- addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)];
+ addr = &bridge->b_type0_cfg_dev[cfgslot].f[fn].c[where ^ (4 - size)];
if (size == 1)
res = put_dbe(value, (u8 *)addr);
@@ -406,6 +410,22 @@ struct bridge_irq_chip_data {
nasid_t nasid;
};
+/* PIC takes the full 48-bit XIO address here, source in bits 55:48. */
+static void bridge_set_int_addr(struct bridge_controller *bc, int pin,
+ int bit, nasid_t nasid)
+{
+ if (bc->is_pic) {
+ void *addr = &bc->base->b_int_addr[pin];
+ u64 xio = bc->intr_addr & ((1ULL << 48) - 1);
+
+ __raw_writeq(((u64)bit << 48) | xio, addr);
+ } else {
+ bridge_write(bc, b_int_addr[pin].addr,
+ (((bc->intr_addr >> 30) & 0x30000) |
+ bit | (nasid << 8)));
+ }
+}
+
static int bridge_set_affinity(struct irq_data *d, const struct cpumask *mask,
bool force)
{
@@ -419,9 +439,7 @@ static int bridge_set_affinity(struct irq_data *d, const struct cpumask *mask,
if (ret >= 0) {
cpu = cpumask_first_and(mask, cpu_online_mask);
data->nasid = cpu_to_node(cpu);
- bridge_write(data->bc, b_int_addr[pin].addr,
- (((data->bc->intr_addr >> 30) & 0x30000) |
- bit | (data->nasid << 8)));
+ bridge_set_int_addr(data->bc, pin, bit, data->nasid);
bridge_read(data->bc, b_wid_tflush);
}
return ret;
@@ -485,9 +503,7 @@ static int bridge_domain_activate(struct irq_domain *domain,
int pin = irqd->hwirq;
u32 device;
- bridge_write(bc, b_int_addr[pin].addr,
- (((bc->intr_addr >> 30) & 0x30000) |
- bit | (data->nasid << 8)));
+ bridge_set_int_addr(bc, pin, bit, data->nasid);
bridge_set(bc, b_int_enable, (1 << pin));
bridge_set(bc, b_int_enable, 0x7ffffe00); /* more stuff in int_enable */
@@ -1044,6 +1060,24 @@ static int bridge_probe(struct platform_device *pdev)
bridge_set(bc, b_wid_control, BRIDGE_CTRL_PAGE_SIZE);
#endif
+ if (bc->is_pic) {
+ unsigned long ctrl = (unsigned long)&bc->base->b_wid_control;
+ void *ctrl64 = (void *)(ctrl & ~7UL);
+ int i;
+
+ /* Keep DMA snooped and ordered. */
+ __raw_writeq(__raw_readq(ctrl64) &
+ ~(PIC_CTRL_NO_SNOOP | PIC_CTRL_RELAX_ORDER),
+ ctrl64);
+
+ /* Clear so a stale entry can't trip a parity error. */
+ for (i = 0; i < PIC_WR_REQ_BUFSIZE * 8; i += 8) {
+ __raw_writeq(0, (u8 *)bc->base + PIC_WR_REQ_LOWER + i);
+ __raw_writeq(0, (u8 *)bc->base + PIC_WR_REQ_UPPER + i);
+ __raw_writeq(0, (u8 *)bc->base + PIC_WR_REQ_PARITY + i);
+ }
+ }
+
/*
* Hmm... IRIX sets additional bits in the address which
* are documented as reserved in the bridge docs.
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (28 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 29/53] MIPS: PCI: xtalk-bridge: match PIC's register-level differences Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 14:28 ` Alexandre Belloni
2026-10-13 13:16 ` [PATCH v2 31/53] mfd: ioc3: Add support for IOC4 Imre Kaloz
` (23 subsequent siblings)
53 siblings, 1 reply; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Alexandre Belloni, linux-rtc
The M48T35 of SGI IP35-family machines counts its year from 1968. Take
the epoch from a platform id table, "rtc-m48t35" for IP27 and
"rtc-m48t35-ip35" for IP35, and bound the year to the part's century: on
IP27 a year past 2069 now fails to set.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/rtc/rtc-m48t35.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 92f19bf997b2..fa228a2bb527 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -49,6 +49,7 @@ struct m48t35_priv {
struct m48t35_rtc __iomem *reg;
size_t size;
unsigned long baseaddr;
+ unsigned int epoch;
spinlock_t lock;
};
@@ -86,10 +87,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time;
*/
- tm->tm_year += 70;
- if (tm->tm_year <= 69)
- tm->tm_year += 100;
-
+ tm->tm_year += priv->epoch - 1900;
tm->tm_mon--;
return 0;
}
@@ -108,18 +106,10 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
min = tm->tm_min;
sec = tm->tm_sec;
- if (yrs < 1970)
- return -EINVAL;
-
- yrs -= 1970;
- if (yrs > 255) /* They are unsigned */
- return -EINVAL;
-
- if (yrs > 169)
+ if (yrs < priv->epoch || yrs >= priv->epoch + 100)
return -EINVAL;
- if (yrs >= 100)
- yrs -= 100;
+ yrs -= priv->epoch;
sec = bin2bcd(sec);
min = bin2bcd(min);
@@ -149,9 +139,13 @@ static const struct rtc_class_ops m48t35_ops = {
static int m48t35_probe(struct platform_device *pdev)
{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
struct resource *res;
struct m48t35_priv *priv;
+ if (!id)
+ return -ENODEV;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
@@ -164,6 +158,7 @@ static int m48t35_probe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
+ priv->epoch = id->driver_data;
priv->baseaddr = res->start;
priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
if (!priv->reg)
@@ -178,11 +173,20 @@ static int m48t35_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(priv->rtc);
}
+/* The year register on SGI IP35-family machines counts from 1968. */
+static const struct platform_device_id m48t35_id_table[] = {
+ { "rtc-m48t35", 1970 },
+ { "rtc-m48t35-ip35", 1968 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, m48t35_id_table);
+
static struct platform_driver m48t35_platform_driver = {
.driver = {
.name = "rtc-m48t35",
},
.probe = m48t35_probe,
+ .id_table = m48t35_id_table,
};
module_platform_driver(m48t35_platform_driver);
@@ -191,3 +195,4 @@ MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
MODULE_DESCRIPTION("M48T35 RTC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rtc-m48t35");
+MODULE_ALIAS("platform:rtc-m48t35-ip35");
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 31/53] mfd: ioc3: Add support for IOC4
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (29 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 32/53] MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4 Imre Kaloz
` (22 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Greg Kroah-Hartman,
Jiri Slaby, mfd, linux-serial
IOC4 is IOC3's successor on IP35 Origin 350-class boards. Its serial and
PS/2 cores reuse IOC3's drivers and its interrupt status splits into two
banks. The serial ports need IOC4 firmware revision 62 or later and are
not registered on older firmware.
The serial, interrupt and GPIO register definitions in asm/sn/ioc4.h
follow include/linux/ioc4.h and drivers/tty/serial/ioc4_serial.c, removed
by commit f7bc6e42bf12 ("drivers: remove the SGI SN2 IOC4 base support")
and commit a017ef17cfd8 ("tty/serial: remove the ioc4_serial driver").
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
MAINTAINERS | 1 +
arch/mips/include/asm/sn/ioc4.h | 142 ++++++++
drivers/mfd/ioc3.c | 323 +++++++++++++++---
drivers/tty/serial/8250/8250_ioc3.c | 5 +-
include/linux/platform_data/sgi-ioc3-serial.h | 13 +
5 files changed, 438 insertions(+), 46 deletions(-)
create mode 100644 arch/mips/include/asm/sn/ioc4.h
create mode 100644 include/linux/platform_data/sgi-ioc3-serial.h
diff --git a/MAINTAINERS b/MAINTAINERS
index f53047aa8d3c..a30c09a55c72 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18219,6 +18219,7 @@ F: arch/mips/
F: drivers/platform/mips/
F: include/dt-bindings/mips/
F: include/linux/platform_data/pic32.h
+F: include/linux/platform_data/sgi-ioc3-serial.h
MIPS BOSTON DEVELOPMENT BOARD
M: Paul Burton <paulburton@kernel.org>
diff --git a/arch/mips/include/asm/sn/ioc4.h b/arch/mips/include/asm/sn/ioc4.h
new file mode 100644
index 000000000000..781fcfb81b5e
--- /dev/null
+++ b/arch/mips/include/asm/sn/ioc4.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * IOC4 -- SGI's I/O ASIC on Tezro/Origin 350-class boards.
+ *
+ * Copyright (C) 2003-2006 Silicon Graphics, Inc.
+ */
+#ifndef MIPS_SN_IOC4_H
+#define MIPS_SN_IOC4_H
+
+#include <linux/types.h>
+
+struct ioc4_serialregs {
+ u32 sscr;
+ u32 stpir;
+ u32 stcir;
+ u32 srpir;
+ u32 srcir;
+ u32 srtr;
+ u32 shadow;
+};
+
+struct ioc4_uartregs {
+ u8 i4u_lcr;
+ union {
+ u8 i4u_iir; /* read only */
+ u8 i4u_fcr; /* write only */
+ };
+ union {
+ u8 i4u_ier; /* DLAB == 0 */
+ u8 i4u_dlm; /* DLAB == 1 */
+ };
+ union {
+ u8 i4u_rbr; /* read only, DLAB == 0 */
+ u8 i4u_thr; /* write only, DLAB == 0 */
+ u8 i4u_dll; /* DLAB == 1 */
+ };
+ u8 i4u_scr;
+ u8 i4u_msr;
+ u8 i4u_lsr;
+ u8 i4u_mcr;
+};
+
+struct ioc4_serioregs {
+ u32 km_csr;
+ u32 k_rd;
+ u32 m_rd;
+ u32 k_wd;
+ u32 m_wd;
+};
+
+/* ATA/ATAPI block: one taskfile register per 32-bit word. */
+struct ioc4_ata_regs {
+ u32 cmd[8]; /* 0x000, data through status/command */
+ u32 ctrl; /* 0x020, altstatus/devctrl */
+ u32 pad[(0x040 - 0x024) / 4]; /* 0x024 */
+ u32 dma[9]; /* 0x040, DMA engine */
+};
+
+/* Register layout of IOC4 in PCI BAR0. */
+struct ioc4 {
+ u32 pci_err_addr_l; /* 0x00000 */
+ u32 pci_err_addr_h; /* 0x00004 */
+ u32 sio_ir; /* 0x00008 */
+ u32 other_ir; /* 0x0000c */
+ u32 sio_ies; /* 0x00010 */
+ u32 other_ies; /* 0x00014 */
+ u32 sio_iec; /* 0x00018 */
+ u32 other_iec; /* 0x0001c */
+ u32 sio_cr; /* 0x00020 */
+ u32 pad0; /* 0x00024 */
+ u32 int_out; /* 0x00028 */
+ u32 pad1; /* 0x0002c */
+ u32 gpcr_s; /* 0x00030 */
+ u32 gpcr_c; /* 0x00034 */
+ u32 gpdr; /* 0x00038 */
+ u32 pad2; /* 0x0003c */
+ u32 gppr[8]; /* 0x00040 */
+
+ u32 pad3[(0x100 - 0x60) / 4]; /* 0x00060 */
+
+ struct ioc4_ata_regs ata; /* 0x00100 */
+
+ u32 pad3b[(0x200 - 0x164) / 4]; /* 0x00164 */
+
+ struct ioc4_serioregs serio; /* 0x00200, IOC4_KBC_BASE */
+
+ u32 pad4[(0x300 - 0x214) / 4];
+
+ u32 sbbr01_l; /* 0x00300 */
+ u32 sbbr01_h; /* 0x00304 */
+ u32 sbbr23_l; /* 0x00308 */
+ u32 sbbr23_h; /* 0x0030c */
+
+ struct ioc4_serialregs port[4]; /* 0x00310 */
+ struct ioc4_uartregs uart[4]; /* 0x00380 */
+};
+
+/* Bytebus device 0, where the TOD chip sits. */
+#define IOC4_BYTEBUS_DEV0 0x80000L
+
+/* sio_ir carries serial events only, one 8-bit group per port. */
+#define IOC4_SIO_IR_TX_MT 0x01 /* TX ring buffer empty */
+#define IOC4_SIO_IR_RX_FULL 0x02 /* RX ring buffer full */
+#define IOC4_SIO_IR_RX_HIGH 0x04 /* RX high-water exceeded */
+#define IOC4_SIO_IR_RX_TIMER 0x08 /* RX timer has triggered */
+#define IOC4_SIO_IR_DELTA_DCD 0x10 /* DELTA_DCD seen */
+#define IOC4_SIO_IR_DELTA_CTS 0x20 /* DELTA_CTS seen */
+#define IOC4_SIO_IR_INT 0x40 /* UART pass-through interrupt */
+#define IOC4_SIO_IR_TX_EXPLICIT 0x80 /* TX, MCW, or delay complete */
+
+#define IOC4_SIO_IR_PORT_SHIFT(port) ((port) * 8)
+#define IOC4_SIO_IR_PORT_INT(port) \
+ (IOC4_SIO_IR_INT << IOC4_SIO_IR_PORT_SHIFT(port))
+
+/* Bit index of IOC4_SIO_IR_INT; DEFINE_RES_IRQ() needs a constant. */
+#define IOC4_SIO_IR_INT_BIT 6
+#define IOC4_SIO_IR_PORT_INT_BIT(port) \
+ (IOC4_SIO_IR_PORT_SHIFT(port) + IOC4_SIO_IR_INT_BIT)
+
+#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATA passthru */
+#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATA PCI error */
+#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* port 0 PCI error */
+#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* port 1 PCI error */
+#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* port 2 PCI error */
+#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* port 3 PCI error */
+/* The IX-brick wires this bit to the PS/2 ports. */
+#define IOC4_OTHER_IR_KBD_INT 0x00000040
+#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT latch */
+#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* generic pins */
+
+#define IOC4_OTHER_IR_KBD_INT_BIT 6 /* bit index */
+
+#define IOC4_SSCR_DMA_EN 0x10000000 /* enable ring buffer DMA */
+
+/* GPIO pins 4-7 select RS232 or RS422 mode for the four UARTs. */
+#define IOC4_GPCR_UART_MODESEL(port) (0x10 << (port))
+#define IOC4_GPPR_UART_MODESEL_PIN(port) (4 + (port))
+
+/* The serial core runs off the PCI bus clock, 66MHz in the IX-brick. */
+#define IOC4_UARTCLK 66666667
+
+#endif /* MIPS_SN_IOC4_H */
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
index 5f8ac364b610..57956c00263f 100644
--- a/drivers/mfd/ioc3.c
+++ b/drivers/mfd/ioc3.c
@@ -19,11 +19,13 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/platform_data/sgi-ioc3-serial.h>
#include <linux/platform_data/sgi-w1.h>
#include <linux/rtc/ds1685.h>
#include <asm/pci/bridge.h>
#include <asm/sn/ioc3.h>
+#include <asm/sn/ioc4.h>
#define IOC3_IRQ_SERIAL_A 6
#define IOC3_IRQ_SERIAL_B 15
@@ -37,9 +39,33 @@
/* 1.2 us latency timer (40 cycles at 33 MHz) */
#define IOC3_LATENCY 40
+struct ioc3_priv_data;
+
+/*
+ * One interrupt bank: an ir/ies/iec register triplet covering 32 hwirqs.
+ * IOC3 has a single bank. IOC4 keeps only the four serial ports in
+ * sio_ir and moves ATA, memory-error, keyboard, RT and generic-pin
+ * events into a second bank, other_ir.
+ */
+struct ioc3_irq_bank {
+ u16 ir, ies, iec; /* BAR0 offsets */
+ u32 lvl_mask; /* hwirqs needing level-triggered handling */
+};
+
+struct ioc3_variant {
+ const char *name;
+ struct irq_chip *chip;
+ const struct ioc3_irq_bank *banks;
+ unsigned int nr_banks;
+ unsigned int nr_irqs;
+ u16 pci_command; /* extra PCI_COMMAND bits to set at probe */
+ int (*setup)(struct ioc3_priv_data *ipd);
+};
+
struct ioc3_priv_data {
struct irq_domain *domain;
- struct ioc3 __iomem *regs;
+ void __iomem *base;
+ const struct ioc3_variant *var;
struct pci_dev *pdev;
int domain_irq;
};
@@ -48,24 +74,27 @@ static void ioc3_irq_ack(struct irq_data *d)
{
struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
unsigned int hwirq = irqd_to_hwirq(d);
+ const struct ioc3_irq_bank *bank = &ipd->var->banks[hwirq / 32];
- writel(BIT(hwirq), &ipd->regs->sio_ir);
+ writel(BIT(hwirq % 32), ipd->base + bank->ir);
}
static void ioc3_irq_mask(struct irq_data *d)
{
struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
unsigned int hwirq = irqd_to_hwirq(d);
+ const struct ioc3_irq_bank *bank = &ipd->var->banks[hwirq / 32];
- writel(BIT(hwirq), &ipd->regs->sio_iec);
+ writel(BIT(hwirq % 32), ipd->base + bank->iec);
}
static void ioc3_irq_unmask(struct irq_data *d)
{
struct ioc3_priv_data *ipd = irq_data_get_irq_chip_data(d);
unsigned int hwirq = irqd_to_hwirq(d);
+ const struct ioc3_irq_bank *bank = &ipd->var->banks[hwirq / 32];
- writel(BIT(hwirq), &ipd->regs->sio_ies);
+ writel(BIT(hwirq % 32), ipd->base + bank->ies);
}
static struct irq_chip ioc3_irq_chip = {
@@ -75,16 +104,25 @@ static struct irq_chip ioc3_irq_chip = {
.irq_unmask = ioc3_irq_unmask,
};
+static struct irq_chip ioc4_irq_chip = {
+ .name = "IOC4",
+ .irq_ack = ioc3_irq_ack,
+ .irq_mask = ioc3_irq_mask,
+ .irq_unmask = ioc3_irq_unmask,
+};
+
static int ioc3_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
- /* Set level IRQs for every interrupt contained in IOC3_LVL_MASK */
- if (BIT(hwirq) & IOC3_LVL_MASK)
- irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_level_irq);
+ struct ioc3_priv_data *ipd = d->host_data;
+ const struct ioc3_irq_bank *bank = &ipd->var->banks[hwirq / 32];
+
+ if (BIT(hwirq % 32) & bank->lvl_mask)
+ irq_set_chip_and_handler(irq, ipd->var->chip, handle_level_irq);
else
- irq_set_chip_and_handler(irq, &ioc3_irq_chip, handle_edge_irq);
+ irq_set_chip_and_handler(irq, ipd->var->chip, handle_edge_irq);
- irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_data(irq, ipd);
return 0;
}
@@ -103,17 +141,21 @@ static void ioc3_irq_handler(struct irq_desc *desc)
{
struct irq_domain *domain = irq_desc_get_handler_data(desc);
struct ioc3_priv_data *ipd = domain->host_data;
- struct ioc3 __iomem *regs = ipd->regs;
- u32 pending, mask;
+ unsigned int i;
- pending = readl(®s->sio_ir);
- mask = readl(®s->sio_ies);
- pending &= mask; /* Mask off not enabled interrupts */
+ for (i = 0; i < ipd->var->nr_banks; i++) {
+ const struct ioc3_irq_bank *bank = &ipd->var->banks[i];
+ u32 pending = readl(ipd->base + bank->ir) &
+ readl(ipd->base + bank->ies);
- if (pending)
- generic_handle_domain_irq(domain, __ffs(pending));
- else
- spurious_interrupt();
+ if (pending) {
+ generic_handle_domain_irq(domain,
+ i * 32 + __ffs(pending));
+ return;
+ }
+ }
+
+ spurious_interrupt();
}
/*
@@ -134,11 +176,12 @@ static int ioc3_irq_domain_setup(struct ioc3_priv_data *ipd, int irq)
struct irq_domain *domain;
struct fwnode_handle *fn;
- fn = irq_domain_alloc_named_fwnode("IOC3");
+ fn = irq_domain_alloc_named_fwnode(ipd->var->name);
if (!fn)
goto err;
- domain = irq_domain_create_linear(fn, 24, &ioc3_irq_domain_ops, ipd);
+ domain = irq_domain_create_linear(fn, ipd->var->nr_irqs,
+ &ioc3_irq_domain_ops, ipd);
if (!domain) {
irq_domain_free_fwnode(fn);
goto err;
@@ -182,21 +225,19 @@ static struct mfd_cell ioc3_serial_cells[] = {
static int ioc3_serial_setup(struct ioc3_priv_data *ipd)
{
+ struct ioc3 __iomem *regs = ipd->base;
int ret;
/* Set gpio pins for RS232/RS422 mode selection */
- writel(GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL,
- &ipd->regs->gpcr_s);
+ writel(GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL, ®s->gpcr_s);
/* Select RS232 mode for uart a */
- writel(0, &ipd->regs->gppr[6]);
+ writel(0, ®s->gppr[6]);
/* Select RS232 mode for uart b */
- writel(0, &ipd->regs->gppr[7]);
+ writel(0, ®s->gppr[7]);
/* Switch both ports to 16650 mode */
- writel(readl(&ipd->regs->port_a.sscr) & ~SSCR_DMA_EN,
- &ipd->regs->port_a.sscr);
- writel(readl(&ipd->regs->port_b.sscr) & ~SSCR_DMA_EN,
- &ipd->regs->port_b.sscr);
+ writel(readl(®s->port_a.sscr) & ~SSCR_DMA_EN, ®s->port_a.sscr);
+ writel(readl(®s->port_b.sscr) & ~SSCR_DMA_EN, ®s->port_b.sscr);
udelay(1000); /* Wait until mode switch is done */
ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO,
@@ -270,10 +311,11 @@ static struct mfd_cell ioc3_eth_cells[] = {
static int ioc3_eth_setup(struct ioc3_priv_data *ipd)
{
+ struct ioc3 __iomem *regs = ipd->base;
int ret;
/* Enable One-Wire bus */
- writel(GPCR_MLAN_EN, &ipd->regs->gpcr_s);
+ writel(GPCR_MLAN_EN, ®s->gpcr_s);
/* Generate unique identifier */
snprintf(ioc3_w1_platform_data.dev_id,
@@ -539,14 +581,12 @@ static struct {
static int ioc3_setup(struct ioc3_priv_data *ipd)
{
+ struct ioc3 __iomem *regs = ipd->base;
u32 sid;
int i;
- /* Clear IRQs */
- writel(~0, &ipd->regs->sio_iec);
- writel(~0, &ipd->regs->sio_ir);
- writel(0, &ipd->regs->eth.eier);
- writel(~0, &ipd->regs->eth.eisr);
+ writel(0, ®s->eth.eier);
+ writel(~0, ®s->eth.eisr);
/* Read subsystem vendor id and subsystem id */
pci_read_config_dword(ipd->pdev, PCI_SUBSYSTEM_VENDOR_ID, &sid);
@@ -565,10 +605,16 @@ static int ioc3_setup(struct ioc3_priv_data *ipd)
static int ioc3_mfd_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id)
{
+ const struct ioc3_variant *var = (void *)pci_id->driver_data;
struct ioc3_priv_data *ipd;
- struct ioc3 __iomem *regs;
+ void __iomem *base;
+ unsigned int i;
+ u16 cmd;
int ret;
+ if (!var)
+ return -ENODEV;
+
ret = pci_enable_device(pdev);
if (ret)
return ret;
@@ -576,6 +622,12 @@ static int ioc3_mfd_probe(struct pci_dev *pdev,
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, IOC3_LATENCY);
pci_set_master(pdev);
+ if (var->pci_command) {
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ pci_write_config_word(pdev, PCI_COMMAND,
+ cmd | var->pci_command);
+ }
+
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
pr_err("%s: No usable DMA configuration, aborting.\n",
@@ -591,24 +643,31 @@ static int ioc3_mfd_probe(struct pci_dev *pdev,
goto out_disable_device;
}
ipd->pdev = pdev;
+ ipd->var = var;
/*
* Map all IOC3 registers. These are shared between subdevices
* so the main IOC3 module manages them.
*/
- regs = pci_ioremap_bar(pdev, 0);
- if (!regs) {
+ base = pci_ioremap_bar(pdev, 0);
+ if (!base) {
dev_warn(&pdev->dev, "ioc3: Unable to remap PCI BAR for %s.\n",
pci_name(pdev));
ret = -ENOMEM;
goto out_disable_device;
}
- ipd->regs = regs;
+ ipd->base = base;
/* Track PCI-device specific data */
pci_set_drvdata(pdev, ipd);
- ret = ioc3_setup(ipd);
+ /* Clear IRQs */
+ for (i = 0; i < var->nr_banks; i++) {
+ writel(~0, base + var->banks[i].iec);
+ writel(~0, base + var->banks[i].ir);
+ }
+
+ ret = var->setup(ipd);
if (ret) {
/* Remove all already added MFD devices */
mfd_remove_devices(&ipd->pdev->dev);
@@ -619,7 +678,7 @@ static int ioc3_mfd_probe(struct pci_dev *pdev,
irq_domain_free_fwnode(fn);
free_irq(ipd->domain_irq, (void *)ipd);
}
- pci_iounmap(pdev, regs);
+ pci_iounmap(pdev, base);
goto out_disable_device;
}
@@ -633,12 +692,15 @@ static int ioc3_mfd_probe(struct pci_dev *pdev,
static void ioc3_mfd_remove(struct pci_dev *pdev)
{
struct ioc3_priv_data *ipd;
+ unsigned int i;
ipd = pci_get_drvdata(pdev);
/* Clear and disable all IRQs */
- writel(~0, &ipd->regs->sio_iec);
- writel(~0, &ipd->regs->sio_ir);
+ for (i = 0; i < ipd->var->nr_banks; i++) {
+ writel(~0, ipd->base + ipd->var->banks[i].iec);
+ writel(~0, ipd->base + ipd->var->banks[i].ir);
+ }
/* Release resources */
mfd_remove_devices(&ipd->pdev->dev);
@@ -649,12 +711,183 @@ static void ioc3_mfd_remove(struct pci_dev *pdev)
irq_domain_free_fwnode(fn);
free_irq(ipd->domain_irq, (void *)ipd);
}
- pci_iounmap(pdev, ipd->regs);
+ pci_iounmap(pdev, ipd->base);
pci_disable_device(pdev);
}
+static const struct ioc3_irq_bank ioc3_irq_banks[] = {
+ {
+ .ir = offsetof(struct ioc3, sio_ir),
+ .ies = offsetof(struct ioc3, sio_ies),
+ .iec = offsetof(struct ioc3, sio_iec),
+ .lvl_mask = IOC3_LVL_MASK,
+ },
+};
+
+static const struct ioc3_variant ioc3_variant = {
+ .name = "IOC3",
+ .chip = &ioc3_irq_chip,
+ .banks = ioc3_irq_banks,
+ .nr_banks = ARRAY_SIZE(ioc3_irq_banks),
+ .nr_irqs = 24,
+ .setup = ioc3_setup,
+};
+
+#define IOC4_NUM_PORTS 4
+#define IOC4_SERIAL_MIN_REV 62
+
+/* hwirqs 0-31 are sio_ir's, 32-63 other_ir's. */
+#define IOC4_IRQ_OTHER_BASE 32
+#define IOC4_NUM_IRQS 64
+
+/* In sio_ir only the four UART pass-through bits are level triggered. */
+#define IOC4_LVL_MASK \
+ (IOC4_SIO_IR_PORT_INT(0) | IOC4_SIO_IR_PORT_INT(1) | \
+ IOC4_SIO_IR_PORT_INT(2) | IOC4_SIO_IR_PORT_INT(3))
+
+#define IOC4_UART_RES(port) { \
+ DEFINE_RES_MEM(offsetof(struct ioc4, uart[port]), \
+ sizeof_field(struct ioc4, uart[port])), \
+ DEFINE_RES_IRQ(IOC4_SIO_IR_PORT_INT_BIT(port)), \
+}
+
+static const struct resource ioc4_uart_resources[][2] = {
+ IOC4_UART_RES(0),
+ IOC4_UART_RES(1),
+ IOC4_UART_RES(2),
+ IOC4_UART_RES(3),
+};
+
+static const struct resource ioc4_kbd_resources[] = {
+ DEFINE_RES_MEM(offsetof(struct ioc4, serio),
+ sizeof_field(struct ioc4, serio)),
+ DEFINE_RES_IRQ(IOC4_IRQ_OTHER_BASE + IOC4_OTHER_IR_KBD_INT_BIT)
+};
+
+static const struct resource ioc4_rtc_resources[] = {
+ DEFINE_RES_MEM(IOC4_BYTEBUS_DEV0, M48T35_REG_SIZE)
+};
+
+static const struct ioc3_serial8250_platform_data ioc4_serial_pdata = {
+ .uartclk = IOC4_UARTCLK,
+};
+
+#define IOC4_SERIAL_CELL(port) { \
+ .name = "ioc3-serial8250", \
+ .resources = ioc4_uart_resources[port], \
+ .num_resources = ARRAY_SIZE(ioc4_uart_resources[port]), \
+ .platform_data = &ioc4_serial_pdata, \
+ .pdata_size = sizeof(ioc4_serial_pdata), \
+}
+
+static const struct mfd_cell ioc4_serial_cells[] = {
+ IOC4_SERIAL_CELL(0),
+ IOC4_SERIAL_CELL(1),
+ IOC4_SERIAL_CELL(2),
+ IOC4_SERIAL_CELL(3),
+};
+
+static const struct mfd_cell ioc4_cells[] = {
+ {
+ .name = "ioc3-kbd",
+ .resources = ioc4_kbd_resources,
+ .num_resources = ARRAY_SIZE(ioc4_kbd_resources),
+ },
+ {
+ .name = "rtc-m48t35-ip35",
+ .resources = ioc4_rtc_resources,
+ .num_resources = ARRAY_SIZE(ioc4_rtc_resources),
+ },
+};
+
+static int ioc4_serial_setup(struct ioc3_priv_data *ipd)
+{
+ struct ioc4 __iomem *regs = ipd->base;
+ int ret, i;
+
+ if (ipd->pdev->revision < IOC4_SERIAL_MIN_REV) {
+ dev_warn(&ipd->pdev->dev,
+ "serial ports need IOC4 firmware revision %u or later\n",
+ IOC4_SERIAL_MIN_REV);
+ return 0;
+ }
+
+ /* Set gpio pins for RS232/RS422 mode selection */
+ writel(IOC4_GPCR_UART_MODESEL(0) | IOC4_GPCR_UART_MODESEL(1) |
+ IOC4_GPCR_UART_MODESEL(2) | IOC4_GPCR_UART_MODESEL(3),
+ ®s->gpcr_s);
+
+ for (i = 0; i < IOC4_NUM_PORTS; i++) {
+ /* Select RS232 mode, and switch the port to 16650 mode */
+ writel(0, ®s->gppr[IOC4_GPPR_UART_MODESEL_PIN(i)]);
+ writel(readl(®s->port[i].sscr) & ~IOC4_SSCR_DMA_EN,
+ ®s->port[i].sscr);
+ }
+ udelay(1000); /* Wait until mode switch is done */
+
+ ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO,
+ ioc4_serial_cells, ARRAY_SIZE(ioc4_serial_cells),
+ &ipd->pdev->resource[0], 0, ipd->domain);
+ if (ret)
+ dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n");
+
+ return ret;
+}
+
+static int ioc4_setup(struct ioc3_priv_data *ipd)
+{
+ int ret;
+
+ dev_info(&ipd->pdev->dev, "IOC4 firmware revision %u\n",
+ ipd->pdev->revision);
+
+ ret = ioc3_irq_domain_setup(ipd, ipd->pdev->irq);
+ if (ret)
+ return ret;
+
+ ret = ioc4_serial_setup(ipd);
+ if (ret)
+ return ret;
+
+ ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO,
+ ioc4_cells, ARRAY_SIZE(ioc4_cells),
+ &ipd->pdev->resource[0], 0, ipd->domain);
+ if (ret)
+ dev_err(&ipd->pdev->dev, "Failed to add IOC4 subdevs\n");
+
+ return ret;
+}
+
+static const struct ioc3_irq_bank ioc4_irq_banks[] = {
+ {
+ .ir = offsetof(struct ioc4, sio_ir),
+ .ies = offsetof(struct ioc4, sio_ies),
+ .iec = offsetof(struct ioc4, sio_iec),
+ .lvl_mask = IOC4_LVL_MASK,
+ },
+ {
+ .ir = offsetof(struct ioc4, other_ir),
+ .ies = offsetof(struct ioc4, other_ies),
+ .iec = offsetof(struct ioc4, other_iec),
+ },
+};
+
+static const struct ioc3_variant ioc4_variant = {
+ .name = "IOC4",
+ .chip = &ioc4_irq_chip,
+ .banks = ioc4_irq_banks,
+ .nr_banks = ARRAY_SIZE(ioc4_irq_banks),
+ .nr_irqs = IOC4_NUM_IRQS,
+ /* Parity and SERR# reporting, as the SN2 IOC4 core enabled them. */
+ .pci_command = PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
+ .setup = ioc4_setup,
+};
+
static struct pci_device_id ioc3_mfd_id_table[] = {
- { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, (kernel_ulong_t)&ioc3_variant },
+ { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, (kernel_ulong_t)&ioc4_variant },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, ioc3_mfd_id_table);
@@ -669,5 +902,5 @@ static struct pci_driver ioc3_mfd_driver = {
module_pci_driver(ioc3_mfd_driver);
MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfer@suse.de>");
-MODULE_DESCRIPTION("SGI IOC3 MFD driver");
+MODULE_DESCRIPTION("SGI IOC3/IOC4 MFD driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/8250_ioc3.c b/drivers/tty/serial/8250/8250_ioc3.c
index 28e28076782b..5d3c0190f19e 100644
--- a/drivers/tty/serial/8250/8250_ioc3.c
+++ b/drivers/tty/serial/8250/8250_ioc3.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/platform_data/sgi-ioc3-serial.h>
#include <linux/platform_device.h>
#include "8250.h"
@@ -33,6 +34,7 @@ static void ioc3_serial_out(struct uart_port *p, unsigned int offset, u32 value)
static int serial8250_ioc3_probe(struct platform_device *pdev)
{
+ const struct ioc3_serial8250_platform_data *pdata;
struct ioc3_8250_data *data;
struct uart_8250_port up;
struct resource *r;
@@ -56,9 +58,10 @@ static int serial8250_ioc3_probe(struct platform_device *pdev)
irq = 0; /* no interrupt -> use polling */
/* Register serial ports with 8250.c */
+ pdata = dev_get_platdata(&pdev->dev);
memset(&up, 0, sizeof(struct uart_8250_port));
up.port.iotype = UPIO_MEM;
- up.port.uartclk = IOC3_UARTCLK;
+ up.port.uartclk = pdata ? pdata->uartclk : IOC3_UARTCLK;
up.port.type = PORT_16550A;
up.port.irq = irq;
up.port.flags = (UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ);
diff --git a/include/linux/platform_data/sgi-ioc3-serial.h b/include/linux/platform_data/sgi-ioc3-serial.h
new file mode 100644
index 000000000000..ae4690f33125
--- /dev/null
+++ b/include/linux/platform_data/sgi-ioc3-serial.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SGI IOC3/IOC4 8250-compatible UART cells
+ */
+
+#ifndef PLATFORM_DATA_SGI_IOC3_SERIAL_H
+#define PLATFORM_DATA_SGI_IOC3_SERIAL_H
+
+struct ioc3_serial8250_platform_data {
+ unsigned int uartclk;
+};
+
+#endif /* PLATFORM_DATA_SGI_IOC3_SERIAL_H */
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 32/53] MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (30 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 31/53] mfd: ioc3: Add support for IOC4 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 33/53] MIPS: SGI-IP27: add L1 system controller support Imre Kaloz
` (21 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
ip27-console.c casts the console UART to struct ioc3 unconditionally,
wrong for a Tezro's IOC4, whose UARTs sit at other offsets.
console_t.type reads back 0x0300fafa there, neither chip's PCI id, so on
IP35 dispatch on uart_base, which both chips populate correctly. A Hub
keeps the IOC3 path.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-console.c | 47 ++++++++++++++++++++++++++++++-
1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c
index 7737a88c6569..a1bcb4376f66 100644
--- a/arch/mips/sgi-ip27/ip27-console.c
+++ b/arch/mips/sgi-ip27/ip27-console.c
@@ -12,6 +12,7 @@
#include <asm/sn/agent.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/ioc3.h>
+#include <asm/sn/ioc4.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
@@ -32,9 +33,53 @@ static inline struct ioc3_uartregs *console_uart(void)
return &ioc3->sregs.uarta;
}
+/*
+ * On IP35 a Tezro drives its console off IOC4, a Fuel off IOC3.
+ * console_t.type cannot tell them apart -- a Tezro reads it back as
+ * 0x0300fafa, which is neither chip's PCI id -- while uart_base is
+ * populated on both, so dispatch on whether that lands on IOC3's own UART.
+ */
+static inline bool console_is_ioc4(nasid_t nasid)
+{
+ unsigned long uart_base;
+
+ if (!system_is_ip35)
+ return false;
+
+ uart_base = KL_CONFIG_CH_CONS_INFO(nasid)->uart_base;
+ if (!uart_base)
+ return false;
+
+ /* NODE_SWIN_ADDR() aliases widget 0 away; the console sits on 0xf. */
+ if (uart_base < RAW_NODE_SWIN_BASE(nasid, 0) ||
+ uart_base >= RAW_NODE_SWIN_BASE(nasid, SWIN_WIDGET_MASK) +
+ SWIN_SIZE)
+ return false;
+
+ return uart_base != (unsigned long)console_uart();
+}
+
+static inline struct ioc4_uartregs *console_uart_ioc4(nasid_t nasid)
+{
+ return (struct ioc4_uartregs *)KL_CONFIG_CH_CONS_INFO(nasid)->uart_base;
+}
+
void prom_putchar(char c)
{
- struct ioc3_uartregs *uart = console_uart();
+ struct ioc3_uartregs *uart;
+ nasid_t nasid;
+
+ nasid = (master_nasid == INVALID_NASID) ? get_nasid() : master_nasid;
+ if (console_is_ioc4(nasid)) {
+ struct ioc4_uartregs *ioc4_uart = console_uart_ioc4(nasid);
+
+ while ((readb(&ioc4_uart->i4u_lsr) & 0x20) == 0)
+ ;
+ writeb(c, &ioc4_uart->i4u_thr);
+ return;
+ }
+
+ uart = console_uart();
while ((readb(&uart->iu_lsr) & 0x20) == 0)
;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 33/53] MIPS: SGI-IP27: add L1 system controller support
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (31 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 32/53] MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 34/53] MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address Imre Kaloz
` (20 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
IP35-family (Bedrock hub) machines carry an L1 system controller on a
polled UART with RFC 1662 async-HDLC framing. Add the transport, the
request/response core and l1_exec_command(); on a Hub every request
fails with -ENODEV.
Ported from OpenBSD sys/arch/sgi/sgi/l1.c and licensed ISC like it,
keeping its notice.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/Kconfig | 1 +
arch/mips/include/asm/sn/l1.h | 14 ++
arch/mips/sgi-ip27/Makefile | 4 +-
arch/mips/sgi-ip27/ip27-l1.c | 423 ++++++++++++++++++++++++++++++++++
4 files changed, 440 insertions(+), 2 deletions(-)
create mode 100644 arch/mips/include/asm/sn/l1.h
create mode 100644 arch/mips/sgi-ip27/ip27-l1.c
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 85f1c4bd6549..b7026672a4c1 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -762,6 +762,7 @@ config SGI_IP27
select FW_ARC64
select ARC_CMDLINE_ONLY
select BOOT_ELF64
+ select CRC_CCITT
select DEFAULT_SGI_PARTITION
select FORCE_PCI
select SYS_HAS_EARLY_PRINTK
diff --git a/arch/mips/include/asm/sn/l1.h b/arch/mips/include/asm/sn/l1.h
new file mode 100644
index 000000000000..e61935e2fac8
--- /dev/null
+++ b/arch/mips/include/asm/sn/l1.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Entry points into the SGI L1 system controller driver.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#ifndef __ASM_SN_L1_H
+#define __ASM_SN_L1_H
+
+#include <linux/types.h>
+
+int l1_exec_command(const char *cmd);
+
+#endif /* __ASM_SN_L1_H */
diff --git a/arch/mips/sgi-ip27/Makefile b/arch/mips/sgi-ip27/Makefile
index 9877fcc512b1..4b6aa641ccc6 100644
--- a/arch/mips/sgi-ip27/Makefile
+++ b/arch/mips/sgi-ip27/Makefile
@@ -4,8 +4,8 @@
#
obj-y := ip27-berr.o ip27-irq.o ip27-init.o ip27-klconfig.o \
- ip27-klnuma.o ip27-memory.o ip27-nmi.o ip27-reset.o ip27-timer.o \
- ip27-xtalk.o
+ ip27-klnuma.o ip27-l1.o ip27-memory.o ip27-nmi.o ip27-reset.o \
+ ip27-timer.o ip27-xtalk.o
obj-$(CONFIG_EARLY_PRINTK) += ip27-console.o
obj-$(CONFIG_SMP) += ip27-smp.o
diff --git a/arch/mips/sgi-ip27/ip27-l1.c b/arch/mips/sgi-ip27/ip27-l1.c
new file mode 100644
index 000000000000..da3095b66cf1
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-l1.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ *
+ * SGI L1 system controller communication, ported from OpenBSD/sgi's
+ * l1.c:
+ *
+ * Copyright (c) 2009 Miodrag Vallat.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * The L1 is the IP35-family (Bedrock hub) system controller, reached
+ * over a polled UART with async-HDLC (PPP) framing. Every request fails
+ * with -ENODEV on an IP27 (Hub) machine.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/serial_reg.h>
+#include <linux/spinlock.h>
+#include <linux/stdarg.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/crc-ccitt.h>
+#include <linux/io.h>
+
+#include <asm/sn/addrs.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/l1.h>
+#include <asm/sn/types.h>
+
+/* packet types */
+#define L1PKT_REQUEST 0x00
+#define L1PKT_RESPONSE 0x20
+
+/* packet subchannels */
+#define L1CH_MISC 0x10 /* available for operating system */
+
+/* argument encoding */
+#define L1_ARG_INT 0x00 /* followed by 32 bit BE value */
+#define L1_ARG_ASCII 0x01 /* followed by NUL terminated string */
+#define L1_ARG_BINARY 0x80 /* length in low 7 bits */
+
+/* command types and destination addresses */
+#define L1_ADDRESS(type, addr) (((type) << 28) | (addr))
+
+#define L1_TYPE_L1 0x00
+
+#define L1_ADDRESS_RACK_LOCAL 0x3ff
+#define L1_ADDRESS_RACK_SHIFT 18
+#define L1_ADDRESS_BAY_LOCAL 0x3f
+#define L1_ADDRESS_BAY_SHIFT 12
+
+#define L1_ADDRESS_LOCAL \
+ ((L1_ADDRESS_RACK_LOCAL << L1_ADDRESS_RACK_SHIFT) | \
+ (L1_ADDRESS_BAY_LOCAL << L1_ADDRESS_BAY_SHIFT))
+
+#define L1_TASK_COMMAND 0x03
+
+/* response codes */
+#define L1_RESP_OK ((u32)0)
+#define L1_RESP_NXDATA ((u32)-0x68)
+#define L1_RESP_INVAL ((u32)-0x6b)
+
+/* L1_TASK_COMMAND requests */
+#define L1_REQ_EXEC_CMD 0x0000 /* interpret plaintext command */
+
+/* Async-HDLC framing bytes; linux/ppp_defs.h would drag in skbuff.h. */
+#define L1_FLAG 0x7e
+#define L1_ESC 0x7d
+#define L1_TRANS 0x20
+#define L1_FCS_INIT 0xffff
+#define L1_FCS_GOOD 0xf0b8
+
+/* A raw iteration count, not a time bound: this can run before jiffies. */
+#define L1_POLL_ITERATIONS 1000000
+
+/*
+ * The L1 UART sits in the HSPEC "local register" window, reached
+ * through the "remote HSPEC" indirection even for the local node.
+ */
+#define L1_RHSPEC_OFFSET 0x20000000 /* remote-HSPEC indirection */
+#define L1_LREG_OFFSET 0x10000000 /* local register window */
+#define L1_UART_REG(r) (L1_LREG_OFFSET + 0x80 + ((r) << 3))
+
+#define L1_UART_ADDRESS(nasid, r) \
+ ((u64 *)(NODE_HSPEC_BASE(nasid) + L1_RHSPEC_OFFSET + L1_UART_REG(r)))
+
+static int l1_serial_getc(nasid_t nasid)
+{
+ unsigned int n;
+ u64 lsr;
+
+ for (n = L1_POLL_ITERATIONS; n != 0; n--) {
+ lsr = __raw_readq(L1_UART_ADDRESS(nasid, UART_LSR));
+ if (lsr & UART_LSR_DR)
+ break;
+ cpu_relax();
+ }
+ if (n == 0)
+ return -ETIMEDOUT;
+
+ return __raw_readq(L1_UART_ADDRESS(nasid, UART_RX)) & 0xff;
+}
+
+static int l1_serial_putc(nasid_t nasid, u8 val)
+{
+ unsigned int n;
+ u64 lsr;
+
+ for (n = L1_POLL_ITERATIONS; n != 0; n--) {
+ lsr = __raw_readq(L1_UART_ADDRESS(nasid, UART_LSR));
+ if (lsr & UART_LSR_THRE)
+ break;
+ cpu_relax();
+ }
+ if (n == 0)
+ return -ETIMEDOUT;
+
+ __raw_writeq(val, L1_UART_ADDRESS(nasid, UART_TX));
+ return 0;
+}
+
+static int l1_serial_ppp_write(nasid_t nasid, u16 *crc, u8 data, bool escape)
+{
+ int rc;
+
+ if (crc)
+ *crc = crc_ccitt_byte(*crc, data);
+
+ if (escape && (data == L1_FLAG || data == L1_ESC)) {
+ rc = l1_serial_putc(nasid, L1_ESC);
+ if (rc)
+ return rc;
+ data ^= L1_TRANS;
+ }
+
+ return l1_serial_putc(nasid, data);
+}
+
+static int l1_packet_put(nasid_t nasid, const u8 *packet, size_t len)
+{
+ u16 crc = L1_FCS_INIT;
+ int rc;
+
+ rc = l1_serial_ppp_write(nasid, NULL, L1_FLAG, false);
+ if (rc)
+ return rc;
+
+ while (len-- != 0) {
+ rc = l1_serial_ppp_write(nasid, &crc, *packet++, true);
+ if (rc)
+ return rc;
+ }
+
+ crc ^= L1_FCS_INIT;
+ rc = l1_serial_ppp_write(nasid, NULL, crc & 0xff, true);
+ if (rc)
+ return rc;
+ rc = l1_serial_ppp_write(nasid, NULL, (crc >> 8) & 0xff, true);
+ if (rc)
+ return rc;
+
+ return l1_serial_ppp_write(nasid, NULL, L1_FLAG, false);
+}
+
+static int l1_packet_get(nasid_t nasid, u8 *buf, size_t buflen, size_t *rlen)
+{
+ u16 crc;
+ size_t rcvlen;
+ int data;
+
+ for (;;) {
+ data = l1_serial_getc(nasid);
+ if (data < 0)
+ return data;
+ if (data == L1_FLAG)
+ break;
+ }
+
+ rcvlen = 0;
+ crc = L1_FCS_INIT;
+ for (;;) {
+ data = l1_serial_getc(nasid);
+ if (data < 0)
+ return data;
+ if (data == L1_FLAG) /* end of packet */
+ break;
+ if (data == L1_ESC) {
+ data = l1_serial_getc(nasid);
+ if (data < 0)
+ return data;
+ data ^= L1_TRANS;
+ }
+ if (rcvlen < buflen)
+ buf[rcvlen] = data;
+ rcvlen++;
+ crc = crc_ccitt_byte(crc, data);
+ }
+
+ if (rcvlen > buflen)
+ return -EMSGSIZE; /* did not fit the buffer */
+ if (rcvlen < 2)
+ return -EBADMSG; /* short packet */
+
+ rcvlen -= 2; /* crc bytes */
+ if (crc != L1_FCS_GOOD)
+ return -EBADMSG; /* CRC error */
+
+ *rlen = rcvlen;
+ return 0;
+}
+
+/*
+ * Returns the would-be length even when the buffer is too small, and
+ * SIZE_MAX for an unknown argument type.
+ */
+static size_t l1_command_build(u8 *buf, size_t buflen, u32 address,
+ u16 request, int nargs, va_list ap)
+{
+ u32 data;
+ size_t len = 0;
+ int argtype;
+ const char *str;
+
+ if (buflen >= 1) {
+ *buf++ = L1PKT_REQUEST | L1CH_MISC;
+ buflen--;
+ }
+ len++;
+
+ if (buflen >= 4) {
+ put_unaligned_be32(address, buf);
+ buf += 4;
+ buflen -= 4;
+ }
+ len += 4;
+
+ if (buflen >= 2) {
+ put_unaligned_be16(request, buf);
+ buf += 2;
+ buflen -= 2;
+ }
+ len += 2;
+
+ if (buflen >= 1) {
+ *buf++ = nargs;
+ buflen--;
+ }
+ len++;
+
+ while (nargs-- != 0) {
+ argtype = va_arg(ap, int);
+ switch (argtype) {
+ case L1_ARG_INT:
+ data = va_arg(ap, u32);
+ if (buflen >= 5) {
+ *buf++ = L1_ARG_INT;
+ put_unaligned_be32(data, buf);
+ buf += 4;
+ buflen -= 5;
+ }
+ len += 5;
+ break;
+ case L1_ARG_ASCII:
+ str = va_arg(ap, const char *);
+ data = strlen(str);
+ if (buflen >= data + 2) {
+ *buf++ = L1_ARG_ASCII;
+ memcpy(buf, str, data + 1);
+ buf += data + 1;
+ buflen -= data + 2;
+ }
+ len += data + 2;
+ break;
+ case L1_ARG_BINARY:
+ data = (u32)va_arg(ap, size_t); /* size */
+ str = va_arg(ap, const char *); /* data */
+ if (buflen >= 1 + data) {
+ *buf++ = L1_ARG_BINARY | data;
+ memcpy(buf, str, data);
+ buf += data;
+ buflen -= data + 1;
+ }
+ len += data + 1;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return SIZE_MAX;
+ }
+ }
+
+ return len;
+}
+
+/* The L1 multiplexes unsolicited event and console traffic onto one wire. */
+static int l1_receive_response(nasid_t nasid, u8 *pkt, size_t *pktlen)
+{
+ size_t rcvlen;
+ int rc;
+
+ for (;;) {
+ rc = l1_packet_get(nasid, pkt, *pktlen, &rcvlen);
+ if (rc == -ETIMEDOUT)
+ return rc;
+ if (rc) /* bad packet */
+ continue;
+ if (pkt[0] != (L1PKT_RESPONSE | L1CH_MISC))
+ continue;
+
+ *pktlen = rcvlen;
+ return 0;
+ }
+}
+
+static int l1_response_to_errno(u32 response)
+{
+ switch (response) {
+ case L1_RESP_OK:
+ return 0;
+ case L1_RESP_INVAL:
+ return -EINVAL;
+ case L1_RESP_NXDATA:
+ return -ENXIO;
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * One request and its response are a single transaction on the wire,
+ * so every entry point holds this.
+ */
+static DEFINE_SPINLOCK(l1_lock);
+
+/*
+ * Run one request/response exchange in pkt, which has to be large enough
+ * for both; returns the response's argument count.
+ */
+static int l1_transact(nasid_t nasid, u8 *pkt, size_t pktsize, u32 address,
+ u16 request, u8 **rargs, size_t *rarglen, int nargs, ...)
+{
+ va_list ap;
+ size_t pktlen;
+ int rc;
+
+ if (!system_is_ip35)
+ return -ENODEV;
+
+ va_start(ap, nargs);
+ pktlen = l1_command_build(pkt, pktsize, address, request, nargs, ap);
+ va_end(ap);
+ if (pktlen > pktsize)
+ return -ENOMEM;
+
+ rc = l1_packet_put(nasid, pkt, pktlen);
+ if (rc)
+ return rc;
+
+ pktlen = pktsize;
+ rc = l1_receive_response(nasid, pkt, &pktlen);
+ if (rc)
+ return rc;
+
+ if (pktlen < 6)
+ return -EIO;
+
+ rc = l1_response_to_errno(get_unaligned_be32(&pkt[1]));
+ if (rc)
+ return rc;
+
+ if (rargs) {
+ *rargs = pkt + 6;
+ *rarglen = pktlen - 6;
+ }
+
+ return pkt[5];
+}
+
+/*
+ * Send a plaintext command to the L1's own console command parser, the
+ * same strings a human types at the L1 console. Such a command can reset
+ * the Hub, or cut power to it, before the L1 answers, so an error here
+ * means "no response", never "retry".
+ */
+int l1_exec_command(const char *cmd)
+{
+ u32 address = L1_ADDRESS(L1_TYPE_L1,
+ L1_ADDRESS_LOCAL | L1_TASK_COMMAND);
+ u8 pkt[64 + 64]; /* command and response packet buffer */
+ unsigned long flags;
+ bool locked;
+ int rc;
+
+ /*
+ * Reset and poweroff reach this after smp_send_stop(), so a stopped
+ * CPU may hold the lock for good; go to the wire either way.
+ */
+ locked = spin_trylock_irqsave(&l1_lock, flags);
+
+ rc = l1_transact(get_nasid(), pkt, sizeof(pkt), address,
+ L1_REQ_EXEC_CMD, NULL, NULL, 1,
+ L1_ARG_ASCII, cmd);
+
+ if (locked)
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ if (rc < 0)
+ return rc;
+
+ /* nothing is expected back besides the response code */
+ return rc == 0 ? 0 : -EIO;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 34/53] MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (32 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 33/53] MIPS: SGI-IP27: add L1 system controller support Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 35/53] MIPS: PCI: xtalk-bridge: take the board part number from the L1 Imre Kaloz
` (19 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
On Bedrock hubs restart, halt and poweroff go through the L1 system
controller, and ioc3-eth takes its MAC address from the L1's board
information area.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-l1.c | 266 ++++++++++++++++++++++++++++++++
arch/mips/sgi-ip27/ip27-reset.c | 47 +++++-
2 files changed, 305 insertions(+), 8 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-l1.c b/arch/mips/sgi-ip27/ip27-l1.c
index da3095b66cf1..25f9be350c0b 100644
--- a/arch/mips/sgi-ip27/ip27-l1.c
+++ b/arch/mips/sgi-ip27/ip27-l1.c
@@ -25,6 +25,8 @@
*/
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/hex.h>
#include <linux/serial_reg.h>
#include <linux/spinlock.h>
#include <linux/stdarg.h>
@@ -66,6 +68,7 @@
(L1_ADDRESS_BAY_LOCAL << L1_ADDRESS_BAY_SHIFT))
#define L1_TASK_COMMAND 0x03
+#define L1_TASK_GENERAL 0x06
/* response codes */
#define L1_RESP_OK ((u32)0)
@@ -75,6 +78,27 @@
/* L1_TASK_COMMAND requests */
#define L1_REQ_EXEC_CMD 0x0000 /* interpret plaintext command */
+/* L1_TASK_GENERAL requests */
+#define L1_REQ_EEPROM 0x0006 /* access eeprom */
+
+/* L1_REQ_EEPROM additional argument values (non C-brick component) */
+#define L1_EEP_LOGIC 0x01 /* logic board */
+/* ia code */
+#define L1_EEP_BOARD 0x02 /* board ia */
+
+#define EEPROM_CHUNK 0x40
+
+/*
+ * Information Area type/length byte: bits 7-6 are the type (0 binary,
+ * 1 BCD, 2 packed 6-bit ascii, 3 8-bit ascii), bits 5-0 the length.
+ */
+#define IA_TYPE_SHIFT 6
+#define IA_TYPE_BINARY 0
+#define IA_TYPE_ASCII 3
+#define IA_LENGTH_MASK 0x3f
+#define IA_TL(t, l) (((t) << IA_TYPE_SHIFT) | (l))
+#define IA_EOF IA_TL(IA_TYPE_ASCII, 1)
+
/* Async-HDLC framing bytes; linux/ppp_defs.h would drag in skbuff.h. */
#define L1_FLAG 0x7e
#define L1_ESC 0x7d
@@ -96,6 +120,10 @@
#define L1_UART_ADDRESS(nasid, r) \
((u64 *)(NODE_HSPEC_BASE(nasid) + L1_RHSPEC_OFFSET + L1_UART_REG(r)))
+/* Board IA record buffer; the probed length is checked against it. */
+#define L1_IA_BUF_SIZE 512
+static u8 l1_ia_buf[L1_IA_BUF_SIZE];
+
static int l1_serial_getc(nasid_t nasid)
{
unsigned int n;
@@ -221,6 +249,48 @@ static int l1_packet_get(nasid_t nasid, u8 *buf, size_t buflen, size_t *rlen)
return 0;
}
+static int l1_packet_get_int(u8 **buf, size_t *buflen, u32 *rval)
+{
+ u8 *b = *buf;
+
+ if (*buflen < 5)
+ return -EIO;
+ if (*b++ != L1_ARG_INT)
+ return -EINVAL;
+
+ *rval = get_unaligned_be32(b);
+ b += 4;
+ *buf = b;
+ *buflen -= 5;
+
+ return 0;
+}
+
+static int l1_packet_get_binary(u8 **buf, size_t *buflen, u8 **rval,
+ size_t *rlen)
+{
+ u8 *b = *buf;
+ size_t datalen;
+
+ if (*buflen < 1)
+ return -EIO;
+ if ((*b & L1_ARG_BINARY) == 0)
+ return -EINVAL;
+
+ datalen = *b & ~L1_ARG_BINARY;
+ if (*buflen < 1 + datalen)
+ return -ENOMEM;
+
+ b++;
+ *rval = b;
+ *rlen = datalen;
+ *buflen -= 1 + datalen;
+ b += datalen;
+ *buf = b;
+
+ return 0;
+}
+
/*
* Returns the would-be length even when the buffer is too small, and
* SIZE_MAX for an unknown argument type.
@@ -421,3 +491,199 @@ int l1_exec_command(const char *cmd)
/* nothing is expected back besides the response code */
return rc == 0 ? 0 : -EIO;
}
+
+/* Read a component's board IA record from EEPROM. */
+static int l1_read_board_ia(nasid_t nasid, int type, unsigned int component,
+ u8 **ria, size_t *rialen)
+{
+ u32 address = L1_ADDRESS(type, L1_ADDRESS_LOCAL | L1_TASK_GENERAL);
+ u8 pkt[64 + EEPROM_CHUNK];
+ u8 *args, *chunk;
+ size_t arglen, chunklen, ialen, iapos;
+ u32 data;
+ int rc;
+
+ rc = l1_transact(nasid, pkt, sizeof(pkt), address, L1_REQ_EEPROM,
+ &args, &arglen, 4,
+ L1_ARG_INT, (u32)component,
+ L1_ARG_INT, (u32)L1_EEP_BOARD,
+ L1_ARG_INT, (u32)0, /* offset */
+ L1_ARG_INT, (u32)0); /* size (0 = probe) */
+ if (rc < 0)
+ return rc;
+ if (rc != 1)
+ return -EIO;
+
+ rc = l1_packet_get_int(&args, &arglen, &data);
+ if (rc)
+ return -EIO;
+
+ ialen = (size_t)data;
+ if (ialen == 0 || ialen > L1_IA_BUF_SIZE)
+ return -ENOMEM;
+
+ iapos = 0;
+ while (iapos < ialen) {
+ rc = l1_transact(nasid, pkt, sizeof(pkt), address,
+ L1_REQ_EEPROM, &args, &arglen, 4,
+ L1_ARG_INT, (u32)component,
+ L1_ARG_INT, (u32)L1_EEP_BOARD,
+ L1_ARG_INT, (u32)iapos,
+ L1_ARG_INT, (u32)EEPROM_CHUNK);
+ if (rc < 0)
+ return rc;
+ if (rc != 2)
+ return -EIO;
+
+ rc = l1_packet_get_int(&args, &arglen, &data);
+ if (rc)
+ return -EIO;
+
+ rc = l1_packet_get_binary(&args, &arglen, &chunk, &chunklen);
+ if (rc)
+ return -EIO;
+
+ /* should not happen; the bound keeps the loop finite */
+ if (chunklen == 0)
+ return -EIO;
+
+ if (chunklen > ialen - iapos)
+ chunklen = ialen - iapos;
+
+ memcpy(l1_ia_buf + iapos, chunk, chunklen);
+ iapos += chunklen;
+ }
+
+ /* the sum of all IA bytes has to be 0 mod 256 */
+ data = 0;
+ for (iapos = 0; iapos < ialen; iapos++)
+ data += l1_ia_buf[iapos];
+ if ((data & 0xff) != 0)
+ return -EINVAL;
+
+ *ria = l1_ia_buf;
+ *rialen = ialen;
+ return 0;
+}
+
+/* IA record walk helper: advance past one record, without passing EOF. */
+static size_t ia_skip(const u8 *ia, size_t iapos, size_t ialen)
+{
+ size_t npos;
+
+ if (ia[iapos] == IA_EOF)
+ return iapos;
+
+ npos = iapos + 1 + (ia[iapos] & IA_LENGTH_MASK);
+ return npos >= ialen ? iapos : npos;
+}
+
+static int l1_get_brick_ethernet_address(nasid_t nasid, u8 *enaddr)
+{
+ u8 *ia;
+ u8 group;
+ size_t iapos, ialen;
+ int i, rc;
+
+ /* assumes the local brick's L1 answers for this node */
+ rc = l1_read_board_ia(nasid, L1_TYPE_L1, L1_EEP_LOGIC, &ia, &ialen);
+ if (rc)
+ return rc;
+
+ /*
+ * ia[0] is the board-IA format version. Below 2 the encryption-key
+ * (G/P/Y) and Ethernet-address groups follow the fixed records
+ * implicitly; from 2 on an extra selector record comes first: 0
+ * keys only, 1 keys then address, 2 neither, 3 a config group.
+ * Otherwise the walk follows l1_get_brick_ethernet_address() in
+ * OpenBSD/sgi's l1.c.
+ */
+ if (ia[1] < 2 || ia[1] * 8UL > ialen)
+ return -EINVAL;
+
+ /* skip the fixed part, then mfg, product, serial, part */
+ iapos = 6;
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ /* skip FRU (1 raw byte) */
+ if (iapos < ialen - 1)
+ iapos++;
+ /* skip board rev, eeprom size, temp waiver */
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+
+ if (ia[0] >= 2) {
+ if (iapos + 1 >= ialen)
+ return -ENXIO;
+ group = ia[iapos + 1];
+ iapos = ia_skip(ia, iapos, ialen);
+ if (group != 1)
+ return -ENXIO; /* no ethernet-address group here */
+ /* the three encryption-key records precede the address */
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ } else {
+ if (iapos < ialen && ia[iapos] == IA_TL(IA_TYPE_BINARY, 4)) {
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ }
+ }
+
+ if (iapos >= ialen - 1 - 12 || ia[iapos] != IA_TL(IA_TYPE_ASCII, 12))
+ return -EINVAL;
+
+ iapos++;
+ for (i = 0; i < ETH_ALEN; i++) {
+ int hi = hex_to_bin(ia[iapos + i * 2]);
+ int lo = hex_to_bin(ia[iapos + i * 2 + 1]);
+
+ if (hi < 0 || lo < 0)
+ return -EINVAL;
+ enaddr[i] = (hi << 4) | lo;
+ }
+
+ return is_valid_ether_addr(enaddr) ? 0 : -EINVAL;
+}
+
+static u8 ip27_l1_mac_addr[ETH_ALEN];
+static bool ip27_l1_mac_addr_valid;
+static bool ip27_l1_mac_addr_tried;
+
+/*
+ * A board that sources its MAC elsewhere, such as Tezro's tg3, yields
+ * nothing here, so a failure logs at debug level rather than warning.
+ */
+unsigned char *arch_get_platform_mac_address(void)
+{
+ unsigned long flags;
+ int rc;
+
+ if (!system_is_ip35)
+ return NULL;
+
+ spin_lock_irqsave(&l1_lock, flags);
+
+ if (!ip27_l1_mac_addr_tried) {
+ ip27_l1_mac_addr_tried = true;
+
+ rc = l1_get_brick_ethernet_address(get_nasid(),
+ ip27_l1_mac_addr);
+ if (rc) {
+ pr_debug("l1: EEPROM MAC address read failed (%d)\n",
+ rc);
+ } else {
+ ip27_l1_mac_addr_valid = true;
+ pr_info("l1: EEPROM Ethernet address %pM\n",
+ ip27_l1_mac_addr);
+ }
+ }
+
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ return ip27_l1_mac_addr_valid ? ip27_l1_mac_addr : NULL;
+}
diff --git a/arch/mips/sgi-ip27/ip27-reset.c b/arch/mips/sgi-ip27/ip27-reset.c
index 5ac5ad638734..1e55636393f0 100644
--- a/arch/mips/sgi-ip27/ip27-reset.c
+++ b/arch/mips/sgi-ip27/ip27-reset.c
@@ -3,12 +3,17 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Reset an IP27.
+ * Reset an IP27, or an IP35-family (Bedrock hub) machine.
+ *
+ * On Bedrock hubs restart and halt ask the L1 system controller to reset
+ * the machine and fall back to an NI port reset if it has not acted
+ * within five seconds; poweroff asks the L1 to cut power.
*
* Copyright (C) 1997, 1998, 1999, 2000, 06 by Ralf Baechle
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/compiler.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
@@ -25,6 +30,7 @@
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
#include <asm/sn/gda.h>
+#include <asm/sn/l1.h>
#include "ip27-common.h"
@@ -34,6 +40,15 @@ void machine_power_off(void) __noreturn;
#define noreturn while(1); /* Silence gcc. */
+static void ip35_reset(void)
+{
+ l1_exec_command("rst");
+ mdelay(5000);
+ LOCAL_HUB_S(NI_RESET_ENABLE_IP35, NI_RESET_OK_IP35);
+ LOCAL_HUB_S(NI_PORT_RESET_IP35,
+ NI_RESET_ACTION_IP35 | NI_RESET_LOCAL_IP35);
+}
+
/* XXX How to pass the reboot command to the firmware??? */
static void ip27_machine_restart(char *command)
{
@@ -45,12 +60,16 @@ static void ip27_machine_restart(char *command)
#ifdef CONFIG_SMP
smp_send_stop();
#endif
+ if (system_is_ip35) {
+ ip35_reset();
+ } else {
#if 0
- for_each_online_node(i)
- REMOTE_HUB_S(i, PROMOP_REG, PROMOP_REBOOT);
+ for_each_online_node(i)
+ REMOTE_HUB_S(i, PROMOP_REG, PROMOP_REBOOT);
#else
- LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+ LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
#endif
+ }
noreturn;
}
@@ -61,15 +80,27 @@ static void ip27_machine_halt(void)
#ifdef CONFIG_SMP
smp_send_stop();
#endif
- for_each_online_node(i)
- REMOTE_HUB_S(i, PROMOP_REG, PROMOP_RESTART);
- LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+ if (system_is_ip35) {
+ ip35_reset();
+ } else {
+ for_each_online_node(i)
+ REMOTE_HUB_S(i, PROMOP_REG, PROMOP_RESTART);
+ LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
+ }
noreturn;
}
static void ip27_machine_power_off(void)
{
- /* To do ... */
+ if (system_is_ip35) {
+#ifdef CONFIG_SMP
+ smp_send_stop();
+#endif
+ l1_exec_command("pwr d");
+ mdelay(5000);
+ pr_info("Please power off the system manually.\n");
+ }
+ /* IP27 (Hub) has no software poweroff path. */
noreturn;
}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 35/53] MIPS: PCI: xtalk-bridge: take the board part number from the L1
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (33 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 34/53] MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 36/53] mfd: ioc3: Add IP34 system-board entry to ioc3_infos[] Imre Kaloz
` (18 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
An XBridge answers nothing on the b_nic 1-wire pin, so
bridge_get_partnum() finds no PROM and the probe defers forever. Stop
waiting on XBridge and PIC widgets; on Bedrock hubs read the assembly
number from the L1 instead.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/l1.h | 7 +++
arch/mips/pci/pci-xtalk-bridge.c | 37 +++++++++++++-
arch/mips/sgi-ip27/ip27-l1.c | 83 ++++++++++++++++++++++++++++++++
3 files changed, 125 insertions(+), 2 deletions(-)
diff --git a/arch/mips/include/asm/sn/l1.h b/arch/mips/include/asm/sn/l1.h
index e61935e2fac8..1a3b7fa0e213 100644
--- a/arch/mips/include/asm/sn/l1.h
+++ b/arch/mips/include/asm/sn/l1.h
@@ -9,6 +9,13 @@
#include <linux/types.h>
+/*
+ * Which L1 EEPROM component id carries which board is undocumented and
+ * differs from machine to machine, so a caller walks the whole range.
+ */
+#define L1_BOARD_COMPONENT_MAX 8
+
int l1_exec_command(const char *cmd);
+int l1_read_board_partnum(unsigned int component, char *buf, size_t len);
#endif /* __ASM_SN_L1_H */
diff --git a/arch/mips/pci/pci-xtalk-bridge.c b/arch/mips/pci/pci-xtalk-bridge.c
index b2c3a8b207da..40d69681227c 100644
--- a/arch/mips/pci/pci-xtalk-bridge.c
+++ b/arch/mips/pci/pci-xtalk-bridge.c
@@ -18,7 +18,9 @@
#include <asm/pci/bridge.h>
#include <asm/paccess.h>
#include <asm/sn/irq_alloc.h>
+#include <asm/sn/addrs.h>
#include <asm/sn/ioc3.h>
+#include <asm/sn/l1.h>
#define CRC16_INIT 0
#define CRC16_VALID 0xb001
@@ -665,6 +667,29 @@ static const struct {
BRIDGE_BOARD_SETUP("030-1707-", bridge_setup_ip34_fuel_sysboard),
};
+/*
+ * The L1 answers one board information area per EEPROM component id, and
+ * which id carries which board is undocumented. Ask for each in turn and take
+ * the first answer the table above knows.
+ */
+static int bridge_l1_partnum(char *partnum, size_t len)
+{
+ unsigned int component;
+ int i;
+
+ for (component = 0; component < L1_BOARD_COMPONENT_MAX; component++) {
+ if (l1_read_board_partnum(component, partnum, len))
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(bridge_ioc3_devid); i++)
+ if (!strncmp(partnum, bridge_ioc3_devid[i].match,
+ strlen(bridge_ioc3_devid[i].match)))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
static void bridge_setup_board(struct bridge_controller *bc, char *partnum)
{
int i;
@@ -984,8 +1009,16 @@ static int bridge_probe(struct platform_device *pdev)
int err;
/* get part number from one wire prom */
- if (bridge_get_partnum(virt_to_phys((void *)bd->bridge_addr), partnum))
- return -EPROBE_DEFER; /* not available yet */
+ if (bridge_get_partnum(virt_to_phys((void *)bd->bridge_addr),
+ partnum)) {
+ /* Only a classic Bridge waits for its 1-wire PROM. */
+ if (!bd->is_xbridge && !bd->is_pic)
+ return -EPROBE_DEFER; /* not available yet */
+
+ if (!IS_ENABLED(CONFIG_SGI_IP27) || !system_is_ip35 ||
+ bridge_l1_partnum(partnum, sizeof(partnum)))
+ partnum[0] = '\0';
+ }
parent = irq_get_default_domain();
if (!parent)
diff --git a/arch/mips/sgi-ip27/ip27-l1.c b/arch/mips/sgi-ip27/ip27-l1.c
index 25f9be350c0b..173a506595da 100644
--- a/arch/mips/sgi-ip27/ip27-l1.c
+++ b/arch/mips/sgi-ip27/ip27-l1.c
@@ -650,6 +650,89 @@ static int l1_get_brick_ethernet_address(nasid_t nasid, u8 *enaddr)
return is_valid_ether_addr(enaddr) ? 0 : -EINVAL;
}
+/*
+ * The L1 stores SGI assembly numbers with underscores where the printed
+ * form has dashes: "030_1707_003" for "030-1707-003".
+ */
+static int l1_get_board_partnum(nasid_t nasid, unsigned int component,
+ char *buf, size_t len)
+{
+ size_t iapos, ialen, fieldlen, i;
+ u8 *ia;
+ int rc;
+
+ rc = l1_read_board_ia(nasid, L1_TYPE_L1, component, &ia, &ialen);
+ if (rc)
+ return rc;
+
+ if (ia[1] < 2 || ia[1] * 8UL > ialen)
+ return -EINVAL;
+
+ /* skip the fixed part, then mfg, product, serial */
+ iapos = 6;
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+ iapos = ia_skip(ia, iapos, ialen);
+
+ if (iapos >= ialen || (ia[iapos] >> IA_TYPE_SHIFT) != IA_TYPE_ASCII)
+ return -EINVAL;
+
+ fieldlen = ia[iapos] & IA_LENGTH_MASK;
+ if (fieldlen == 0 || iapos + 1 + fieldlen > ialen || fieldlen >= len)
+ return -EINVAL;
+
+ iapos++;
+ for (i = 0; i < fieldlen; i++)
+ buf[i] = ia[iapos + i] == '_' ? '-' : ia[iapos + i];
+ buf[fieldlen] = '\0';
+
+ return 0;
+}
+
+/*
+ * Part numbers cannot change at runtime, so failures are cached along
+ * with successes and each component is queried once.
+ */
+#define L1_PARTNUM_SIZE 24
+static char l1_partnum[L1_BOARD_COMPONENT_MAX][L1_PARTNUM_SIZE];
+static int l1_partnum_rc[L1_BOARD_COMPONENT_MAX];
+static bool l1_partnum_cached[L1_BOARD_COMPONENT_MAX];
+
+int l1_read_board_partnum(unsigned int component, char *buf, size_t len)
+{
+ unsigned long flags;
+
+ if (component >= L1_BOARD_COMPONENT_MAX)
+ return -EINVAL;
+
+ spin_lock_irqsave(&l1_lock, flags);
+
+ if (!l1_partnum_cached[component]) {
+ l1_partnum_rc[component] =
+ l1_get_board_partnum(get_nasid(), component,
+ l1_partnum[component],
+ sizeof(l1_partnum[component]));
+ l1_partnum_cached[component] = true;
+
+ if (l1_partnum_rc[component])
+ pr_debug("l1: component %u: no part number (%d)\n",
+ component, l1_partnum_rc[component]);
+ else
+ pr_debug("l1: component %u: part number %s\n",
+ component, l1_partnum[component]);
+ }
+
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ if (l1_partnum_rc[component])
+ return l1_partnum_rc[component];
+
+ if (strscpy(buf, l1_partnum[component], len) < 0)
+ return -E2BIG;
+
+ return 0;
+}
+
static u8 ip27_l1_mac_addr[ETH_ALEN];
static bool ip27_l1_mac_addr_valid;
static bool ip27_l1_mac_addr_tried;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 36/53] mfd: ioc3: Add IP34 system-board entry to ioc3_infos[]
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (34 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 35/53] MIPS: PCI: xtalk-bridge: take the board part number from the L1 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 37/53] mfd: ioc3: Cross the serial IRQs on the Origin 300 IO8 Imre Kaloz
` (17 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, mfd
Register the Fuel system board's on-board IOC3 and its M48T35, whose
year register counts from 1968 as on other IP35-family machines.
Bridge-side detection was already merged in commit 2c4288719806 ("MIPS:
PCI: Add detection of IOC3 on IO7, IO8, IO9 and Fuel"), which matches
the board part number "030-1707-"; the cell table had no entry to go
with it. Without one the IOC3 falls to the CAD DUO default, which
instantiates no serial cell, so Fuel comes up with no ttyS0.
Fixes: 2c4288719806 ("MIPS: PCI: Add detection of IOC3 on IO7, IO8, IO9 and Fuel")
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/mfd/ioc3.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
index 57956c00263f..95794dc9931c 100644
--- a/drivers/mfd/ioc3.c
+++ b/drivers/mfd/ioc3.c
@@ -517,6 +517,51 @@ static int ip30_sysboard_setup(struct ioc3_priv_data *ipd)
return ioc3_led_setup(ipd);
}
+static const struct mfd_cell ip34_m48t35_cells[] = {
+ {
+ .name = "rtc-m48t35-ip35",
+ .resources = ioc3_m48t35_resources,
+ .num_resources = ARRAY_SIZE(ioc3_m48t35_resources),
+ }
+};
+
+/*
+ * Fuel's timekeeper is the M48T35 at IOC3_BYTEBUS_DEV0. A read at IP30's
+ * DS1685 address never completes on the Fuel system board.
+ */
+static int ip34_sysboard_setup(struct ioc3_priv_data *ipd)
+{
+ int ret, io_irq;
+
+ io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn),
+ PCI_INTERRUPT_INTB);
+ ret = ioc3_irq_domain_setup(ipd, io_irq);
+ if (ret)
+ return ret;
+
+ ret = ioc3_eth_setup(ipd);
+ if (ret)
+ return ret;
+
+ ret = ioc3_serial_setup(ipd);
+ if (ret)
+ return ret;
+
+ ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO,
+ ip34_m48t35_cells, ARRAY_SIZE(ip34_m48t35_cells),
+ &ipd->pdev->resource[0], 0, ipd->domain);
+ if (ret) {
+ dev_err(&ipd->pdev->dev, "Failed to add M48T35 subdev\n");
+ return ret;
+ }
+
+ ret = ioc3_kbd_setup(ipd);
+ if (ret)
+ return ret;
+
+ return ioc3_led_setup(ipd);
+}
+
static int ioc3_menet_setup(struct ioc3_priv_data *ipd)
{
int ret, io_irq;
@@ -574,6 +619,7 @@ static struct {
IOC3_SID("IP27 BaseIO", IP27_BASEIO, &ip27_baseio_setup),
IOC3_SID("IP29 System Board", IP29_SYSBOARD, &ip27_baseio6g_setup),
IOC3_SID("IP30 System Board", IP30_SYSBOARD, &ip30_sysboard_setup),
+ IOC3_SID("IP34 System Board", IP34_SYSBOARD, &ip34_sysboard_setup),
IOC3_SID("MENET", MENET, &ioc3_menet_setup),
IOC3_SID("MENET4", MENET4, &ioc3_menet4_setup)
};
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 37/53] mfd: ioc3: Cross the serial IRQs on the Origin 300 IO8
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (35 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 36/53] mfd: ioc3: Add IP34 system-board entry to ioc3_infos[] Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 38/53] net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address Imre Kaloz
` (16 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, mfd
On the IO8 base I/O board the 16550 at sregs.uarta raises
SIO_IR_SB_INT and the one at sregs.uartb raises SIO_IR_SA_INT. With
the usual mapping the console never receives, because the interrupt
it enables is never asserted.
Add an IO8 entry to ioc3_infos[] that registers the serial cells with
the two IRQ resources exchanged. The board was matched as CAD DUO and
printed "ioc3: CAD DUO"; it now prints "ioc3: IO8" and gets its serial
ports.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/mfd/ioc3.c | 67 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 63 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
index 95794dc9931c..01c919c18b06 100644
--- a/drivers/mfd/ioc3.c
+++ b/drivers/mfd/ioc3.c
@@ -223,7 +223,37 @@ static struct mfd_cell ioc3_serial_cells[] = {
}
};
-static int ioc3_serial_setup(struct ioc3_priv_data *ipd)
+/*
+ * The UART at sregs.uarta raises IOC3_IRQ_SERIAL_B on the IO8 and
+ * sregs.uartb raises IOC3_IRQ_SERIAL_A.
+ */
+static const struct resource ioc3_uarta_crossed_resources[] = {
+ DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uarta),
+ sizeof_field(struct ioc3, sregs.uarta)),
+ DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_B)
+};
+
+static const struct resource ioc3_uartb_crossed_resources[] = {
+ DEFINE_RES_MEM(offsetof(struct ioc3, sregs.uartb),
+ sizeof_field(struct ioc3, sregs.uartb)),
+ DEFINE_RES_IRQ(IOC3_IRQ_SERIAL_A)
+};
+
+static const struct mfd_cell ioc3_serial_crossed_cells[] = {
+ {
+ .name = "ioc3-serial8250",
+ .resources = ioc3_uarta_crossed_resources,
+ .num_resources = ARRAY_SIZE(ioc3_uarta_crossed_resources),
+ },
+ {
+ .name = "ioc3-serial8250",
+ .resources = ioc3_uartb_crossed_resources,
+ .num_resources = ARRAY_SIZE(ioc3_uartb_crossed_resources),
+ }
+};
+
+static int __ioc3_serial_setup(struct ioc3_priv_data *ipd,
+ const struct mfd_cell *cells, int num_cells)
{
struct ioc3 __iomem *regs = ipd->base;
int ret;
@@ -241,8 +271,8 @@ static int ioc3_serial_setup(struct ioc3_priv_data *ipd)
udelay(1000); /* Wait until mode switch is done */
ret = mfd_add_devices(&ipd->pdev->dev, PLATFORM_DEVID_AUTO,
- ioc3_serial_cells, ARRAY_SIZE(ioc3_serial_cells),
- &ipd->pdev->resource[0], 0, ipd->domain);
+ cells, num_cells, &ipd->pdev->resource[0], 0,
+ ipd->domain);
if (ret) {
dev_err(&ipd->pdev->dev, "Failed to add 16550 subdevs\n");
return ret;
@@ -251,6 +281,12 @@ static int ioc3_serial_setup(struct ioc3_priv_data *ipd)
return 0;
}
+static int ioc3_serial_setup(struct ioc3_priv_data *ipd)
+{
+ return __ioc3_serial_setup(ipd, ioc3_serial_cells,
+ ARRAY_SIZE(ioc3_serial_cells));
+}
+
static const struct resource ioc3_kbd_resources[] = {
DEFINE_RES_MEM(offsetof(struct ioc3, serio),
sizeof_field(struct ioc3, serio)),
@@ -601,6 +637,28 @@ static int ioc3_cad_duo_setup(struct ioc3_priv_data *ipd)
return ioc3_kbd_setup(ipd);
}
+static int ioc3_io8_setup(struct ioc3_priv_data *ipd)
+{
+ int ret, io_irq;
+
+ io_irq = ioc3_map_irq(ipd->pdev, PCI_SLOT(ipd->pdev->devfn),
+ PCI_INTERRUPT_INTB);
+ ret = ioc3_irq_domain_setup(ipd, io_irq);
+ if (ret)
+ return ret;
+
+ ret = ioc3_eth_setup(ipd);
+ if (ret)
+ return ret;
+
+ ret = __ioc3_serial_setup(ipd, ioc3_serial_crossed_cells,
+ ARRAY_SIZE(ioc3_serial_crossed_cells));
+ if (ret)
+ return ret;
+
+ return ioc3_kbd_setup(ipd);
+}
+
/* Helper macro for filling ioc3_info array */
#define IOC3_SID(_name, _sid, _setup) \
{ \
@@ -621,7 +679,8 @@ static struct {
IOC3_SID("IP30 System Board", IP30_SYSBOARD, &ip30_sysboard_setup),
IOC3_SID("IP34 System Board", IP34_SYSBOARD, &ip34_sysboard_setup),
IOC3_SID("MENET", MENET, &ioc3_menet_setup),
- IOC3_SID("MENET4", MENET4, &ioc3_menet4_setup)
+ IOC3_SID("MENET4", MENET4, &ioc3_menet4_setup),
+ IOC3_SID("IO8", IO8, &ioc3_io8_setup)
};
#undef IOC3_SID
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 38/53] net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (36 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 37/53] mfd: ioc3: Cross the serial IRQs on the Origin 300 IO8 Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 39/53] MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC Imre Kaloz
` (15 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev
The IOC3 on Fuel's system board has no MAC PROM on its 1-wire bus, so
ioc3eth_get_mac_addr() cannot succeed there and the probe defers for
ever. Ask eth_platform_get_mac_address() before deferring; on IP35
machines it answers from the L1 system controller. Once deferred probing
gives up with neither source answering, use a random address.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/net/ethernet/sgi/ioc3-eth.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c
index f86dd192a3d3..13102177ea88 100644
--- a/drivers/net/ethernet/sgi/ioc3-eth.c
+++ b/drivers/net/ethernet/sgi/ioc3-eth.c
@@ -850,6 +850,7 @@ static int ioc3eth_probe(struct platform_device *pdev)
struct ioc3_private *ip;
struct net_device *dev;
struct resource *regs;
+ bool random_mac = false;
u8 mac_addr[6];
int err;
@@ -858,9 +859,16 @@ static int ioc3eth_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Invalid resource\n");
return -EINVAL;
}
- /* get mac addr from one wire prom */
- if (ioc3eth_get_mac_addr(regs, mac_addr))
- return -EPROBE_DEFER; /* not available yet */
+ /* get mac addr from one wire prom, or from the platform */
+ if (ioc3eth_get_mac_addr(regs, mac_addr) &&
+ eth_platform_get_mac_address(&pdev->dev, mac_addr)) {
+ err = driver_deferred_probe_check_state(&pdev->dev);
+ if (err == -EPROBE_DEFER)
+ return err; /* not available yet */
+
+ dev_warn(&pdev->dev, "no MAC address, using a random one\n");
+ random_mac = true;
+ }
dev = alloc_etherdev(sizeof(struct ioc3_private));
if (!dev)
@@ -939,7 +947,10 @@ static int ioc3eth_probe(struct platform_device *pdev)
ioc3_mii_start(ip);
ioc3_ssram_disc(ip);
- eth_hw_addr_set(dev, mac_addr);
+ if (random_mac)
+ eth_hw_addr_random(dev);
+ else
+ eth_hw_addr_set(dev, mac_addr);
/* The IOC3-specific entries in the device structure. */
dev->watchdog_timeo = 5 * HZ;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 39/53] MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (37 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 38/53] net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address Imre Kaloz
@ 2026-10-13 13:16 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 40/53] MIPS: SGI-IP27: report a Bedrock machine as SGI IP35 Imre Kaloz
` (14 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
On an XBridge the b_nic register's 1-wire bit-bang path reads back a
constant mcr, so a sgi_w1 master registered there enumerates nothing but
phantom slave IDs. Move the w1 registration into bridge_w1_create() and
call it for classic Bridges only. On IP35 the L1 supplies the board part
number and MAC address instead.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-xtalk.c | 41 +++++++++++++++++++++++++--------
1 file changed, 31 insertions(+), 10 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index 73b2eef96bdb..091186cb7a04 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -31,13 +31,10 @@ enum xtalk_bridge_type {
XTALK_PIC_BUS1,
};
-static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
- enum xtalk_bridge_type type)
+static struct platform_device *bridge_w1_create(nasid_t nasid, int widget)
{
- struct xtalk_bridge_platform_data *bd;
struct sgi_w1_platform_data *wd;
struct platform_device *pdev_wd;
- struct platform_device *pdev_bd;
struct resource w1_res;
unsigned long offset;
@@ -46,7 +43,7 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
wd = kzalloc_obj(*wd);
if (!wd) {
pr_warn("xtalk:n%d/%x bridge create out of memory\n", nasid, widget);
- return;
+ return NULL;
}
snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
@@ -77,6 +74,35 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
}
/* platform_device_add_data() duplicates the data */
kfree(wd);
+ return pdev_wd;
+
+err_put_pdev_wd:
+ platform_device_put(pdev_wd);
+err_kfree_wd:
+ kfree(wd);
+ return NULL;
+}
+
+static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
+ enum xtalk_bridge_type type)
+{
+ struct xtalk_bridge_platform_data *bd;
+ struct platform_device *pdev_wd = NULL;
+ struct platform_device *pdev_bd;
+ unsigned long offset;
+
+ offset = NODE_OFFSET(nasid);
+
+ /*
+ * An XBridge's b_nic register reads back a constant mcr on the
+ * 1-wire bit-bang path, so a sgi_w1 master there enumerates only
+ * phantom slave IDs. Only a classic Bridge gets one.
+ */
+ if (type == XTALK_BRIDGE) {
+ pdev_wd = bridge_w1_create(nasid, widget);
+ if (!pdev_wd)
+ return;
+ }
bd = kzalloc_obj(*bd);
if (!bd) {
@@ -139,11 +165,6 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
err_unregister_pdev_wd:
platform_device_unregister(pdev_wd);
return;
-err_put_pdev_wd:
- platform_device_put(pdev_wd);
-err_kfree_wd:
- kfree(wd);
- return;
}
static int probe_one_port(nasid_t nasid, int widget, int masterwid)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 40/53] MIPS: SGI-IP27: report a Bedrock machine as SGI IP35
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (38 preceding siblings ...)
2026-10-13 13:16 ` [PATCH v2 39/53] MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 41/53] MIPS: SGI-IP27: identify the machine by the L1 brick type Imre Kaloz
` (13 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
get_system_type() answers "SGI Origin" on either hub generation, but a
Fuel or Tezro is no Origin. Return "SGI IP35" on a Bedrock hub and in
the node banner.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-init.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 15811fb3cdee..c92f1bef692e 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -111,7 +111,8 @@ void __init plat_mem_setup(void)
* hub_rtc init and cpu clock intr enabled for later calibrate_delay.
*/
nid = get_nasid();
- printk("IP27: Running on node %d.\n", nid);
+ pr_info("%s: Running on node %d.\n",
+ system_is_ip35 ? "IP35" : "IP27", nid);
p = LOCAL_HUB_L(PI_CPU_PRESENT_A) & 1;
e = LOCAL_HUB_L(PI_CPU_ENABLE_A) & 1;
@@ -150,7 +151,7 @@ void __init plat_mem_setup(void)
const char *get_system_type(void)
{
- return "SGI Origin";
+ return system_is_ip35 ? "SGI IP35" : "SGI Origin";
}
static inline bool hub_is_bedrock(void)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 41/53] MIPS: SGI-IP27: identify the machine by the L1 brick type
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (39 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 40/53] MIPS: SGI-IP27: report a Bedrock machine as SGI IP35 Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 42/53] MIPS: SGI-IP27: register the Odyssey graphics widget Imre Kaloz
` (12 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
On a Bedrock hub, name the machine after the brick-type code the L1
reports for its chassis. Attached graphics makes an Origin an Onyx, so
a graphics-class KLCONFIG board picks between the two names. An
unlisted code keeps "SGI IP35".
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/l1.h | 1 +
arch/mips/sgi-ip27/ip27-init.c | 68 ++++++++++++++++++++++++++++++++++
arch/mips/sgi-ip27/ip27-l1.c | 45 ++++++++++++++++++++++
3 files changed, 114 insertions(+)
diff --git a/arch/mips/include/asm/sn/l1.h b/arch/mips/include/asm/sn/l1.h
index 1a3b7fa0e213..f9fa8a8051e6 100644
--- a/arch/mips/include/asm/sn/l1.h
+++ b/arch/mips/include/asm/sn/l1.h
@@ -17,5 +17,6 @@
int l1_exec_command(const char *cmd);
int l1_read_board_partnum(unsigned int component, char *buf, size_t len);
+int l1_read_brick_type(void);
#endif /* __ASM_SN_L1_H */
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index c92f1bef692e..7f16d7f437c9 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -13,12 +13,15 @@
#include <linux/mm.h>
#include <linux/export.h>
#include <linux/cpumask.h>
+#include <linux/ctype.h>
#include <asm/bootinfo.h>
#include <asm/cpu.h>
#include <asm/io.h>
+#include <asm/prom.h>
#include <asm/sgialib.h>
#include <asm/time.h>
#include <asm/sn/agent.h>
+#include <asm/sn/l1.h>
#include <asm/sn/types.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/ioc3.h>
@@ -98,6 +101,69 @@ void per_cpu_init(void)
enable_percpu_irq(IP27_HUB_PEND1_IRQ, IRQ_TYPE_NONE);
}
+/*
+ * The boot brick's L1 type character names the machine family. What makes
+ * an Origin an Onyx is attached graphics, so a graphics-class board in the
+ * KLCONFIG picks between the two names; gfx_model is NULL for a family
+ * that has only one.
+ */
+static const struct {
+ char brick_type;
+ const char *model;
+ const char *gfx_model;
+} ip27_models[] = {
+ { '4', "SGI Origin 300", "SGI Onyx 300" },
+ { '7', "SGI Origin 350", "SGI Onyx 350" },
+ { '8', "SGI Onyx 350", NULL }, /* graphics blade */
+ { '9', "SGI Tezro", NULL }, /* deskside */
+ { 'A', "SGI Fuel", NULL },
+ { 'C', "SGI Origin 3000", "SGI Onyx 3000" },
+ { 'Q', "SGI Tezro Rackmount", NULL },
+};
+
+static bool __init ip27_has_graphics(void)
+{
+ nasid_t nasid;
+
+ for_each_online_node(nasid)
+ if (find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
+ KLCLASS_GFX))
+ return true;
+
+ return false;
+}
+
+static void __init ip27_set_machine_name(void)
+{
+ int brick_type, i;
+ bool gfx;
+
+ if (!system_is_ip35)
+ return;
+
+ brick_type = l1_read_brick_type();
+ if (brick_type < 0)
+ return;
+
+ gfx = ip27_has_graphics();
+
+ for (i = 0; i < ARRAY_SIZE(ip27_models); i++) {
+ if (ip27_models[i].brick_type != brick_type)
+ continue;
+
+ if (gfx && ip27_models[i].gfx_model)
+ mips_set_machine_name(ip27_models[i].gfx_model);
+ else
+ mips_set_machine_name(ip27_models[i].model);
+ return;
+ }
+
+ mips_set_machine_name(get_system_type());
+ pr_info("Unlisted L1 brick type 0x%02x '%c', %s a graphics board\n",
+ brick_type, isprint(brick_type) ? brick_type : '?',
+ gfx ? "with" : "without");
+}
+
void __init plat_mem_setup(void)
{
u64 p, e, n_mode;
@@ -147,6 +213,8 @@ void __init plat_mem_setup(void)
ioport_resource.start = 0;
ioport_resource.end = ~0UL;
set_io_port_base(IO_BASE);
+
+ ip27_set_machine_name();
}
const char *get_system_type(void)
diff --git a/arch/mips/sgi-ip27/ip27-l1.c b/arch/mips/sgi-ip27/ip27-l1.c
index 173a506595da..98c94d14bd5f 100644
--- a/arch/mips/sgi-ip27/ip27-l1.c
+++ b/arch/mips/sgi-ip27/ip27-l1.c
@@ -79,6 +79,7 @@
#define L1_REQ_EXEC_CMD 0x0000 /* interpret plaintext command */
/* L1_TASK_GENERAL requests */
+#define L1_REQ_BRICK_ID 0x0003 /* rack, slot and brick type */
#define L1_REQ_EEPROM 0x0006 /* access eeprom */
/* L1_REQ_EEPROM additional argument values (non C-brick component) */
@@ -733,6 +734,50 @@ int l1_read_board_partnum(unsigned int component, char *buf, size_t len)
return 0;
}
+/*
+ * The L1 answers two integers; the low byte of the second is the
+ * one-character brick-type code of its chassis.
+ */
+static int l1_get_brick_type(void)
+{
+ u32 address = L1_ADDRESS(L1_TYPE_L1,
+ L1_ADDRESS_LOCAL | L1_TASK_GENERAL);
+ u8 pkt[64]; /* command and response packet buffer */
+ u8 *args;
+ size_t arglen;
+ u32 rack_slot, brick_type;
+ int rc;
+
+ rc = l1_transact(get_nasid(), pkt, sizeof(pkt), address,
+ L1_REQ_BRICK_ID, &args, &arglen, 0);
+ if (rc < 0)
+ return rc;
+ if (rc != 2)
+ return -EIO;
+
+ rc = l1_packet_get_int(&args, &arglen, &rack_slot);
+ if (rc)
+ return rc;
+
+ rc = l1_packet_get_int(&args, &arglen, &brick_type);
+ if (rc)
+ return rc;
+
+ return brick_type & 0xff;
+}
+
+int l1_read_brick_type(void)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&l1_lock, flags);
+ rc = l1_get_brick_type();
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ return rc;
+}
+
static u8 ip27_l1_mac_addr[ETH_ALEN];
static bool ip27_l1_mac_addr_valid;
static bool ip27_l1_mac_addr_tried;
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 42/53] MIPS: SGI-IP27: register the Odyssey graphics widget
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (40 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 41/53] MIPS: SGI-IP27: identify the machine by the L1 brick type Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 43/53] MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35 Imre Kaloz
` (11 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
The Odyssey (VPro) graphics widget is a direct xtalk widget with no PCI
bridge in front of it, so it needs a platform_device of its own rather
than piggybacking on bridge_platform_create(). Its register aperture is
the widget's small window, and the sgi-odyssey DRM driver binds it.
The driver programs the board with the widget id of its master hub,
passed as the u32 property "sgi,master-widget".
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-xtalk.c | 39 +++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/mips/sgi-ip27/ip27-xtalk.c b/arch/mips/sgi-ip27/ip27-xtalk.c
index 091186cb7a04..b56e70db20cf 100644
--- a/arch/mips/sgi-ip27/ip27-xtalk.c
+++ b/arch/mips/sgi-ip27/ip27-xtalk.c
@@ -7,9 +7,11 @@
* Generic XTALK initialization code
*/
+#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/platform_data/sgi-w1.h>
#include <linux/platform_data/xtalk-bridge.h>
#include <asm/sn/addrs.h>
@@ -24,6 +26,9 @@
#define PXBOW_WIDGET_PART_NUM 0xd100 /* PXbow */
#define BASE_XBOW_PORT 8 /* Lowest external port */
+/* Odyssey's register aperture, as sized by the sgi-odyssey driver. */
+#define ODYSSEY_REG_SIZE 0x00410000
+
enum xtalk_bridge_type {
XTALK_BRIDGE,
XTALK_XBRIDGE,
@@ -167,6 +172,37 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
return;
}
+static void odyssey_platform_create(nasid_t nasid, int widget, int masterwid)
+{
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_U32("sgi,master-widget", masterwid),
+ { }
+ };
+ struct resource res;
+ struct platform_device_info info = {
+ .name = "sgi-odyssey",
+ .id = PLATFORM_DEVID_AUTO,
+ .res = &res,
+ .num_res = 1,
+ .properties = props,
+ };
+ struct platform_device *pdev;
+
+ memset(&res, 0, sizeof(res));
+ res.name = "sgi-odyssey";
+ res.start = NODE_OFFSET(nasid) + (widget << SWIN_SIZE_BITS);
+ res.end = res.start + ODYSSEY_REG_SIZE - 1;
+ res.flags = IORESOURCE_MEM;
+
+ pr_info("xtalk:n%d/%x odyssey widget: reg=[%pa-%pa]\n",
+ nasid, widget, &res.start, &res.end);
+
+ pdev = platform_device_register_full(&info);
+ if (IS_ERR(pdev))
+ pr_warn("xtalk:n%d/%x odyssey platform device registration failed: %ld\n",
+ nasid, widget, PTR_ERR(pdev));
+}
+
static int probe_one_port(nasid_t nasid, int widget, int masterwid)
{
widgetreg_t widget_id;
@@ -190,6 +226,9 @@ static int probe_one_port(nasid_t nasid, int widget, int masterwid)
bridge_platform_create(nasid, widget, masterwid,
XTALK_PIC_BUS1);
break;
+ case WIDGET_ODYS_PART_NUM:
+ odyssey_platform_create(nasid, widget, masterwid);
+ break;
default:
pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
nasid, widget, partnum);
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 43/53] MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (41 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 42/53] MIPS: SGI-IP27: register the Odyssey graphics widget Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 44/53] MIPS: SGI-IP27: give each CPU its own topology package id Imre Kaloz
` (10 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
An IP35 PROM records its console UART and baud rate in the KLCONFIG
header. That UART is ttyS0 on Fuel and Tezro, so when the record is
present and the command line names no console, prefer ttyS0 at that baud
rate.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-init.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index 7f16d7f437c9..e657155ef7c9 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -6,6 +6,7 @@
* Copyright (C) 2000 - 2001 by Kanoj Sarcar (kanoj@sgi.com)
* Copyright (C) 2000 - 2001 by Silicon Graphics, Inc.
*/
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
@@ -164,6 +165,26 @@ static void __init ip27_set_machine_name(void)
gfx ? "with" : "without");
}
+/*
+ * Prefer ttyS0 at the baud rate the IP35 PROM recorded for its console
+ * UART in the KLCONFIG header.
+ */
+static void __init ip27_setup_console(void)
+{
+ static char options[8];
+ console_t *cons;
+
+ if (!system_is_ip35 || strstr(arcs_cmdline, "console="))
+ return;
+
+ cons = KL_CONFIG_CH_CONS_INFO(master_nasid);
+ if (!cons->uart_base)
+ return;
+
+ snprintf(options, sizeof(options), "%u", (u16)cons->baud);
+ add_preferred_console("ttyS", 0, options);
+}
+
void __init plat_mem_setup(void)
{
u64 p, e, n_mode;
@@ -215,6 +236,7 @@ void __init plat_mem_setup(void)
set_io_port_base(IO_BASE);
ip27_set_machine_name();
+ ip27_setup_console();
}
const char *get_system_type(void)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 44/53] MIPS: SGI-IP27: give each CPU its own topology package id
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (42 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 43/53] MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35 Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 45/53] ata: add pata_sgiioc4 driver for the SGI IOC4 ATA/ATAPI controller Imre Kaloz
` (9 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
cpu_data[].package is left at its zero-init default, so /proc/cpuinfo
and cpu_core_map treat every CPU in the system as one package, though
each PI slice is a separate physical processor. Derive it from the
klconfig nasid and physid node_scan_cpus() already records as the
CPU's slice.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-smp.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/mips/sgi-ip27/ip27-smp.c b/arch/mips/sgi-ip27/ip27-smp.c
index 13b85e3ba311..7739ac9618b8 100644
--- a/arch/mips/sgi-ip27/ip27-smp.c
+++ b/arch/mips/sgi-ip27/ip27-smp.c
@@ -47,6 +47,13 @@ static int node_scan_cpus(nasid_t nasid, int highest)
set_cpu_possible(cpuid, true);
cputonasid(cpus_found) = nasid;
cputoslice(cpus_found) = acpu->cpu_info.physid;
+ /*
+ * Each PI slice is a separate physical CPU
+ * package; these processors have no on-die
+ * multiprocessing, so core is 0.
+ */
+ cpu_data[cpus_found].package =
+ (nasid << 2) | acpu->cpu_info.physid;
sn_cpu_info[cpus_found].p_speed =
acpu->cpu_speed;
cpus_found++;
@@ -85,6 +92,7 @@ void cpu_node_probe(void)
if (cputonasid(i) == master_nasid &&
cputoslice(i) == LOCAL_HUB_L(PI_CPU_NUM)) {
swap(sn_cpu_info[0], sn_cpu_info[i]);
+ swap(cpu_data[0].package, cpu_data[i].package);
break;
}
}
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 45/53] ata: add pata_sgiioc4 driver for the SGI IOC4 ATA/ATAPI controller
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (43 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 44/53] MIPS: SGI-IP27: give each CPU its own topology package id Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 46/53] mfd: ioc3: Add IOC4 ATA/ATAPI cell Imre Kaloz
` (8 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Damien Le Moal,
Niklas Cassel, linux-ide
The IOC4 on Origin 350-class I-brick boards drives the DVD-ROM from an
ATA/ATAPI controller at BAR0+0x100. drivers/ide/sgiioc4.c went away in
v5.4.
Add a libata driver bound to an IOC4 MFD cell, with PIO4 and multiword
DMA mode 2. Each taskfile register sits at the lowest address of its
own 32-bit slot; the IDE driver's "+3" byte-lane correction reads
zeros there.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/ata/Kconfig | 10 ++
drivers/ata/Makefile | 1 +
drivers/ata/pata_sgiioc4.c | 311 +++++++++++++++++++++++++++++++++++++
3 files changed, 322 insertions(+)
create mode 100644 drivers/ata/pata_sgiioc4.c
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 28ca856ecc75..704414501f35 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -940,6 +940,16 @@ config PATA_SERVERWORKS
If unsure, say N.
+config PATA_SGIIOC4
+ tristate "SGI IOC4 ATA/ATAPI support"
+ depends on SGI_MFD_IOC3
+ help
+ This option enables support for the ATA/ATAPI controller
+ integrated into SGI's IOC4 I/O ASIC, found on Tezro/Origin
+ 350-class I-brick system boards.
+
+ If unsure, say N.
+
config PATA_SIL680
tristate "CMD / Silicon Image 680 PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index b96025abd45e..f2947a2b86be 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_PATA_RDC) += pata_rdc.o
obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o
obj-$(CONFIG_PATA_SCH) += pata_sch.o
obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
+obj-$(CONFIG_PATA_SGIIOC4) += pata_sgiioc4.o
obj-$(CONFIG_PATA_SIL680) += pata_sil680.o
obj-$(CONFIG_PATA_SIS) += pata_sis.o
obj-$(CONFIG_PATA_TOSHIBA) += pata_piccolo.o
diff --git a/drivers/ata/pata_sgiioc4.c b/drivers/ata/pata_sgiioc4.c
new file mode 100644
index 000000000000..454c43c6b6c7
--- /dev/null
+++ b/drivers/ata/pata_sgiioc4.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * libata driver for the ATA/ATAPI controller in SGI's IOC4 I/O ASIC.
+ *
+ * Copyright (C) 1999-2004 Silicon Graphics, Inc.
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#include <linux/ata.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/libata.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/string.h>
+
+#include <asm/sn/ioc4.h>
+
+#define DRV_NAME "pata_sgiioc4"
+
+#define IOC4_ATA_CTRL_OFFSET offsetof(struct ioc4_ata_regs, ctrl)
+
+/* The ATA controller needs IOC4 firmware revision 46 or later. */
+#define IOC4_ATA_MIN_REV 46
+
+/* DMA engine registers, as offsets into the ATA block */
+#define IOC4_DMA_REG(n) offsetof(struct ioc4_ata_regs, dma[n])
+#define IOC4_DMA_PTR_L IOC4_DMA_REG(1) /* descriptor list */
+#define IOC4_DMA_PTR_H IOC4_DMA_REG(2)
+#define IOC4_DMA_ADDR_L IOC4_DMA_REG(3) /* current memory address */
+#define IOC4_DMA_ADDR_H IOC4_DMA_REG(4)
+#define IOC4_DMA_BC_DEV IOC4_DMA_REG(5)
+#define IOC4_DMA_BC_MEM IOC4_DMA_REG(6)
+#define IOC4_DMA_CTRL IOC4_DMA_REG(7)
+#define IOC4_DMA_END IOC4_DMA_REG(8) /* completion area */
+
+#define IOC4_DMA_START BIT(0)
+#define IOC4_DMA_STOP BIT(1) /* reads 1 until the engine stops */
+#define IOC4_DMA_TO_MEM BIT(2)
+#define IOC4_DMA_ACTIVE BIT(3)
+#define IOC4_DMA_MEMERR BIT(4)
+
+#define IOC4_DMA_STOP_TIMEOUT_US 200
+
+/* Descriptors are big-endian; the last one has bit 31 set in its count. */
+struct ioc4_ata_prd {
+ __be64 addr;
+ __be64 count;
+};
+
+#define IOC4_PRD_EOL 0x80000000
+#define IOC4_PRD_ENTRIES ATA_MAX_PRD
+#define IOC4_PRD_SIZE (IOC4_PRD_ENTRIES * sizeof(struct ioc4_ata_prd))
+
+/*
+ * The count field holds at most 0xfffe bytes, so a whole 64 KiB segment
+ * does not fit one descriptor. Splitting at 32 KiB boundaries keeps
+ * libata's segments within two descriptors each.
+ */
+#define IOC4_PRD_CHUNK SZ_32K
+
+/*
+ * After the last data of a transfer the engine writes nonzero bytes into
+ * a 128-byte, 128-byte aligned area. Register reads are not ordered
+ * against its data writes, so the data is valid only once this area
+ * changes.
+ */
+#define IOC4_DMAEND_SIZE 128
+#define IOC4_DMAEND_TIMEOUT_US 1000
+
+struct ioc4_ata_port {
+ struct ioc4_ata_prd *prd;
+ void *dmaend;
+ dma_addr_t prd_dma;
+ dma_addr_t dmaend_dma;
+ u32 dir;
+};
+
+static void ioc4_ata_dma_halt(struct ata_port *ap)
+{
+ void __iomem *base = ap->ioaddr.cmd_addr;
+ u32 val;
+
+ writel(IOC4_DMA_STOP, base + IOC4_DMA_CTRL);
+ if (readl_poll_timeout_atomic(base + IOC4_DMA_CTRL, val,
+ !(val & IOC4_DMA_STOP), 1,
+ IOC4_DMA_STOP_TIMEOUT_US))
+ ata_port_warn(ap, "DMA engine did not stop\n");
+}
+
+static enum ata_completion_errors ioc4_ata_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct ioc4_ata_port *pp = qc->ap->private_data;
+ struct scatterlist *sg;
+ unsigned int si, pi = 0;
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return AC_ERR_OK;
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ dma_addr_t addr = sg_dma_address(sg);
+ u32 sg_len = sg_dma_len(sg);
+
+ while (sg_len) {
+ u32 len = min_t(u32, sg_len, IOC4_PRD_CHUNK -
+ (addr & (IOC4_PRD_CHUNK - 1)));
+
+ pp->prd[pi].addr = cpu_to_be64(addr);
+ pp->prd[pi].count = cpu_to_be64(len);
+ pi++;
+ addr += len;
+ sg_len -= len;
+ }
+ }
+ pp->prd[pi - 1].count |= cpu_to_be64(IOC4_PRD_EOL);
+
+ return AC_ERR_OK;
+}
+
+static void ioc4_ata_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ioc4_ata_port *pp = ap->private_data;
+ void __iomem *base = ap->ioaddr.cmd_addr;
+
+ /* A failed transfer can leave the engine running or an error latched. */
+ if (readl(base + IOC4_DMA_CTRL) & (IOC4_DMA_ACTIVE | IOC4_DMA_MEMERR))
+ ioc4_ata_dma_halt(ap);
+
+ writel(lower_32_bits(pp->prd_dma), base + IOC4_DMA_PTR_L);
+ writel(upper_32_bits(pp->prd_dma), base + IOC4_DMA_PTR_H);
+
+ memset(pp->dmaend, 0, IOC4_DMAEND_SIZE);
+ writel(lower_32_bits(pp->dmaend_dma), base + IOC4_DMA_END);
+
+ pp->dir = qc->tf.flags & ATA_TFLAG_WRITE ? 0 : IOC4_DMA_TO_MEM;
+
+ ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void ioc4_ata_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct ioc4_ata_port *pp = ap->private_data;
+
+ writel(pp->dir | IOC4_DMA_START, ap->ioaddr.cmd_addr + IOC4_DMA_CTRL);
+}
+
+static void ioc4_ata_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ ioc4_ata_dma_halt(qc->ap);
+}
+
+/*
+ * The engine raises no completion interrupt; the port interrupt is
+ * the drive's. Once the drive has finished without error, wait for the
+ * completion area before the data is handed back.
+ */
+static u8 ioc4_ata_bmdma_status(struct ata_port *ap)
+{
+ struct ioc4_ata_port *pp = ap->private_data;
+ u32 ctrl = readl(ap->ioaddr.cmd_addr + IOC4_DMA_CTRL);
+ u8 status = ATA_DMA_INTR;
+ void *seen;
+
+ if (ctrl & IOC4_DMA_ACTIVE)
+ status |= ATA_DMA_ACTIVE;
+
+ if (ctrl & IOC4_DMA_MEMERR)
+ return status | ATA_DMA_ERR;
+
+ if (ioread8(ap->ioaddr.altstatus_addr) & (ATA_BUSY | ATA_DRQ | ATA_ERR))
+ return status;
+
+ if (read_poll_timeout_atomic(memchr_inv, seen, seen, 1,
+ IOC4_DMAEND_TIMEOUT_US, false,
+ pp->dmaend, 0, IOC4_DMAEND_SIZE))
+ return status | ATA_DMA_ERR;
+ dma_rmb();
+
+ return status;
+}
+
+static int ioc4_ata_port_start(struct ata_port *ap)
+{
+ struct device *dev = ap->host->dev;
+ struct ioc4_ata_port *pp;
+
+ pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
+ if (!pp)
+ return -ENOMEM;
+
+ /* The page-aligned list leaves the completion area 128-byte aligned. */
+ pp->prd = dmam_alloc_coherent(dev, IOC4_PRD_SIZE + IOC4_DMAEND_SIZE,
+ &pp->prd_dma, GFP_KERNEL);
+ if (!pp->prd)
+ return -ENOMEM;
+
+ pp->dmaend = (void *)pp->prd + IOC4_PRD_SIZE;
+ pp->dmaend_dma = pp->prd_dma + IOC4_PRD_SIZE;
+ ap->private_data = pp;
+
+ return 0;
+}
+
+static struct ata_port_operations ioc4_ata_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+
+ .cable_detect = ata_cable_40wire,
+
+ /* No .set_piomode: what the ATA timing register does is unknown. */
+
+ .qc_prep = ioc4_ata_qc_prep,
+ .bmdma_setup = ioc4_ata_bmdma_setup,
+ .bmdma_start = ioc4_ata_bmdma_start,
+ .bmdma_stop = ioc4_ata_bmdma_stop,
+ .bmdma_status = ioc4_ata_bmdma_status,
+ .port_start = ioc4_ata_port_start,
+};
+
+static const struct scsi_host_template ioc4_ata_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static int ioc4_ata_probe(struct platform_device *pdev)
+{
+ struct ata_port_info pinfo = {
+ .flags = ATA_FLAG_SLAVE_POSS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2_ONLY,
+ .port_ops = &ioc4_ata_port_ops,
+ };
+ const struct ata_port_info *ppi[] = { &pinfo, NULL };
+ struct pci_dev *pci_parent;
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct resource *res;
+ void __iomem *base;
+ int irq, ret;
+
+ if (!dev_is_pci(pdev->dev.parent))
+ return -EINVAL;
+ pci_parent = to_pci_dev(pdev->dev.parent);
+
+ if (pci_parent->revision < IOC4_ATA_MIN_REV) {
+ dev_err(&pdev->dev,
+ "IOC4 firmware revision %u is not supported, %u or later is required\n",
+ pci_parent->revision, IOC4_ATA_MIN_REV);
+ return -ENODEV;
+ }
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /*
+ * The cell inherits its parent's 64-bit DMA mask, but the engine
+ * takes 32-bit bus addresses only.
+ */
+ ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 1);
+ if (!host)
+ return -ENOMEM;
+
+ ap = host->ports[0];
+
+ /*
+ * Each taskfile register has a 32-bit slot of its own and sits at the
+ * slot's lowest address, with no byte-lane correction.
+ */
+ ap->ioaddr.cmd_addr = base;
+ ap->ioaddr.data_addr = base + (ATA_REG_DATA << 2);
+ ap->ioaddr.error_addr = base + (ATA_REG_ERR << 2);
+ ap->ioaddr.feature_addr = ap->ioaddr.error_addr;
+ ap->ioaddr.nsect_addr = base + (ATA_REG_NSECT << 2);
+ ap->ioaddr.lbal_addr = base + (ATA_REG_LBAL << 2);
+ ap->ioaddr.lbam_addr = base + (ATA_REG_LBAM << 2);
+ ap->ioaddr.lbah_addr = base + (ATA_REG_LBAH << 2);
+ ap->ioaddr.device_addr = base + (ATA_REG_DEVICE << 2);
+ ap->ioaddr.status_addr = base + (ATA_REG_STATUS << 2);
+ ap->ioaddr.command_addr = ap->ioaddr.status_addr;
+ ap->ioaddr.altstatus_addr = base + IOC4_ATA_CTRL_OFFSET;
+ ap->ioaddr.ctl_addr = ap->ioaddr.altstatus_addr;
+
+ ata_port_desc(ap, "cmd 0x%llx", (unsigned long long)res->start);
+
+ return ata_host_activate(host, irq, ata_bmdma_interrupt, IRQF_SHARED,
+ &ioc4_ata_sht);
+}
+
+static struct platform_driver ioc4_ata_driver = {
+ .probe = ioc4_ata_probe,
+ .remove = ata_platform_remove_one,
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+module_platform_driver(ioc4_ata_driver);
+
+MODULE_AUTHOR("Imre Kaloz <kaloz@kernel.org>");
+MODULE_DESCRIPTION("SGI IOC4 ATA/ATAPI libata driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 46/53] mfd: ioc3: Add IOC4 ATA/ATAPI cell
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (44 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 45/53] ata: add pata_sgiioc4 driver for the SGI IOC4 ATA/ATAPI controller Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver Imre Kaloz
` (7 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, mfd
Add an IOC4 cell for pata_sgiioc4: a DEFINE_RES_MEM() over struct
ioc4_ata_regs and a DEFINE_RES_IRQ() for other_ir bit 0
(IOC4_OTHER_IR_ATA_INT). It joins ioc4_cells[] between the kbd and rtc
cells.
The ATA passthrough bit follows the drive's INTRQ line, which stays
asserted until Status is read, unlike the discrete events on the rest
of other_ir. Make it the one level-triggered bit of the other_ir bank.
libata reads Status while servicing the interrupt, so ioc3_irq_ack()'s
W1C write is the only ack needed.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/ioc4.h | 1 +
drivers/mfd/ioc3.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/arch/mips/include/asm/sn/ioc4.h b/arch/mips/include/asm/sn/ioc4.h
index 781fcfb81b5e..d23676ae82b4 100644
--- a/arch/mips/include/asm/sn/ioc4.h
+++ b/arch/mips/include/asm/sn/ioc4.h
@@ -129,6 +129,7 @@ struct ioc4 {
#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* generic pins */
#define IOC4_OTHER_IR_KBD_INT_BIT 6 /* bit index */
+#define IOC4_OTHER_IR_ATA_INT_BIT 0 /* bit index */
#define IOC4_SSCR_DMA_EN 0x10000000 /* enable ring buffer DMA */
diff --git a/drivers/mfd/ioc3.c b/drivers/mfd/ioc3.c
index 01c919c18b06..b471c90e737c 100644
--- a/drivers/mfd/ioc3.c
+++ b/drivers/mfd/ioc3.c
@@ -850,6 +850,13 @@ static const struct ioc3_variant ioc3_variant = {
(IOC4_SIO_IR_PORT_INT(0) | IOC4_SIO_IR_PORT_INT(1) | \
IOC4_SIO_IR_PORT_INT(2) | IOC4_SIO_IR_PORT_INT(3))
+/*
+ * In other_ir only ATA passthrough is level triggered. It follows the
+ * drive's INTRQ line, which stays asserted until the host reads Status;
+ * the other other_ir bits are discrete events.
+ */
+#define IOC4_OTHER_LVL_MASK IOC4_OTHER_IR_ATA_INT
+
#define IOC4_UART_RES(port) { \
DEFINE_RES_MEM(offsetof(struct ioc4, uart[port]), \
sizeof_field(struct ioc4, uart[port])), \
@@ -869,6 +876,12 @@ static const struct resource ioc4_kbd_resources[] = {
DEFINE_RES_IRQ(IOC4_IRQ_OTHER_BASE + IOC4_OTHER_IR_KBD_INT_BIT)
};
+static const struct resource ioc4_ata_resources[] = {
+ DEFINE_RES_MEM(offsetof(struct ioc4, ata),
+ sizeof_field(struct ioc4, ata)),
+ DEFINE_RES_IRQ(IOC4_IRQ_OTHER_BASE + IOC4_OTHER_IR_ATA_INT_BIT)
+};
+
static const struct resource ioc4_rtc_resources[] = {
DEFINE_RES_MEM(IOC4_BYTEBUS_DEV0, M48T35_REG_SIZE)
};
@@ -898,6 +911,11 @@ static const struct mfd_cell ioc4_cells[] = {
.resources = ioc4_kbd_resources,
.num_resources = ARRAY_SIZE(ioc4_kbd_resources),
},
+ {
+ .name = "pata_sgiioc4",
+ .resources = ioc4_ata_resources,
+ .num_resources = ARRAY_SIZE(ioc4_ata_resources),
+ },
{
.name = "rtc-m48t35-ip35",
.resources = ioc4_rtc_resources,
@@ -974,6 +992,7 @@ static const struct ioc3_irq_bank ioc4_irq_banks[] = {
.ir = offsetof(struct ioc4, other_ir),
.ies = offsetof(struct ioc4, other_ies),
.iec = offsetof(struct ioc4, other_iec),
+ .lvl_mask = IOC4_OTHER_LVL_MASK,
},
};
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (45 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 46/53] mfd: ioc3: Add IOC4 ATA/ATAPI cell Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:25 ` Damien Le Moal
2026-10-13 13:17 ` [PATCH v2 48/53] MIPS: ip27_defconfig: update for the onboard hardware Imre Kaloz
` (6 subsequent siblings)
53 siblings, 1 reply; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Damien Le Moal,
Niklas Cassel, linux-ide
Add an entry for drivers/ata/pata_sgiioc4.c, the libata driver for the
IOC4 ATA/ATAPI controller.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index a30c09a55c72..826f0ffb71fd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24925,6 +24925,12 @@ F: include/linux/phylink.h
F: include/linux/sfp.h
K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
+SGI IOC4 ATA DRIVER
+M: Imre Kaloz <kaloz@kernel.org>
+L: linux-ide@vger.kernel.org
+S: Maintained
+F: drivers/ata/pata_sgiioc4.c
+
SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
M: D. Wythe <alibuda@linux.alibaba.com>
M: Dust Li <dust.li@linux.alibaba.com>
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 48/53] MIPS: ip27_defconfig: update for the onboard hardware
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (46 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 49/53] MIPS: ip27_defconfig: enable pata_sgiioc4 and the DVD-ROM path Imre Kaloz
` (5 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Drop add-in card and SoC drivers no IP27 or IP35 machine carries
(wireless, PCI NICs and PHYs, FC/iSCSI/SAS HBAs, UIO drivers, I2C), the
OMFS filesystem, and options for removed symbols or with unmet
dependencies. Regenerate the file with make savedefconfig.
Enable USB host, storage and HID for the onboard USB of the Fuel, Tezro
and Origin 300, and input for the IOC3 PS/2 ports. Set NETDEVICES,
which only the iSCSI drivers selected.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/configs/ip27_defconfig | 177 +++----------------------------
1 file changed, 13 insertions(+), 164 deletions(-)
diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 4c8abe4611b8..2dfa929a6553 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -10,10 +10,7 @@ CONFIG_CPUSETS=y
CONFIG_RELAY=y
CONFIG_EXPERT=y
CONFIG_SGI_IP27=y
-CONFIG_NUMA=y
-CONFIG_SMP=y
CONFIG_HZ_1000=y
-CONFIG_PCI=y
CONFIG_MIPS32_O32=y
CONFIG_MIPS32_N32=y
CONFIG_PM=y
@@ -40,7 +37,6 @@ CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
CONFIG_INET6_IPCOMP=m
CONFIG_IPV6_MIP6=m
-CONFIG_IPV6_SIT=m
CONFIG_IPV6_SIT_6RD=y
CONFIG_IPV6_TUNNEL=m
CONFIG_IPV6_MULTIPLE_TABLES=y
@@ -49,7 +45,6 @@ CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_PIMSM_V2=y
CONFIG_NETWORK_SECMARK=y
CONFIG_NET_SCHED=y
-CONFIG_NET_SCH_CBQ=m
CONFIG_NET_SCH_HTB=m
CONFIG_NET_SCH_HFSC=m
CONFIG_NET_SCH_PRIO=m
@@ -59,7 +54,6 @@ CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TEQL=m
CONFIG_NET_SCH_TBF=m
CONFIG_NET_SCH_GRED=m
-CONFIG_NET_SCH_DSMARK=m
CONFIG_NET_SCH_NETEM=m
CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_CLS_BASIC=m
@@ -67,8 +61,6 @@ CONFIG_NET_CLS_ROUTE4=m
CONFIG_NET_CLS_FW=m
CONFIG_NET_CLS_U32=m
CONFIG_CLS_U32_MARK=y
-CONFIG_NET_CLS_RSVP=m
-CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_FLOW=m
CONFIG_NET_CLS_CGROUP=y
CONFIG_NET_CLS_ACT=y
@@ -79,9 +71,7 @@ CONFIG_NET_ACT_MIRRED=m
CONFIG_NET_ACT_NAT=m
CONFIG_NET_ACT_PEDIT=m
CONFIG_NET_ACT_SKBEDIT=m
-CONFIG_CFG80211=m
-CONFIG_MAC80211=m
-CONFIG_RFKILL=m
+# CONFIG_VGA_ARB is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_ATA_OVER_ETH=m
CONFIG_SCSI=y
@@ -94,21 +84,8 @@ CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
-CONFIG_SCSI_CXGB3_ISCSI=m
-CONFIG_SCSI_BNX2_ISCSI=m
-CONFIG_BE2ISCSI=m
-CONFIG_SCSI_HPSA=m
-CONFIG_SCSI_3W_SAS=m
-CONFIG_SCSI_AIC94XX=m
-# CONFIG_AIC94XX_DEBUG is not set
-CONFIG_SCSI_MVSAS=m
-# CONFIG_SCSI_MVSAS_DEBUG is not set
CONFIG_SCSI_MPT2SAS=m
-CONFIG_LIBFC=m
CONFIG_SCSI_QLOGIC_1280=y
-CONFIG_SCSI_PMCRAID=m
-CONFIG_SCSI_BFA_FC=m
CONFIG_SCSI_DH=y
CONFIG_SCSI_DH_RDAC=m
CONFIG_SCSI_DH_HP_SW=m
@@ -121,8 +98,6 @@ CONFIG_MD_RAID0=y
CONFIG_MD_RAID1=y
CONFIG_MD_RAID10=m
CONFIG_MD_RAID456=y
-CONFIG_MD_MULTIPATH=m
-CONFIG_MD_FAULTY=m
CONFIG_BLK_DEV_DM=m
CONFIG_DM_CRYPT=m
CONFIG_DM_SNAPSHOT=m
@@ -133,146 +108,33 @@ CONFIG_DM_MULTIPATH=m
CONFIG_DM_MULTIPATH_QL=m
CONFIG_DM_MULTIPATH_ST=m
CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
CONFIG_IFB=m
CONFIG_MACVLAN=m
CONFIG_VETH=m
-CONFIG_ATL2=m
-CONFIG_ATL1E=m
-CONFIG_ATL1C=m
-CONFIG_B44=m
CONFIG_TIGON3=y
-CONFIG_BNX2X=m
-CONFIG_ENIC=m
-CONFIG_BE2NET=m
-CONFIG_E1000E=m
-CONFIG_IGB=m
-CONFIG_IGBVF=m
-CONFIG_IXGBE=m
-CONFIG_JME=m
-CONFIG_MLX4_EN=m
-# CONFIG_MLX4_DEBUG is not set
-CONFIG_KS8851_MLL=m
-CONFIG_AX88796=m
-CONFIG_AX88796_93CX6=y
-CONFIG_ETHOC=m
-CONFIG_QLA3XXX=m
-CONFIG_NETXEN_NIC=m
-CONFIG_SFC=m
CONFIG_SGI_IOC3_ETH=y
-CONFIG_SMC91X=m
-CONFIG_SMSC911X=m
-CONFIG_NIU=m
-CONFIG_TEHUTI=m
-CONFIG_VIA_VELOCITY=m
-CONFIG_PHYLIB=y
-CONFIG_CICADA_PHY=m
-CONFIG_DAVICOM_PHY=m
-CONFIG_ICPLUS_PHY=m
-CONFIG_LSI_ET1011C_PHY=m
-CONFIG_LXT_PHY=m
-CONFIG_MARVELL_PHY=m
-CONFIG_NATIONAL_PHY=m
-CONFIG_QSEMI_PHY=m
-CONFIG_REALTEK_PHY=m
-CONFIG_SMSC_PHY=m
-CONFIG_STE10XP=m
-CONFIG_VITESSE_PHY=m
-CONFIG_ADM8211=m
-CONFIG_ATH5K=m
-CONFIG_ATH9K=m
-CONFIG_ATMEL=m
-CONFIG_PCI_ATMEL=m
-CONFIG_B43=m
-CONFIG_B43LEGACY=m
-# CONFIG_B43LEGACY_DEBUG is not set
-CONFIG_IPW2100=m
-CONFIG_IPW2100_MONITOR=y
-CONFIG_IPW2100_DEBUG=y
-CONFIG_IPW2200=m
-CONFIG_IPW2200_MONITOR=y
-CONFIG_IPW2200_PROMISCUOUS=y
-CONFIG_IPW2200_QOS=y
-CONFIG_IPW2200_DEBUG=y
-CONFIG_IWL4965=m
-CONFIG_IWL3945=m
-CONFIG_IWLWIFI=m
-CONFIG_HOSTAP=m
-CONFIG_HOSTAP_FIRMWARE=y
-CONFIG_HOSTAP_FIRMWARE_NVRAM=y
-CONFIG_HOSTAP_PLX=m
-CONFIG_HOSTAP_PCI=m
-CONFIG_HERMES=m
-# CONFIG_HERMES_CACHE_FW_ON_INIT is not set
-CONFIG_PLX_HERMES=m
-CONFIG_TMD_HERMES=m
-CONFIG_NORTEL_HERMES=m
-CONFIG_P54_COMMON=m
-CONFIG_P54_PCI=m
-CONFIG_LIBERTAS=m
-CONFIG_LIBERTAS_THINFIRM=m
-CONFIG_MWL8K=m
-CONFIG_RT2X00=m
-CONFIG_RT2400PCI=m
-CONFIG_RT2500PCI=m
-CONFIG_RT61PCI=m
-CONFIG_RT2800PCI=m
-CONFIG_RTL8180=m
-CONFIG_WL1251=m
-CONFIG_WL12XX=m
-# CONFIG_INPUT is not set
+CONFIG_INPUT_EVDEV=m
CONFIG_SERIO_SGI_IOC3=m
-CONFIG_SERIO_LIBPS2=m
CONFIG_SERIO_RAW=m
-CONFIG_SERIO_ALTERA_PS2=m
# CONFIG_VT is not set
-CONFIG_NOZOMI=m
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
-CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_IOC3=y
-CONFIG_HW_RANDOM_TIMERIOMEM=m
-CONFIG_I2C_CHARDEV=m
-CONFIG_I2C_ALI1535=m
-CONFIG_I2C_ALI1563=m
-CONFIG_I2C_ALI15X3=m
-CONFIG_I2C_AMD756=m
-CONFIG_I2C_AMD8111=m
-CONFIG_I2C_I801=m
-CONFIG_I2C_ISCH=m
-CONFIG_I2C_PIIX4=m
-CONFIG_I2C_NFORCE2=m
-CONFIG_I2C_SIS5595=m
-CONFIG_I2C_SIS630=m
-CONFIG_I2C_SIS96X=m
-CONFIG_I2C_VIA=m
-CONFIG_I2C_VIAPRO=m
-CONFIG_I2C_OCORES=m
-CONFIG_I2C_PCA_PLATFORM=m
-CONFIG_I2C_SIMTEC=m
-CONFIG_I2C_TAOS_EVM=m
-CONFIG_I2C_STUB=m
CONFIG_W1=y
CONFIG_W1_MASTER_SGI=y
CONFIG_W1_SLAVE_DS250X=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
-# CONFIG_VGA_ARB is not set
-CONFIG_LEDS_LP3944=m
-CONFIG_LEDS_PCA955X=m
-CONFIG_LEDS_BD2802=m
-CONFIG_LEDS_TRIGGER_TIMER=m
-CONFIG_LEDS_TRIGGER_HEARTBEAT=m
-CONFIG_LEDS_TRIGGER_BACKLIGHT=m
-CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
CONFIG_SGI_MFD_IOC3=y
+CONFIG_USB=m
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_OHCI_HCD=m
+CONFIG_USB_STORAGE=m
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_M48T35=y
CONFIG_UIO=y
-CONFIG_UIO_AEC=m
-CONFIG_UIO_SERCOS3=m
-CONFIG_UIO_PCI_GENERIC=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
@@ -288,35 +150,22 @@ CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_FUSE_FS=m
CONFIG_CUSE=m
-CONFIG_NETFS_SUPPORT=m
-CONFIG_FSCACHE=y
-CONFIG_FSCACHE_STATS=y
-CONFIG_CACHEFILES=m
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_SQUASHFS=m
-CONFIG_OMFS_FS=m
CONFIG_NFS_FS=y
CONFIG_SECURITYFS=y
CONFIG_CRYPTO_CRYPTD=m
-CONFIG_CRYPTO_CTS=m
-CONFIG_CRYPTO_LRW=m
-CONFIG_CRYPTO_XTS=m
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_XCBC=m
-CONFIG_CRYPTO_MD4=m
-CONFIG_CRYPTO_RMD160=m
-CONFIG_CRYPTO_SHA512=m
-CONFIG_CRYPTO_WP512=m
-CONFIG_CRYPTO_ANUBIS=m
CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_CAMELLIA=m
CONFIG_CRYPTO_CAST5=m
CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_SEED=m
CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_LRW=m
+CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_LZO=m
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 49/53] MIPS: ip27_defconfig: enable pata_sgiioc4 and the DVD-ROM path
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (47 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 48/53] MIPS: ip27_defconfig: update for the onboard hardware Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 50/53] MIPS: SGI-IP27: add L1 front-panel LED and display requests Imre Kaloz
` (4 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones
Set CONFIG_ATA=y and CONFIG_PATA_SGIIOC4=y. Both are built in rather
than modular, because ip27_defconfig lacks BLK_DEV_INITRD, so no module
can load before root is mounted.
The I-brick's IOC4 ATA channel carries an ATAPI DVD-ROM drive. Change
CONFIG_BLK_DEV_SR from =m to =y for the same reason, and add
CONFIG_ISO9660_FS and CONFIG_UDF_FS so the disc can be mounted.
CONFIG_SCSI=y drops out of the diff: CONFIG_ATA selects SCSI, so make
savedefconfig removes the explicit line.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/configs/ip27_defconfig | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 2dfa929a6553..2959615d656b 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -74,10 +74,9 @@ CONFIG_NET_ACT_SKBEDIT=m
# CONFIG_VGA_ARB is not set
CONFIG_BLK_DEV_LOOP=y
CONFIG_ATA_OVER_ETH=m
-CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
-CONFIG_BLK_DEV_SR=m
+CONFIG_BLK_DEV_SR=y
CONFIG_CHR_DEV_SG=m
CONFIG_CHR_DEV_SCH=m
CONFIG_SCSI_CONSTANTS=y
@@ -91,6 +90,8 @@ CONFIG_SCSI_DH_RDAC=m
CONFIG_SCSI_DH_HP_SW=m
CONFIG_SCSI_DH_EMC=m
CONFIG_SCSI_DH_ALUA=m
+CONFIG_ATA=y
+CONFIG_PATA_SGIIOC4=y
CONFIG_MD=y
CONFIG_BLK_DEV_MD=y
CONFIG_MD_LINEAR=m
@@ -150,6 +151,8 @@ CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_FUSE_FS=m
CONFIG_CUSE=m
+CONFIG_ISO9660_FS=y
+CONFIG_UDF_FS=y
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 50/53] MIPS: SGI-IP27: add L1 front-panel LED and display requests
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (48 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 49/53] MIPS: ip27_defconfig: enable pata_sgiioc4 and the DVD-ROM path Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 51/53] leds: Add driver for the SGI Fuel and Tezro front-panel LEDs Imre Kaloz
` (3 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-leds
Add l1_set_leds(), which sets the Fuel and Tezro front-panel LEDs, and
l1_set_display(), which writes line 2 of the Tezro's L1 display. Both
can be called from a panic notifier.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/include/asm/sn/l1.h | 16 +++++++++
arch/mips/sgi-ip27/ip27-l1.c | 67 +++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/arch/mips/include/asm/sn/l1.h b/arch/mips/include/asm/sn/l1.h
index f9fa8a8051e6..42611fe51a3a 100644
--- a/arch/mips/include/asm/sn/l1.h
+++ b/arch/mips/include/asm/sn/l1.h
@@ -7,6 +7,7 @@
#ifndef __ASM_SN_L1_H
#define __ASM_SN_L1_H
+#include <linux/bits.h>
#include <linux/types.h>
/*
@@ -15,8 +16,23 @@
*/
#define L1_BOARD_COMPONENT_MAX 8
+/*
+ * Front-panel LEDs of a Fuel or a deskside Tezro. ON and BLINK switch off
+ * every LED outside the mask; OFF switches off only the masked ones.
+ */
+#define L1_LED_RED BIT(0)
+#define L1_LED_WHITE BIT(1)
+
+#define L1_LED_ON 0
+#define L1_LED_OFF 1
+#define L1_LED_BLINK 2
+
+#define L1_DISPLAY_WIDTH 16
+
int l1_exec_command(const char *cmd);
int l1_read_board_partnum(unsigned int component, char *buf, size_t len);
int l1_read_brick_type(void);
+int l1_set_leds(unsigned int mask, unsigned int state);
+int l1_set_display(const char *text);
#endif /* __ASM_SN_L1_H */
diff --git a/arch/mips/sgi-ip27/ip27-l1.c b/arch/mips/sgi-ip27/ip27-l1.c
index 98c94d14bd5f..3fe0214d0f91 100644
--- a/arch/mips/sgi-ip27/ip27-l1.c
+++ b/arch/mips/sgi-ip27/ip27-l1.c
@@ -26,7 +26,9 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
+#include <linux/export.h>
#include <linux/hex.h>
+#include <linux/panic.h>
#include <linux/serial_reg.h>
#include <linux/spinlock.h>
#include <linux/stdarg.h>
@@ -81,6 +83,8 @@
/* L1_TASK_GENERAL requests */
#define L1_REQ_BRICK_ID 0x0003 /* rack, slot and brick type */
#define L1_REQ_EEPROM 0x0006 /* access eeprom */
+#define L1_REQ_DISP2 0x1005 /* write line 2 of the display */
+#define L1_REQ_LED 0x1009 /* set the front-panel LEDs */
/* L1_REQ_EEPROM additional argument values (non C-brick component) */
#define L1_EEP_LOGIC 0x01 /* logic board */
@@ -414,6 +418,19 @@ static int l1_response_to_errno(u32 response)
*/
static DEFINE_SPINLOCK(l1_lock);
+/*
+ * Once a panic has stopped the other CPUs, one of them may hold the lock
+ * for good; the panicking CPU then goes to the wire without it.
+ */
+static bool l1_lock_irqsave(unsigned long *flags)
+{
+ if (panic_in_progress())
+ return spin_trylock_irqsave(&l1_lock, *flags);
+
+ spin_lock_irqsave(&l1_lock, *flags);
+ return true;
+}
+
/*
* Run one request/response exchange in pkt, which has to be large enough
* for both; returns the response's argument count.
@@ -493,6 +510,56 @@ int l1_exec_command(const char *cmd)
return rc == 0 ? 0 : -EIO;
}
+int l1_set_leds(unsigned int mask, unsigned int state)
+{
+ u32 address = L1_ADDRESS(L1_TYPE_L1,
+ L1_ADDRESS_LOCAL | L1_TASK_GENERAL);
+ u8 pkt[64]; /* command and response packet buffer */
+ unsigned long flags;
+ bool locked;
+ int rc;
+
+ locked = l1_lock_irqsave(&flags);
+
+ /* arguments: LED mask, then new state, both INT */
+ rc = l1_transact(get_nasid(), pkt, sizeof(pkt), address, L1_REQ_LED,
+ NULL, NULL, 2,
+ L1_ARG_INT, (u32)mask, L1_ARG_INT, (u32)state);
+
+ if (locked)
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ return rc < 0 ? rc : 0;
+}
+EXPORT_SYMBOL_GPL(l1_set_leds);
+
+/*
+ * Write line 2 of the display; line 1 belongs to the L1. Padding to the
+ * full width leaves nothing of a longer earlier line behind.
+ */
+int l1_set_display(const char *text)
+{
+ u32 address = L1_ADDRESS(L1_TYPE_L1,
+ L1_ADDRESS_LOCAL | L1_TASK_GENERAL);
+ char line[L1_DISPLAY_WIDTH + 1];
+ u8 pkt[64]; /* command and response packet buffer */
+ unsigned long flags;
+ bool locked;
+ int rc;
+
+ snprintf(line, sizeof(line), "%-*s", L1_DISPLAY_WIDTH, text);
+
+ locked = l1_lock_irqsave(&flags);
+
+ rc = l1_transact(get_nasid(), pkt, sizeof(pkt), address,
+ L1_REQ_DISP2, NULL, NULL, 1, L1_ARG_ASCII, line);
+
+ if (locked)
+ spin_unlock_irqrestore(&l1_lock, flags);
+
+ return rc < 0 ? rc : 0;
+}
+
/* Read a component's board IA record from EEPROM. */
static int l1_read_board_ia(nasid_t nasid, int type, unsigned int component,
u8 **ria, size_t *rialen)
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 51/53] leds: Add driver for the SGI Fuel and Tezro front-panel LEDs
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (49 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 50/53] MIPS: SGI-IP27: add L1 front-panel LED and display requests Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 52/53] MIPS: SGI-IP27: show the OS state on the Fuel and Tezro front panels Imre Kaloz
` (2 subsequent siblings)
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Pavel Machek, linux-leds
The SGI Fuel and Tezro front-panel LEDs are set through the L1 system
controller, whose LED request switches off every LED outside its mask,
so send the pattern of both LEDs on each change. Probe takes over the
PROM's state without writing it.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
drivers/leds/Kconfig | 12 ++++
drivers/leds/Makefile | 1 +
drivers/leds/leds-ip35.c | 144 +++++++++++++++++++++++++++++++++++++++
3 files changed, 157 insertions(+)
create mode 100644 drivers/leds/leds-ip35.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index d0d3182236be..9ca75364264b 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -1035,6 +1035,18 @@ config LEDS_IP30
To compile this driver as a module, choose M here: the module
will be called leds-ip30.
+config LEDS_IP35
+ tristate "LED support for SGI Fuel and Tezro"
+ depends on LEDS_CLASS
+ depends on SGI_IP27
+ help
+ This option enables support for the red and white front-panel
+ LEDs of SGI Fuel and Tezro workstations, which the L1 system
+ controller drives.
+
+ To compile this driver as a module, choose M here: the module
+ will be called leds-ip35.
+
config LEDS_ACER_A500
tristate "Power button LED support for Acer Iconia Tab A500"
depends on LEDS_CLASS && MFD_ACER_A500_EC
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a68244bd50fb..b45d1d70c431 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
obj-$(CONFIG_LEDS_IP30) += leds-ip30.o
+obj-$(CONFIG_LEDS_IP35) += leds-ip35.o
obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
diff --git a/drivers/leds/leds-ip35.c b/drivers/leds/leds-ip35.c
new file mode 100644
index 000000000000..fba918265e3d
--- /dev/null
+++ b/drivers/leds/leds-ip35.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LED driver for the SGI Fuel and Tezro front panels, which the L1
+ * system controller drives.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/leds/common.h>
+
+#include <asm/sn/l1.h>
+
+struct ip35_leds;
+
+struct ip35_led {
+ struct led_classdev cdev;
+ struct ip35_leds *leds;
+ unsigned int mask;
+ unsigned int state;
+};
+
+struct ip35_leds {
+ struct mutex lock; /* protects ->state of both LEDs */
+ struct ip35_led led[2];
+};
+
+/*
+ * ON and BLINK switch off every LED outside their mask, so each change
+ * sends the pattern of both LEDs. A steady LED next to a blinking one
+ * cannot be shown; the blinking one wins.
+ */
+static int ip35_led_update(struct ip35_led *led, unsigned int state)
+{
+ struct ip35_leds *leds = led->leds;
+ unsigned int old, on = 0, blink = 0;
+ int i, ret;
+
+ mutex_lock(&leds->lock);
+
+ old = led->state;
+ led->state = state;
+
+ for (i = 0; i < ARRAY_SIZE(leds->led); i++) {
+ if (leds->led[i].state == L1_LED_ON)
+ on |= leds->led[i].mask;
+ else if (leds->led[i].state == L1_LED_BLINK)
+ blink |= leds->led[i].mask;
+ }
+
+ if (blink)
+ ret = l1_set_leds(blink, L1_LED_BLINK);
+ else if (on)
+ ret = l1_set_leds(on, L1_LED_ON);
+ else
+ ret = l1_set_leds(L1_LED_RED | L1_LED_WHITE, L1_LED_OFF);
+
+ if (ret)
+ led->state = old;
+
+ mutex_unlock(&leds->lock);
+
+ return ret;
+}
+
+static int ip35_led_set(struct led_classdev *cdev, enum led_brightness value)
+{
+ struct ip35_led *led = container_of(cdev, struct ip35_led, cdev);
+
+ return ip35_led_update(led, value ? L1_LED_ON : L1_LED_OFF);
+}
+
+/* The L1 blinks at a fixed 2 s period of its own, whatever the delays. */
+static int ip35_led_blink_set(struct led_classdev *cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct ip35_led *led = container_of(cdev, struct ip35_led, cdev);
+
+ *delay_on = 1000;
+ *delay_off = 1000;
+
+ return ip35_led_update(led, L1_LED_BLINK);
+}
+
+/* The PROM leaves the white LED lit and the red one dark. */
+static const struct {
+ const char *name;
+ unsigned int mask;
+ unsigned int state;
+} ip35_led_info[] = {
+ { "white:" LED_FUNCTION_STATUS, L1_LED_WHITE, L1_LED_ON },
+ { "red:" LED_FUNCTION_FAULT, L1_LED_RED, L1_LED_OFF },
+};
+
+static int ip35_leds_probe(struct platform_device *pdev)
+{
+ struct ip35_leds *leds;
+ int i, ret;
+
+ leds = devm_kzalloc(&pdev->dev, sizeof(*leds), GFP_KERNEL);
+ if (!leds)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(&pdev->dev, &leds->lock);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(leds->led); i++) {
+ struct ip35_led *led = &leds->led[i];
+
+ led->leds = leds;
+ led->mask = ip35_led_info[i].mask;
+ led->state = ip35_led_info[i].state;
+ led->cdev.name = ip35_led_info[i].name;
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness = led->state == L1_LED_ON;
+ led->cdev.brightness_set_blocking = ip35_led_set;
+ led->cdev.blink_set = ip35_led_blink_set;
+
+ ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver ip35_leds_driver = {
+ .probe = ip35_leds_probe,
+ .driver = {
+ .name = "ip35-leds",
+ },
+};
+module_platform_driver(ip35_leds_driver);
+
+MODULE_AUTHOR("Imre Kaloz <kaloz@kernel.org>");
+MODULE_DESCRIPTION("SGI Fuel and Tezro front-panel LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ip35-leds");
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 52/53] MIPS: SGI-IP27: show the OS state on the Fuel and Tezro front panels
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (50 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 51/53] leds: Add driver for the SGI Fuel and Tezro front-panel LEDs Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 53/53] MIPS: ip27_defconfig: enable the Fuel and Tezro front-panel LEDs Imre Kaloz
2026-10-13 13:35 ` [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Niklas Cassel
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-leds
Register the front-panel LED device on a Fuel and a deskside Tezro and
blink the red LED on panic. A Tezro also shows "Linux booting", "Linux
running" or "Linux panic" on line 2 of its L1 display.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/sgi-ip27/ip27-init.c | 52 ++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
index e657155ef7c9..f7de09515c0c 100644
--- a/arch/mips/sgi-ip27/ip27-init.c
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -13,6 +13,9 @@
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/export.h>
+#include <linux/notifier.h>
+#include <linux/panic_notifier.h>
+#include <linux/platform_device.h>
#include <linux/cpumask.h>
#include <linux/ctype.h>
#include <asm/bootinfo.h>
@@ -122,6 +125,8 @@ static const struct {
{ 'Q', "SGI Tezro Rackmount", NULL },
};
+static int ip27_brick_type;
+
static bool __init ip27_has_graphics(void)
{
nasid_t nasid;
@@ -145,6 +150,7 @@ static void __init ip27_set_machine_name(void)
brick_type = l1_read_brick_type();
if (brick_type < 0)
return;
+ ip27_brick_type = brick_type;
gfx = ip27_has_graphics();
@@ -165,6 +171,52 @@ static void __init ip27_set_machine_name(void)
gfx ? "with" : "without");
}
+/*
+ * A Fuel (brick type 'A') and a deskside Tezro ('9') have L1-driven
+ * front-panel LEDs, the Tezro also an L1 display. A blinking red LED,
+ * with the white one off, marks a panic.
+ */
+static int ip27_panel_panic(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ l1_set_leds(L1_LED_RED, L1_LED_BLINK);
+ if (ip27_brick_type == '9')
+ l1_set_display("Linux panic");
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ip27_panel_panic_nb = {
+ .notifier_call = ip27_panel_panic,
+};
+
+static int __init ip27_panel_init(void)
+{
+ struct platform_device *pdev;
+
+ if (ip27_brick_type != 'A' && ip27_brick_type != '9')
+ return 0;
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &ip27_panel_panic_nb);
+ if (ip27_brick_type == '9')
+ l1_set_display("Linux booting");
+
+ pdev = platform_device_register_simple("ip35-leds", PLATFORM_DEVID_NONE,
+ NULL, 0);
+ return PTR_ERR_OR_ZERO(pdev);
+}
+arch_initcall(ip27_panel_init);
+
+static int __init ip27_panel_running(void)
+{
+ if (ip27_brick_type == '9')
+ l1_set_display("Linux running");
+
+ return 0;
+}
+late_initcall_sync(ip27_panel_running);
+
/*
* Prefer ttyS0 at the baud rate the IP35 PROM recorded for its console
* UART in the KLCONFIG header.
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* [PATCH v2 53/53] MIPS: ip27_defconfig: enable the Fuel and Tezro front-panel LEDs
2026-10-13 13:16 [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Imre Kaloz
` (51 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 52/53] MIPS: SGI-IP27: show the OS state on the Fuel and Tezro front panels Imre Kaloz
@ 2026-10-13 13:17 ` Imre Kaloz
2026-10-13 13:35 ` [PATCH v2 00/53] MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines Niklas Cassel
53 siblings, 0 replies; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:17 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-kernel, Lee Jones, linux-leds
Enable the LED class with its timer, heartbeat and default-on triggers,
and build the SGI Fuel and Tezro front-panel LED driver as a module.
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
arch/mips/configs/ip27_defconfig | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 2959615d656b..74f4a88f927a 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -133,6 +133,13 @@ CONFIG_USB=m
CONFIG_USB_EHCI_HCD=m
CONFIG_USB_OHCI_HCD=m
CONFIG_USB_STORAGE=m
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_IP35=m
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_M48T35=y
CONFIG_UIO=y
--
2.47.3
^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver
2026-10-13 13:17 ` [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver Imre Kaloz
@ 2026-10-13 13:25 ` Damien Le Moal
2026-10-13 13:38 ` Imre Kaloz
0 siblings, 1 reply; 60+ messages in thread
From: Damien Le Moal @ 2026-10-13 13:25 UTC (permalink / raw)
To: Imre Kaloz, Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Lee Jones, Niklas Cassel, linux-ide
On 2026/10/13 15:17, Imre Kaloz wrote:
> Add an entry for drivers/ata/pata_sgiioc4.c, the libata driver for the
> IOC4 ATA/ATAPI controller.
>
> Signed-off-by: Imre Kaloz <kaloz@kernel.org>
> ---
> MAINTAINERS | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a30c09a55c72..826f0ffb71fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24925,6 +24925,12 @@ F: include/linux/phylink.h
> F: include/linux/sfp.h
> K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
>
> +SGI IOC4 ATA DRIVER
> +M: Imre Kaloz <kaloz@kernel.org>
> +L: linux-ide@vger.kernel.org
> +S: Maintained
> +F: drivers/ata/pata_sgiioc4.c
> +
> SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
> M: D. Wythe <alibuda@linux.alibaba.com>
> M: Dust Li <dust.li@linux.alibaba.com>
Please squash this into patch 45.
Also, the cover letter mentions that patch 45-47 are for libata but I did not
receive patch 46. I only received the cover letter, and patches 45 and 47.
Are you planning to get all of this merged through the mips tree?
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 60+ 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
` (52 preceding siblings ...)
2026-10-13 13:17 ` [PATCH v2 53/53] MIPS: ip27_defconfig: enable the Fuel and Tezro front-panel LEDs Imre Kaloz
@ 2026-10-13 13:35 ` Niklas Cassel
2026-10-13 14:25 ` Imre Kaloz
53 siblings, 1 reply; 60+ 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] 60+ messages in thread
* Re: [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver
2026-10-13 13:25 ` Damien Le Moal
@ 2026-10-13 13:38 ` Imre Kaloz
2026-10-13 15:13 ` Damien Le Moal
0 siblings, 1 reply; 60+ messages in thread
From: Imre Kaloz @ 2026-10-13 13:38 UTC (permalink / raw)
To: Damien Le Moal
Cc: Thomas Bogendoerfer, linux-mips, linux-kernel, Lee Jones,
Niklas Cassel, linux-ide
[-- Attachment #1: Type: text/plain, Size: 1841 bytes --]
Hi Damien,
On Tue, 13 Oct 2026, Damien Le Moal wrote:
> On 2026/10/13 15:17, Imre Kaloz wrote:
>> Add an entry for drivers/ata/pata_sgiioc4.c, the libata driver for the
>> IOC4 ATA/ATAPI controller.
>>
>> Signed-off-by: Imre Kaloz <kaloz@kernel.org>
>> ---
>> MAINTAINERS | 6 ++++++
>> 1 file changed, 6 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index a30c09a55c72..826f0ffb71fd 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -24925,6 +24925,12 @@ F: include/linux/phylink.h
>> F: include/linux/sfp.h
>> K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate)
>>
>> +SGI IOC4 ATA DRIVER
>> +M: Imre Kaloz <kaloz@kernel.org>
>> +L: linux-ide@vger.kernel.org
>> +S: Maintained
>> +F: drivers/ata/pata_sgiioc4.c
>> +
>> SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
>> M: D. Wythe <alibuda@linux.alibaba.com>
>> M: Dust Li <dust.li@linux.alibaba.com>
>
> Please squash this into patch 45.
>
> Also, the cover letter mentions that patch 45-47 are for libata but I did not
> receive patch 46. I only received the cover letter, and patches 45 and 47.
>
> Are you planning to get all of this merged through the mips tree?
Will squash 47 into 45 in v3.
Sorry about 46: it only touches drivers/mfd/ioc3.c and
arch/mips/include/asm/sn/ioc4.h, so get_maintainer left linux-ide off it.
It adds the IOC4 ATA/ATAPI MFD cell that pata_sgiioc4 binds to:
https://lore.kernel.org/all/754bae380c938b63eeaa87e3dccf9755314675ed.1791890466.git.kaloz@kernel.org/
v3 will copy you and linux-ide on it.
As for merging: pata_sgiioc4 depends on the IOC4 support added earlier in
the series (asm/sn/ioc4.h and the MFD cell), so I’d like it to go through
the MIPS tree with your ack, if that works for you.
Best,
Imre
^ permalink raw reply [flat|nested] 60+ 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; 60+ 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] 60+ messages in thread
* Re: [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base
2026-10-13 13:16 ` [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base Imre Kaloz
@ 2026-10-13 14:28 ` Alexandre Belloni
0 siblings, 0 replies; 60+ messages in thread
From: Alexandre Belloni @ 2026-10-13 14:28 UTC (permalink / raw)
To: Imre Kaloz
Cc: Thomas Bogendoerfer, linux-mips, linux-kernel, Lee Jones, linux-rtc
[-- Attachment #1: Type: text/plain, Size: 3838 bytes --]
Hello,
On 13/10/2026 15:16:50+0200, Imre Kaloz wrote:
> The M48T35 of SGI IP35-family machines counts its year from 1968. Take
> the epoch from a platform id table, "rtc-m48t35" for IP27 and
> "rtc-m48t35-ip35" for IP35, and bound the year to the part's century: on
> IP27 a year past 2069 now fails to set.
>
> Signed-off-by: Imre Kaloz <kaloz@kernel.org>
> ---
> drivers/rtc/rtc-m48t35.c | 33 +++++++++++++++++++--------------
> 1 file changed, 19 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
> index 92f19bf997b2..fa228a2bb527 100644
> --- a/drivers/rtc/rtc-m48t35.c
> +++ b/drivers/rtc/rtc-m48t35.c
> @@ -49,6 +49,7 @@ struct m48t35_priv {
> struct m48t35_rtc __iomem *reg;
> size_t size;
> unsigned long baseaddr;
> + unsigned int epoch;
> spinlock_t lock;
> };
>
> @@ -86,10 +87,7 @@ static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
> * Account for differences between how the RTC uses the values
> * and how they are defined in a struct rtc_time;
> */
> - tm->tm_year += 70;
> - if (tm->tm_year <= 69)
> - tm->tm_year += 100;
> -
> + tm->tm_year += priv->epoch - 1900;
> tm->tm_mon--;
> return 0;
> }
> @@ -108,18 +106,10 @@ static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
> min = tm->tm_min;
> sec = tm->tm_sec;
>
> - if (yrs < 1970)
> - return -EINVAL;
> -
> - yrs -= 1970;
> - if (yrs > 255) /* They are unsigned */
> - return -EINVAL;
> -
> - if (yrs > 169)
> + if (yrs < priv->epoch || yrs >= priv->epoch + 100)
> return -EINVAL;
>
> - if (yrs >= 100)
> - yrs -= 100;
> + yrs -= priv->epoch;
>
Instead of open coding, you should use start_secs after setting the rtc
range in probe(). attached i a patch I refreshed for the
devm_rtc_allocate_device/devm_rtc_register_device conversion that you
can include in this series. drivers/rtc/rtc-mt6397.c is an example that
should be working for you.
I never sent the patch because it is not clear from the datasheet how
well leap year calculation works. My guess is that it worked in 2000 but
fails in 2100.
> sec = bin2bcd(sec);
> min = bin2bcd(min);
> @@ -149,9 +139,13 @@ static const struct rtc_class_ops m48t35_ops = {
>
> static int m48t35_probe(struct platform_device *pdev)
> {
> + const struct platform_device_id *id = platform_get_device_id(pdev);
> struct resource *res;
> struct m48t35_priv *priv;
>
> + if (!id)
> + return -ENODEV;
> +
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (!res)
> return -ENODEV;
> @@ -164,6 +158,7 @@ static int m48t35_probe(struct platform_device *pdev)
> pdev->name))
> return -EBUSY;
>
> + priv->epoch = id->driver_data;
> priv->baseaddr = res->start;
> priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
> if (!priv->reg)
> @@ -178,11 +173,20 @@ static int m48t35_probe(struct platform_device *pdev)
> return PTR_ERR_OR_ZERO(priv->rtc);
> }
>
> +/* The year register on SGI IP35-family machines counts from 1968. */
> +static const struct platform_device_id m48t35_id_table[] = {
> + { "rtc-m48t35", 1970 },
> + { "rtc-m48t35-ip35", 1968 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(platform, m48t35_id_table);
> +
> static struct platform_driver m48t35_platform_driver = {
> .driver = {
> .name = "rtc-m48t35",
> },
> .probe = m48t35_probe,
> + .id_table = m48t35_id_table,
> };
>
> module_platform_driver(m48t35_platform_driver);
> @@ -191,3 +195,4 @@ MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
> MODULE_DESCRIPTION("M48T35 RTC driver");
> MODULE_LICENSE("GPL");
> MODULE_ALIAS("platform:rtc-m48t35");
> +MODULE_ALIAS("platform:rtc-m48t35-ip35");
> --
> 2.47.3
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
[-- Attachment #2: 0001-rtc-m48t35-switch-to-devm_rtc_allocate_device.patch --]
[-- Type: text/plain, Size: 1183 bytes --]
From 7aed40aa17edfdd132ad10f6fb1ca9f1f1ddac07 Mon Sep 17 00:00:00 2001
From: Alexandre Belloni <alexandre.belloni@bootlin.com>
Date: Tue, 13 Oct 2026 16:21:06 +0200
Subject: [PATCH] rtc: m48t35: switch to devm_rtc_allocate_device
Switch to devm_rtc_allocate_device/devm_rtc_register_device, this allows
for further improvement of the driver.
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---
drivers/rtc/rtc-m48t35.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
index 92f19bf997b2..0f4a3cb1e1f0 100644
--- a/drivers/rtc/rtc-m48t35.c
+++ b/drivers/rtc/rtc-m48t35.c
@@ -173,9 +173,13 @@ static int m48t35_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35",
- &m48t35_ops, THIS_MODULE);
- return PTR_ERR_OR_ZERO(priv->rtc);
+ priv->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(priv->rtc))
+ return PTR_ERR(priv->rtc);
+
+ priv->rtc->ops = &m48t35_ops;
+
+ return devm_rtc_register_device(priv->rtc);
}
static struct platform_driver m48t35_platform_driver = {
--
2.56.0
^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver
2026-10-13 13:38 ` Imre Kaloz
@ 2026-10-13 15:13 ` Damien Le Moal
0 siblings, 0 replies; 60+ messages in thread
From: Damien Le Moal @ 2026-10-13 15:13 UTC (permalink / raw)
To: Imre Kaloz
Cc: Thomas Bogendoerfer, linux-mips, linux-kernel, Lee Jones,
Niklas Cassel, linux-ide
On 2026/10/13 15:38, Imre Kaloz wrote:
> As for merging: pata_sgiioc4 depends on the IOC4 support added earlier in
> the series (asm/sn/ioc4.h and the MFD cell), so I’d like it to go through
> the MIPS tree with your ack, if that works for you.
OK for me to have this go through the mips tree.
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 60+ messages in thread
end of thread, other threads:[~2026-10-13 15:13 UTC | newest]
Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [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 ` [PATCH v2 04/53] MIPS: PCI: xtalk-bridge: flush config space writes Imre Kaloz
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 ` [PATCH v2 06/53] MIPS: Xtalk: guard the widget identification table from assembly Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 07/53] MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 08/53] MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L() Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 09/53] MIPS: SGI-IP27: detect Bedrock hubs at boot Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 10/53] MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 11/53] MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 12/53] MIPS: SGI-IP27: choose the node address layout at runtime Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 13/53] MIPS: SGI-IP27: add the Bedrock hub register windows Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 14/53] MIPS: SGI-IP27: decode a Bedrock hub's node id and system size Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 15/53] MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 16/53] MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 17/53] MIPS: SGI-IP27: reserve the bottom 64MB of node 0 " Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 18/53] MIPS: SGI-IP27: route interrupt masks and slices through both PIs Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 19/53] MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 20/53] MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init() Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 21/53] MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 22/53] MIPS: SGI-IP27: load the kernel at node offset 0x40000 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 23/53] MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 24/53] MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 25/53] net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit addresses 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:16 ` [PATCH v2 27/53] MIPS: SGI-IP27: recognize the PXBow crossbar Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 28/53] MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only registers Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 29/53] MIPS: PCI: xtalk-bridge: match PIC's register-level differences Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 30/53] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base Imre Kaloz
2026-10-13 14:28 ` Alexandre Belloni
2026-10-13 13:16 ` [PATCH v2 31/53] mfd: ioc3: Add support for IOC4 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 32/53] MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 33/53] MIPS: SGI-IP27: add L1 system controller support Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 34/53] MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 35/53] MIPS: PCI: xtalk-bridge: take the board part number from the L1 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 36/53] mfd: ioc3: Add IP34 system-board entry to ioc3_infos[] Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 37/53] mfd: ioc3: Cross the serial IRQs on the Origin 300 IO8 Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 38/53] net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address Imre Kaloz
2026-10-13 13:16 ` [PATCH v2 39/53] MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 40/53] MIPS: SGI-IP27: report a Bedrock machine as SGI IP35 Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 41/53] MIPS: SGI-IP27: identify the machine by the L1 brick type Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 42/53] MIPS: SGI-IP27: register the Odyssey graphics widget Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 43/53] MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35 Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 44/53] MIPS: SGI-IP27: give each CPU its own topology package id Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 45/53] ata: add pata_sgiioc4 driver for the SGI IOC4 ATA/ATAPI controller Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 46/53] mfd: ioc3: Add IOC4 ATA/ATAPI cell Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 47/53] MAINTAINERS: add an entry for the SGI IOC4 ATA driver Imre Kaloz
2026-10-13 13:25 ` Damien Le Moal
2026-10-13 13:38 ` Imre Kaloz
2026-10-13 15:13 ` Damien Le Moal
2026-10-13 13:17 ` [PATCH v2 48/53] MIPS: ip27_defconfig: update for the onboard hardware Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 49/53] MIPS: ip27_defconfig: enable pata_sgiioc4 and the DVD-ROM path Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 50/53] MIPS: SGI-IP27: add L1 front-panel LED and display requests Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 51/53] leds: Add driver for the SGI Fuel and Tezro front-panel LEDs Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 52/53] MIPS: SGI-IP27: show the OS state on the Fuel and Tezro front panels Imre Kaloz
2026-10-13 13:17 ` [PATCH v2 53/53] MIPS: ip27_defconfig: enable the Fuel and Tezro front-panel LEDs Imre Kaloz
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®