[Skiboot] [PATCH V2 03/15] rainier: detect pau devices

Frederic Barrat fbarrat at linux.ibm.com
Fri Oct 1 23:56:35 AEST 2021



On 23/09/2021 11:03, Christophe Lombard wrote:
> Update the platform_ocapi structure to store Rainier platform-specific
> values for detecting and resetting OpenCAPI devices via the module
> I2C (PCA9553)
> The unique number I2C bus ID associated to each OpenCapi device
> is get from the I2C port and engine.
> (De)Assert a reset and detect an OpenCapi device is available through
> the I2C bus id and address.
> 
> Signed-off-by: Christophe Lombard <clombard at linux.vnet.ibm.com>
> ---

If HDAT ever adds the i2c bus definition, we could remove it from here. 
But the way the code is written, it is safe and shouldn't cause any trouble.

Reviewed-by: Frederic Barrat <fbarrat at linux.ibm.com>


>   include/pau.h              |   3 +
>   include/platform.h         |   5 +
>   platforms/astbmc/rainier.c | 239 +++++++++++++++++++++++++++++++++++++
>   3 files changed, 247 insertions(+)
> 
> diff --git a/include/pau.h b/include/pau.h
> index 2a26a65f..e946e0fd 100644
> --- a/include/pau.h
> +++ b/include/pau.h
> @@ -24,6 +24,9 @@ struct pau_dev {
>   	uint32_t		index;
>   	struct dt_node		*dn;
>   
> +	/* Associated I2C information */
> +	uint8_t			i2c_bus_id;
> +
>   	/* Associated PHY information */
>   	uint32_t		pau_unit; /* 0,3,4,5,6,7 */
>   	uint32_t		odl_index;
> diff --git a/include/platform.h b/include/platform.h
> index 6fafddbf..db0c0867 100644
> --- a/include/platform.h
> +++ b/include/platform.h
> @@ -69,6 +69,11 @@ struct platform_ocapi {
>   	uint8_t i2c_presence_brick5;	/* I2C pin to read for presence on brick 5 */
>   	bool odl_phy_swap;		/* Swap ODL1 to use brick 2 rather than
>   					 * brick 1 lanes */
> +	uint8_t i2c_dev_addr;		/* I2C device address */
> +	uint8_t i2c_intreset_pin;	/* I2C pin to write to reset */
> +	uint8_t i2c_predetect_pin;	/* I2C pin to read for presence */
> +	int64_t (*i2c_assert_reset)(uint8_t i2c_bus_id);
> +	int64_t (*i2c_deassert_reset)(uint8_t i2c_bus_id);
>   	const char *(*ocapi_slot_label)(uint32_t chip_id, uint32_t brick_index);
>   	const struct ocapi_phy_setup *phy_setup;
>   };
> diff --git a/platforms/astbmc/rainier.c b/platforms/astbmc/rainier.c
> index 17d9fe2b..3e21e1b9 100644
> --- a/platforms/astbmc/rainier.c
> +++ b/platforms/astbmc/rainier.c
> @@ -6,6 +6,7 @@
>   #include <skiboot.h>
>   #include <device.h>
>   #include <ipmi.h>
> +#include <pau.h>
>   #include <chip.h>
>   #include <i2c.h>
>   #include <timebase.h>
> @@ -99,6 +100,227 @@ static void rainier_init_slot_power(void)
>   	}
>   }
>   
> +static int64_t rainier_i2c_assert_reset(uint8_t i2c_bus_id)
> +{
> +	uint8_t data;
> +	int64_t rc = OPAL_SUCCESS;
> +
> +	/*
> +	 * Set the i2c reset pin in output mode (9553 device)
> +	 * To write a register:
> +	 *   puti2c pu 0 0|1 C4 <data> <offset> 1,
> +	 *   with data being a 2-nibble hex value and offset being the
> +	 *   register offset from the datasheet
> +	 *
> +	 * puti2c (-p1) 0 0|1 C4 51 5 1	     0		: i2c engine
> +	 *				     0|1	: i2c_port
> +	 *				     C4 (C4 > 1 = 62) : Address
> +	 *				     51		: data
> +	 *				     5		: register (offset)
> +	 *				     1		: offset byte
> +	 *
> +	 * 7.3.6 LS0 - LED selector register: default value 0x55
> +	 * bit 1:0 01* LED0 selected  (OpenCapi card)
> +	 *
> +	 * offset 0x05, register name: LS0, Fct: LED selector
> +	 * see Table 4. Control register definition (PCA9553)
> +	 */
> +	data = 0x51;
> +	rc = i2c_request_send(i2c_bus_id,
> +			      platform.ocapi->i2c_dev_addr,
> +			      SMBUS_WRITE, 0x5, 1,
> +			      &data, sizeof(data), 120);
> +
> +	return rc;
> +}
> +
> +static int64_t rainier_i2c_deassert_reset(uint8_t i2c_bus_id)
> +{
> +	uint8_t data;
> +	int64_t rc = OPAL_SUCCESS;
> +
> +	/* puti2c (-p1) 0 0|1 C4 55 <offset> 1
> +	 *
> +	 * offset 0x05, register name: LS0, Fct: LED selector
> +	 * see Table 4. Control register definition (PCA9553)
> +	 */
> +	data = 0x55;
> +	rc = i2c_request_send(i2c_bus_id,
> +			      platform.ocapi->i2c_dev_addr,
> +			      SMBUS_WRITE, 0x5, 1,
> +			      &data, sizeof(data), 120);
> +
> +	return rc;
> +}
> +
> +static int get_i2c_info(struct pau_dev *dev, int *engine, int *port)
> +{
> +	uint32_t chip_id = dev->pau->chip_id;
> +	uint32_t pau_index = dev->pau->index;
> +	uint32_t link = dev->index;
> +
> +	switch (chip_id) {
> +	case 0:
> +	case 4:
> +		/*
> +		 * OP3: links 0 and 1 on chip 0
> +		 *      link 0 only on chip 4
> +		 */
> +		if (pau_index == 1) {
> +			if (link == 1 && chip_id == 4)
> +				return -1;
> +			*engine = 1;
> +			*port = link;
> +			return 0;
> +		}
> +		break;
> +	case 2:
> +	case 6:
> +		/*
> +		 * OP0: links 0 and 1 on chip 2
> +		 *      link 1 only on chip 6
> +		 */
> +		if (pau_index == 0) {
> +			if (link == 0 && chip_id == 6)
> +				return -1;
> +			*engine = 1;
> +			*port = link;
> +			return 0;
> +		}
> +		break;
> +	}
> +	return -1;
> +}
> +
> +static void rainier_i2c_presence_init(struct pau_dev *dev)
> +{
> +	char port_name[17];
> +	struct dt_node *np;
> +	int engine, port;
> +
> +	/* Find I2C port */
> +	if (dev->i2c_bus_id)
> +		return;
> +
> +	if (get_i2c_info(dev, &engine, &port))
> +		return;
> +
> +	snprintf(port_name, sizeof(port_name), "p8_%08x_e%dp%d",
> +		 dev->pau->chip_id, engine, port);
> +
> +	dt_for_each_compatible(dt_root, np, "ibm,power10-i2c-port") {
> +		if (streq(port_name, dt_prop_get(np, "ibm,port-name"))) {
> +			dev->i2c_bus_id = dt_prop_get_u32(np, "ibm,opal-id");
> +			break;
> +		}
> +	}
> +}
> +
> +static int64_t rainier_i2c_dev_detect(struct pau_dev *dev,
> +				      bool *presence)
> +{
> +	int64_t rc = OPAL_SUCCESS;
> +	uint8_t detect;
> +
> +	/* Read the presence value
> +	 * geti2c (-p1) pu 0 0|1 C4 1 <offset> 1
> +	 *
> +	 * offset 0x00, register name: INPUT, Fct: input register
> +	 * see Table 4. Control register definition (PCA9553)
> +	 */
> +	detect = 0x00;
> +	*presence = false;
> +	rc = i2c_request_send(dev->i2c_bus_id,
> +			      platform.ocapi->i2c_dev_addr,
> +			      SMBUS_READ, 0x00, 1,
> +			      &detect, 1, 120);
> +
> +	/* LED0 (bit 0): a high level no card is plugged */
> +	if (!rc && !(detect & platform.ocapi->i2c_predetect_pin))
> +		*presence = true;
> +
> +	return rc;
> +}
> +
> +static void rainier_pau_device_detect(struct pau *pau)
> +{
> +	struct pau_dev *dev;
> +	bool presence;
> +	int64_t rc;
> +
> +	/* OpenCapi devices are possibly connected on Optical link pair:
> +	 * OP0 or OP3
> +	 * pau_index	Interface Link - OPxA/B
> +	 * 0		OPT0 -- PAU0
> +	 *		OPT1 -- no PAU, SMP only
> +	 *		OPT2 -- no PAU, SMP only
> +	 * 1		OPT3 -- PAU3
> +	 * 2		OPT4 -- PAU4 by default, but can be muxed to use PAU5 - N/A on Rainier
> +	 * 3		OPT5 -- PAU5 by default, but can be muxed to use PAU4 - N/A on Rainier
> +	 * 4		OPT6 -- PAU6 by default, but can be muxed to use PAU7 - N/A on Rainier
> +	 * 5		OPT7 -- PAU7 by default, but can be muxed to use PAU6 - N/A on Rainier
> +	 */
> +	pau_for_each_dev(dev, pau) {
> +		dev->type = PAU_DEV_TYPE_UNKNOWN;
> +
> +		rainier_i2c_presence_init(dev);
> +		if (dev->i2c_bus_id) {
> +			rc = rainier_i2c_dev_detect(dev, &presence);
> +			if (!rc && presence)
> +				dev->type = PAU_DEV_TYPE_OPENCAPI;
> +		}
> +
> +		dt_add_property_u64(dev->dn, "ibm,link-speed", 25000000000ull);
> +	}
> +}
> +
> +static void rainier_pau_create_i2c_bus(void)
> +{
> +	struct dt_node *xscom, *i2cm, *i2c_bus;
> +
> +	prlog(PR_DEBUG, "PLAT: Adding I2C bus device node for PAU reset\n");
> +	dt_for_each_compatible(dt_root, xscom, "ibm,xscom") {
> +		i2cm = dt_find_by_name(xscom, "i2cm at a1000");
> +		if (!i2cm) {
> +			prlog(PR_DEBUG, "PLAT: Adding master @a1000\n");
> +			i2cm = dt_new(xscom, "i2cm at a1000");
> +			dt_add_property_cells(i2cm, "reg", 0xa1000, 0x1000);
> +			dt_add_property_strings(i2cm, "compatible",
> +						"ibm,power8-i2cm", "ibm,power9-i2cm");
> +			dt_add_property_cells(i2cm, "#size-cells", 0x0);
> +			dt_add_property_cells(i2cm, "#address-cells", 0x1);
> +			dt_add_property_cells(i2cm, "chip-engine#", 0x1);
> +			dt_add_property_cells(i2cm, "clock-frequency", 0x7735940);
> +		}
> +
> +		i2c_bus = dt_find_by_name(i2cm, "i2c-bus at 0");
> +		if (!i2c_bus) {
> +			prlog(PR_DEBUG, "PLAT: Adding bus 0 to master @a1000\n");
> +			i2c_bus = dt_new_addr(i2cm, "i2c-bus", 0);
> +			dt_add_property_cells(i2c_bus, "reg", 0);
> +			dt_add_property_cells(i2c_bus, "bus-frequency", 0x61a80);
> +			dt_add_property_strings(i2c_bus, "compatible",
> +						"ibm,opal-i2c",
> +						"ibm,power8-i2c-port",
> +						"ibm,power9-i2c-port",
> +						"ibm,power10-i2c-port");
> +		}
> +
> +		i2c_bus = dt_find_by_name(i2cm, "i2c-bus at 1");
> +		if (!i2c_bus) {
> +			prlog(PR_DEBUG, "PLAT: Adding bus 1 to master @a1000\n");
> +			i2c_bus = dt_new_addr(i2cm, "i2c-bus", 1);
> +			dt_add_property_cells(i2c_bus, "reg", 1);
> +			dt_add_property_cells(i2c_bus, "bus-frequency", 0x61a80);
> +			dt_add_property_strings(i2c_bus, "compatible",
> +						"ibm,opal-i2c",
> +						"ibm,power8-i2c-port",
> +						"ibm,power9-i2c-port",
> +						"ibm,power10-i2c-port");
> +		}
> +	}
> +}
> +
>   static void rainier_init(void)
>   {
>   	astbmc_init();
> @@ -118,9 +340,24 @@ static bool rainier_probe(void)
>   	/* Setup UART for use by OPAL (Linux hvc) */
>   	uart_set_console_policy(UART_CONSOLE_OPAL);
>   
> +	/* create i2c entries for PAU */
> +	rainier_pau_create_i2c_bus();
> +
>   	return true;
>   }
>   
> +static struct platform_ocapi rainier_ocapi = {
> +	.i2c_dev_addr		= 0x62, /* C4 >> 1 */
> +	.i2c_intreset_pin	= 0x02, /* PIN 2 - LED1 - INT/RESET */
> +	.i2c_predetect_pin	= 0x01, /* PIN 1 - LED0 - PRE-DETECT */
> +	/* As previously for NPU/NPU2, we use indirect functions for
> +	 * this platform to reset the device. This makes the code more
> +	 * generic in PAU.
> +	 */
> +	.i2c_assert_reset	= rainier_i2c_assert_reset,
> +	.i2c_deassert_reset	= rainier_i2c_deassert_reset,
> +};
> +
>   DECLARE_PLATFORM(rainier) = {
>   	.name			= "Rainier",
>   	.probe			= rainier_probe,
> @@ -131,6 +368,8 @@ DECLARE_PLATFORM(rainier) = {
>   	.cec_power_down         = astbmc_ipmi_power_down,
>   	.cec_reboot             = astbmc_ipmi_reboot,
>   	.elog_commit		= ipmi_elog_commit,
> +	.pau_device_detect	= rainier_pau_device_detect,
> +	.ocapi			= &rainier_ocapi,
>   	.exit			= astbmc_exit,
>   	.terminate		= ipmi_terminate,
>   };
> 


More information about the Skiboot mailing list