[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