Skip to contentSkip to conversation

MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines

PATCH Superseded Imre Kaloz 5 participants 47 messages Oct 1 – Oct 5

Subsystems (7)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Imre Kaloz

author
#0 lore raw

cover MIPS: SGI-IP27: add support for the IP35 (Bedrock hub) machines

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-17 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 18-27 add the crossbar, bridge and I/O parts of these machines:
PXBow, XBridge, PIC and IOC4.

Patches 28-38 add the L1 system controller and what it provides, the Odyssey graphics widget, the KLCONFIG console and a topology package id per CPU.

Tested on SGI Fuel and Tezro. The NMI dump reaches the 8250 console only with "MIPS: SGI-IP27: print the NMI dump on an nbcon console", posted separately.

On IP27 (Hub) machines:
- patch 17 moves the kernel up by 144KB, and the 144KB below it stays reserved and unused;
- with patch 21 SWIOTLB takes 64MB of node 0 on a multi-node Origin;
- the XBridge DMA window (patch 21) and the w1 change (patch 33) do nothing on a Hub with classic Bridges.

Given I have no IP27 boxes, none of this was booted on one. ip27_defconfig and ip30_defconfig build at every patch.

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 30), so on a machine with several bricks every such bridge gets that one board's setup.

Patch 36 registers the Odyssey graphics widget as the "sgi-odyssey" platform device used by the Odyssey DRM driver, a separate series.

checkpatch reports "spaces required around that ':'" in patches 9 and 16 for the GAS numeric local labels 1: and 2: in assembler macros.

Imre Kaloz (38):
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: route XBridge and PIC DMA through the 32-bit window
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[]
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

 arch/mips/Kconfig                             |  11 +-
 arch/mips/configs/ip27_defconfig              |   4 +
 .../include/asm/mach-ip27/kernel-entry-init.h |  33 +-
 arch/mips/include/asm/pci/bridge.h            |  83 +-
 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               | 130 +++
 arch/mips/include/asm/sn/klconfig.h           |  19 +-
 arch/mips/include/asm/sn/kldir.h              |   1 +
 arch/mips/include/asm/sn/l1.h                 |  22 +
 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              | 124 ++-
 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                | 126 ++-
 arch/mips/sgi-ip27/ip27-irq.c                 |  17 +-
 arch/mips/sgi-ip27/ip27-l1.c                  | 817 ++++++++++++++++++
 arch/mips/sgi-ip27/ip27-memory.c              |  53 +-
 arch/mips/sgi-ip27/ip27-nmi.c                 |  54 +-
 arch/mips/sgi-ip27/ip27-reset.c               |  47 +-
 arch/mips/sgi-ip27/ip27-smp.c                 |  37 +-
 arch/mips/sgi-ip27/ip27-timer.c               |  12 +-
 arch/mips/sgi-ip27/ip27-xtalk.c               | 127 ++-
 drivers/mfd/ioc3.c                            | 335 ++++++-
 drivers/net/ethernet/sgi/ioc3-eth.c           |  37 +-
 drivers/rtc/rtc-m48t35.c                      |  29 +-
 drivers/tty/serial/8250/8250_ioc3.c           |   5 +-
 include/linux/platform_data/sgi-ioc3-serial.h |  13 +
 include/linux/platform_data/xtalk-bridge.h    |   2 +
 40 files changed, 2186 insertions(+), 225 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 include/linux/platform_data/sgi-ioc3-serial.h

base-commit: 5dd1818b15d98d4a20806cd00b1b40320b06004f

Signature · 1 line
2.47.3
author
#1 lore raw

PATCH 1/38 MIPS: Xtalk: guard the widget identification table from assembly

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.

---
 arch/mips/include/asm/xtalk/xwidget.h | 2 ++
 1 file changed, 2 insertions(+)
arch/mips/include/asm/xtalk/xwidget.h +2 -0
index efcfe4494576..6dae5d9628f7 100644
@@ -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
Signature · 1 line
2.47.3
author
#2 lore raw

PATCH 2/38 MIPS: SGI-IP27: dump the NMI save areas the CPU scan recorded

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-nmi.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
arch/mips/sgi-ip27/ip27-nmi.c +8 -8
index fc2816398d0c..07d63d020d22 100644
@@ -4,6 +4,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>
@@ -13,12 +14,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)
 
@@ -148,13 +143,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);
 	}
Signature · 1 line
2.47.3
author
#3 lore raw

PATCH 3/38 MIPS: SGI-IP27: add REMOTE_HUB_PI_S()/REMOTE_HUB_PI_L()

In reply to Imre Kaloz

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().

---
 arch/mips/include/asm/sn/addrs.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
arch/mips/include/asm/sn/addrs.h +21 -0
index 7c675fecbf9a..d5c7cfea7761 100644
@@ -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
Signature · 1 line
2.47.3
author
#4 lore raw

PATCH 4/38 MIPS: SGI-IP27: detect Bedrock hubs at boot

In reply to Imre Kaloz

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.

---
 arch/mips/include/asm/sn/addrs.h | 6 ++++++
 arch/mips/sgi-ip27/ip27-init.c   | 9 +++++++++
 2 files changed, 15 insertions(+)
arch/mips/include/asm/sn/addrs.h +6 -0
index d5c7cfea7761..51161fd5130b 100644
@@ -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
arch/mips/sgi-ip27/ip27-init.c +9 -0
index a4daf8ccd16c..6394c2076041 100644
@@ -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();
 }
Signature · 1 line
2.47.3
author
#5 lore raw

PATCH 5/38 MIPS: SGI-IP27: widen Kconfig for the Bedrock-based machines

In reply to Imre Kaloz

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.

---
 arch/mips/Kconfig | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
arch/mips/Kconfig +5 -4
index e2eb9627bd14..eb291fa7fd08 100644
@@ -757,7 +757,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
@@ -782,9 +782,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)"
Signature · 1 line
2.47.3
author
#6 lore raw

PATCH 6/38 MIPS: SGI-IP27: fold the dead SN1 arm out of klconfig.h

In reply to Imre Kaloz

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.

---
 arch/mips/include/asm/sn/klconfig.h | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)
arch/mips/include/asm/sn/klconfig.h +2 -17
index 3d1670b3e052..433d2d05aba3 100644
@@ -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;
 
Signature · 1 line
2.47.3
author
#7 lore raw

PATCH 7/38 MIPS: SGI-IP27: choose the node address layout at runtime

In reply to Imre Kaloz

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.

---
 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
arch/mips/include/asm/sn/addrs.h +11 -1
index 51161fd5130b..499615abd501 100644
@@ -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__ */
 
 
arch/mips/include/asm/sn/sn0/addrs.h +4 -4
index a28158a91ecf..f1a161913ae4 100644
@@ -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
 
arch/mips/include/asm/sn/sn1/addrs.h new file +22 -0
new file mode 100644
index 000000000000..58878a5b4710
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) address space layout.
+ *
+ * Copyright (C) 2026 Imre Kaloz <[email protected]>
+ */
+#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 */
Signature · 1 line
2.47.3
author
#8 lore raw

PATCH 8/38 MIPS: SGI-IP27: add the Bedrock hub register windows

In reply to Imre Kaloz

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.

---
 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
arch/mips/include/asm/sn/agent.h +1 -2
index 7e9b3271737a..8873eb34e31b 100644
@@ -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
arch/mips/include/asm/sn/sn1/hub.h new file +15 -0
new file mode 100644
index 000000000000..db41bcb3d34a
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) hub register windows.
+ *
+ * Copyright (C) 2026 Imre Kaloz <[email protected]>
+ */
+#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 */
Signature · 1 line
2.47.3
author
#9 lore raw

PATCH 9/38 MIPS: SGI-IP27: decode a Bedrock hub's node id and system size

In reply to Imre Kaloz

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.

---
 .../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(-)
arch/mips/include/asm/mach-ip27/kernel-entry-init.h +6 -2
index 3e54f605a70b..cd50e6d3d966 100644
@@ -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
 
 	/*
arch/mips/include/asm/sn/agent.h +1 -1
index 8873eb34e31b..1d6d0cd5e5fd 100644
@@ -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 */
 
 /*
arch/mips/include/asm/sn/sn0/hub.h +33 -4
index 916394319af5..c5429dbddc7a 100644
@@ -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
 
arch/mips/include/asm/sn/sn0/hubni.h +3 -0
index 4830bae723e4..ff7df5e27f66 100644
@@ -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;
 
arch/mips/include/asm/sn/sn1/hub.h +14 -0
index db41bcb3d34a..71ccd2ba3698 100644
@@ -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 */
arch/mips/include/asm/xtalk/xwidget.h +1 -1
index 6dae5d9628f7..978877e8a090 100644
@@ -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,
arch/mips/sgi-ip27/ip27-init.c +5 -1
index 6394c2076041..108d90908763 100644
@@ -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)
Signature · 1 line
2.47.3
author
#10 lore raw

PATCH 10/38 MIPS: SGI-IP27: put a Bedrock hub's boot CPU in slot 0

In reply to Imre Kaloz

ip27_smp_setup() takes CPU 0 to be the boot CPU. On a Bedrock hub, find the boot CPU's sn_cpu_info[] entry by its nasid and PI_CPU_NUM slice and swap it into slot 0. The PROM's virtids, which set the possible mask, run densely from 0 in board-list order and so match the table's slots.

---
 arch/mips/sgi-ip27/ip27-smp.c | 9 +++++++++
 1 file changed, 9 insertions(+)
arch/mips/sgi-ip27/ip27-smp.c +9 -0
index 62733e049570..159dbd6ddda8 100644
@@ -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());
 }
 
Signature · 1 line
2.47.3
author
#11 lore raw

PATCH 11/38 MIPS: SGI-IP27: lay out node memory in 1GB banks on Bedrock hubs

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-memory.c | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)
arch/mips/sgi-ip27/ip27-memory.c +20 -5
index 4317f5ae1fd1..0204dc8bb1bb 100644
@@ -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)),
Signature · 1 line
2.47.3
author
#12 lore raw

PATCH 12/38 MIPS: SGI-IP27: reserve the bottom 64MB of node 0 on Bedrock hubs

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-memory.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/mips/sgi-ip27/ip27-memory.c +12 -0
index 0204dc8bb1bb..98184dfcd8c5 100644
@@ -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++) {
Signature · 1 line
2.47.3
author
#13 lore raw

PATCH 13/38 MIPS: SGI-IP27: route interrupt masks and slices through both PIs

In reply to Imre Kaloz

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.

---
 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(-)
arch/mips/include/asm/sn/arch.h +8 -0
index 9a9682543e89..602219f98b04 100644
@@ -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
arch/mips/include/asm/sn/intr.h +6 -4
index 3d6954d370dc..90b613f68623 100644
@@ -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:
arch/mips/sgi-ip27/ip27-berr.c +2 -2
index 9eb497cb5d52..b176856aa77c 100644
@@ -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);
arch/mips/sgi-ip27/ip27-irq.c +9 -8
index 7e5aa04c6695..02a49bb8529d 100644
@@ -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;
arch/mips/sgi-ip27/ip27-smp.c +9 -7
index 159dbd6ddda8..ac960023d334 100644
@@ -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)
arch/mips/sgi-ip27/ip27-timer.c +2 -2
index 5f4da05cb2c9..aee14a7aad8a 100644
@@ -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
Signature · 1 line
2.47.3
author
#14 lore raw

PATCH 14/38 MIPS: SGI-IP27: address a CPU's own PI for NMI, IPI and RT counter

In reply to Imre Kaloz

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.

---
 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
arch/mips/include/asm/sn/intr.h +2 -2
index 90b613f68623..4927232fafc3 100644
@@ -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
arch/mips/include/asm/sn/kldir.h +1 -0
index f394b1e0c956..23999dd43eb2 100644
@@ -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 */
arch/mips/include/asm/sn/sn1/hub.h +11 -0
index 71ccd2ba3698..60e0f249852f 100644
@@ -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 */
arch/mips/include/asm/sn/sn1/kldir.h new file +14 -0
new file mode 100644
index 000000000000..c95dccb3ce55
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SN1 (Bedrock) kldir constants.
+ *
+ * Copyright (C) 2026 Imre Kaloz <[email protected]>
+ */
+#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 */
arch/mips/sgi-ip27/ip27-init.c +11 -3
index 108d90908763..88d79c50d9a7 100644
@@ -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),
arch/mips/sgi-ip27/ip27-nmi.c +26 -12
index 07d63d020d22..53875b93cca1 100644
@@ -14,8 +14,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;
 
@@ -44,12 +45,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);
 
@@ -119,18 +126,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);
@@ -227,5 +235,11 @@ static void nmi_dump(void)
 	 * Save the nmi cpu registers for all cpu in the eframe format.
 	 */
 	nmi_eframes_save();
-	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);
+	}
 }
arch/mips/sgi-ip27/ip27-smp.c +2 -2
index ac960023d334..fc4d9379a93e 100644
@@ -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)
arch/mips/sgi-ip27/ip27-timer.c +6 -2
index aee14a7aad8a..14df8608b14b 100644
@@ -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)
Signature · 1 line
2.47.3
author
#15 lore raw

PATCH 15/38 MIPS: SGI-IP27: take a CPU's node from the CPU table in per_cpu_init()

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-init.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/mips/sgi-ip27/ip27-init.c +1 -1
index 88d79c50d9a7..15811fb3cdee 100644
@@ -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);
 
Signature · 1 line
2.47.3
author
#16 lore raw

PATCH 16/38 MIPS: SGI-IP27: resolve the mapped-kernel node shift at runtime

In reply to Imre Kaloz

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.

---
 .../include/asm/mach-ip27/kernel-entry-init.h | 23 ++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)
arch/mips/include/asm/mach-ip27/kernel-entry-init.h +20 -3
index cd50e6d3d966..1bedd3c3bc58 100644
@@ -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)
Signature · 1 line
2.47.3
author
#17 lore raw

PATCH 17/38 MIPS: SGI-IP27: load the kernel at node offset 0x40000

In reply to Imre Kaloz

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.

---
 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(-)
arch/mips/include/asm/mach-ip27/kernel-entry-init.h +1 -1
index 1bedd3c3bc58..958f4b390df8 100644
@@ -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
arch/mips/include/asm/sn/mapped_kernel.h +3 -3
index 3f1049807018..28227718e391 100644
@@ -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.
  */
arch/mips/sgi-ip27/Platform +6 -5
index e734ee6abd44..b312bf69bce1 100644
@@ -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
Signature · 1 line
2.47.3
author
#18 lore raw

PATCH 18/38 MIPS: SGI-IP27: xbow_probe(): recognize KLTYPE_PBRICK_XBOW

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-xtalk.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
arch/mips/sgi-ip27/ip27-xtalk.c +13 -2
index 10834880c261..baa937b474a8 100644
@@ -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)
Signature · 1 line
2.47.3
author
#19 lore raw

PATCH 19/38 MIPS: ip27_defconfig: enable the IOC3 MFD, its cell drivers and tg3

In reply to Imre Kaloz

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.

Fixes 0ce5ebd24d25 ("mfd: ioc3: Add driver for SGI IOC3 chip")
---
 arch/mips/configs/ip27_defconfig | 4 ++++
 1 file changed, 4 insertions(+)
arch/mips/configs/ip27_defconfig +4 -0
index ac885ff5b712..73abeb534550 100644
@@ -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
@@ -260,6 +263,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
Signature · 1 line
2.47.3
author
#20 lore raw

PATCH 20/38 net: ethernet: sgi: ioc3-eth: apply the DMA attributes only to 64-bit addresses

In reply to Imre Kaloz

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, in preparation for driving an XBridge through its 32-bit window.

---
 drivers/net/ethernet/sgi/ioc3-eth.c | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)
drivers/net/ethernet/sgi/ioc3-eth.c +21 -3
index 009f37105eaf..8702dfdf4c36 100644
@@ -152,19 +152,37 @@ 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 XBridge is driven through
+ * its 32-bit direct window instead, where an address 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 +745,7 @@ static void ioc3_init(struct net_device *dev)
 	readl(&regs->emcr);
 
 	/* Misc registers  */
-	writel(ERBAR_VAL, &regs->erbar);
+	writel(ioc3_erbar(ip->rxr_dma), &regs->erbar);
 	readl(&regs->etcdc);			/* Clear on read */
 	writel(15, &regs->ercsr);		/* RX low watermark  */
 	writel(0, &regs->ertr);			/* Interrupt immediately */
Signature · 1 line
2.47.3
author
#21 lore raw

PATCH 21/38 MIPS: SGI-IP27: route XBridge and PIC DMA through the 32-bit window

In reply to Imre Kaloz

XBridge does not honour the SN0-style 64-bit direct-mapped DMA address:
commands complete but no data moves. PIC moves no data either. Route both through the 2GB 32-bit window and cap bus_dma_limit at its top, so SWIOTLB bounces anything above it. PIC is flagged once a following patch enumerates it.

Only XBridge (part 0xd002) is flagged; a Bridge (0xc002) behind a Hub keeps the 64-bit direct map. The 64MB bounce pool comes from node 0 whenever DRAM passes 2GB, on Hub any multi-node machine. A PIC bus in PCI-X mode takes the window too; IRIX does not.

---
 arch/mips/Kconfig                          |  1 +
 arch/mips/include/asm/pci/bridge.h         |  2 ++
 arch/mips/pci/pci-xtalk-bridge.c           | 26 ++++++++++++++++++++++
 arch/mips/sgi-ip27/ip27-memory.c           | 16 +++++++++++++
 arch/mips/sgi-ip27/ip27-xtalk.c            | 15 ++++++++++---
 include/linux/platform_data/xtalk-bridge.h |  2 ++
 6 files changed, 59 insertions(+), 3 deletions(-)
arch/mips/Kconfig +1 -0
index eb291fa7fd08..20212194e763 100644
@@ -773,6 +773,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
arch/mips/include/asm/pci/bridge.h +2 -0
index eaeafccd82c7..2ea0f1e2cd76 100644
@@ -810,6 +810,8 @@ struct bridge_controller {
 	unsigned int		int_mapping[8][2];
 	u32			ioc3_sid[8];
 	nasid_t			nasid;
+	bool			is_xbridge;
+	bool			is_pic;
 };
 
 #define BRIDGE_CONTROLLER(bus) \
arch/mips/pci/pci-xtalk-bridge.c +26 -0
index cf115abb54e0..c5dec9b0f72d 100644
@@ -31,14 +31,38 @@ 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);
 
+	/*
+	 * XBridge and PIC move no data for this direct-mapped address; use
+	 * the 2GB 32-bit window instead. An address above the window is not
+	 * clamped here: pcibios_bus_add_device() caps bus_dma_limit at the
+	 * window's top, so such a buffer is bounced through SWIOTLB.
+	 */
+	if (bc->is_xbridge || bc->is_pic)
+		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 = to_pci_dev(dev);
+	struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus);
+
+	if (bc->is_xbridge || bc->is_pic)
+		return dma_addr - BRIDGE_DMA_DIRECT_BASE;
+
 	return dma_addr & ~(0xffUL << 56);
 }
 
+/* Bounce a widened DMA mask's out-of-window addresses through SWIOTLB. */
+void pcibios_bus_add_device(struct pci_dev *pdev)
+{
+	struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus);
+
+	if (bc->is_xbridge || bc->is_pic)
+		pdev->dev.bus_dma_limit = 0xffffffff;
+}
+
 /*
  * Most of the IOC3 PCI config register aren't present
  * we emulate what is needed for a normal PCI enumeration
@@ -663,6 +687,8 @@ 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;
 
 	/*
 	 * Clear all pending interrupts.
arch/mips/sgi-ip27/ip27-memory.c +16 -0
index 98184dfcd8c5..d51e02ce12a6 100644
@@ -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>
@@ -437,3 +439,17 @@ void __init arch_zone_limits_init(unsigned long *max_zone_pfns)
 {
 	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
 }
+
+/*
+ * XBridge and PIC are driven through their 2GB 32-bit DMA window, so
+ * cap the bounce 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);
+}
arch/mips/sgi-ip27/ip27-xtalk.c +12 -3
index baa937b474a8..bd2ac24452f5 100644
@@ -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:
include/linux/platform_data/xtalk-bridge.h +2 -0
index 51e5001f2c05..171b5bcf56e4 100644
@@ -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 */
Signature · 1 line
2.47.3
#22 lore raw

Re: [PATCH 21/38] MIPS: SGI-IP27: route XBridge and PIC DMA through the 32-bit window

Quoted 10 lines · Imre Kaloz

XBridge does not honour the SN0-style 64-bit direct-mapped DMA address:
commands complete but no data moves. PIC moves no data either. Route both through the 2GB 32-bit window and cap bus_dma_limit at its top, so SWIOTLB bounces anything above it. PIC is flagged once a following patch enumerates it.

Only XBridge (part 0xd002) is flagged; a Bridge (0xc002) behind a Hub keeps the 64-bit direct map. The 64MB bounce pool comes from node 0 whenever DRAM passes 2GB, on Hub any multi-node machine. A PIC bus in PCI-X mode takes the window too; IRIX does not.

It's been a while, but IIRC XBridge also support 64-bit direct-mapped DMA addresses, just differently.

If you find a late 2.4 or early 2.6 kernel, it comes with an open source copy of the IRIX-style PCI code under arch/ia64/ that also includes the older chips never used on Altix. It might be worth to take a look at that for clues.

author
#23 lore raw

Re: [PATCH 21/38] MIPS: SGI-IP27: route XBridge and PIC DMA through the 32-bit window

Quoted 12 lines · Christoph Hellwig
Quoted 10 lines · Imre Kaloz

XBridge does not honour the SN0-style 64-bit direct-mapped DMA address:
commands complete but no data moves. PIC moves no data either. Route both through the 2GB 32-bit window and cap bus_dma_limit at its top, so SWIOTLB bounces anything above it. PIC is flagged once a following patch enumerates it.

Only XBridge (part 0xd002) is flagged; a Bridge (0xc002) behind a Hub keeps the 64-bit direct map. The 64MB bounce pool comes from node 0 whenever DRAM passes 2GB, on Hub any multi-node machine. A PIC bus in PCI-X mode takes the window too; IRIX does not.

Quoted 8 lines

It's been a while, but IIRC XBridge also support 64-bit direct-mapped DMA addresses, just differently.

If you find a late 2.4 or early 2.6 kernel, it comes with an open source copy of the IRIX-style PCI code under arch/ia64/ that also includes the older chips never used on Altix. It might be worth to take a look at that for clues.

Thanks, you were right. I dug out the SN1 pcibr from the old ia64 tree (Bedrock + XBridge, so about as close to this box as it gets), and the XBridge difference is bit 55 in the 64-bit address, which pcibr sets as the byte-swap attribute. Without it the Fuel's qla1280 never got any data back, which is what I based this patch on; with the SN0 address plus bit 55 disk reads match the 32-bit window bit for bit, on the Fuel's XBridge and on the Tezro's PIC alike. The IOC3 behind an XBridge still wants the window, so for v2 devices with a 64-bit mask get the direct map and the rest stay on the window.

Thanks again,
Imre

author
#24 lore raw

PATCH 22/38 MIPS: SGI-IP27: recognize the PXBow crossbar

In reply to Imre Kaloz

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.

---
 arch/mips/include/asm/xtalk/xwidget.h | 7 +++++++
 arch/mips/sgi-ip27/ip27-xtalk.c       | 2 ++
 2 files changed, 9 insertions(+)
arch/mips/include/asm/xtalk/xwidget.h +7 -0
index 978877e8a090..6a98e87b4711 100644
@@ -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,
arch/mips/sgi-ip27/ip27-xtalk.c +2 -0
index bd2ac24452f5..105977316391 100644
@@ -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;
Signature · 1 line
2.47.3
author
#25 lore raw

PATCH 23/38 MIPS: PCI: xtalk-bridge: recognize the PIC widget's 64-bit-only registers

In reply to Imre Kaloz

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.

---
 arch/mips/include/asm/pci/bridge.h | 70 +++++++++++++++++++++++++++---
 arch/mips/sgi-ip27/ip27-xtalk.c    | 30 ++++++++++---
 2 files changed, 89 insertions(+), 11 deletions(-)
arch/mips/include/asm/pci/bridge.h +64 -6
index 2ea0f1e2cd76..76c5db983748 100644
@@ -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
@@ -817,11 +832,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 */
arch/mips/sgi-ip27/ip27-xtalk.c +25 -5
index 105977316391..73b2eef96bdb 100644
@@ -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);
Signature · 1 line
2.47.3
author
#26 lore raw

PATCH 24/38 MIPS: PCI: xtalk-bridge: match PIC's register-level differences

In reply to Imre Kaloz

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 in the config accessors and in bridge_probe(), which also clears PIC's NO_SNOOP and RELAX_ORDER control bits.

---
 arch/mips/include/asm/pci/bridge.h | 11 ++++++
 arch/mips/pci/pci-xtalk-bridge.c   | 61 +++++++++++++++++++++++-------
 2 files changed, 59 insertions(+), 13 deletions(-)
arch/mips/include/asm/pci/bridge.h +11 -0
index 76c5db983748..81204c3d211d 100644
@@ -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 */
@@ -458,6 +465,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)
arch/mips/pci/pci-xtalk-bridge.c +48 -13

132 lines not on this page · raw message

Signature · 1 line
2.47.3
author
#27 lore raw

PATCH 25/38 rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base

In reply to Imre Kaloz

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.

---
 drivers/rtc/rtc-m48t35.c | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)
drivers/rtc/rtc-m48t35.c +15 -14
index 92f19bf997b2..69c313b4b6e8 100644
@@ -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 */
+	if (yrs < priv->epoch || yrs >= priv->epoch + 100)
 		return -EINVAL;
 
-	if (yrs > 169)
-		return -EINVAL;
-
-	if (yrs >= 100)
-		yrs -= 100;
+	yrs -= priv->epoch;
 
 	sec = bin2bcd(sec);
 	min = bin2bcd(min);
@@ -164,6 +154,7 @@ static int m48t35_probe(struct platform_device *pdev)
 				     pdev->name))
 		return -EBUSY;
 
+	priv->epoch = platform_get_device_id(pdev)->driver_data;
 	priv->baseaddr = res->start;
 	priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
 	if (!priv->reg)
@@ -178,11 +169,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 +191,4 @@ MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
 MODULE_DESCRIPTION("M48T35 RTC driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:rtc-m48t35");
+MODULE_ALIAS("platform:rtc-m48t35-ip35");
Signature · 1 line
2.47.3
#28 lore raw

Re: [PATCH 25/38] rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] rtc: m48t35: null pointer dereference in m48t35_probe() via driver_override --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] rtc: m48t35: null pointer dereference in m48t35_probe() via driver_override --

commit 697b757d9c2a3da93baaf70e4fd5d745beae964e
Author: Imre Kaloz <[email protected]>

rtc: m48t35: support the SGI IP35 timekeeper's 1968 year base

This commit updates the M48T35 RTC driver to support the 1968 year base for SGI IP35-family machines. It assigns a base epoch using the platform device ID table and bounds the year to the appropriate century.

Quoted 4 lines
drivers/rtc/rtc-m48t35.c +0 -0
index 92f19bf997b2f..69c313b4b6e84 100644

[ ... ]

drivers/rtc/rtc-m48t35.c static int m48t35_probe(struct platform_device *pdev) · line 157

Quoted 5 lines
drivers/rtc/rtc-m48t35.c +1 -0
@@ -164,6 +154,7 @@ static int m48t35_probe(struct platform_device *pdev)
 				     pdev->name))
 		return -EBUSY;
 
+	priv->epoch = platform_get_device_id(pdev)->driver_data;

[Severity: High]
Does this code handle devices bound via sysfs driver_override?

If the driver is manually bound to a device using the sysfs driver_override interface, the platform bus platform_match() function returns success without calling platform_match_id(). This leaves pdev->id_entry as null.

Since platform_get_device_id(pdev) returns pdev->id_entry, wouldn't dereferencing its result directly cause a null pointer dereference regression?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=25
author
#29 lore raw

PATCH 26/38 mfd: ioc3: add support for IOC4

In reply to Imre Kaloz

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, 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").

---
 arch/mips/include/asm/sn/ioc4.h               | 130 ++++++++
 drivers/mfd/ioc3.c                            | 289 +++++++++++++++---
 drivers/tty/serial/8250/8250_ioc3.c           |   5 +-
 include/linux/platform_data/sgi-ioc3-serial.h |  13 +
 4 files changed, 391 insertions(+), 46 deletions(-)
 create mode 100644 arch/mips/include/asm/sn/ioc4.h
 create mode 100644 include/linux/platform_data/sgi-ioc3-serial.h
arch/mips/include/asm/sn/ioc4.h new file +130 -0
new file mode 100644
index 000000000000..fca363c10035
@@ -0,0 +1,130 @@
+/* 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;
+};
+
+/* 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[(0x200 - 0x60) / 4];
+
+	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 */
drivers/mfd/ioc3.c +244 -45

451 lines not on this page · raw message

drivers/tty/serial/8250/8250_ioc3.c +4 -1
index 28e28076782b..5d3c0190f19e 100644
@@ -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);
include/linux/platform_data/sgi-ioc3-serial.h new file +13 -0
new file mode 100644
index 000000000000..ae4690f33125
@@ -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 */
Signature · 1 line
2.47.3
#30 lore raw

Re: [PATCH 26/38] mfd: ioc3: add support for IOC4

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] mfd: NULL pointer dereference due to complex pointer in pci_device_id match table - [Low] mfd…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] mfd: NULL pointer dereference due to complex pointer in pci_device_id match table
- [Low] mfd: ioc4_cells array is not static const
- [Low] mfd: ioc3: commit message capitalization violation
--

commit 6ec254319e574e5b3a17ca8a7e4c5206dfed5e04
Author: Imre Kaloz <[email protected]>

mfd: ioc3: add support for IOC4

This commit adds support for IOC4, which is IOC3's successor on IP35 Origin 350-class boards. It reuses the serial and PS/2 drivers of IOC3 and splits interrupt status into two banks.

Quoted 1 line

mfd: ioc3: add support for IOC4

[Severity: Low]
Does this commit title violate the MFD subsystem formatting requirements?

The MFD subsystem conventions mandate that the description after the subsystem prefix must always be capitalized (e.g., "mfd: ioc3: Add support...").

Quoted 4 lines
drivers/mfd/ioc3.c +0 -0
index 5f8ac364b610e..fd50bae0f10b3 100644

[ ... ]

drivers/mfd/ioc3.c static int ioc3_setup(struct ioc3_priv_data *ipd) · line 608

Quoted 5 lines
drivers/mfd/ioc3.c +1 -0
@@ -565,8 +605,11 @@ 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;

[Severity: High]
Is it safe to blindly retrieve the pointer from driver_data?

If the driver is bound to a device dynamically via the sysfs 'new_id' interface (where driver_data often defaults to 0), the 'var' pointer becomes NULL.

drivers/mfd/ioc3.c static int ioc3_mfd_probe(struct pci_dev *pdev, · line 622

Quoted 13 lines
drivers/mfd/ioc3.c +4 -1
 	struct ioc3_priv_data *ipd;
-	struct ioc3 __iomem *regs;
+	void __iomem *base;
+	unsigned int i;
+	u16 cmd;
 	int ret;
 
 	ret = pci_enable_device(pdev);
@@ -576,6 +619,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) {

[Severity: High]
Does this cause a NULL pointer dereference if 'var' is NULL?

As mentioned above, if 'driver_data' is 0, 'var' will be NULL, leading to a crash here. The MFD subsystem guidelines explicitly prohibit passing complex pointers through match data tables and mandate using an integer ID/enum, selecting the static const data in the probe function.

[ ... ]

Quoted 1 line
drivers/mfd/ioc3.c +0 -0
@@ -649,12 +708,152 @@ static void ioc3_mfd_remove(struct pci_dev *pdev)

[ ... ]

drivers/mfd/ioc3.c static void ioc3_mfd_remove(struct pci_dev *pdev) · line 779

Quoted 9 lines
drivers/mfd/ioc3.c +9 -0
+#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 struct mfd_cell ioc4_cells[] = {

[Severity: Low]
Should this array be declared as 'static const'?

The MFD subsystem guidelines strictly require that mfd_cell arrays must be defined as 'static const' to prevent dynamic platform data amendments and memory safety hazards. The newly introduced 'ioc4_cells' array is missing the 'const' qualifier.

Quoted 2 lines
drivers/mfd/ioc3.c +2 -0
+	IOC4_SERIAL_CELL(0),
+	IOC4_SERIAL_CELL(1),

[ ... ]

Quoted 9 lines
drivers/mfd/ioc3.c +9 -0
+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,

drivers/mfd/ioc3.c static void ioc3_mfd_remove(struct pci_dev *pdev) · line 858

Quoted 10 lines
drivers/mfd/ioc3.c +6 -1
+};
+
 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, 0x100a, PCI_ANY_ID, PCI_ANY_ID,
+	  0, 0, (kernel_ulong_t)&ioc4_variant },
 	{ 0, },
 };

[Severity: High]
Is it safe to embed complex pointers in the match table?

The patch passes a pointer to 'ioc3_variant' / 'ioc4_variant' through the pci_device_id match table's driver_data field. The MFD subsystem guidelines explicitly prohibit this and mandate using an integer ID/enum, selecting the static const data in the probe function instead.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=26
author
#31 lore raw

PATCH 27/38 MIPS: SGI-IP27: dispatch the early console between IOC3 and IOC4

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-console.c | 47 ++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)
arch/mips/sgi-ip27/ip27-console.c +46 -1
index 7737a88c6569..a1bcb4376f66 100644
@@ -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)
 		;
Signature · 1 line
2.47.3
author
#32 lore raw

PATCH 28/38 MIPS: SGI-IP27: add L1 system controller support

In reply to Imre Kaloz

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.

---
 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
arch/mips/Kconfig +1 -0
index 20212194e763..348217328c9d 100644
@@ -764,6 +764,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
arch/mips/include/asm/sn/l1.h new file +14 -0
new file mode 100644
index 000000000000..e61935e2fac8
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Entry points into the SGI L1 system controller driver.
+ *
+ * Copyright (C) 2026 Imre Kaloz <[email protected]>
+ */
+#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 */
arch/mips/sgi-ip27/Makefile +2 -2
index 9877fcc512b1..4b6aa641ccc6 100644
@@ -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
arch/mips/sgi-ip27/ip27-l1.c new file +423 -0

423 lines not on this page · raw message

Signature · 1 line
2.47.3
author
#33 lore raw

PATCH 29/38 MIPS: SGI-IP27: wire the L1 into reset and the Ethernet address

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-l1.c    | 266 ++++++++++++++++++++++++++++++++
 arch/mips/sgi-ip27/ip27-reset.c |  47 +++++-
 2 files changed, 305 insertions(+), 8 deletions(-)
arch/mips/sgi-ip27/ip27-l1.c +266 -0
index da3095b66cf1..25f9be350c0b 100644
@@ -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;
+}
arch/mips/sgi-ip27/ip27-reset.c +39 -8

90 lines not on this page · raw message

Signature · 1 line
2.47.3
author
#34 lore raw

PATCH 30/38 MIPS: PCI: xtalk-bridge: take the board part number from the L1

In reply to Imre Kaloz

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.

---
 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(-)
arch/mips/include/asm/sn/l1.h +7 -0
index e61935e2fac8..1a3b7fa0e213 100644
@@ -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 */
arch/mips/pci/pci-xtalk-bridge.c +35 -2
index 35ae7c423973..c97533653ef6 100644
@@ -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
@@ -583,6 +585,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;
@@ -657,8 +682,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)
arch/mips/sgi-ip27/ip27-l1.c +83 -0

89 lines not on this page · raw message

Signature · 1 line
2.47.3
author
#35 lore raw

PATCH 31/38 mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

In reply to Imre Kaloz

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")
---
 drivers/mfd/ioc3.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)
drivers/mfd/ioc3.c +46 -0
index fd50bae0f10b..34dbf42087ee 100644
@@ -517,6 +517,51 @@ static int ip30_sysboard_setup(struct ioc3_priv_data *ipd)
 	return ioc3_led_setup(ipd);
 }
 
+static 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)
 };
Signature · 1 line
2.47.3
maintainer
#36 lore raw

Re: [PATCH 31/38] mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

Quoted 7 lines · Imre Kaloz

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.

I received 26 and 31. Where is the rest of the set?

I'd like to have full context please.

Signature · 1 line
Lee Jones
author
#37 lore raw

Re: [PATCH 31/38] mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

Hi Lee,

Quoted 10 lines · Lee Jones
Quoted 7 lines · Imre Kaloz

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.

Quoted 4 lines

I received 26 and 31. Where is the rest of the set?

I'd like to have full context please.

Sorry about that, my send used get_maintainer per patch, so you only got the two mfd changes. The whole set with the cover letter is here:

https://lore.kernel.org/all/[email protected]/

The series adds SGI IP35 (Fuel, Tezro, Origin 300/3000) support to the IP27 platform code. The two mfd patches are the IOC3 side of it:
26 adds the IOC4 variant found on these boards and 31 adds the Fuel system board's cell table entry so its IOC3 gets a serial cell. They apply on mainline with nothing else from the set, 31 on top of 26, but are only useful together with the platform patches.

Happy to resend the full series to you if you prefer to have it in your inbox.

Thanks,
Imre

#38 lore raw

Re: [PATCH 31/38] mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

In reply to Imre Kaloz
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Low] mfd: missing const qualifier on ip34_m48t35_cells array - [Low] mfd: commit message description is…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] mfd: missing const qualifier on ip34_m48t35_cells array
- [Low] mfd: commit message description is not capitalized
--

commit 7d3e4b93a3f3a8869f87ab0153c8c6e16f517b90
Author: Imre Kaloz <[email protected]>

mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

This commit registers the Fuel system board's on-board IOC3 and its M48T35. Bridge-side detection was previously merged, but the cell table lacked an entry, causing the IOC3 to fall to the CAD DUO default and leaving Fuel with no ttyS0.

Quoted 1 line

mfd: ioc3: add IP34 system-board entry to ioc3_infos[]

[Severity: Low]
This isn't a regression, but does this violate the MFD subsystem capitalization convention? Should the description after the subsystem prefix be capitalized to "Add"?

drivers/mfd/ioc3.c static int ip30_sysboard_setup(struct ioc3_priv_data *ipd) · line 520

Quoted 8 lines
drivers/mfd/ioc3.c +1 -0
@@ -517,6 +517,51 @@ static int ip30_sysboard_setup(struct ioc3_priv_data *ipd)
 	return ioc3_led_setup(ipd);
 }
 
+static struct mfd_cell ip34_m48t35_cells[] = {

[Severity: Low]
This isn't a regression, but should ip34_m48t35_cells be declared as const? The MFD subsystem guidelines recommend cell arrays be static const to prevent accidental dynamic amendments in memory.

Quoted 6 lines
drivers/mfd/ioc3.c +6 -0
+	{
+		.name = "rtc-m48t35-ip35",
+		.resources = ioc3_m48t35_resources,
+		.num_resources = ARRAY_SIZE(ioc3_m48t35_resources),
+	}
+};

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=31
author
#39 lore raw

PATCH 32/38 net: ethernet: sgi: ioc3-eth: fall back to the platform MAC address

In reply to Imre Kaloz

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.

---
 drivers/net/ethernet/sgi/ioc3-eth.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/net/ethernet/sgi/ioc3-eth.c +10 -3
index 8702dfdf4c36..85b49e7b0f79 100644
@@ -857,9 +857,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");
+		eth_random_addr(mac_addr);
+	}
 
 	dev = alloc_etherdev(sizeof(struct ioc3_private));
 	if (!dev)
Signature · 1 line
2.47.3
author
#40 lore raw

PATCH 33/38 MIPS: SGI-IP27: don't register a w1 master on an XBridge or PIC

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-xtalk.c | 41 +++++++++++++++++++++++++--------
 1 file changed, 31 insertions(+), 10 deletions(-)
arch/mips/sgi-ip27/ip27-xtalk.c +31 -10
index 73b2eef96bdb..091186cb7a04 100644
@@ -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)
Signature · 1 line
2.47.3
author
#41 lore raw

PATCH 34/38 MIPS: SGI-IP27: report a Bedrock machine as SGI IP35

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-init.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
arch/mips/sgi-ip27/ip27-init.c +3 -2
index 15811fb3cdee..c92f1bef692e 100644
@@ -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)
Signature · 1 line
2.47.3
author
#42 lore raw

PATCH 35/38 MIPS: SGI-IP27: identify the machine by the L1 brick type

In reply to Imre Kaloz

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".

---
 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(+)
arch/mips/include/asm/sn/l1.h +1 -0
index 1a3b7fa0e213..f9fa8a8051e6 100644
@@ -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 */
arch/mips/sgi-ip27/ip27-init.c +68 -0

92 lines not on this page · raw message

arch/mips/sgi-ip27/ip27-l1.c +45 -0

57 lines not on this page · raw message

Signature · 1 line
2.47.3
author
#43 lore raw

PATCH 36/38 MIPS: SGI-IP27: register the Odyssey graphics widget

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-xtalk.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
arch/mips/sgi-ip27/ip27-xtalk.c +28 -0
index 091186cb7a04..b754bbb3ca33 100644
@@ -7,6 +7,7 @@
  * Generic XTALK initialization code
  */
 
+#include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/platform_device.h>
@@ -24,6 +25,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 +171,27 @@ static void bridge_platform_create(nasid_t nasid, int widget, int masterwid,
 	return;
 }
 
+static void odyssey_platform_create(nasid_t nasid, int widget)
+{
+	struct platform_device *pdev;
+	struct resource res;
+
+	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_simple("sgi-odyssey",
+					       PLATFORM_DEVID_AUTO, &res, 1);
+	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 +215,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);
+		break;
 	default:
 		pr_info("xtalk:n%d/%d unknown widget (0x%x)\n",
 			nasid, widget, partnum);
Signature · 1 line
2.47.3
author
#44 lore raw

PATCH 37/38 MIPS: SGI-IP27: take the console from the KLCONFIG header on IP35

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-init.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/mips/sgi-ip27/ip27-init.c +22 -0
index 7f16d7f437c9..6a15ba577b60 100644
@@ -6,6 +6,7 @@
  * Copyright (C) 2000 - 2001 by Kanoj Sarcar ([email protected])
  * 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), "%d", 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)
Signature · 1 line
2.47.3
author
#45 lore raw

PATCH 38/38 MIPS: SGI-IP27: give each CPU its own topology package id

In reply to Imre Kaloz

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.

---
 arch/mips/sgi-ip27/ip27-smp.c | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/mips/sgi-ip27/ip27-smp.c +8 -0
index fc4d9379a93e..c155582699d4 100644
@@ -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;
 		}
 	}
Signature · 1 line
2.47.3
#46 lore raw
In reply to Imre Kaloz
Quoted 29 lines · Imre Kaloz

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-17 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 18-27 add the crossbar, bridge and I/O parts of these machines:
PXBow, XBridge, PIC and IOC4.

Patches 28-38 add the L1 system controller and what it provides, the Odyssey graphics widget, the KLCONFIG console and a topology package id per CPU.

Tested on SGI Fuel and Tezro. The NMI dump reaches the 8250 console only with "MIPS: SGI-IP27: print the NMI dump on an nbcon console", posted separately.

On IP27 (Hub) machines:
- patch 17 moves the kernel up by 144KB, and the 144KB below it stays reserved and unused;
- with patch 21 SWIOTLB takes 64MB of node 0 on a multi-node Origin;
- the XBridge DMA window (patch 21) and the w1 change (patch 33) do nothing on a Hub with classic Bridges.

Given I have no IP27 boxes, none of this was booted on one. ip27_defconfig and ip30_defconfig build at every patch.

Quoted 7 lines

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 30), so on a machine with several bricks every such bridge gets that one board's setup.

Patch 36 registers the Odyssey graphics widget as the "sgi-odyssey" platform device used by the Odyssey DRM driver, a separate series.

thank you for your patches, I'm going on vacation tomorrow for 2 weeks, so I'll look at them after it.

Thomas.

Signature · 2 lines
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea.                                                [ RFC1925, 2.3 ]