[Skiboot] [PATCH V2 11/15] pau: phy init
Christophe Lombard
clombard at linux.vnet.ibm.com
Thu Sep 23 19:03:27 AEST 2021
Follow the Procedure IO_INIT_RESET_PON as described in the
P10 OPHY workbook document to reset and initialize the PHY lanes.
The memory mapped SRAM (64 bit aligned) has to be used to configure the
PHY, which is reachable the linked registers: address and data.
The different links can be configured at the same time, that implies using
a global lock to avoid conflicts.
Authored-by: Frederic Barrat <fbarrat at linux.ibm.com>
Signed-off-by: Christophe Lombard <clombard at linux.vnet.ibm.com>
---
hw/Makefile.inc | 2 +-
hw/pau-hw-procedures.c | 310 +++++++++++++++++++++++++++++++++++++++++
include/pau.h | 11 ++
3 files changed, 322 insertions(+), 1 deletion(-)
create mode 100644 hw/pau-hw-procedures.c
diff --git a/hw/Makefile.inc b/hw/Makefile.inc
index 0b12f0be..9fcbb63b 100644
--- a/hw/Makefile.inc
+++ b/hw/Makefile.inc
@@ -8,7 +8,7 @@ HW_OBJS += dts.o lpc-rtc.o npu.o npu-hw-procedures.o xive.o phb4.o
HW_OBJS += fake-nvram.o lpc-mbox.o npu2.o npu2-hw-procedures.o
HW_OBJS += npu2-common.o npu2-opencapi.o phys-map.o sbe-p9.o capp.o
HW_OBJS += occ-sensor.o vas.o sbe-p8.o dio-p9.o lpc-port80h.o cache-p9.o
-HW_OBJS += npu-opal.o ocmb.o xive2.o pau.o
+HW_OBJS += npu-opal.o ocmb.o xive2.o pau.o pau-hw-procedures.o
HW=hw/built-in.a
include $(SRC)/hw/fsp/Makefile.inc
diff --git a/hw/pau-hw-procedures.c b/hw/pau-hw-procedures.c
new file mode 100644
index 00000000..bd299b3f
--- /dev/null
+++ b/hw/pau-hw-procedures.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright 2020 IBM Corp.
+ */
+#include <timebase.h>
+#include <pau.h>
+
+#define PAU_PHY_INIT_TIMEOUT 8000 /* ms */
+
+#define PAU_PHY_ADDR_REG 0x10012C0D
+#define PAU_PHY_ADDR_CHIPLET PPC_BITMASK(32, 39)
+#define PAU_PHY_ADDR_SRAM_ADDR PPC_BITMASK(15, 31)
+#define PAU_PHY_DATA_REG 0x10012C0E
+#define PAU_PHY_DATA_CHIPLET PPC_BITMASK(32, 39)
+
+#define PAU_MAX_PHY_LANE 18
+
+/*
+ * We configure the PHY using the memory mapped SRAM, which is
+ * accessible through a pair of (addr, data) registers. The caveat is
+ * that accesses to the SRAM must be 64-bit aligned, yet the PHY
+ * registers are 16-bit, so special care is needed.
+ *
+ * A PAU chiplet may control up to 2 OP units = 4 links and each link
+ * has its own virtual PHB in skiboot. They can be initialized or
+ * reset concurrently so we need a lock when accessing the SRAM.
+
+ * See section "5.2.5 PPE SRAM" of the workbook for the layout of the
+ * SRAM registers. Here is the subset of the table which is meaningful
+ * for us, since we're only touching a few registers:
+ *
+ * Address Bytes Linker Symbol Description
+ * FFFF_11B0 16 _fw_regs0_start fw_regs for thread 0
+ * FFFF_11C0 16 _fw_regs1_start fw_regs for thread 1
+ *
+ * FFFF_2800 1024 _mem_regs0_start mem_regs for thread 0
+ * FFFF_2C00 1024 _mem_regs1_start mem_regs for thread 1
+ *
+ * In each PAU, per-group registers are replicated for every OP (each
+ * OP units is being called a 'thread' in the workbook).
+ * Per-lane registers have an offset < 0x10 and are replicated for
+ * each lane. Their offset in their section is:
+ * 0byyyyyxxxx (y = 5-bit lane number, x = 4-bit per-lane register offset)
+ */
+
+struct PPE_sram_section {
+ uint32_t offset;
+ uint32_t size;
+};
+
+static struct PPE_sram_section PPE_FIRMWARE = { 0x111B0, 0x10 };
+static struct PPE_sram_section PPE_MEMORY = { 0x12800, 0x400 };
+
+struct PPE_sram_reg {
+ struct PPE_sram_section *section;
+ uint32_t offset;
+};
+
+/* PPE firmware */
+static struct PPE_sram_reg PAU_PHY_EXT_CMD_LANES_00_15 = { &PPE_FIRMWARE, 0x000 };
+static struct PPE_sram_reg PAU_PHY_EXT_CMD_LANES_16_31 = { &PPE_FIRMWARE, 0x001 };
+static struct PPE_sram_reg PAU_PHY_EXT_CMD_REQ = { &PPE_FIRMWARE, 0x002 };
+#define PAU_PHY_EXT_CMD_REQ_IO_RESET PPC_BIT16(1)
+#define PAU_PHY_EXT_CMD_REQ_DCCAL PPC_BIT16(3)
+#define PAU_PHY_EXT_CMD_REQ_TX_ZCAL PPC_BIT16(4)
+#define PAU_PHY_EXT_CMD_REQ_TX_FFE PPC_BIT16(5)
+#define PAU_PHY_EXT_CMD_REQ_POWER_ON PPC_BIT16(7)
+static struct PPE_sram_reg PAU_PHY_EXT_CMD_DONE = { &PPE_FIRMWARE, 0x005 };
+
+/* PPE memory */
+static struct PPE_sram_reg PAU_PHY_RX_PPE_CNTL1 = { &PPE_MEMORY, 0x000 };
+#define PAU_PHY_RX_ENABLE_AUTO_RECAL PPC_BIT16(1)
+
+enum pau_phy_status {
+ PAU_PROC_INPROGRESS,
+ PAU_PROC_COMPLETE,
+ PAU_PROC_NEXT,
+ PAU_PROC_FAILED
+};
+
+struct procedure {
+ const char *name;
+ uint32_t (*steps[])(struct pau_dev *);
+};
+
+#define DEFINE_PROCEDURE(NAME, STEPS...) \
+ static struct procedure procedure_##NAME = { \
+ .name = #NAME, \
+ .steps = { STEPS } \
+ }
+
+/*
+ * We could/should have one phy_sram_lock per PAU chiplet. Each PAU
+ * chiplet drives 2 OPT units. Since we don't have a PAU chiplet
+ * structure to host the lock and don't anticipate much contention, we
+ * go with a global lock for now
+ */
+static struct lock phy_sram_lock = LOCK_UNLOCKED;
+
+static int get_thread_id(uint32_t op_unit)
+{
+ int ppe_thread[8] = { 0, 1, 1, 0, 1, 0, 1, 0 };
+
+ /* static mapping between OP unit and PPE thread ID */
+ if (op_unit >= sizeof(ppe_thread))
+ return -1;
+ return ppe_thread[op_unit];
+}
+
+/*
+ * Compute the address in the memory mapped SRAM of a 16-bit PHY register
+ */
+static uint32_t pau_phy_sram_addr(struct pau_dev *dev,
+ struct PPE_sram_reg *reg,
+ int lane)
+{
+ uint32_t base, addr;
+
+ base = reg->section->offset +
+ reg->section->size * get_thread_id(dev->op_unit);
+ addr = reg->offset;
+ if (lane >= 0) {
+ assert(reg->offset < 0x10);
+ addr += lane << 4;
+ }
+ addr <<= 1; // each register is 16-bit
+ return base + addr;
+}
+
+static void pau_phy_set_access(struct pau_dev *dev,
+ struct PPE_sram_reg *reg, int lane,
+ uint64_t *data_addr, uint64_t *mask)
+{
+ struct pau *pau = dev->pau;
+ uint64_t scom_addr, sram_addr, addr, bit_start;
+
+ scom_addr = SETFIELD(PAU_PHY_ADDR_CHIPLET, PAU_PHY_ADDR_REG,
+ pau->op_chiplet);
+ sram_addr = pau_phy_sram_addr(dev, reg, lane);
+ bit_start = 8 * (sram_addr & 7);
+
+ addr = SETFIELD(PAU_PHY_ADDR_SRAM_ADDR, 0ull, sram_addr & 0xFFFFFFF8);
+ xscom_write(pau->chip_id, scom_addr, addr);
+
+ *data_addr = SETFIELD(PAU_PHY_DATA_CHIPLET, PAU_PHY_DATA_REG,
+ pau->op_chiplet);
+ *mask = PPC_BITMASK(bit_start, bit_start + 15);
+}
+
+static void pau_phy_write_lane(struct pau_dev *dev,
+ struct PPE_sram_reg *reg, int lane,
+ uint16_t val)
+{
+ struct pau *pau = dev->pau;
+ uint64_t data_addr, scom_val, mask;
+
+ lock(&phy_sram_lock);
+ pau_phy_set_access(dev, reg, lane, &data_addr, &mask);
+ xscom_read(pau->chip_id, data_addr, &scom_val);
+ scom_val = SETFIELD(mask, scom_val, val);
+ xscom_write(pau->chip_id, data_addr, scom_val);
+ unlock(&phy_sram_lock);
+}
+
+static uint16_t pau_phy_read_lane(struct pau_dev *dev,
+ struct PPE_sram_reg *reg, int lane)
+{
+ struct pau *pau = dev->pau;
+ uint64_t data_addr, scom_val, mask;
+ uint16_t res;
+
+ lock(&phy_sram_lock);
+ pau_phy_set_access(dev, reg, lane, &data_addr, &mask);
+ xscom_read(pau->chip_id, data_addr, &scom_val);
+ res = GETFIELD(mask, scom_val);
+ unlock(&phy_sram_lock);
+ return res;
+}
+
+static void pau_phy_write(struct pau_dev *dev, struct PPE_sram_reg *reg,
+ uint16_t val)
+{
+ pau_phy_write_lane(dev, reg, -1, val);
+}
+
+static uint16_t pau_phy_read(struct pau_dev *dev, struct PPE_sram_reg *reg)
+{
+ return pau_phy_read_lane(dev, reg, -1);
+}
+
+static uint16_t get_reset_request_val(void)
+{
+ return PAU_PHY_EXT_CMD_REQ_IO_RESET |
+ PAU_PHY_EXT_CMD_REQ_DCCAL |
+ PAU_PHY_EXT_CMD_REQ_TX_ZCAL |
+ PAU_PHY_EXT_CMD_REQ_TX_FFE |
+ PAU_PHY_EXT_CMD_REQ_POWER_ON;
+}
+
+static uint32_t reset_start(struct pau_dev *dev)
+{
+ uint16_t val16;
+
+ // Procedure IO_INIT_RESET_PON
+
+ // Clear external command request / done registers
+ val16 = 0;
+ pau_phy_write(dev, &PAU_PHY_EXT_CMD_REQ, val16);
+ pau_phy_write(dev, &PAU_PHY_EXT_CMD_DONE, val16);
+
+ // Write the external command lanes to target
+ val16 = dev->phy_lane_mask >> 16;
+ pau_phy_write(dev, &PAU_PHY_EXT_CMD_LANES_00_15, val16);
+ val16 = dev->phy_lane_mask & 0xFFFF;
+ pau_phy_write(dev, &PAU_PHY_EXT_CMD_LANES_16_31, val16);
+
+ // Initialize PHY Lanes
+ val16 = get_reset_request_val();
+ pau_phy_write(dev, &PAU_PHY_EXT_CMD_REQ, val16);
+ return PAU_PROC_NEXT;
+}
+
+static uint32_t reset_check(struct pau_dev *dev)
+{
+ uint16_t val16, done;
+
+ val16 = get_reset_request_val();
+ done = pau_phy_read(dev, &PAU_PHY_EXT_CMD_DONE);
+
+ if (val16 == done)
+ return PAU_PROC_NEXT;
+ else
+ return PAU_PROC_INPROGRESS;
+}
+
+static uint32_t enable_recal(struct pau_dev *dev)
+{
+ uint32_t lane;
+
+ // Enable auto-recalibration
+ for (lane = 0; lane <= PAU_MAX_PHY_LANE; lane++)
+ if (!(dev->phy_lane_mask & (1 << (31 - lane))))
+ continue;
+ else
+ pau_phy_write_lane(dev, &PAU_PHY_RX_PPE_CNTL1,
+ lane, PAU_PHY_RX_ENABLE_AUTO_RECAL);
+
+ return PAU_PROC_COMPLETE;
+}
+
+DEFINE_PROCEDURE(phy_reset, reset_start, reset_check, enable_recal);
+
+static enum pau_phy_status run_steps(struct pau_dev *dev)
+{
+ struct procedure *p = &procedure_phy_reset;
+ struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
+ enum pau_phy_status rc;
+
+ do {
+ rc = p->steps[procedure_state->step](dev);
+ if (rc == PAU_PROC_NEXT) {
+ procedure_state->step++;
+ PAUDEVDBG(dev, "Running procedure %s step %d\n",
+ p->name, procedure_state->step);
+ }
+ } while (rc == PAU_PROC_NEXT);
+ return rc;
+}
+
+static enum pau_phy_status run_procedure(struct pau_dev *dev)
+{
+ struct procedure *p = &procedure_phy_reset;
+ struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
+ enum pau_phy_status rc;
+
+ do {
+ rc = run_steps(dev);
+ if (rc == PAU_PROC_INPROGRESS) {
+ if (tb_compare(mftb(), procedure_state->timeout) == TB_AAFTERB) {
+ PAUDEVERR(dev, "Procedure %s timed out\n", p->name);
+ rc = PAU_PROC_FAILED;
+ } else {
+ time_wait_ms(1);
+ }
+ }
+ } while (rc == PAU_PROC_INPROGRESS);
+ return rc;
+}
+
+int pau_dev_phy_reset(struct pau_dev *dev)
+{
+ struct procedure *p = &procedure_phy_reset;
+ struct phy_proc_state *procedure_state = &dev->pau->procedure_state;
+ enum pau_phy_status rc;
+
+ lock(&procedure_state->lock);
+ procedure_state->step = 0;
+ procedure_state->timeout = mftb() + msecs_to_tb(PAU_PHY_INIT_TIMEOUT);
+ PAUDEVDBG(dev, "Running procedure %s step %d\n",
+ p->name, procedure_state->step);
+ rc = run_procedure(dev);
+ unlock(&procedure_state->lock);
+
+ if (rc == PAU_PROC_COMPLETE) {
+ PAUDEVDBG(dev, "Procedure %s complete\n", p->name);
+ return OPAL_SUCCESS;
+ }
+ PAUDEVDBG(dev, "Procedure %s failed\n", p->name);
+ return OPAL_HARDWARE;
+}
diff --git a/include/pau.h b/include/pau.h
index 547c1934..9fbccb87 100644
--- a/include/pau.h
+++ b/include/pau.h
@@ -28,6 +28,12 @@ struct pau_bar {
uint64_t cfg;
};
+struct phy_proc_state {
+ struct lock lock; /* protect any change to this structure */
+ unsigned long timeout;
+ uint16_t step;
+};
+
struct pau_dev {
enum pau_dev_type type;
uint32_t index;
@@ -54,6 +60,7 @@ struct pau {
uint32_t index;
struct dt_node *dt_node;
uint32_t chip_id;
+ uint32_t op_chiplet; /* from pervasive: 0x10 -> 0x13 */
uint64_t xscom_base;
/* Global MMIO window (all PAU regs) */
@@ -65,6 +72,7 @@ struct pau {
uint32_t links;
struct pau_dev devices[PAU_LINKS_OPENCAPI_PER_PAU];
+ struct phy_proc_state procedure_state;
};
#define PAUDBG(pau, fmt, a...) PAULOG(PR_DEBUG, pau, fmt, ##a)
@@ -191,4 +199,7 @@ static inline uint64_t pau_read(struct pau *pau, uint64_t reg)
void pau_opencapi_dump_scoms(struct pau *pau);
+/* PHY */
+int pau_dev_phy_reset(struct pau_dev *dev);
+
#endif /* __PAU_H */
--
2.31.1
More information about the Skiboot
mailing list