[Skiboot] [PATCH 12/16] [PATCH 12/16] opencapi5: phy init
Christophe Lombard
clombard at linux.vnet.ibm.com
Tue Sep 21 02:14:43 AEST 2021
Le 08/09/2021 à 15:06, Frederic Barrat a écrit :
>
>
> 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
>
I agree. Sorry. You even wrote it all down ! :-)
>
>> 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