[Skiboot] [PATCH 5/7] hdat: Add BMC and LPC IOPATH support

Oliver O'Halloran oohall at gmail.com
Fri Jan 13 17:56:20 AEDT 2017


BMC based systems use the LPC bus for communicating between the host and
service processor. This is represented as a "SP IOPATH" in the HDAT and
this patch adds parsing and generation of the devicetree entries to
describe this.

Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
---
 hdata/fsp.c   | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hdata/spira.h |  35 +++++++++++++
 2 files changed, 198 insertions(+)

diff --git a/hdata/fsp.c b/hdata/fsp.c
index 069a2d189d9f..933945af4b37 100644
--- a/hdata/fsp.c
+++ b/hdata/fsp.c
@@ -223,6 +223,165 @@ static void fsp_create_links(const void *spss, int index,
 	free(links);
 }
 
+static struct dt_node *add_lpc_io_node(struct dt_node *parent,
+	const char *name, u32 offset, u32 size)
+{
+	struct dt_node *n;
+	char buffer[32];
+
+	/*
+	 * LPC bus addresses have strange DT names, they have the
+	 * Bus address space embedded into the unit address e.g.
+	 * serial at i3f8 - refers to offset 0x3f8 in the IO space
+	 */
+
+	snprintf(buffer, sizeof(name), "%s at i%x", name, offset);
+	n = dt_new(parent, buffer);
+	assert(n);
+
+	/* first address cell of 1 indicates the LPC IO space */
+	dt_add_property_cells(n, "reg", 1, offset, size);
+
+	return n;
+}
+
+static void add_uart(const struct spss_iopath *iopath, struct dt_node *lpc)
+{
+	struct dt_node *serial;
+	u64 base;
+
+	/* XXX: The spec says this is supposed to be a MMIO address.
+	 *      However, in practice we get an LPC IO Space offset.
+	 */
+	base = be64_to_cpu(iopath->lpc.uart_base);
+
+	serial = add_lpc_io_node(lpc, "serial", base,
+		be32_to_cpu(iopath->lpc.uart_size));
+
+	dt_add_property_string(serial, "compatible", "ns16550");
+
+	dt_add_property_cells(serial, "current-speed",
+		be32_to_cpu(iopath->lpc.uart_baud));
+	dt_add_property_cells(serial, "clock-frequency",
+		be32_to_cpu(iopath->lpc.uart_clk));
+	dt_add_property_cells(serial, "serirq-interrupt",
+		be32_to_cpu(iopath->lpc.uart_int_number));
+
+	prlog(PR_DEBUG, "LPC UART: base addr = %#" PRIx64" (%#" PRIx64 ") size = %#x clk = %u, baud = %u\n",
+		be64_to_cpu(iopath->lpc.uart_base),
+		base,
+		be32_to_cpu(iopath->lpc.uart_size),
+		be32_to_cpu(iopath->lpc.uart_clk),
+		be32_to_cpu(iopath->lpc.uart_baud));
+}
+
+static void bmc_create_node(const struct HDIF_common_hdr *sp)
+{
+	u32 fw_bar, io_bar, mem_bar, internal_bar;
+	const struct spss_iopath *iopath;
+	struct dt_node *lpcm, *lpc, *n;
+	u64 lpcm_base, lpcm_end;
+	int chip_id;
+
+	iopath = HDIF_get_iarray_item(sp, SPSS_IDATA_SP_IOPATH, 0, NULL);
+
+	if (be16_to_cpu(iopath->iopath_type) != SPSS_IOPATH_TYPE_LPC) {
+		prerror("BMC: Non-LPC IOPATH, this is probably broken\n");
+		return;
+	}
+
+	/*
+	 * For now we only instantiate the LPC node for the LPC that is used
+	 * for Host <-> BMC comms. The secondary LPCs can be skipped.
+	 */
+	if (be16_to_cpu(iopath->lpc.link_status) != LPC_STATUS_ACTIVE)
+		return;
+
+#define GB (1024ul * 1024ul * 1024ul)
+#define MMIO_LPC_BASE_P9 0x6030000000000ul
+#define MMIO_STRIDE_P9     0x40000000000ul
+
+	chip_id = be32_to_cpu(iopath->lpc.chip_id);
+
+	lpcm_base = MMIO_LPC_BASE_P9 + MMIO_STRIDE_P9 * chip_id;
+	lpcm = dt_new_addr(dt_root, "lpcm-opb", lpcm_base);
+	assert(lpcm);
+
+	dt_add_property_cells(lpcm, "#address-cells", 1);
+	dt_add_property_cells(lpcm, "#size-cells", 1);
+	dt_add_property_strings(lpcm, "compatible",
+		"ibm,power9-lpcm-opb", "simple-bus");
+	dt_add_property_u64s(lpcm, "reg", lpcm_base, 0x100000000ul);
+
+	dt_add_property_cells(lpcm, "ibm,chip-id", chip_id);
+
+	/* Setup the ranges for the MMIO LPC */
+	lpcm_end = lpcm_base + 2 * GB;
+	dt_add_property_cells(lpcm, "ranges",
+		0x00000000, hi32(lpcm_base), lo32(lpcm_base), 2 * GB,
+		0x80000000, hi32(lpcm_end),  lo32(lpcm_end),  2 * GB);
+
+	/*
+	 * Despite the name the "BAR" values provided through the HDAT are
+	 * the base addresses themselves rather than the BARs
+	 */
+	fw_bar = be32_to_cpu(iopath->lpc.firmware_bar);
+	mem_bar = be32_to_cpu(iopath->lpc.memory_bar);
+	io_bar = be32_to_cpu(iopath->lpc.io_bar);
+	internal_bar = be32_to_cpu(iopath->lpc.internal_bar);
+
+	prlog(PR_DEBUG, "LPC: IOPATH chip id = %x\n", chip_id);
+	prlog(PR_DEBUG, "LPC: FW BAR       = %#x\n", fw_bar);
+	prlog(PR_DEBUG, "LPC: MEM BAR      = %#x\n", mem_bar);
+	prlog(PR_DEBUG, "LPC: IO BAR       = %#x\n", io_bar);
+	prlog(PR_DEBUG, "LPC: Internal BAR = %#x\n", internal_bar);
+
+	/*
+	 * The internal address space BAR actually points to the LPC master
+	 * registers. So we "fix" it by masking off the low bits.
+	 *
+	 * XXX: we probably need separate base addresses for all these things
+	 */
+	internal_bar &= 0xf0000000;
+
+	/* Add the various internal bus devices */
+	n = dt_new_addr(lpcm, "opb-master", internal_bar + 0x10000);
+	dt_add_property_string(n, "compatible", "ibm,power9-lpcm-opb-master");
+	dt_add_property_cells(n, "reg", internal_bar + 0x10000, 0x60);
+
+	n = dt_new_addr(lpcm, "opb-arbiter", internal_bar + 0x11000);
+	dt_add_property_string(n, "compatible", "ibm,power9-lpcm-opb-arbiter");
+	dt_add_property_cells(n, "reg", internal_bar + 0x11000, 0x8);
+
+	n = dt_new_addr(lpcm, "lpc-controller", internal_bar + 0x12000);
+	dt_add_property_string(n, "compatible", "ibm,power9-lpc-controller");
+	dt_add_property_cells(n, "reg", internal_bar + 0x12000, 0x100);
+
+	/*
+	 * FIXME: lpc at 0 might not be accurate, but i'm pretty sure
+	 * lpc at f0000000 isn't right either.
+	 */
+	lpc = dt_new_addr(lpcm, "lpc", 0x0);
+	dt_add_property_cells(lpc, "#address-cells", 2);
+	dt_add_property_cells(lpc, "#size-cells", 1);
+	dt_add_property_strings(lpc, "compatible", "ibm,power9-lpc");
+
+	dt_add_property_cells(lpc, "ranges",
+		0, 0, mem_bar, 0x10000000, /* MEM space */
+		1, 0, io_bar,  0x00010000, /* IO space  */
+		/* we don't expose the internal space */
+		3, 0, fw_bar,  0x10000000  /* FW space  */
+	);
+
+	add_uart(iopath, lpc);
+
+	/* BT device info isn't currently populated */
+	prlog(PR_DEBUG, "LPC: BT [%#"PRIx64", %#x] sms_int: %u, bmc_int: %u\n",
+		iopath->lpc.bt_base, iopath->lpc.bt_size,
+		iopath->lpc.bt_sms_int_num, iopath->lpc.bt_bmc_response_int_num
+	);
+}
+
 void fsp_parse(void)
 {
 	struct dt_node *fsp_root = NULL, *fsp_node;
@@ -255,6 +414,10 @@ void fsp_parse(void)
 
 			break;
 
+		case SP_BMC:
+			bmc_create_node(sp);
+			break;
+
 		case SP_BAD:
 			break;
 
diff --git a/hdata/spira.h b/hdata/spira.h
index bd9d1b541f1f..4159a6b53ec4 100644
--- a/hdata/spira.h
+++ b/hdata/spira.h
@@ -273,6 +273,7 @@ struct spss_iopath {
 	__be16	iopath_type;
 #define SPSS_IOPATH_TYPE_IOHUB_PHB	0x0001
 #define SPSS_IOPATH_TYPE_PSI		0x0002
+#define SPSS_IOPATH_TYPE_LPC		0x0003
 	union {
 		struct {
 			__be16	iohub_chip_inst;
@@ -293,6 +294,40 @@ struct spss_iopath {
 			__be32	reserved2;
 			__be64	gxhb_base;
 		} __packed psi;
+
+		struct { /* only populated after version 0x30 */
+			__be16	link_status;
+#define LPC_STATUS_STUFFED 0x0000
+#define LPC_STATUS_ACTIVE  0x0001
+			uint8_t ml2_version;
+			uint8_t reserved[3];
+			__be32	chip_id;
+
+			__be32	io_bar;
+			__be32	memory_bar;
+			__be32	firmware_bar;
+			__be32	internal_bar;
+
+			__be32	reserved2;
+
+			__be64	uart_base;
+			__be32	uart_size;
+			__be32	uart_clk;  /* UART baud clock in Hz */
+			__be32	uart_baud; /* UART baud rate */
+
+			uint8_t uart_int_number;
+			uint8_t uart_int_type;
+#define			UART_INT_LVL_LOW	0x1
+#define			UART_INT_RISING		0x2
+#define			UART_INT_LVL_HIGH	0x3
+			uint8_t reserved3[2];
+
+			__be64	bt_base;
+			__be32	bt_size;
+			uint8_t	bt_sms_int_num;
+			uint8_t	bt_bmc_response_int_num;
+			uint8_t	reserved4[2];
+		} __packed lpc;
 	};
 } __packed;
 
-- 
2.7.4



More information about the Skiboot mailing list