[Skiboot] [PATCH V3 03/15] rainier: detect pau devices
Christophe Lombard
clombard at linux.vnet.ibm.com
Fri Oct 15 02:56:52 AEDT 2021
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>
---
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,
};
--
2.31.1
More information about the Skiboot
mailing list