[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