Message ID | 20210923090331.23415-13-clombard@linux.vnet.ibm.com |
---|---|
State | Superseded |
Headers | show |
Series | OpenCAPI support for 10 | expand |
On 23/09/2021 11:03, Christophe Lombard wrote: > Add elementary functions to handle a phb complete, fundamental and > hot resets. > For the time being, specific creset and hreset are not supported. > > A complete fundamental reset is based on the following steps, in this > order: > - Place all bricks into Fence state > - Disable BARs > - Reset ODL to Power-on Values > - Set the i2c reset pin in output mode > - Initialize PHY Lanes > - Deassert ODL reset > - Clear the the i2c reset pin > - Unfence bricks > - Enable BARs > - Enable ODL training mode > > Link training is also set up. > > Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> > --- Reviewed-by: Frederic Barrat <fbarrat@linux.ibm.com> > hw/pau.c | 529 +++++++++++++++++++++++++++++++++++++++ > include/pau-regs.h | 5 + > include/pau.h | 2 + > include/xscom-p10-regs.h | 46 ++++ > 4 files changed, 582 insertions(+) > > diff --git a/hw/pau.c b/hw/pau.c > index d4222361..91dee12c 100644 > --- a/hw/pau.c > +++ b/hw/pau.c > @@ -15,6 +15,22 @@ > #define PAU_MAX_PE_NUM 16 > #define PAU_RESERVED_PE_NUM 15 > > +#define PAU_SLOT_NORMAL PCI_SLOT_STATE_NORMAL > +#define PAU_SLOT_LINK PCI_SLOT_STATE_LINK > +#define PAU_SLOT_LINK_START (PAU_SLOT_LINK + 1) > +#define PAU_SLOT_LINK_WAIT (PAU_SLOT_LINK + 2) > +#define PAU_SLOT_LINK_TRAINED (PAU_SLOT_LINK + 3) > +#define PAU_SLOT_FRESET PCI_SLOT_STATE_FRESET > +#define PAU_SLOT_FRESET_START (PAU_SLOT_FRESET + 1) > +#define PAU_SLOT_FRESET_INIT (PAU_SLOT_FRESET + 2) > +#define PAU_SLOT_FRESET_ASSERT_DELAY (PAU_SLOT_FRESET + 3) > +#define PAU_SLOT_FRESET_DEASSERT_DELAY (PAU_SLOT_FRESET + 4) > +#define PAU_SLOT_FRESET_INIT_DELAY (PAU_SLOT_FRESET + 5) > + > +#define PAU_LINK_TRAINING_RETRIES 2 > +#define PAU_LINK_TRAINING_TIMEOUT 15000 /* ms */ > +#define PAU_LINK_STATE_TRAINED 0x7 > + > struct pau_dev *pau_next_dev(struct pau *pau, struct pau_dev *dev, > enum pau_dev_type type) > { > @@ -168,6 +184,7 @@ static void pau_dt_create_pau(struct dt_node *xscom, uint32_t pau_index) > dt_add_property_cells(pau, "#address-cells", 1); > dt_add_property_cells(pau, "reg", pau_base[pau_index], 0x2c); > dt_add_property_string(pau, "compatible", "ibm,power10-pau"); > + dt_add_property_cells(pau, "ibm,pau-chiplet", pau_base[pau_index] >> 24); > dt_add_property_cells(pau, "ibm,pau-index", pau_index); > > links = PAU_LINKS_OPENCAPI_PER_PAU; > @@ -202,12 +219,14 @@ static struct pau *pau_create(struct dt_node *dn) > assert(pau); > > init_lock(&pau->lock); > + init_lock(&pau->procedure_state.lock); > > pau->dt_node = dn; > pau->index = dt_prop_get_u32(dn, "ibm,pau-index"); > pau->xscom_base = dt_get_address(dn, 0, NULL); > > pau->chip_id = dt_get_chip_id(dn); > + pau->op_chiplet = dt_prop_get_u32(dn, "ibm,pau-chiplet"); > assert(get_chip(pau->chip_id)); > > pau->links = PAU_LINKS_OPENCAPI_PER_PAU; > @@ -503,6 +522,452 @@ static void pau_opencapi_enable_bars(struct pau_dev *dev, bool enable) > pau_write(pau, reg, val); > } > > +static int64_t pau_opencapi_creset(struct pci_slot *slot) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + > + PAUDEVERR(dev, "creset not supported\n"); > + return OPAL_UNSUPPORTED; > +} > + > +static int64_t pau_opencapi_hreset(struct pci_slot *slot) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + > + PAUDEVERR(dev, "hreset not supported\n"); > + return OPAL_UNSUPPORTED; > +} > + > +static void pau_opencapi_assert_odl_reset(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); > + val = P10_OB_ODL_CONFIG_RESET; > + val = SETFIELD(P10_OB_ODL_CONFIG_VERSION, val, 0b000100); // OCAPI 4 > + val = SETFIELD(P10_OB_ODL_CONFIG_TRAIN_MODE, val, 0b0101); // ts2 > + val = SETFIELD(P10_OB_ODL_CONFIG_SUPPORTED_MODES, val, 0b0010); > + val |= P10_OB_ODL_CONFIG_X4_BACKOFF_ENABLE; > + val = SETFIELD(P10_OB_ODL_CONFIG_PHY_CNTR_LIMIT, val, 0b1111); > + val |= P10_OB_ODL_CONFIG_DEBUG_ENABLE; > + val = SETFIELD(P10_OB_ODL_CONFIG_FWD_PROGRESS_TIMER, val, 0b0110); > + xscom_write(pau->chip_id, reg, val); > +} > + > +static void pau_opencapi_deassert_odl_reset(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); > + xscom_read(pau->chip_id, reg, &val); > + val &= ~P10_OB_ODL_CONFIG_RESET; > + xscom_write(pau->chip_id, reg, val); > +} > + > +static void pau_opencapi_training_mode(struct pau_dev *dev, > + uint8_t pattern) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); > + xscom_read(pau->chip_id, reg, &val); > + val = SETFIELD(P10_OB_ODL_CONFIG_TRAIN_MODE, val, pattern); > + xscom_write(pau->chip_id, reg, val); > +} > + > +static int64_t pau_opencapi_assert_adapter_reset(struct pau_dev *dev) > +{ > + int64_t rc = OPAL_PARAMETER; > + > + if (platform.ocapi->i2c_assert_reset) > + rc = platform.ocapi->i2c_assert_reset(dev->i2c_bus_id); > + > + if (rc) > + PAUDEVERR(dev, "Error writing I2C reset signal: %lld\n", rc); > + return rc; > +} > + > +static int64_t pau_opencapi_deassert_adapter_reset(struct pau_dev *dev) > +{ > + int64_t rc = OPAL_PARAMETER; > + > + if (platform.ocapi->i2c_deassert_reset) > + rc = platform.ocapi->i2c_deassert_reset(dev->i2c_bus_id); > + > + if (rc) > + PAUDEVERR(dev, "Error writing I2C reset signal: %lld\n", rc); > + return rc; > +} > + > +static void pau_opencapi_fence_brick(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + > + PAUDEVDBG(dev, "Fencing brick\n"); > + pau_opencapi_set_fence_control(dev, 0b11); > + > + /* Place all bricks into Fence state */ > + pau_write(pau, PAU_MISC_FENCE_STATE, > + PAU_MISC_FENCE_STATE_SET(pau_dev_index(dev, PAU_LINKS_OPENCAPI_PER_PAU))); > +} > + > +static void pau_opencapi_unfence_brick(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + > + PAUDEVDBG(dev, "Unfencing brick\n"); > + pau_write(pau, PAU_MISC_FENCE_STATE, > + PAU_MISC_FENCE_STATE_CLEAR(pau_dev_index(dev, PAU_LINKS_OPENCAPI_PER_PAU))); > + > + pau_opencapi_set_fence_control(dev, 0b10); > + pau_opencapi_set_fence_control(dev, 0b00); > +} > + > +static int64_t pau_opencapi_freset(struct pci_slot *slot) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + uint8_t presence = 1; > + int64_t rc = OPAL_SUCCESS; > + > + switch (slot->state) { > + case PAU_SLOT_NORMAL: > + case PAU_SLOT_FRESET_START: > + PAUDEVDBG(dev, "FRESET: Starts\n"); > + > + if (slot->ops.get_presence_state) > + slot->ops.get_presence_state(slot, &presence); > + if (!presence) { > + /* > + * FIXME: if there's no card on the link, we > + * should consider powering off the unused > + * lanes to save energy > + */ > + PAUDEVINF(dev, "no card detected\n"); > + return OPAL_SUCCESS; > + } > + slot->link_retries = PAU_LINK_TRAINING_RETRIES; > + /* fall-through */ > + case PAU_SLOT_FRESET_INIT: > + pau_opencapi_fence_brick(dev); > + pau_opencapi_enable_bars(dev, false); > + pau_opencapi_assert_odl_reset(dev); > + pau_opencapi_assert_adapter_reset(dev); > + pci_slot_set_state(slot, PAU_SLOT_FRESET_ASSERT_DELAY); > + /* assert for 5ms */ > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(5)); > + > + case PAU_SLOT_FRESET_ASSERT_DELAY: > + rc = pau_dev_phy_reset(dev); > + if (rc) { > + PAUDEVERR(dev, "FRESET: PHY reset error\n"); > + return OPAL_HARDWARE; > + } > + pau_opencapi_deassert_odl_reset(dev); > + pau_opencapi_deassert_adapter_reset(dev); > + pci_slot_set_state(slot, PAU_SLOT_FRESET_DEASSERT_DELAY); > + /* give 250ms to device to be ready */ > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(250)); > + > + case PAU_SLOT_FRESET_DEASSERT_DELAY: > + pau_opencapi_unfence_brick(dev); > + pau_opencapi_enable_bars(dev, true); > + pau_opencapi_training_mode(dev, 0b0001); /* send pattern A */ > + pci_slot_set_state(slot, PAU_SLOT_FRESET_INIT_DELAY); > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(5)); > + > + case PAU_SLOT_FRESET_INIT_DELAY: > + pau_opencapi_training_mode(dev, 0b1000); /* enable training */ > + dev->train_start = mftb(); > + dev->train_timeout = dev->train_start + > + msecs_to_tb(PAU_LINK_TRAINING_TIMEOUT); > + pci_slot_set_state(slot, PAU_SLOT_LINK_START); > + return slot->ops.poll_link(slot); > + > + default: > + PAUDEVERR(dev, "FRESET: unexpected slot state %08x\n", > + slot->state); > + } > + pci_slot_set_state(slot, PAU_SLOT_NORMAL); > + return OPAL_HARDWARE; > +} > + > +static uint64_t pau_opencapi_get_odl_endpoint_info(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t val; > + > + xscom_read(pau->chip_id, > + P10_OB_ODL_DLX_INFO(dev->op_unit, dev->odl_index), > + &val); > + return val; > +} > + > +static uint64_t pau_opencapi_get_odl_training_status(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t val; > + > + xscom_read(pau->chip_id, > + P10_OB_ODL_TRAIN_STAT(dev->op_unit, dev->odl_index), > + &val); > + return val; > +} > + > +static uint64_t pau_opencapi_get_odl_status(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t val; > + > + xscom_read(pau->chip_id, > + P10_OB_ODL_STATUS(dev->op_unit, dev->odl_index), > + &val); > + return val; > +} > + > +static uint64_t pau_opencapi_get_odl_link_speed_status(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t val; > + > + xscom_read(pau->chip_id, > + P10_OB_ODL_LINK_SPEED_STATUS(dev->op_unit, dev->odl_index), > + &val); > + return val; > +} > + > +static enum OpalShpcLinkState pau_opencapi_get_link_width(uint64_t status) > +{ > + uint64_t tx_lanes, rx_lanes, state; > + > + state = GETFIELD(P10_OB_ODL_STATUS_TRAINING_STATE, status); > + if (state != PAU_LINK_STATE_TRAINED) > + return OPAL_SHPC_LINK_DOWN; > + > + rx_lanes = GETFIELD(P10_OB_ODL_STATUS_RX_TRAINED_LANES, status); > + tx_lanes = GETFIELD(P10_OB_ODL_STATUS_TX_TRAINED_LANES, status); > + if ((rx_lanes != 0xFF) || (tx_lanes != 0xFF)) > + return OPAL_SHPC_LINK_UP_x4; > + else > + return OPAL_SHPC_LINK_UP_x8; > +} > + > +static int64_t pau_opencapi_get_link_state(struct pci_slot *slot, > + uint8_t *val) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + uint64_t status; > + > + status = pau_opencapi_get_odl_status(dev); > + *val = pau_opencapi_get_link_width(status); > + > + return OPAL_SUCCESS; > + > +} > + > +static int64_t pau_opencapi_get_power_state(struct pci_slot *slot, > + uint8_t *val) > +{ > + *val = slot->power_state; > + return OPAL_SUCCESS; > +} > + > +static int64_t pau_opencapi_get_presence_state(struct pci_slot __unused * slot, > + uint8_t *val) > +{ > + /* > + * Presence detection for OpenCAPI is currently done at the start of > + * PAU initialisation, and we only create slots if a device is present. > + * As such we will never be asked to get the presence of a slot that's > + * empty. > + * > + * This may change if we ever support hotplug down the track. > + */ > + *val = OPAL_PCI_SLOT_PRESENT; > + return OPAL_SUCCESS; > +} > + > +static void pau_opencapi_check_trained_link(struct pau_dev *dev, > + uint64_t status) > +{ > + if (pau_opencapi_get_link_width(status) != OPAL_SHPC_LINK_UP_x8) { > + PAUDEVERR(dev, "Link trained in degraded mode (%016llx)\n", > + status); > + PAUDEVDBG(dev, "Link endpoint info: %016llx\n", > + pau_opencapi_get_odl_endpoint_info(dev)); > + } > +} > + > +static int64_t pau_opencapi_retry_state(struct pci_slot *slot, > + uint64_t status) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + > + if (!slot->link_retries--) { > + /** > + * @fwts-label OCAPILinkTrainingFailed > + * @fwts-advice The OpenCAPI link training procedure failed. > + * This indicates a hardware or firmware bug. OpenCAPI > + * functionality will not be available on this link. > + */ > + PAUDEVERR(dev, > + "Link failed to train, final link status: %016llx\n", > + status); > + PAUDEVDBG(dev, "Final link training status: %016llx (Link Speed Status: %016llx)\n", > + pau_opencapi_get_odl_training_status(dev), > + pau_opencapi_get_odl_link_speed_status(dev)); > + return OPAL_HARDWARE; > + } > + > + PAUDEVERR(dev, "Link failed to train, retrying\n"); > + PAUDEVERR(dev, "Link status: %016llx, training status: %016llx " > + "(Link Speed Status: %016llx)\n", > + status, > + pau_opencapi_get_odl_training_status(dev), > + pau_opencapi_get_odl_link_speed_status(dev)); > + > + pci_slot_set_state(slot, PAU_SLOT_FRESET_INIT); > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); > +} > + > +static void pau_opencapi_otl_tx_send_enable(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + /* Allows OTL TX to send out packets to AFU */ > + PAUDEVDBG(dev, "OTL TX Send Enable\n"); > + > + reg = PAU_OTL_MISC_CFG_TX2(dev->index); > + val = pau_read(pau, reg); > + val |= PAU_OTL_MISC_CFG_TX2_SEND_EN; > + pau_write(pau, reg, val); > +} > + > +static void pau_opencapi_setup_perf_counters(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + PAUDEVDBG(dev, "Setup perf counter\n"); > + > + reg = P10_OB_ODL_PERF_MON_CONFIG(dev->op_unit); > + xscom_read(pau->chip_id, reg, &val); > + val = SETFIELD(P10_OB_ODL_PERF_MON_CONFIG_ENABLE, val, > + P10_OB_ODL_PERF_MON_CONFIG_LINK0 >> dev->index); > + val = SETFIELD(P10_OB_ODL_PERF_MON_CONFIG_SIZE, val, > + P10_OB_ODL_PERF_MON_CONFIG_SIZE16); > + xscom_write(pau->chip_id, reg, val); > + PAUDEVDBG(dev, "perf counter config %llx = %llx\n", reg, val); > + > + reg = P10_OB_ODL_PERF_MON_SELECT(dev->op_unit); > + xscom_read(pau->chip_id, reg, &val); > + val = SETFIELD(P10_OB_ODL_PERF_MON_SELECT_COUNTER >> (dev->index * 16), > + val, P10_OB_ODL_PERF_MON_SELECT_CRC_ODL); > + val = SETFIELD(P10_OB_ODL_PERF_MON_SELECT_COUNTER >> ((dev->index * 16) + 8), > + val, P10_OB_ODL_PERF_MON_SELECT_CRC_DLX); > + xscom_write(pau->chip_id, reg, val); > + PAUDEVDBG(dev, "perf counter select %llx = %llx\n", reg, val); > +} > + > +static void pau_opencapi_check_perf_counters(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint64_t reg, val; > + > + reg = P10_OB_PERF_COUNTER0(dev->op_unit); > + xscom_read(pau->chip_id, reg, &val); > + > + if (val) > + PAUDEVERR(dev, "CRC error count perf_counter0..3=0%#llx\n", > + val); > +} > + > +static int64_t pau_opencapi_poll_link(struct pci_slot *slot) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + uint64_t status; > + > + switch (slot->state) { > + case PAU_SLOT_NORMAL: > + case PAU_SLOT_LINK_START: > + PAUDEVDBG(dev, "Start polling\n"); > + pci_slot_set_state(slot, PAU_SLOT_LINK_WAIT); > + /* fall-through */ > + case PAU_SLOT_LINK_WAIT: > + status = pau_opencapi_get_odl_status(dev); > + if (GETFIELD(P10_OB_ODL_STATUS_TRAINING_STATE, status) == > + PAU_LINK_STATE_TRAINED) { > + PAUDEVINF(dev, "link trained in %ld ms (Link Speed Status: %016llx)\n", > + tb_to_msecs(mftb() - dev->train_start), > + pau_opencapi_get_odl_link_speed_status(dev)); > + pau_opencapi_check_trained_link(dev, status); > + > + pci_slot_set_state(slot, PAU_SLOT_LINK_TRAINED); > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); > + } > + if (tb_compare(mftb(), dev->train_timeout) == TB_AAFTERB) > + return pau_opencapi_retry_state(slot, status); > + > + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); > + > + case PAU_SLOT_LINK_TRAINED: > + pau_opencapi_otl_tx_send_enable(dev); > + pci_slot_set_state(slot, PAU_SLOT_NORMAL); > + if (dev->status & PAU_DEV_STATUS_BROKEN) { > + PAUDEVERR(dev, "Resetting a device which hit a " > + "previous error. Device recovery " > + "is not supported, so future behavior is undefined\n"); > + dev->status &= ~PAU_DEV_STATUS_BROKEN; > + } > + pau_opencapi_check_perf_counters(dev); > + dev->phb.scan_map = 1; > + return OPAL_SUCCESS; > + > + default: > + PAUDEVERR(dev, "unexpected slot state %08x\n", slot->state); > + > + } > + pci_slot_set_state(slot, PAU_SLOT_NORMAL); > + return OPAL_HARDWARE; > +} > + > +static void pau_opencapi_prepare_link_change(struct pci_slot *slot __unused, > + bool up __unused) > +{ > + /* > + * PCI hotplug wants it defined, but we don't need to do anything > + */ > +} > + > +static int64_t pau_opencapi_set_power_state(struct pci_slot *slot, > + uint8_t val) > +{ > + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); > + > + switch (val) { > + case PCI_SLOT_POWER_OFF: > + PAUDEVDBG(dev, "Fake power off\n"); > + pau_opencapi_fence_brick(dev); > + pau_opencapi_assert_adapter_reset(dev); > + slot->power_state = PCI_SLOT_POWER_OFF; > + return OPAL_SUCCESS; > + > + case PCI_SLOT_POWER_ON: > + if (slot->power_state != PCI_SLOT_POWER_OFF) > + return OPAL_SUCCESS; > + PAUDEVDBG(dev, "Fake power on\n"); > + slot->power_state = PCI_SLOT_POWER_ON; > + slot->state = PAU_SLOT_NORMAL; > + return OPAL_SUCCESS; > + > + default: > + return OPAL_UNSUPPORTED; > + } > +} > + > static void pau_opencapi_create_phb_slot(struct pau_dev *dev) > { > struct pci_slot *slot; > @@ -516,6 +981,21 @@ static void pau_opencapi_create_phb_slot(struct pau_dev *dev) > */ > PAUDEVERR(dev, "Cannot create PHB slot\n"); > } > + > + /* Elementary functions */ > + slot->ops.creset = pau_opencapi_creset; > + slot->ops.hreset = pau_opencapi_hreset; > + slot->ops.freset = pau_opencapi_freset; > + slot->ops.get_link_state = pau_opencapi_get_link_state; > + slot->ops.get_power_state = pau_opencapi_get_power_state; > + slot->ops.get_presence_state = pau_opencapi_get_presence_state; > + slot->ops.poll_link = pau_opencapi_poll_link; > + slot->ops.prepare_link_change = pau_opencapi_prepare_link_change; > + slot->ops.set_power_state = pau_opencapi_set_power_state; > + > + /* hotplug capability */ > + slot->pluggable = 1; > + > } > > static int64_t pau_opencapi_pcicfg_check(struct pau_dev *dev, > @@ -827,6 +1307,26 @@ static void pau_opencapi_dt_add_mmio_window(struct pau_dev *dev) > hi32(mm_win[1]), lo32(mm_win[1])); > } > > +static void pau_opencapi_dt_add_hotpluggable(struct pau_dev *dev) > +{ > + struct pci_slot *slot = dev->phb.slot; > + struct dt_node *dn = dev->phb.dt_node; > + char label[40]; > + > + /* > + * Add a few definitions to the DT so that the linux PCI > + * hotplug framework can find the slot and identify it as > + * hot-pluggable. > + * > + * The "ibm,slot-label" property is used by linux as the slot name > + */ > + pci_slot_add_dt_properties(slot, dn); > + > + snprintf(label, sizeof(label), "OPENCAPI-%04x", > + (int)PCI_SLOT_PHB_INDEX(slot->id)); > + dt_add_property_string(dn, "ibm,slot-label", label); > +} > + > static void pau_opencapi_dt_add_props(struct pau_dev *dev) > { > struct dt_node *dn = dev->phb.dt_node; > @@ -856,6 +1356,7 @@ static void pau_opencapi_dt_add_props(struct pau_dev *dev) > dt_add_property_cells(dn, "ibm,opal-reserved-pe", PAU_RESERVED_PE_NUM); > > pau_opencapi_dt_add_mmio_window(dev); > + pau_opencapi_dt_add_hotpluggable(dev); > } > > static void pau_opencapi_set_transport_mux_controls(struct pau_dev *dev) > @@ -874,6 +1375,30 @@ static void pau_opencapi_set_transport_mux_controls(struct pau_dev *dev) > pau_write(pau, reg, val); > } > > +static void pau_opencapi_odl_config_phy(struct pau_dev *dev) > +{ > + struct pau *pau = dev->pau; > + uint8_t typemap = 0; > + uint64_t reg, val; > + > + PAUDEVDBG(dev, "Configure ODL\n"); > + > + /* ODL must be in reset when enabling. > + * It stays in reset until the link is trained > + */ > + pau_opencapi_assert_odl_reset(dev); > + > + /* DLO (Open CAPI links) */ > + typemap = 0x2 >> dev->odl_index; > + > + reg = P10_OB_ODL_PHY_CONFIG(dev->op_unit); > + xscom_read(pau->chip_id, reg, &val); > + typemap |= GETFIELD(P10_OB_ODL_PHY_CONFIG_LINK_SELECT, val); > + val = SETFIELD(P10_OB_ODL_PHY_CONFIG_LINK_SELECT, val, typemap); > + val = SETFIELD(P10_OB_ODL_PHY_CONFIG_DL_SELECT, val, 0b10); > + xscom_write(pau->chip_id, reg, val); > +} > + > static void pau_opencapi_enable_xsl_clocks(struct pau *pau) > { > uint64_t reg, val; > @@ -1165,6 +1690,7 @@ static void pau_opencapi_init_hw(struct pau *pau) > pau_for_each_opencapi_dev(dev, pau) { > PAUDEVINF(dev, "Configuring link ...\n"); > pau_opencapi_set_transport_mux_controls(dev); /* step 1 */ > + pau_opencapi_odl_config_phy(dev); > } > pau_opencapi_enable_xsl_clocks(pau); /* step 2 */ > pau_opencapi_enable_misc_clocks(pau); /* step 3 */ > @@ -1232,6 +1758,9 @@ static void pau_opencapi_init_hw(struct pau *pau) > /* done in pau_opencapi_setup_irqs() */ > pau_opencapi_enable_interrupt_on_error(dev); > > + /* enable performance monitor */ > + pau_opencapi_setup_perf_counters(dev); > + > /* Reset disabled. Place OTLs into Run State */ > pau_opencapi_set_fence_control(dev, 0b00); > > diff --git a/include/pau-regs.h b/include/pau-regs.h > index b852a5b5..7a5aaa5f 100644 > --- a/include/pau-regs.h > +++ b/include/pau-regs.h > @@ -142,6 +142,8 @@ > #define PAU_OTL_MISC_ERROR_SIG_RXI(brk) (PAU_BLOCK_OTL(brk) + 0x070) > #define PAU_OTL_MISC_ERROR_SIG_RXO(brk) (PAU_BLOCK_OTL(brk) + 0x078) > #define PAU_OTL_MISC_ERR_RPT_HOLD1(brk) (PAU_BLOCK_OTL(brk) + 0x0B0) > +#define PAU_OTL_MISC_CFG_TX2(brk) (PAU_BLOCK_OTL(brk) + 0x0C0) > +#define PAU_OTL_MISC_CFG_TX2_SEND_EN PPC_BIT(0) > #define PAU_OTL_MISC_PSL_DSISR_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x000) > #define PAU_OTL_MISC_PSL_DAR_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x008) > #define PAU_OTL_MISC_PSL_TFC_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x010) > @@ -178,6 +180,9 @@ > #define PAU_MISC_INT_1_CONFIG (PAU_BLOCK_PAU_MISC + 0x068) > #define PAU_MISC_INT_BAR (PAU_BLOCK_PAU_MISC + 0x098) > #define PAU_MISC_INT_BAR_ADDR PPC_BITMASK(0, 39) > +#define PAU_MISC_FENCE_STATE (PAU_BLOCK_PAU_MISC + 0x0B0) > +#define PAU_MISC_FENCE_STATE_CLEAR(brk) PPC_BIT(0 + (brk)) > +#define PAU_MISC_FENCE_STATE_SET(brk) PPC_BIT(12 + (brk)) > #define PAU_MISC_BDF2PE_CFG(n) (PAU_BLOCK_PAU_MISC + 0x100 + (n) * 8) > #define PAU_MISC_BDF2PE_CFG_ENABLE PPC_BIT(0) > #define PAU_MISC_BDF2PE_CFG_PE PPC_BITMASK(4, 7) > diff --git a/include/pau.h b/include/pau.h > index 9fbccb87..c0a09401 100644 > --- a/include/pau.h > +++ b/include/pau.h > @@ -40,6 +40,8 @@ struct pau_dev { > struct dt_node *dn; > struct phb phb; > uint32_t status; > + unsigned long train_start; > + unsigned long train_timeout; > > struct pau_bar ntl_bar; > struct pau_bar genid_bar; > diff --git a/include/xscom-p10-regs.h b/include/xscom-p10-regs.h > index 5ca4703f..51fec518 100644 > --- a/include/xscom-p10-regs.h > +++ b/include/xscom-p10-regs.h > @@ -58,4 +58,50 @@ > > #define P10_ROOT_CONTROL_7 0x50017 > > +/* PB DLL Configuration Registers */ > +#define P10_OB_ODL(ob) (0x18011000 + (ob) * 0x1000000) > + > +#define P10_OB_ODL_PHY_CONFIG(ob) (P10_OB_ODL(ob) + 0x0C) > +#define P10_OB_ODL_PHY_CONFIG_LINK_SELECT PPC_BITMASK(56, 57) > +#define P10_OB_ODL_PHY_CONFIG_DL_SELECT PPC_BITMASK(62, 63) > + > +#define P10_OB_ODL_PERF_MON_CONFIG(ob) (P10_OB_ODL(ob) + 0x1C) > +#define P10_OB_ODL_PERF_MON_CONFIG_ENABLE PPC_BITMASK(0, 1) > +#define P10_OB_ODL_PERF_MON_CONFIG_LINK0 0b10 > +#define P10_OB_ODL_PERF_MON_CONFIG_LINK1 0b01 > +#define P10_OB_ODL_PERF_MON_CONFIG_SIZE PPC_BITMASK(16, 23) > +#define P10_OB_ODL_PERF_MON_CONFIG_SIZE16 0xFF > + > +#define P10_OB_ODL_PERF_MON_SELECT(ob) (P10_OB_ODL(ob) + 0x1D) > +#define P10_OB_ODL_PERF_MON_SELECT_COUNTER PPC_BITMASK(0, 7) > +#define P10_OB_ODL_PERF_MON_SELECT_CRC_ODL 0x44 > +#define P10_OB_ODL_PERF_MON_SELECT_CRC_DLX 0x45 > + > +#define P10_OB_PERF_COUNTER0(ob) (P10_OB_ODL(ob) + 0x1E) > +#define P10_OB_PERF_COUNTER0_LOW PPC_BITMASK(0, 31) > +#define P10_OB_PERF_COUNTER0_HIGH PPC_BITMASK(32, 63) > + > +#define P10_OB_ODL_CONFIG(ob, brk) (P10_OB_ODL(ob) + 0x2A + brk) > +#define P10_OB_ODL_CONFIG_RESET PPC_BIT(0) > +#define P10_OB_ODL_CONFIG_VERSION PPC_BITMASK(2, 7) > +#define P10_OB_ODL_CONFIG_TRAIN_MODE PPC_BITMASK(8, 11) > +#define P10_OB_ODL_CONFIG_SUPPORTED_MODES PPC_BITMASK(12, 15) > +#define P10_OB_ODL_CONFIG_X4_BACKOFF_ENABLE PPC_BIT(16) > +#define P10_OB_ODL_CONFIG_PHY_CNTR_LIMIT PPC_BITMASK(20, 23) > +#define P10_OB_ODL_CONFIG_DEBUG_ENABLE PPC_BIT(33) > +#define P10_OB_ODL_CONFIG_FWD_PROGRESS_TIMER PPC_BITMASK(40, 43) > + > +#define P10_OB_ODL_STATUS(ob, brk) (P10_OB_ODL(ob) + 0x2C + brk) > +#define P10_OB_ODL_STATUS_TRAINED_MODE PPC_BITMASK(0, 3) > +#define P10_OB_ODL_STATUS_RX_TRAINED_LANES PPC_BITMASK(16, 23) > +#define P10_OB_ODL_STATUS_TX_TRAINED_LANES PPC_BITMASK(24, 31) > +#define P10_OB_ODL_STATUS_TRAINING_STATE PPC_BITMASK(49, 51) > + > +#define P10_OB_ODL_TRAIN_STAT(ob, brk) (P10_OB_ODL(ob) + 0x2E + brk) > +#define P10_OB_ODL_TRAIN_STAT_PATTERN_B PPC_BITMASK(8, 15) > + > +#define P10_OB_ODL_DLX_INFO(ob, brk) (P10_OB_ODL(ob) + 0x32 + brk) > + > +#define P10_OB_ODL_LINK_SPEED_STATUS(ob, brk) (P10_OB_ODL(ob) + 0x34 + brk) > + > #endif /* __XSCOM_P10_REGS_H__ */ >
diff --git a/hw/pau.c b/hw/pau.c index d4222361..91dee12c 100644 --- a/hw/pau.c +++ b/hw/pau.c @@ -15,6 +15,22 @@ #define PAU_MAX_PE_NUM 16 #define PAU_RESERVED_PE_NUM 15 +#define PAU_SLOT_NORMAL PCI_SLOT_STATE_NORMAL +#define PAU_SLOT_LINK PCI_SLOT_STATE_LINK +#define PAU_SLOT_LINK_START (PAU_SLOT_LINK + 1) +#define PAU_SLOT_LINK_WAIT (PAU_SLOT_LINK + 2) +#define PAU_SLOT_LINK_TRAINED (PAU_SLOT_LINK + 3) +#define PAU_SLOT_FRESET PCI_SLOT_STATE_FRESET +#define PAU_SLOT_FRESET_START (PAU_SLOT_FRESET + 1) +#define PAU_SLOT_FRESET_INIT (PAU_SLOT_FRESET + 2) +#define PAU_SLOT_FRESET_ASSERT_DELAY (PAU_SLOT_FRESET + 3) +#define PAU_SLOT_FRESET_DEASSERT_DELAY (PAU_SLOT_FRESET + 4) +#define PAU_SLOT_FRESET_INIT_DELAY (PAU_SLOT_FRESET + 5) + +#define PAU_LINK_TRAINING_RETRIES 2 +#define PAU_LINK_TRAINING_TIMEOUT 15000 /* ms */ +#define PAU_LINK_STATE_TRAINED 0x7 + struct pau_dev *pau_next_dev(struct pau *pau, struct pau_dev *dev, enum pau_dev_type type) { @@ -168,6 +184,7 @@ static void pau_dt_create_pau(struct dt_node *xscom, uint32_t pau_index) dt_add_property_cells(pau, "#address-cells", 1); dt_add_property_cells(pau, "reg", pau_base[pau_index], 0x2c); dt_add_property_string(pau, "compatible", "ibm,power10-pau"); + dt_add_property_cells(pau, "ibm,pau-chiplet", pau_base[pau_index] >> 24); dt_add_property_cells(pau, "ibm,pau-index", pau_index); links = PAU_LINKS_OPENCAPI_PER_PAU; @@ -202,12 +219,14 @@ static struct pau *pau_create(struct dt_node *dn) assert(pau); init_lock(&pau->lock); + init_lock(&pau->procedure_state.lock); pau->dt_node = dn; pau->index = dt_prop_get_u32(dn, "ibm,pau-index"); pau->xscom_base = dt_get_address(dn, 0, NULL); pau->chip_id = dt_get_chip_id(dn); + pau->op_chiplet = dt_prop_get_u32(dn, "ibm,pau-chiplet"); assert(get_chip(pau->chip_id)); pau->links = PAU_LINKS_OPENCAPI_PER_PAU; @@ -503,6 +522,452 @@ static void pau_opencapi_enable_bars(struct pau_dev *dev, bool enable) pau_write(pau, reg, val); } +static int64_t pau_opencapi_creset(struct pci_slot *slot) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + + PAUDEVERR(dev, "creset not supported\n"); + return OPAL_UNSUPPORTED; +} + +static int64_t pau_opencapi_hreset(struct pci_slot *slot) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + + PAUDEVERR(dev, "hreset not supported\n"); + return OPAL_UNSUPPORTED; +} + +static void pau_opencapi_assert_odl_reset(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); + val = P10_OB_ODL_CONFIG_RESET; + val = SETFIELD(P10_OB_ODL_CONFIG_VERSION, val, 0b000100); // OCAPI 4 + val = SETFIELD(P10_OB_ODL_CONFIG_TRAIN_MODE, val, 0b0101); // ts2 + val = SETFIELD(P10_OB_ODL_CONFIG_SUPPORTED_MODES, val, 0b0010); + val |= P10_OB_ODL_CONFIG_X4_BACKOFF_ENABLE; + val = SETFIELD(P10_OB_ODL_CONFIG_PHY_CNTR_LIMIT, val, 0b1111); + val |= P10_OB_ODL_CONFIG_DEBUG_ENABLE; + val = SETFIELD(P10_OB_ODL_CONFIG_FWD_PROGRESS_TIMER, val, 0b0110); + xscom_write(pau->chip_id, reg, val); +} + +static void pau_opencapi_deassert_odl_reset(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); + xscom_read(pau->chip_id, reg, &val); + val &= ~P10_OB_ODL_CONFIG_RESET; + xscom_write(pau->chip_id, reg, val); +} + +static void pau_opencapi_training_mode(struct pau_dev *dev, + uint8_t pattern) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + reg = P10_OB_ODL_CONFIG(dev->op_unit, dev->odl_index); + xscom_read(pau->chip_id, reg, &val); + val = SETFIELD(P10_OB_ODL_CONFIG_TRAIN_MODE, val, pattern); + xscom_write(pau->chip_id, reg, val); +} + +static int64_t pau_opencapi_assert_adapter_reset(struct pau_dev *dev) +{ + int64_t rc = OPAL_PARAMETER; + + if (platform.ocapi->i2c_assert_reset) + rc = platform.ocapi->i2c_assert_reset(dev->i2c_bus_id); + + if (rc) + PAUDEVERR(dev, "Error writing I2C reset signal: %lld\n", rc); + return rc; +} + +static int64_t pau_opencapi_deassert_adapter_reset(struct pau_dev *dev) +{ + int64_t rc = OPAL_PARAMETER; + + if (platform.ocapi->i2c_deassert_reset) + rc = platform.ocapi->i2c_deassert_reset(dev->i2c_bus_id); + + if (rc) + PAUDEVERR(dev, "Error writing I2C reset signal: %lld\n", rc); + return rc; +} + +static void pau_opencapi_fence_brick(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + + PAUDEVDBG(dev, "Fencing brick\n"); + pau_opencapi_set_fence_control(dev, 0b11); + + /* Place all bricks into Fence state */ + pau_write(pau, PAU_MISC_FENCE_STATE, + PAU_MISC_FENCE_STATE_SET(pau_dev_index(dev, PAU_LINKS_OPENCAPI_PER_PAU))); +} + +static void pau_opencapi_unfence_brick(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + + PAUDEVDBG(dev, "Unfencing brick\n"); + pau_write(pau, PAU_MISC_FENCE_STATE, + PAU_MISC_FENCE_STATE_CLEAR(pau_dev_index(dev, PAU_LINKS_OPENCAPI_PER_PAU))); + + pau_opencapi_set_fence_control(dev, 0b10); + pau_opencapi_set_fence_control(dev, 0b00); +} + +static int64_t pau_opencapi_freset(struct pci_slot *slot) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + uint8_t presence = 1; + int64_t rc = OPAL_SUCCESS; + + switch (slot->state) { + case PAU_SLOT_NORMAL: + case PAU_SLOT_FRESET_START: + PAUDEVDBG(dev, "FRESET: Starts\n"); + + if (slot->ops.get_presence_state) + slot->ops.get_presence_state(slot, &presence); + if (!presence) { + /* + * FIXME: if there's no card on the link, we + * should consider powering off the unused + * lanes to save energy + */ + PAUDEVINF(dev, "no card detected\n"); + return OPAL_SUCCESS; + } + slot->link_retries = PAU_LINK_TRAINING_RETRIES; + /* fall-through */ + case PAU_SLOT_FRESET_INIT: + pau_opencapi_fence_brick(dev); + pau_opencapi_enable_bars(dev, false); + pau_opencapi_assert_odl_reset(dev); + pau_opencapi_assert_adapter_reset(dev); + pci_slot_set_state(slot, PAU_SLOT_FRESET_ASSERT_DELAY); + /* assert for 5ms */ + return pci_slot_set_sm_timeout(slot, msecs_to_tb(5)); + + case PAU_SLOT_FRESET_ASSERT_DELAY: + rc = pau_dev_phy_reset(dev); + if (rc) { + PAUDEVERR(dev, "FRESET: PHY reset error\n"); + return OPAL_HARDWARE; + } + pau_opencapi_deassert_odl_reset(dev); + pau_opencapi_deassert_adapter_reset(dev); + pci_slot_set_state(slot, PAU_SLOT_FRESET_DEASSERT_DELAY); + /* give 250ms to device to be ready */ + return pci_slot_set_sm_timeout(slot, msecs_to_tb(250)); + + case PAU_SLOT_FRESET_DEASSERT_DELAY: + pau_opencapi_unfence_brick(dev); + pau_opencapi_enable_bars(dev, true); + pau_opencapi_training_mode(dev, 0b0001); /* send pattern A */ + pci_slot_set_state(slot, PAU_SLOT_FRESET_INIT_DELAY); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(5)); + + case PAU_SLOT_FRESET_INIT_DELAY: + pau_opencapi_training_mode(dev, 0b1000); /* enable training */ + dev->train_start = mftb(); + dev->train_timeout = dev->train_start + + msecs_to_tb(PAU_LINK_TRAINING_TIMEOUT); + pci_slot_set_state(slot, PAU_SLOT_LINK_START); + return slot->ops.poll_link(slot); + + default: + PAUDEVERR(dev, "FRESET: unexpected slot state %08x\n", + slot->state); + } + pci_slot_set_state(slot, PAU_SLOT_NORMAL); + return OPAL_HARDWARE; +} + +static uint64_t pau_opencapi_get_odl_endpoint_info(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t val; + + xscom_read(pau->chip_id, + P10_OB_ODL_DLX_INFO(dev->op_unit, dev->odl_index), + &val); + return val; +} + +static uint64_t pau_opencapi_get_odl_training_status(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t val; + + xscom_read(pau->chip_id, + P10_OB_ODL_TRAIN_STAT(dev->op_unit, dev->odl_index), + &val); + return val; +} + +static uint64_t pau_opencapi_get_odl_status(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t val; + + xscom_read(pau->chip_id, + P10_OB_ODL_STATUS(dev->op_unit, dev->odl_index), + &val); + return val; +} + +static uint64_t pau_opencapi_get_odl_link_speed_status(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t val; + + xscom_read(pau->chip_id, + P10_OB_ODL_LINK_SPEED_STATUS(dev->op_unit, dev->odl_index), + &val); + return val; +} + +static enum OpalShpcLinkState pau_opencapi_get_link_width(uint64_t status) +{ + uint64_t tx_lanes, rx_lanes, state; + + state = GETFIELD(P10_OB_ODL_STATUS_TRAINING_STATE, status); + if (state != PAU_LINK_STATE_TRAINED) + return OPAL_SHPC_LINK_DOWN; + + rx_lanes = GETFIELD(P10_OB_ODL_STATUS_RX_TRAINED_LANES, status); + tx_lanes = GETFIELD(P10_OB_ODL_STATUS_TX_TRAINED_LANES, status); + if ((rx_lanes != 0xFF) || (tx_lanes != 0xFF)) + return OPAL_SHPC_LINK_UP_x4; + else + return OPAL_SHPC_LINK_UP_x8; +} + +static int64_t pau_opencapi_get_link_state(struct pci_slot *slot, + uint8_t *val) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + uint64_t status; + + status = pau_opencapi_get_odl_status(dev); + *val = pau_opencapi_get_link_width(status); + + return OPAL_SUCCESS; + +} + +static int64_t pau_opencapi_get_power_state(struct pci_slot *slot, + uint8_t *val) +{ + *val = slot->power_state; + return OPAL_SUCCESS; +} + +static int64_t pau_opencapi_get_presence_state(struct pci_slot __unused * slot, + uint8_t *val) +{ + /* + * Presence detection for OpenCAPI is currently done at the start of + * PAU initialisation, and we only create slots if a device is present. + * As such we will never be asked to get the presence of a slot that's + * empty. + * + * This may change if we ever support hotplug down the track. + */ + *val = OPAL_PCI_SLOT_PRESENT; + return OPAL_SUCCESS; +} + +static void pau_opencapi_check_trained_link(struct pau_dev *dev, + uint64_t status) +{ + if (pau_opencapi_get_link_width(status) != OPAL_SHPC_LINK_UP_x8) { + PAUDEVERR(dev, "Link trained in degraded mode (%016llx)\n", + status); + PAUDEVDBG(dev, "Link endpoint info: %016llx\n", + pau_opencapi_get_odl_endpoint_info(dev)); + } +} + +static int64_t pau_opencapi_retry_state(struct pci_slot *slot, + uint64_t status) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + + if (!slot->link_retries--) { + /** + * @fwts-label OCAPILinkTrainingFailed + * @fwts-advice The OpenCAPI link training procedure failed. + * This indicates a hardware or firmware bug. OpenCAPI + * functionality will not be available on this link. + */ + PAUDEVERR(dev, + "Link failed to train, final link status: %016llx\n", + status); + PAUDEVDBG(dev, "Final link training status: %016llx (Link Speed Status: %016llx)\n", + pau_opencapi_get_odl_training_status(dev), + pau_opencapi_get_odl_link_speed_status(dev)); + return OPAL_HARDWARE; + } + + PAUDEVERR(dev, "Link failed to train, retrying\n"); + PAUDEVERR(dev, "Link status: %016llx, training status: %016llx " + "(Link Speed Status: %016llx)\n", + status, + pau_opencapi_get_odl_training_status(dev), + pau_opencapi_get_odl_link_speed_status(dev)); + + pci_slot_set_state(slot, PAU_SLOT_FRESET_INIT); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); +} + +static void pau_opencapi_otl_tx_send_enable(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + /* Allows OTL TX to send out packets to AFU */ + PAUDEVDBG(dev, "OTL TX Send Enable\n"); + + reg = PAU_OTL_MISC_CFG_TX2(dev->index); + val = pau_read(pau, reg); + val |= PAU_OTL_MISC_CFG_TX2_SEND_EN; + pau_write(pau, reg, val); +} + +static void pau_opencapi_setup_perf_counters(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + PAUDEVDBG(dev, "Setup perf counter\n"); + + reg = P10_OB_ODL_PERF_MON_CONFIG(dev->op_unit); + xscom_read(pau->chip_id, reg, &val); + val = SETFIELD(P10_OB_ODL_PERF_MON_CONFIG_ENABLE, val, + P10_OB_ODL_PERF_MON_CONFIG_LINK0 >> dev->index); + val = SETFIELD(P10_OB_ODL_PERF_MON_CONFIG_SIZE, val, + P10_OB_ODL_PERF_MON_CONFIG_SIZE16); + xscom_write(pau->chip_id, reg, val); + PAUDEVDBG(dev, "perf counter config %llx = %llx\n", reg, val); + + reg = P10_OB_ODL_PERF_MON_SELECT(dev->op_unit); + xscom_read(pau->chip_id, reg, &val); + val = SETFIELD(P10_OB_ODL_PERF_MON_SELECT_COUNTER >> (dev->index * 16), + val, P10_OB_ODL_PERF_MON_SELECT_CRC_ODL); + val = SETFIELD(P10_OB_ODL_PERF_MON_SELECT_COUNTER >> ((dev->index * 16) + 8), + val, P10_OB_ODL_PERF_MON_SELECT_CRC_DLX); + xscom_write(pau->chip_id, reg, val); + PAUDEVDBG(dev, "perf counter select %llx = %llx\n", reg, val); +} + +static void pau_opencapi_check_perf_counters(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint64_t reg, val; + + reg = P10_OB_PERF_COUNTER0(dev->op_unit); + xscom_read(pau->chip_id, reg, &val); + + if (val) + PAUDEVERR(dev, "CRC error count perf_counter0..3=0%#llx\n", + val); +} + +static int64_t pau_opencapi_poll_link(struct pci_slot *slot) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + uint64_t status; + + switch (slot->state) { + case PAU_SLOT_NORMAL: + case PAU_SLOT_LINK_START: + PAUDEVDBG(dev, "Start polling\n"); + pci_slot_set_state(slot, PAU_SLOT_LINK_WAIT); + /* fall-through */ + case PAU_SLOT_LINK_WAIT: + status = pau_opencapi_get_odl_status(dev); + if (GETFIELD(P10_OB_ODL_STATUS_TRAINING_STATE, status) == + PAU_LINK_STATE_TRAINED) { + PAUDEVINF(dev, "link trained in %ld ms (Link Speed Status: %016llx)\n", + tb_to_msecs(mftb() - dev->train_start), + pau_opencapi_get_odl_link_speed_status(dev)); + pau_opencapi_check_trained_link(dev, status); + + pci_slot_set_state(slot, PAU_SLOT_LINK_TRAINED); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); + } + if (tb_compare(mftb(), dev->train_timeout) == TB_AAFTERB) + return pau_opencapi_retry_state(slot, status); + + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1)); + + case PAU_SLOT_LINK_TRAINED: + pau_opencapi_otl_tx_send_enable(dev); + pci_slot_set_state(slot, PAU_SLOT_NORMAL); + if (dev->status & PAU_DEV_STATUS_BROKEN) { + PAUDEVERR(dev, "Resetting a device which hit a " + "previous error. Device recovery " + "is not supported, so future behavior is undefined\n"); + dev->status &= ~PAU_DEV_STATUS_BROKEN; + } + pau_opencapi_check_perf_counters(dev); + dev->phb.scan_map = 1; + return OPAL_SUCCESS; + + default: + PAUDEVERR(dev, "unexpected slot state %08x\n", slot->state); + + } + pci_slot_set_state(slot, PAU_SLOT_NORMAL); + return OPAL_HARDWARE; +} + +static void pau_opencapi_prepare_link_change(struct pci_slot *slot __unused, + bool up __unused) +{ + /* + * PCI hotplug wants it defined, but we don't need to do anything + */ +} + +static int64_t pau_opencapi_set_power_state(struct pci_slot *slot, + uint8_t val) +{ + struct pau_dev *dev = pau_phb_to_opencapi_dev(slot->phb); + + switch (val) { + case PCI_SLOT_POWER_OFF: + PAUDEVDBG(dev, "Fake power off\n"); + pau_opencapi_fence_brick(dev); + pau_opencapi_assert_adapter_reset(dev); + slot->power_state = PCI_SLOT_POWER_OFF; + return OPAL_SUCCESS; + + case PCI_SLOT_POWER_ON: + if (slot->power_state != PCI_SLOT_POWER_OFF) + return OPAL_SUCCESS; + PAUDEVDBG(dev, "Fake power on\n"); + slot->power_state = PCI_SLOT_POWER_ON; + slot->state = PAU_SLOT_NORMAL; + return OPAL_SUCCESS; + + default: + return OPAL_UNSUPPORTED; + } +} + static void pau_opencapi_create_phb_slot(struct pau_dev *dev) { struct pci_slot *slot; @@ -516,6 +981,21 @@ static void pau_opencapi_create_phb_slot(struct pau_dev *dev) */ PAUDEVERR(dev, "Cannot create PHB slot\n"); } + + /* Elementary functions */ + slot->ops.creset = pau_opencapi_creset; + slot->ops.hreset = pau_opencapi_hreset; + slot->ops.freset = pau_opencapi_freset; + slot->ops.get_link_state = pau_opencapi_get_link_state; + slot->ops.get_power_state = pau_opencapi_get_power_state; + slot->ops.get_presence_state = pau_opencapi_get_presence_state; + slot->ops.poll_link = pau_opencapi_poll_link; + slot->ops.prepare_link_change = pau_opencapi_prepare_link_change; + slot->ops.set_power_state = pau_opencapi_set_power_state; + + /* hotplug capability */ + slot->pluggable = 1; + } static int64_t pau_opencapi_pcicfg_check(struct pau_dev *dev, @@ -827,6 +1307,26 @@ static void pau_opencapi_dt_add_mmio_window(struct pau_dev *dev) hi32(mm_win[1]), lo32(mm_win[1])); } +static void pau_opencapi_dt_add_hotpluggable(struct pau_dev *dev) +{ + struct pci_slot *slot = dev->phb.slot; + struct dt_node *dn = dev->phb.dt_node; + char label[40]; + + /* + * Add a few definitions to the DT so that the linux PCI + * hotplug framework can find the slot and identify it as + * hot-pluggable. + * + * The "ibm,slot-label" property is used by linux as the slot name + */ + pci_slot_add_dt_properties(slot, dn); + + snprintf(label, sizeof(label), "OPENCAPI-%04x", + (int)PCI_SLOT_PHB_INDEX(slot->id)); + dt_add_property_string(dn, "ibm,slot-label", label); +} + static void pau_opencapi_dt_add_props(struct pau_dev *dev) { struct dt_node *dn = dev->phb.dt_node; @@ -856,6 +1356,7 @@ static void pau_opencapi_dt_add_props(struct pau_dev *dev) dt_add_property_cells(dn, "ibm,opal-reserved-pe", PAU_RESERVED_PE_NUM); pau_opencapi_dt_add_mmio_window(dev); + pau_opencapi_dt_add_hotpluggable(dev); } static void pau_opencapi_set_transport_mux_controls(struct pau_dev *dev) @@ -874,6 +1375,30 @@ static void pau_opencapi_set_transport_mux_controls(struct pau_dev *dev) pau_write(pau, reg, val); } +static void pau_opencapi_odl_config_phy(struct pau_dev *dev) +{ + struct pau *pau = dev->pau; + uint8_t typemap = 0; + uint64_t reg, val; + + PAUDEVDBG(dev, "Configure ODL\n"); + + /* ODL must be in reset when enabling. + * It stays in reset until the link is trained + */ + pau_opencapi_assert_odl_reset(dev); + + /* DLO (Open CAPI links) */ + typemap = 0x2 >> dev->odl_index; + + reg = P10_OB_ODL_PHY_CONFIG(dev->op_unit); + xscom_read(pau->chip_id, reg, &val); + typemap |= GETFIELD(P10_OB_ODL_PHY_CONFIG_LINK_SELECT, val); + val = SETFIELD(P10_OB_ODL_PHY_CONFIG_LINK_SELECT, val, typemap); + val = SETFIELD(P10_OB_ODL_PHY_CONFIG_DL_SELECT, val, 0b10); + xscom_write(pau->chip_id, reg, val); +} + static void pau_opencapi_enable_xsl_clocks(struct pau *pau) { uint64_t reg, val; @@ -1165,6 +1690,7 @@ static void pau_opencapi_init_hw(struct pau *pau) pau_for_each_opencapi_dev(dev, pau) { PAUDEVINF(dev, "Configuring link ...\n"); pau_opencapi_set_transport_mux_controls(dev); /* step 1 */ + pau_opencapi_odl_config_phy(dev); } pau_opencapi_enable_xsl_clocks(pau); /* step 2 */ pau_opencapi_enable_misc_clocks(pau); /* step 3 */ @@ -1232,6 +1758,9 @@ static void pau_opencapi_init_hw(struct pau *pau) /* done in pau_opencapi_setup_irqs() */ pau_opencapi_enable_interrupt_on_error(dev); + /* enable performance monitor */ + pau_opencapi_setup_perf_counters(dev); + /* Reset disabled. Place OTLs into Run State */ pau_opencapi_set_fence_control(dev, 0b00); diff --git a/include/pau-regs.h b/include/pau-regs.h index b852a5b5..7a5aaa5f 100644 --- a/include/pau-regs.h +++ b/include/pau-regs.h @@ -142,6 +142,8 @@ #define PAU_OTL_MISC_ERROR_SIG_RXI(brk) (PAU_BLOCK_OTL(brk) + 0x070) #define PAU_OTL_MISC_ERROR_SIG_RXO(brk) (PAU_BLOCK_OTL(brk) + 0x078) #define PAU_OTL_MISC_ERR_RPT_HOLD1(brk) (PAU_BLOCK_OTL(brk) + 0x0B0) +#define PAU_OTL_MISC_CFG_TX2(brk) (PAU_BLOCK_OTL(brk) + 0x0C0) +#define PAU_OTL_MISC_CFG_TX2_SEND_EN PPC_BIT(0) #define PAU_OTL_MISC_PSL_DSISR_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x000) #define PAU_OTL_MISC_PSL_DAR_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x008) #define PAU_OTL_MISC_PSL_TFC_AN(brk) (PAU_BLOCK_OTL_PSL(brk) + 0x010) @@ -178,6 +180,9 @@ #define PAU_MISC_INT_1_CONFIG (PAU_BLOCK_PAU_MISC + 0x068) #define PAU_MISC_INT_BAR (PAU_BLOCK_PAU_MISC + 0x098) #define PAU_MISC_INT_BAR_ADDR PPC_BITMASK(0, 39) +#define PAU_MISC_FENCE_STATE (PAU_BLOCK_PAU_MISC + 0x0B0) +#define PAU_MISC_FENCE_STATE_CLEAR(brk) PPC_BIT(0 + (brk)) +#define PAU_MISC_FENCE_STATE_SET(brk) PPC_BIT(12 + (brk)) #define PAU_MISC_BDF2PE_CFG(n) (PAU_BLOCK_PAU_MISC + 0x100 + (n) * 8) #define PAU_MISC_BDF2PE_CFG_ENABLE PPC_BIT(0) #define PAU_MISC_BDF2PE_CFG_PE PPC_BITMASK(4, 7) diff --git a/include/pau.h b/include/pau.h index 9fbccb87..c0a09401 100644 --- a/include/pau.h +++ b/include/pau.h @@ -40,6 +40,8 @@ struct pau_dev { struct dt_node *dn; struct phb phb; uint32_t status; + unsigned long train_start; + unsigned long train_timeout; struct pau_bar ntl_bar; struct pau_bar genid_bar; diff --git a/include/xscom-p10-regs.h b/include/xscom-p10-regs.h index 5ca4703f..51fec518 100644 --- a/include/xscom-p10-regs.h +++ b/include/xscom-p10-regs.h @@ -58,4 +58,50 @@ #define P10_ROOT_CONTROL_7 0x50017 +/* PB DLL Configuration Registers */ +#define P10_OB_ODL(ob) (0x18011000 + (ob) * 0x1000000) + +#define P10_OB_ODL_PHY_CONFIG(ob) (P10_OB_ODL(ob) + 0x0C) +#define P10_OB_ODL_PHY_CONFIG_LINK_SELECT PPC_BITMASK(56, 57) +#define P10_OB_ODL_PHY_CONFIG_DL_SELECT PPC_BITMASK(62, 63) + +#define P10_OB_ODL_PERF_MON_CONFIG(ob) (P10_OB_ODL(ob) + 0x1C) +#define P10_OB_ODL_PERF_MON_CONFIG_ENABLE PPC_BITMASK(0, 1) +#define P10_OB_ODL_PERF_MON_CONFIG_LINK0 0b10 +#define P10_OB_ODL_PERF_MON_CONFIG_LINK1 0b01 +#define P10_OB_ODL_PERF_MON_CONFIG_SIZE PPC_BITMASK(16, 23) +#define P10_OB_ODL_PERF_MON_CONFIG_SIZE16 0xFF + +#define P10_OB_ODL_PERF_MON_SELECT(ob) (P10_OB_ODL(ob) + 0x1D) +#define P10_OB_ODL_PERF_MON_SELECT_COUNTER PPC_BITMASK(0, 7) +#define P10_OB_ODL_PERF_MON_SELECT_CRC_ODL 0x44 +#define P10_OB_ODL_PERF_MON_SELECT_CRC_DLX 0x45 + +#define P10_OB_PERF_COUNTER0(ob) (P10_OB_ODL(ob) + 0x1E) +#define P10_OB_PERF_COUNTER0_LOW PPC_BITMASK(0, 31) +#define P10_OB_PERF_COUNTER0_HIGH PPC_BITMASK(32, 63) + +#define P10_OB_ODL_CONFIG(ob, brk) (P10_OB_ODL(ob) + 0x2A + brk) +#define P10_OB_ODL_CONFIG_RESET PPC_BIT(0) +#define P10_OB_ODL_CONFIG_VERSION PPC_BITMASK(2, 7) +#define P10_OB_ODL_CONFIG_TRAIN_MODE PPC_BITMASK(8, 11) +#define P10_OB_ODL_CONFIG_SUPPORTED_MODES PPC_BITMASK(12, 15) +#define P10_OB_ODL_CONFIG_X4_BACKOFF_ENABLE PPC_BIT(16) +#define P10_OB_ODL_CONFIG_PHY_CNTR_LIMIT PPC_BITMASK(20, 23) +#define P10_OB_ODL_CONFIG_DEBUG_ENABLE PPC_BIT(33) +#define P10_OB_ODL_CONFIG_FWD_PROGRESS_TIMER PPC_BITMASK(40, 43) + +#define P10_OB_ODL_STATUS(ob, brk) (P10_OB_ODL(ob) + 0x2C + brk) +#define P10_OB_ODL_STATUS_TRAINED_MODE PPC_BITMASK(0, 3) +#define P10_OB_ODL_STATUS_RX_TRAINED_LANES PPC_BITMASK(16, 23) +#define P10_OB_ODL_STATUS_TX_TRAINED_LANES PPC_BITMASK(24, 31) +#define P10_OB_ODL_STATUS_TRAINING_STATE PPC_BITMASK(49, 51) + +#define P10_OB_ODL_TRAIN_STAT(ob, brk) (P10_OB_ODL(ob) + 0x2E + brk) +#define P10_OB_ODL_TRAIN_STAT_PATTERN_B PPC_BITMASK(8, 15) + +#define P10_OB_ODL_DLX_INFO(ob, brk) (P10_OB_ODL(ob) + 0x32 + brk) + +#define P10_OB_ODL_LINK_SPEED_STATUS(ob, brk) (P10_OB_ODL(ob) + 0x34 + brk) + #endif /* __XSCOM_P10_REGS_H__ */
Add elementary functions to handle a phb complete, fundamental and hot resets. For the time being, specific creset and hreset are not supported. A complete fundamental reset is based on the following steps, in this order: - Place all bricks into Fence state - Disable BARs - Reset ODL to Power-on Values - Set the i2c reset pin in output mode - Initialize PHY Lanes - Deassert ODL reset - Clear the the i2c reset pin - Unfence bricks - Enable BARs - Enable ODL training mode Link training is also set up. Signed-off-by: Christophe Lombard <clombard@linux.vnet.ibm.com> --- hw/pau.c | 529 +++++++++++++++++++++++++++++++++++++++ include/pau-regs.h | 5 + include/pau.h | 2 + include/xscom-p10-regs.h | 46 ++++ 4 files changed, 582 insertions(+)