[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