[PATCH 7/9] board: hpe: Add GSC board support

Jorge Cisneros jorge.cisneros at hpe.com
Wed Apr 8 21:24:19 CEST 2026


Add board support for the HPE GSC (Gen Server Controller), the BMC
SoC used in HPE Gen12 ProLiant and Synergy servers.

Board files include:
- gsc_board.c: Board init, DRAM sizing from Denali memory controller,
  MAC address provisioning from memory-mapped EEPROM, and FDT fixups
  for network configuration based on CPLD xregisters
- common-phy.c: Shared Ethernet/DisplayPort PHY initialization with
  configurable transmitter amplitude and de-emphasis
- server_id.c: Custom command to read server identification from CPLD
- gsc.env: Default SPI flash boot environment
- gsc.h: Board configuration header with GICv3 and spin-table addresses

Signed-off-by: Jorge Cisneros <jorge.cisneros at hpe.com>
---
 board/hpe/gsc/Makefile                       |   3 +
 board/hpe/gsc/common-phy-wrapper-addresses.h |  86 +++++
 board/hpe/gsc/common-phy.c                   | 296 +++++++++++++++
 board/hpe/gsc/common-phy.h                   |   8 +
 board/hpe/gsc/gsc.env                        |   9 +
 board/hpe/gsc/gsc_board.c                    | 538 +++++++++++++++++++++++++++
 board/hpe/gsc/server_id.c                    |  51 +++
 include/configs/gsc.h                        |  25 ++
 8 files changed, 1016 insertions(+)

diff --git a/board/hpe/gsc/Makefile b/board/hpe/gsc/Makefile
new file mode 100644
index 00000000000..bc14c9d3bbf
--- /dev/null
+++ b/board/hpe/gsc/Makefile
@@ -0,0 +1,3 @@
+obj-y += gsc_board.o
+obj-y += common-phy.o
+obj-y += server_id.o
diff --git a/board/hpe/gsc/common-phy-wrapper-addresses.h b/board/hpe/gsc/common-phy-wrapper-addresses.h
new file mode 100644
index 00000000000..50bacd756f2
--- /dev/null
+++ b/board/hpe/gsc/common-phy-wrapper-addresses.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2022-2025 Hewlett-Packard Enterprise Development Company, L.P. */
+
+#ifndef _COMMON_PHY_WRAPPER_ADDRESSES_H
+#define _COMMON_PHY_WRAPPER_ADDRESSES_H
+
+#define kWRAP_CDB_ENABLE		0x8018
+#define kWRAP_CDB_BUSSEL		0x801c
+#define kWRAP_CDB_ACC_OK		0x8020
+#define kWRAP_CDB_REG_INIT_END		0x8024
+
+#define kWRAP_PHY_REFCLK_ACTIVE		0x803c
+#define kWRAP_PHY_CONFIG_RSTN		0x8040
+#define kWRAP_PHY_CMN_READY		0x8044
+#define kWRAP_PHY_LANE_EN_BITS		0x8048
+#define kWRAP_PHY_LANE_RSTN_BITS	0x804c
+
+#define kWRAP_PHY_PLL0_ENABLE		0x8050
+#define kWRAP_PHY_PLL0_STATUS		0x8054
+
+#define kWRAP_PHY_PLL1_ENABLE		0x8058
+#define kWRAP_PHY_PLL1_STATUS		0x805c
+
+#define kWRAP_PHY_DP_LANE_MODE		0x8060
+#define kWRAP_PHY_DP_LANESET_READY	0x8064
+#define kWRAP_LANE_ELEC_IDLE_BITS	0x8070
+#define kWRAP_DP_DUAL_MODE_ENABLE	0x8074
+#define kWRAP_PHY_DP_LINK_RATE		0x8078
+#define kWRAP_DISABLE_AUTO_RXLINK	0x807c
+
+#define kWRAP_TX_DEEMPH_LANE0		0x8080
+#define kWRAP_TX_DEEMPH_LANE1		0x8084
+#define kWRAP_TX_DEEMPH_LANE2		0x8088
+#define kWRAP_TX_DEEMPH_LANE3		0x808c
+
+#define kWRAP_TX_VMARGIN_LANE0		0x8090
+#define kWRAP_TX_VMARGIN_LANE1		0x8094
+#define kWRAP_TX_VMARGIN_LANE2		0x8098
+#define kWRAP_TX_VMARGIN_LANE3		0x809c
+
+#define kWRAP_XCVR_PLLCLK0_ENABLE	0x80a0
+#define kWRAP_XCVR_PLLCLK1_ENABLE	0x80a4
+#define kWRAP_XCVR_PLLCLK2_ENABLE	0x80a8
+#define kWRAP_XCVR_PLLCLK3_ENABLE	0x80ac
+
+#define kWRAP_XCVR_PLLCLK0_EN_ACK	0x80b0
+#define kWRAP_XCVR_PLLCLK1_EN_ACK	0x80b4
+#define kWRAP_XCVR_PLLCLK2_EN_ACK	0x80b8
+#define kWRAP_XCVR_PLLCLK3_EN_ACK	0x80bc
+
+#define kWRAP_XCVR_RATE_CHNG0_REQ	0x80c0
+#define kWRAP_XCVR_RATE_CHNG1_REQ	0x80c4
+#define kWRAP_XCVR_RATE_CHNG2_REQ	0x80c8
+#define kWRAP_XCVR_RATE_CHNG3_REQ	0x80cc
+
+#define kWRAP_XCVR_RATE_CHNG0_ACK	0x80d0
+#define kWRAP_XCVR_RATE_CHNG1_ACK	0x80d4
+#define kWRAP_XCVR_RATE_CHNG2_ACK	0x80d8
+#define kWRAP_XCVR_RATE_CHNG3_ACK	0x80dc
+
+#define kWRAP_XCVR_MODE_LANE0		0x80e0
+#define kWRAP_XCVR_MODE_LANE1		0x80e4
+#define kWRAP_XCVR_MODE_LANE2		0x80e8
+#define kWRAP_XCVR_MODE_LANE3		0x80ec
+
+#define kWRAP_PHY_RX_DATA_ENABLE	0x80f0
+#define kWRAP_PHY_TX_DATA_ENABLE	0x80f4
+
+#define kWRAP_PHY_XCVR0_SCANIN0	0x8100
+#define kWRAP_PHY_XCVR0_SCANIN1	0x8104
+#define kWRAP_PHY_XCVR1_SCANIN0	0x8108
+#define kWRAP_PHY_XCVR1_SCANIN1	0x810c
+#define kWRAP_PHY_XCVR2_SCANIN0	0x8110
+#define kWRAP_PHY_XCVR2_SCANIN1	0x8114
+#define kWRAP_PHY_XCVR3_SCANIN0	0x8118
+#define kWRAP_PHY_XCVR3_SCANIN1	0x811c
+#define kWRAP_PHY_XCVR0_SCANOUT0	0x8120
+#define kWRAP_PHY_XCVR0_SCANOUT1	0x8124
+#define kWRAP_PHY_XCVR1_SCANOUT0	0x8128
+#define kWRAP_PHY_XCVR1_SCANOUT1	0x812c
+#define kWRAP_PHY_XCVR2_SCANOUT0	0x8130
+#define kWRAP_PHY_XCVR2_SCANOUT1	0x8134
+#define kWRAP_PHY_XCVR3_SCANOUT0	0x8138
+#define kWRAP_PHY_XCVR3_SCANOUT1	0x813c
+
+#endif /* _COMMON_PHY_WRAPPER_ADDRESSES_H */
diff --git a/board/hpe/gsc/common-phy.c b/board/hpe/gsc/common-phy.c
new file mode 100644
index 00000000000..c39653727e9
--- /dev/null
+++ b/board/hpe/gsc/common-phy.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2022-2025 Hewlett-Packard Enterprise Development Company, L.P. */
+
+#include <dm.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#include "common-phy-wrapper-addresses.h"
+
+#define DP_TX_BASE			0xc0020000
+#define PHY_PIP_CONFIG_REG_INDEX_ADDR	0x0FC0
+#define PHY_PIP_CONFIG_REG_DATA_ADDR	0x0FC4
+
+/*
+ * Xreg 0x39 Layout - Transmitter Amplitude
+ *
+ * Bits | Desc
+ *  7   | Secondary Transmitter Disable
+ * 6:4  | Secondary Transmitter Amplitude
+ *  3   | Primary Transmitter Disable
+ * 2:0  | Primary Transmitter Amplitude
+ */
+#define V_AMPLT_400		0x4
+#define V_AMPLT_600		0x5
+#define V_AMPLT_800		0x6
+#define V_AMPLT_945		0x7
+
+/* Xreg 0x38 De-emphasis Levels */
+#define DE_EMPH_0		0x0
+#define DE_EMPH_1		0x1
+#define DE_EMPH_2		0x2
+#define DE_EMPH_3		0x3
+
+/* Xreg Mask and Shift */
+#define V_AMPLT_MASK			0x7
+#define DE_EMPH_MASK			0x3
+#define DISABLE_MASK			0x8
+#define SECONDARY_SHIFT_V_AMPLT		0x4
+#define SECONDARY_SHIFT_DE_EMPH		0x2
+
+static void common_phy_register_write(unsigned int reg, unsigned int data)
+{
+	writel(reg, DP_TX_BASE + PHY_PIP_CONFIG_REG_INDEX_ADDR);
+	writel(data, DP_TX_BASE + PHY_PIP_CONFIG_REG_DATA_ADDR);
+}
+
+static void common_phy_register_read(unsigned int reg, unsigned int *data)
+{
+	writel(reg, DP_TX_BASE + PHY_PIP_CONFIG_REG_INDEX_ADDR);
+	*data = readl(DP_TX_BASE + PHY_PIP_CONFIG_REG_DATA_ADDR);
+}
+
+static int poll_register(unsigned int reg, int value)
+{
+	unsigned int data = 0;
+	int retry_cnt = 20;
+
+	while (retry_cnt > 0) {
+		common_phy_register_read(reg, &data);
+		if (data == value)
+			return 0;
+		mdelay(1);
+		retry_cnt--;
+	}
+
+	pr_err("CommonPhy: polling failed: reg=0x%x expected=0x%x got=0x%x\n",
+	       reg, value, data);
+	return -1;
+}
+
+static void common_phy_get_deemph_and_vamplitude(unsigned int amplt,
+						  unsigned int emph,
+						  unsigned int *reg_amplt,
+						  unsigned int *reg_emph)
+{
+	/* Default: 400 mV, de-emphasis level 0 */
+	*reg_amplt = 0x00040404;
+	*reg_emph = 0x00000000;
+
+	if (emph > DE_EMPH_3) {
+		pr_err("CommonPhy: invalid config: amplitude 0x%x, de-emph 0x%x\n",
+		       amplt, emph);
+		return;
+	}
+
+	if (amplt == V_AMPLT_400) {
+		if (emph == DE_EMPH_0) {
+			*reg_amplt = 0x00040404;
+			*reg_emph = 0x00000000;
+		} else if (emph == DE_EMPH_1) {
+			*reg_amplt = 0x00030303;
+			*reg_emph = 0x00030404;
+		} else if (emph == DE_EMPH_2) {
+			*reg_amplt = 0x00010101;
+			*reg_emph = 0x00050606;
+		} else if (emph == DE_EMPH_3) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00060606;
+		}
+	} else if (amplt == V_AMPLT_600) {
+		if (emph == DE_EMPH_0) {
+			*reg_amplt = 0x00030303;
+			*reg_emph = 0x00000000;
+		} else if (emph == DE_EMPH_1) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00030404;
+		} else if (emph == DE_EMPH_2) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00050606;
+		} else if (emph == DE_EMPH_3) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00060606;
+		}
+	} else if (amplt == V_AMPLT_800) {
+		if (emph == DE_EMPH_0) {
+			*reg_amplt = 0x00040404;
+			*reg_emph = 0x00010101;
+		} else if (emph == DE_EMPH_1) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00030404;
+		} else if (emph == DE_EMPH_2) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00050606;
+		} else if (emph == DE_EMPH_3) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00060606;
+		}
+	} else if (amplt == V_AMPLT_945) {
+		if (emph == DE_EMPH_0) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00000000;
+		} else if (emph == DE_EMPH_1) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00030404;
+		} else if (emph == DE_EMPH_2) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00050606;
+		} else if (emph == DE_EMPH_3) {
+			*reg_amplt = 0x00000000;
+			*reg_emph = 0x00060606;
+		}
+	} else {
+		pr_err("CommonPhy: invalid config: amplitude 0x%x, de-emph 0x%x\n",
+		       amplt, emph);
+	}
+}
+
+/*
+ * Power On Initialization of Common PHY.
+ * Configures the shared PHY for Ethernet and Display Port operation.
+ */
+int common_phy_poweron_init(void)
+{
+	unsigned int xreg_amplt, xreg_emph, emph, amplt;
+	unsigned int rstn_bits = 0x0F;
+
+	/* 1. Clear registers to default un-initialized state */
+	common_phy_register_write(kWRAP_PHY_RX_DATA_ENABLE, 0x00);
+	common_phy_register_write(kWRAP_PHY_TX_DATA_ENABLE, 0x00);
+	common_phy_register_write(kWRAP_PHY_LANE_RSTN_BITS, 0x00);
+	common_phy_register_write(kWRAP_PHY_LANE_EN_BITS, 0x00);
+	common_phy_register_write(kWRAP_PHY_CONFIG_RSTN, 0x00);
+	common_phy_register_write(kWRAP_LANE_ELEC_IDLE_BITS, 0x0F);
+	common_phy_register_write(kWRAP_PHY_PLL0_ENABLE, 0x00);
+	common_phy_register_write(kWRAP_PHY_PLL1_ENABLE, 0x00);
+	common_phy_register_write(kWRAP_CDB_BUSSEL, 0x00);
+	common_phy_register_write(kWRAP_CDB_REG_INIT_END, 0x00);
+	common_phy_register_write(kWRAP_PHY_DP_LANE_MODE, 0x00);
+	common_phy_register_write(kWRAP_DISABLE_AUTO_RXLINK, 0x01);
+	common_phy_register_write(kWRAP_XCVR_MODE_LANE0, 0x00);
+	common_phy_register_write(kWRAP_XCVR_MODE_LANE1, 0x00);
+	common_phy_register_write(kWRAP_XCVR_MODE_LANE2, 0x00);
+	common_phy_register_write(kWRAP_XCVR_MODE_LANE3, 0x00);
+
+	/* 2. Release PHY lanes out of reset */
+	common_phy_register_write(kWRAP_PHY_CONFIG_RSTN, 0x01);
+
+	/* 3. Poll for internal initialization completion */
+	if (poll_register(kWRAP_CDB_ACC_OK, 0x01) != 0) {
+		pr_err("CommonPhy: kWRAP_CDB_ACC_OK polling failed\n");
+		return -1;
+	}
+
+	/* 4. Additional PHY register modifications */
+	common_phy_register_write(kWRAP_CDB_BUSSEL, 0x01);
+	common_phy_register_write(kWRAP_CDB_ENABLE, 0x01);
+
+	/* Set DP lanes for Dual Mode */
+	common_phy_register_write(0x4021, 0x00);
+	common_phy_register_write(0x4221, 0x00);
+
+	/* RX Lane Fixes B0 */
+	common_phy_register_write(0x4026, 0x124A);
+	common_phy_register_write(0x4226, 0x124A);
+	common_phy_register_write(0x4226, 0x124A);
+	common_phy_register_write(0x4626, 0x124A);
+
+	common_phy_register_write(kWRAP_CDB_BUSSEL, 0x00);
+	common_phy_register_write(kWRAP_CDB_ENABLE, 0x00);
+
+	/* 5. Set PHY initialization complete */
+	common_phy_register_write(kWRAP_CDB_REG_INIT_END, 0x01);
+
+	/* 6. Enable PLL 0 and PLL 1 */
+	common_phy_register_write(kWRAP_PHY_PLL0_ENABLE, 0x01);
+	common_phy_register_write(kWRAP_PHY_PLL1_ENABLE, 0x01);
+
+	/* 7. Program vswing/pre-emphasis for DP lanes 0 and 1 */
+	common_phy_register_write(kWRAP_TX_VMARGIN_LANE0, 0x00040404);
+	common_phy_register_write(kWRAP_TX_VMARGIN_LANE1, 0x00040404);
+	common_phy_register_write(kWRAP_TX_DEEMPH_LANE0, 0x00000000);
+	common_phy_register_write(kWRAP_TX_DEEMPH_LANE1, 0x00000000);
+
+	/* 8. Program vswing/pre-emphasis for Ethernet lanes 2 and 3 */
+	xreg_amplt = readb(0xd1000039);
+	xreg_emph = readb(0xd1000038);
+
+	debug("CommonPhy: amplitude=0x%x de-emph=0x%x\n",
+	      xreg_amplt, xreg_emph);
+
+	/* Primary transmitter */
+	if ((xreg_amplt & DISABLE_MASK) == 0) {
+		debug("CommonPhy: configuring primary transmitter\n");
+		common_phy_get_deemph_and_vamplitude(
+			xreg_amplt & V_AMPLT_MASK,
+			xreg_emph & DE_EMPH_MASK,
+			&amplt, &emph);
+		common_phy_register_write(kWRAP_TX_VMARGIN_LANE2, amplt);
+		common_phy_register_write(kWRAP_TX_DEEMPH_LANE2, emph);
+	} else {
+		debug("CommonPhy: primary transmitter disabled\n");
+		rstn_bits &= 0x0B;
+	}
+
+	/* Secondary transmitter */
+	if (((xreg_amplt >> SECONDARY_SHIFT_V_AMPLT) & DISABLE_MASK) == 0) {
+		debug("CommonPhy: configuring secondary transmitter\n");
+		common_phy_get_deemph_and_vamplitude(
+			(xreg_amplt >> SECONDARY_SHIFT_V_AMPLT) & V_AMPLT_MASK,
+			(xreg_emph >> SECONDARY_SHIFT_DE_EMPH) & DE_EMPH_MASK,
+			&amplt, &emph);
+		common_phy_register_write(kWRAP_TX_VMARGIN_LANE3, amplt);
+		common_phy_register_write(kWRAP_TX_DEEMPH_LANE3, emph);
+	} else {
+		debug("CommonPhy: secondary transmitter disabled\n");
+		rstn_bits &= 0x07;
+	}
+
+	/* 9. Take lanes out of Electrical Idle */
+	common_phy_register_write(kWRAP_LANE_ELEC_IDLE_BITS, 0x0);
+
+	/* 10. Enable PLL Clocks */
+	common_phy_register_write(kWRAP_XCVR_PLLCLK0_ENABLE, 0x01);
+	common_phy_register_write(kWRAP_XCVR_PLLCLK2_ENABLE, 0x01);
+	common_phy_register_write(kWRAP_XCVR_PLLCLK3_ENABLE, 0x01);
+
+	/* 11. Release lane resets */
+	common_phy_register_write(kWRAP_PHY_LANE_RSTN_BITS, rstn_bits);
+
+	/* 12. Enable Lanes (DisplayPort only) */
+	common_phy_register_write(kWRAP_PHY_LANE_EN_BITS, 0x0F);
+
+	/* 13. Poll for Common Ready */
+	if (poll_register(kWRAP_PHY_CMN_READY, 0x01) != 0) {
+		pr_err("CommonPhy: kWRAP_PHY_CMN_READY polling failed\n");
+		return -1;
+	}
+
+	/* 14. Poll for PLL status */
+	if (poll_register(kWRAP_XCVR_PLLCLK0_EN_ACK, 0x01) != 0) {
+		pr_err("CommonPhy: PLLCLK0_EN_ACK polling failed\n");
+		return -1;
+	}
+	if (poll_register(kWRAP_XCVR_PLLCLK1_EN_ACK, 0x01) != 0) {
+		pr_err("CommonPhy: PLLCLK1_EN_ACK polling failed\n");
+		return -1;
+	}
+	if (poll_register(kWRAP_XCVR_PLLCLK2_EN_ACK, 0x01) != 0) {
+		pr_err("CommonPhy: PLLCLK2_EN_ACK polling failed\n");
+		return -1;
+	}
+
+	/* 15. Poll for Lanes ready */
+	if (poll_register(kWRAP_PHY_DP_LANESET_READY, 0x0F) != 0) {
+		pr_err("CommonPhy: DP_LANESET_READY polling failed\n");
+		return -1;
+	}
+
+	/* 16. Enable TX data flow */
+	common_phy_register_write(kWRAP_PHY_TX_DATA_ENABLE, 0x01);
+
+	/* 17. Set DP Link Rate to 1.62 Gbps */
+	common_phy_register_write(kWRAP_PHY_DP_LINK_RATE, 0x6);
+
+	return 0;
+}
diff --git a/board/hpe/gsc/common-phy.h b/board/hpe/gsc/common-phy.h
new file mode 100644
index 00000000000..bb41ea21ffa
--- /dev/null
+++ b/board/hpe/gsc/common-phy.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _COMMON_PHY_H
+#define _COMMON_PHY_H
+
+int common_phy_poweron_init(void);
+
+#endif /* _COMMON_PHY_H */
diff --git a/board/hpe/gsc/gsc.env b/board/hpe/gsc/gsc.env
new file mode 100644
index 00000000000..9723c30ba40
--- /dev/null
+++ b/board/hpe/gsc/gsc.env
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+loadfit=sf probe;sf read 0x2000000 0x1a0000 0xa00000;
+recover_cmd=bootm 0xfc1a0000;
+spiboot=run loadfit; saveenv; bootm 0x2000000;
+run recover_cmd;
+while itest 0 < 1; do
+	echo KERNEL BOOT FAILURE!;
+done;
diff --git a/board/hpe/gsc/gsc_board.c b/board/hpe/gsc/gsc_board.c
new file mode 100644
index 00000000000..bc86fd07485
--- /dev/null
+++ b/board/hpe/gsc/gsc_board.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (C) 2019-2025 Hewlett-Packard Enterprise Development Company, L.P.
+
+#include <dm.h>
+#include <env.h>
+#include <ram.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <dm/uclass.h>
+#include <console.h>
+#include <asm/armv8/mmu.h>
+#include <fdt_support.h>
+#include <phy.h>
+#include <net.h>
+#include <i2c_eeprom.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ARCH_TIMER_CNTCR_REG	0xc0034000
+#define ARCH_TIMER_CNTFID0	0xc0034020
+#define ARCH_TIMER_FREQUENCY	50000000
+#define ARCH_TIMER_CNTCR_ENABLE	BIT(0)
+#define ARCH_TIMER_CNTCR_HDBG	BIT(1)
+
+#define DEFAULT_RESERVED_MEMORY_SIZE	(0x300000u)
+#define DEFAULT_VIDEO_RAM_SIZE		(0x1000000u)
+#define DENALI_ROW_DIFF			(0xCEFE219C)
+#define DENALI_ECC_ENABLE_DISABLE	(0xCEFE2134)
+#define DENALI_NON_ECC_REGION_ENABLED	(0xCEFE2170)
+#define DENALI_REG_NONECC_REGION_ONE	(0xCEFE2168)
+#define DENALI_REG_NONECC_REGION_TWO	(0xCEFE216C)
+#define VMEMOFF_ADDRESS			(0xc000001c)
+#define VIDEOMEM_ARRAY_SIZE		(7u)
+#define V_EEPROM_RO_START		(128)
+#define V_EEPROM_RO_MAC0		(V_EEPROM_RO_START + 4)
+#define V_EEPROM_RO_MAC1		(V_EEPROM_RO_START + 10)
+#define V_EEPROM_RO_SN			(1)
+#define V_EEPROM_RO_PN			(109)
+#define V_EEPROM_RO_PCA_SN		(144)
+#define V_EEPROM_RO_PCA_PN		(160)
+#define SSTRAP				0xc0000af0
+#define MEMID_BYTE_4			0xd1e10004
+#define MEMCGF_ADDRESS			0xc0000024
+
+#define GSC_MACB0		"/ahb/ethernet at c0004000/"
+#define GSC_MACB2		"/ahb/ethernet at c0006000/"
+#define GSC_MACB0_ETHERNET_PHY	"/ahb/ethernet at c0004000/ethernet-phy at 0/"
+#define GSC_MACB2_ETHERNET_PHY	"/ahb/ethernet at c0006000/ethernet-phy at 0/"
+#define GSC_I3C_HUB_PORT_0	"/ahb/i3c at c0003300/hub at 0,0/target-port at 0/"
+#define GSC_I3C_HUB_PORT_1	"/ahb/i3c at c0003300/hub at 0,0/target-port at 1/"
+#define GSC_I3C_HUB_PORT_2	"/ahb/i3c at c0003300/hub at 0,0/target-port at 2/"
+#define GSC_I3C_HUB_PORT_3	"/ahb/i3c at c0003300/hub at 0,0/target-port at 3/"
+
+/* Xregister Network Configuration Definitions v0x25 */
+#define GSC_PRIMARY_SECONDARY_CFG	0xd1000038
+#define GSC_SECONDARY_MAC_SHIFT		4
+#define GSC_SECONDARY_MAC_MASK		0x30
+#define GSC_PRIMARY_MAC_SHIFT		6
+#define GSC_PRIMARY_MAC_MASK		0xc0
+#define FAILOVER_SOFTSTRAP_MASK		0x6000
+#define GSC_PRIMARY_SECONDARY_PHY_TYPE	0xd100003b
+#define GSC_SECONDARY_TYPE_SHIFT	1
+#define GSC_SECONDARY_TYPE_MASK		0x0E
+#define GSC_PRIMARY_TYPE_SHIFT		4
+#define GSC_PRIMARY_TYPE_MASK		0x30
+#define GSC_PRIMARY_SPEED_DUPLEX	0xd100003c
+#define GSC_SECONDARY_SPEED_DUPLEX	0xd100003d
+#define GSC_DUPLEX_AUTO_MASK		0x08
+#define GSC_DUPLEX_FULL_MASK		0x04
+#define GSC_SPEED_AUTO_MASK		0x80
+#define GSC_SPEED_1000_MASK		0x40
+
+#include "common-phy.h"
+
+static int get_eeprom_val(uint offset, u8 *v_ptr, size_t len)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_first_device_err(UCLASS_I2C_EEPROM, &dev);
+	if (ret) {
+		debug("%s Uclass_first_device_err %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = i2c_eeprom_read(dev, offset, v_ptr, len);
+	if (ret) {
+		debug("%s Read eeprom failure %d\n", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __ft_add_fixed_link(void *blob, int nodeoff)
+{
+	int rc;
+
+	nodeoff = fdt_add_subnode(blob, nodeoff, "fixed-link");
+	if (nodeoff < 0) {
+		pr_err("Failed to add fixed-link subnode %d\n", nodeoff);
+		return nodeoff;
+	}
+
+	rc = fdt_setprop_u32(blob, nodeoff, "speed", 1000);
+	if (rc < 0) {
+		pr_err("Failed to set speed property: %d\n", rc);
+		return rc;
+	}
+
+	rc = fdt_setprop(blob, nodeoff, "full-duplex", NULL, 0);
+	if (rc < 0) {
+		pr_err("Failed to set full-duplex property: %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int set_vpd_val(void *blob, uint offset, u8 *val, size_t len,
+		       char *name)
+{
+	int ret, i;
+	size_t str_len;
+	int actual_len = 0;
+
+	ret = get_eeprom_val(offset, val, len);
+	if (ret) {
+		pr_err("Failed to read %s from veeprom: %d\n", name, ret);
+		return ret;
+	}
+
+	for (i = 0; i < len - 1; i++) {
+		if (val[i] == '\0' || val[i] < 0x20 || val[i] > 0x7E) {
+			actual_len = i;
+			break;
+		}
+	}
+
+	if (actual_len == 0 && i == len - 1)
+		actual_len = len - 1;
+
+	val[actual_len] = '\0';
+	str_len = actual_len + 1;
+
+	ret = fdt_setprop(blob, fdt_path_offset(blob, "/"), name, val, str_len);
+	if (ret < 0) {
+		pr_err("Failed to set %s in DTB: %s\n", name,
+		       fdt_strerror(ret));
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ft_board_setup_vpd(void *blob, struct bd_info *bd)
+{
+	if (IS_ENABLED(CONFIG_GSC_PATCH_KERNEL_DTS_VPD)) {
+		u8 val[16];
+
+		set_vpd_val(blob, V_EEPROM_RO_SN, val, 16, "sn");
+		set_vpd_val(blob, V_EEPROM_RO_PN, val, 16, "pn");
+		set_vpd_val(blob, V_EEPROM_RO_PCA_SN, val, 16, "serial-number");
+		set_vpd_val(blob, V_EEPROM_RO_PCA_PN, val, 16, "model");
+	}
+
+	return 0;
+}
+
+static int ft_board_config_i3c_hub(void *blob, struct bd_info *bd)
+{
+	static const char * const port_paths[] = {
+		GSC_I3C_HUB_PORT_0,
+		GSC_I3C_HUB_PORT_1,
+		GSC_I3C_HUB_PORT_2,
+		GSC_I3C_HUB_PORT_3,
+	};
+	u8 cpu;
+	int nodeoff, rc, i;
+
+	if (!IS_ENABLED(CONFIG_GSC_I3C_HUB_DT))
+		return 0;
+
+	cpu = readb(MEMID_BYTE_4);
+	if (cpu == 0) {
+		pr_err("Error: CPU Presence = 0\n");
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(port_paths); i++) {
+		if (cpu & BIT(i))
+			continue;
+
+		nodeoff = fdt_path_offset(blob, port_paths[i]);
+		if (nodeoff < 0) {
+			pr_err("Failed to find I3C hub port %d\n", i);
+			continue;
+		}
+
+		rc = fdt_del_node(blob, nodeoff);
+		if (rc < 0)
+			pr_err("Failed to delete I3C hub port %d: %d\n", i, rc);
+	}
+
+	return 0;
+}
+
+int ft_board_setup(void *blob, struct bd_info *bd)
+{
+	int rc, nodeoff;
+	unsigned int tmp;
+	u8 val;
+	u8 mac2_is_primary = 0;
+
+	rc = ft_board_config_i3c_hub(blob, bd);
+	if (rc)
+		pr_err("I3C Hub DT Adjustment failed: %d\n", rc);
+
+	tmp = readl(SSTRAP);
+
+	if (tmp & FAILOVER_SOFTSTRAP_MASK) {
+		val = (readb(GSC_PRIMARY_SECONDARY_CFG) & GSC_PRIMARY_MAC_MASK)
+				>> GSC_PRIMARY_MAC_SHIFT;
+		if (val == 0x00) {
+			val = (readb(GSC_PRIMARY_SECONDARY_PHY_TYPE) &
+					GSC_PRIMARY_TYPE_MASK)
+					>> GSC_PRIMARY_TYPE_SHIFT;
+			if (val == 0x01) {
+				nodeoff = fdt_path_offset(blob, GSC_MACB0);
+				if (nodeoff < 0) {
+					pr_err("Failed to find macb0 node\n");
+					return 0;
+				}
+
+				rc = fdt_delprop(blob, nodeoff, "phy-handle");
+				if (rc < 0) {
+					pr_err("Failed to delete phy-handle\n");
+					return 0;
+				}
+
+				nodeoff = fdt_path_offset(blob, GSC_MACB0_ETHERNET_PHY);
+				if (nodeoff < 0) {
+					pr_err("Failed to find macb0 phy node\n");
+					return 0;
+				}
+
+				rc = fdt_del_node(blob, nodeoff);
+				if (rc < 0) {
+					pr_err("Failed to delete phy node\n");
+					return 0;
+				}
+			}
+
+			val = readb(GSC_PRIMARY_SPEED_DUPLEX);
+			if (val == (GSC_DUPLEX_FULL_MASK | GSC_SPEED_1000_MASK)) {
+				nodeoff = fdt_path_offset(blob, GSC_MACB0);
+				if (nodeoff < 0) {
+					pr_err("Failed to find macb0 node\n");
+					return 0;
+				}
+
+				rc = __ft_add_fixed_link(blob, nodeoff);
+				if (rc < 0) {
+					pr_err("Failed to add macb0 fixed-link\n");
+					return 0;
+				}
+			}
+
+			rc = fdt_find_and_setprop(blob, GSC_MACB0, "status",
+						  "okay", 6, 1);
+			if (rc < 0)
+				pr_err("Failed to set okay (%s)\n", GSC_MACB0);
+
+		} else if (val == 0x02) {
+			mac2_is_primary = 1;
+			rc = fdt_find_and_setprop(blob, GSC_MACB2, "status",
+						  "okay", 6, 1);
+			if (rc < 0)
+				pr_err("Failed to set okay (%s)\n", GSC_MACB2);
+		}
+
+		val = (readb(GSC_PRIMARY_SECONDARY_CFG) & GSC_SECONDARY_MAC_MASK)
+				>> GSC_SECONDARY_MAC_SHIFT;
+		if (val == 0x00) {
+			if (mac2_is_primary) {
+				rc = fdt_find_and_setprop(blob, GSC_MACB0,
+							  "status",
+							  "disabled", 10, 1);
+				if (rc < 0)
+					pr_err("Failed to disable (%s)\n",
+					       GSC_MACB0);
+			} else {
+				val = (readb(GSC_PRIMARY_SECONDARY_PHY_TYPE) &
+					GSC_SECONDARY_TYPE_MASK)
+					>> GSC_SECONDARY_TYPE_SHIFT;
+				if (val == 0x00) {
+					rc = fdt_find_and_setprop(blob,
+								  GSC_MACB2,
+								  "status",
+								  "disabled",
+								  10, 1);
+					if (rc < 0)
+						pr_err("Failed to disable (%s)\n",
+						       GSC_MACB2);
+				}
+			}
+		}
+	} else {
+		/* Failover mode: configure 1000base-x */
+		nodeoff = fdt_path_offset(blob, GSC_MACB0);
+		if (nodeoff < 0) {
+			pr_err("Failed to find macb0 node\n");
+			return 0;
+		}
+
+		rc = fdt_delprop(blob, nodeoff, "phy-handle");
+		if (rc < 0) {
+			pr_err("Failed to delete phy-handle\n");
+			return 0;
+		}
+
+		nodeoff = fdt_path_offset(blob, GSC_MACB0_ETHERNET_PHY);
+		if (nodeoff < 0) {
+			pr_err("Failed to find macb0 phy node\n");
+			return 0;
+		}
+
+		rc = fdt_del_node(blob, nodeoff);
+		if (rc < 0) {
+			pr_err("Failed to delete phy node\n");
+			return 0;
+		}
+
+		rc = fdt_find_and_setprop(blob, GSC_MACB0, "phy-mode",
+					  "1000base-x", 12, 0);
+		if (rc < 0) {
+			pr_err("Failed to set phy-mode\n");
+			return 0;
+		}
+
+		nodeoff = fdt_path_offset(blob, GSC_MACB0);
+		if (nodeoff < 0) {
+			pr_err("Failed to find macb0 node\n");
+			return 0;
+		}
+
+		rc = __ft_add_fixed_link(blob, nodeoff);
+		if (rc < 0) {
+			pr_err("Failed to add macb0 fixed-link\n");
+			return 0;
+		}
+
+		rc = fdt_find_and_setprop(blob, GSC_MACB0, "status",
+					  "okay", 6, 1);
+		if (rc < 0)
+			pr_err("Failed to set okay (%s)\n", GSC_MACB0);
+
+		rc = fdt_find_and_setprop(blob, GSC_MACB2, "status",
+					  "disabled", 10, 1);
+		if (rc < 0)
+			pr_err("Failed to disable (%s)\n", GSC_MACB2);
+	}
+
+	ft_board_setup_vpd(blob, bd);
+
+	return 0;
+}
+
+int board_early_init_f(void)
+{
+	/* Apply runtime FDT changes to U-Boot's own device tree */
+	ft_board_setup((void *)gd->fdt_blob, gd->bd);
+
+	return 0;
+}
+
+static struct mm_region gsc_mem_map[] = {
+	{
+		.virt = 0x00000000,
+		.phys = 0x00000000,
+		.size = 0x00000000,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_OUTER_SHARE
+	}, {
+		.virt = 0x40000000,
+		.phys = 0x40000000,
+		.size = 0x00000000,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) |
+			 PTE_BLOCK_OUTER_SHARE
+	}, {
+		.virt = 0xa0008000,
+		.phys = 0xa0008000,
+		.size = 0x10000,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) |
+			 PTE_BLOCK_OUTER_SHARE
+	}, {
+		.virt = 0xc0000000,
+		.phys = 0xc0000000,
+		.size = 0x1f000000,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+			 PTE_BLOCK_NON_SHARE |
+			 PTE_BLOCK_PXN |
+			 PTE_BLOCK_UXN
+	}, {
+		.virt = 0xfc000000,
+		.phys = 0xfc000000,
+		.size = 0x04000000,
+		.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+			 PTE_BLOCK_OUTER_SHARE
+	}, {
+		/* List terminator */
+		0,
+	}
+};
+
+struct mm_region *mem_map = gsc_mem_map;
+
+static void calculate_available_dram_size(void)
+{
+	phys_size_t total_ddr_size = 0x40000000u;
+	phys_size_t non_ecc_region_1_size = 0u;
+	phys_size_t non_ecc_region_2_size = 0u;
+	u8 index = 0u;
+	phys_size_t video_mem_size_a[VIDEOMEM_ARRAY_SIZE] = {
+		0x0, 0x400000, 0x800000, 0x1000000,
+		0x2000000, 0x4000000, 0x8000000
+	};
+
+	total_ddr_size = 1u << (17u - ((readl(DENALI_ROW_DIFF) >> 24u) & 0x7));
+	total_ddr_size = (total_ddr_size / 8u) * (128u * 1024u);
+
+	if (((readl(DENALI_ECC_ENABLE_DISABLE) >> 16u) & 0x3) > 0u) {
+		total_ddr_size = (total_ddr_size / 8u) * 7u;
+
+		if ((readl(DENALI_NON_ECC_REGION_ENABLED) & 0x1) > 0u) {
+			non_ecc_region_1_size =
+				((readl(DENALI_REG_NONECC_REGION_ONE) >> 16u) & 0xfff) -
+				(readl(DENALI_REG_NONECC_REGION_ONE) & 0xfff);
+			non_ecc_region_1_size <<= 20u;
+			total_ddr_size -= non_ecc_region_1_size;
+		}
+
+		if ((readl(DENALI_NON_ECC_REGION_ENABLED) & 0x2) > 0u) {
+			non_ecc_region_2_size =
+				((readl(DENALI_REG_NONECC_REGION_TWO) >> 16u) & 0xfff) -
+				(readl(DENALI_REG_NONECC_REGION_TWO) & 0xfff);
+			non_ecc_region_2_size <<= 20u;
+			total_ddr_size -= non_ecc_region_2_size;
+		}
+	} else {
+		index = readl(VMEMOFF_ADDRESS) & 0x7;
+		if (index < VIDEOMEM_ARRAY_SIZE)
+			total_ddr_size -= video_mem_size_a[index];
+		else
+			total_ddr_size -= DEFAULT_VIDEO_RAM_SIZE;
+		total_ddr_size -= DEFAULT_RESERVED_MEMORY_SIZE;
+	}
+
+	gd->ram_size = total_ddr_size;
+	gsc_mem_map[0].size = total_ddr_size;
+	if ((17u - ((readl(DENALI_ROW_DIFF) >> 24u) & 0x7)) == 16u)
+		gsc_mem_map[1].size = total_ddr_size;
+}
+
+int dram_init(void)
+{
+	calculate_available_dram_size();
+	return 0;
+}
+
+static int get_eeprom_mac(uint offset, u8 *v_mac)
+{
+	return get_eeprom_val(offset, v_mac, 6);
+}
+
+static int set_mac_addr(const char *name, uint offset)
+{
+	int ret;
+	u8 v_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+	ret = get_eeprom_mac(offset, v_mac);
+	if (ret || !is_valid_ethaddr(v_mac)) {
+		pr_err("Warning: MAC %s - %pM ", name, v_mac);
+		if (IS_ENABLED(CONFIG_NET_RANDOM_ETHADDR)) {
+			net_random_ethaddr(v_mac);
+			pr_err("invalid, using random MAC - %pM\n", v_mac);
+		} else {
+			pr_err("is not valid\n");
+			return -1;
+		}
+	}
+
+	env_set(".flags", name);
+	env_set(name, NULL);
+	eth_env_set_enetaddr(name, v_mac);
+	debug("%s MAC Address %pM\n", name, v_mac);
+
+	return 0;
+}
+
+int board_init(void)
+{
+	return 0;
+}
+
+int misc_init_r(void)
+{
+	set_mac_addr("ethaddr", V_EEPROM_RO_MAC0);
+	set_mac_addr("eth1addr", V_EEPROM_RO_MAC1);
+	return 0;
+}
+
+static void arch_timer_start_reg_set(void)
+{
+	writel(ARCH_TIMER_FREQUENCY, ARCH_TIMER_CNTFID0);
+	writel(ARCH_TIMER_CNTCR_ENABLE | ARCH_TIMER_CNTCR_HDBG,
+	       ARCH_TIMER_CNTCR_REG);
+}
+
+int arch_early_init_r(void)
+{
+	if (IS_ENABLED(CONFIG_ARMV8_SPIN_TABLE)) {
+		writel(gd->relocaddr, SECONDARY_RELEASE_ADDR);
+		dsb();
+		asm volatile("sev");
+	}
+
+	arch_timer_start_reg_set();
+
+	if (common_phy_poweron_init() == 0)
+		debug("CommonPhy poweron init succeeded\n");
+	else
+		pr_err("CommonPhy poweron init failed\n");
+
+	return 0;
+}
diff --git a/board/hpe/gsc/server_id.c b/board/hpe/gsc/server_id.c
new file mode 100644
index 00000000000..acfcc889f87
--- /dev/null
+++ b/board/hpe/gsc/server_id.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GSC server_id command
+ *
+ * (C) Copyright 2019-2025 Hewlett Packard Enterprise Development LP.
+ * Author: Jorge Cisneros <jorge.cisneros at hpe.com>
+ */
+
+#include <env.h>
+#include <asm/io.h>
+#include <command.h>
+
+#define REGISTER_BASE_ADDRESS	0xD1000000
+#define SERVER_ID_OFFSET	0x0
+#define SERVER_ID_MASK		0xFFFF00
+
+static unsigned int get_server_id(void)
+{
+	unsigned int server_id;
+
+	server_id = readl(REGISTER_BASE_ADDRESS + SERVER_ID_OFFSET);
+	server_id &= SERVER_ID_MASK;
+	server_id >>= 8;
+	return server_id;
+}
+
+static int do_server_id(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	unsigned int server_id = get_server_id();
+	char env_value[16];
+
+	if (argc == 2) {
+		if (strcmp(argv[1], "show") == 0)
+			printf("Server ID: 0x%04X\n", server_id);
+		else
+			printf("Usage: %s <show>\n", cmdtp->name);
+	} else {
+		snprintf(env_value, sizeof(env_value), "0x%04x", server_id);
+		env_set("server_id", env_value);
+	}
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	server_id, 2, 0, do_server_id,
+	"Get the server ID from the GSC registers",
+	"<show> - Show the server id instead of setting the environment variable\n"
+	"         otherwise set an environment variable \"server_id\" with the value\n"
+);
diff --git a/include/configs/gsc.h b/include/configs/gsc.h
new file mode 100644
index 00000000000..6a199f52674
--- /dev/null
+++ b/include/configs/gsc.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * GSC board configuration
+ *
+ * (C) Copyright 2019-2025 Hewlett Packard Enterprise Development LP.
+ */
+
+#ifndef _GSC_H_
+#define _GSC_H_
+
+#include <linux/sizes.h>
+
+#define CFG_SYS_SDRAM_BASE		0x0
+#define CFG_SYS_INIT_RAM_ADDR		0x0
+#define CFG_SYS_INIT_RAM_SIZE		0x100000
+#define CFG_SYS_INIT_SP_OFFSET \
+	(CFG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
+#define CFG_SYS_INIT_SP_ADDR \
+	(CFG_SYS_INIT_RAM_ADDR + CFG_SYS_INIT_SP_OFFSET)
+
+#define GICD_BASE			0xce000000
+#define GICR_BASE			0xce060000
+#define SECONDARY_RELEASE_ADDR		0xa0008048
+
+#endif

-- 
2.43.0



More information about the U-Boot mailing list