diff options
-rw-r--r-- | include/pau.h | 3 | ||||
-rw-r--r-- | include/platform.h | 5 | ||||
-rw-r--r-- | platforms/astbmc/rainier.c | 239 |
3 files changed, 247 insertions, 0 deletions
diff --git a/include/pau.h b/include/pau.h index 2a26a65..e946e0f 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 6fafddb..db0c086 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 17d9fe2..3e21e1b 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@a1000"); + if (!i2cm) { + prlog(PR_DEBUG, "PLAT: Adding master @a1000\n"); + i2cm = dt_new(xscom, "i2cm@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@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@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, }; |