[Skiboot] [PATCH 12/16] [PATCH 12/16] opencapi5: phy init
Frederic Barrat
fbarrat at linux.ibm.com
Wed Sep 8 23:06:59 AEST 2021
On 20/08/2021 11:45, Christophe Lombard wrote:
> 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.
>
> Signed-off-by: Christophe Lombard <clombard at linux.vnet.ibm.com>
> ---
Looks ok.
I wouldn't mind a signed-off-by since I wrote most of it.
Fred
> 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 6e96318a..5ede16ee 100644
> --- a/hw/Makefile.inc
> +++ b/hw/Makefile.inc
> @@ -9,7 +9,7 @@ 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 npu3.o npu3-nvlink.o npu3-hw-procedures.o
> -HW_OBJS += ocmb.o xive2.o pau.o
> +HW_OBJS += 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 ea15f0b8..660931c0 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 phb *phb);
>
> +/* PHY */
> +int pau_dev_phy_reset(struct pau_dev *dev);
> +
> #endif /* __PAU_H */
>
More information about the Skiboot
mailing list