diff options
author | Andrew Donnellan <andrew.donnellan@au1.ibm.com> | 2018-03-01 18:57:14 +1100 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2018-03-01 20:17:54 -0600 |
commit | 9db58b1e5c031782c442f92775ee75326a7be1b3 (patch) | |
tree | 14360368a1ce60a349192bd20d0ff54a4b67ed64 /hw/npu2-hw-procedures.c | |
parent | b5f1fd30ef56290a6171c79b71bbecb3516e7cf7 (diff) | |
download | skiboot-9db58b1e5c031782c442f92775ee75326a7be1b3.zip skiboot-9db58b1e5c031782c442f92775ee75326a7be1b3.tar.gz skiboot-9db58b1e5c031782c442f92775ee75326a7be1b3.tar.bz2 |
npu2-hw-procedures: Add support for OpenCAPI PHY link training
Unlike NVLink, which uses the pci-virt framework to fake a PCI
configuration space for NVLink devices, the OpenCAPI device model presents
us with a real configuration space handled by the device over the OpenCAPI
link.
As a result, we have to train the OpenCAPI link in skiboot before we do PCI
probing, so that config space can be accessed, rather than having link
training being triggered by the Linux driver.
Add some helper functions to wrap the existing NVLink PHY training sequence
so we can easily run it within skiboot.
Additionally, we add OpenCAPI-specific lane settings, and a function to
"bump" lanes that haven't trained properly (this process isn't documented
in the workbook, but the hardware experts assure us that this improves link
training reliability...) We also support the PRBS31 pattern that's used for
bringup and test purposes.
Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Reviewed-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Acked-by: Reza Arbab <arbab@linux.vnet.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/npu2-hw-procedures.c')
-rw-r--r-- | hw/npu2-hw-procedures.c | 128 |
1 files changed, 116 insertions, 12 deletions
diff --git a/hw/npu2-hw-procedures.c b/hw/npu2-hw-procedures.c index eb087bc..69e788e 100644 --- a/hw/npu2-hw-procedures.c +++ b/hw/npu2-hw-procedures.c @@ -60,6 +60,7 @@ struct npu2_phy_reg NPU2_PHY_TX_FIFO_INIT = {0x105, 53, 1}; struct npu2_phy_reg NPU2_PHY_TX_RXCAL = {0x103, 57, 1}; struct npu2_phy_reg NPU2_PHY_RX_INIT_DONE = {0x0ca, 48, 1}; struct npu2_phy_reg NPU2_PHY_RX_PR_EDGE_TRACK_CNTL = {0x092, 48, 2}; +struct npu2_phy_reg NPU2_PHY_RX_PR_BUMP_SL_1UI = {0x092, 57, 1}; struct npu2_phy_reg NPU2_PHY_RX_PR_FW_OFF = {0x08a, 56, 1}; struct npu2_phy_reg NPU2_PHY_RX_PR_FW_INERTIA_AMT = {0x08a, 57, 3}; struct npu2_phy_reg NPU2_PHY_RX_CFG_LTE_MC = {0x000, 60, 4}; @@ -68,6 +69,8 @@ struct npu2_phy_reg NPU2_PHY_RX_B_INTEG_COARSE_GAIN = {0x026, 48, 4}; struct npu2_phy_reg NPU2_PHY_RX_E_INTEG_COARSE_GAIN = {0x030, 48, 4}; /* These registers are per-PHY, not per lane */ +struct npu2_phy_reg NPU2_PHY_RX_SPEED_SELECT = {0x262, 51, 2}; +struct npu2_phy_reg NPU2_PHY_RX_AC_COUPLED = {0x262, 53, 1}; struct npu2_phy_reg NPU2_PHY_TX_ZCAL_SWO_EN = {0x3c9, 48, 1}; struct npu2_phy_reg NPU2_PHY_TX_ZCAL_REQ = {0x3c1, 49, 1}; struct npu2_phy_reg NPU2_PHY_TX_ZCAL_DONE = {0x3c1, 50, 1}; @@ -98,6 +101,7 @@ struct npu2_phy_reg NPU2_PHY_RX_CLKDIST_PDWN = {0x204, 48, 3}; struct npu2_phy_reg NPU2_PHY_RX_IREF_PDWN = {0x230, 54, 1}; struct npu2_phy_reg NPU2_PHY_TX_CLKDIST_PDWN = {0x305, 48, 3}; struct npu2_phy_reg NPU2_PHY_RX_CTL_DATASM_CLKDIST_PDWN = {0x2e0, 60, 1}; +struct npu2_phy_reg NPU2_PHY_TX_DRV_DATA_PATTERN_GCRMSG = {0x309, 50, 4}; #define NPU2_PHY_REG(scom_base, reg, lane) \ SETFIELD(PPC_BITMASK(27, 31), ((reg)->offset << 42) | scom_base, lane) @@ -378,6 +382,25 @@ static uint32_t phy_reset_complete(struct npu2_dev *ndev) { int lane; + if (ndev->type == NPU2_DEV_TYPE_OPENCAPI) { + phy_write(ndev, &NPU2_PHY_RX_AC_COUPLED, 1); + + switch (ndev->link_speed) { + case 20000000000: + prlog(PR_INFO, "OCAPI: Link speed set at 20Gb/s\n"); + phy_write(ndev, &NPU2_PHY_RX_SPEED_SELECT, 1); + break; + case 25000000000: + case 25781250000: + prlog(PR_INFO, "OCAPI: Link speed set at 25.xGb/s\n"); + phy_write(ndev, &NPU2_PHY_RX_SPEED_SELECT, 0); + break; + default: + prlog(PR_CRIT, "OCAPI: Invalid link speed!\n"); + assert(false); + } + } + FOR_EACH_LANE(ndev, lane) { phy_write_lane(ndev, &NPU2_PHY_RX_LANE_ANA_PDWN, lane, 0); phy_write_lane(ndev, &NPU2_PHY_RX_LANE_DIG_PDWN, lane, 0); @@ -640,18 +663,25 @@ static uint32_t phy_rx_dccal_complete(struct npu2_dev *ndev) static uint32_t phy_rx_clock_sel(struct npu2_dev *ndev) { - /* - * Change the RX clk mux control to be done by software instead of HW. This - * avoids glitches caused by changing the mux setting. - * - * Work around a known DL bug by doing these writes twice. - */ - npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), 0x80000002, 0x80000003); - npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), 0x80000002, 0x80000003); - - npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), 0x80000000, 0x80000003); - npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), 0x80000000, 0x80000003); - + if (ndev->type != NPU2_DEV_TYPE_OPENCAPI) { + /* + * Change the RX clk mux control to be done by + * software instead of HW. This avoids glitches caused + * by changing the mux setting. + * + * Work around a known DL bug by doing these writes + * twice. + */ + npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), + 0x80000002, 0x80000003); + npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), + 0x80000002, 0x80000003); + + npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), + 0x80000000, 0x80000003); + npu2_write_mask_4b(ndev->npu, NPU2_NTL_DL_CLK_CTRL(ndev), + 0x80000000, 0x80000003); + } return PROCEDURE_NEXT; } @@ -887,3 +917,77 @@ void npu2_dev_procedure_reset(struct npu2_dev *dev) { npu2_clear_link_flag(dev, NPU2_DEV_DL_RESET); } + +static uint32_t run_procedure(struct npu2_dev *dev, uint16_t procedure_number) +{ + struct procedure *proc; + const char *name; + uint32_t result; + + assert(procedure_number <= ARRAY_SIZE(npu_procedures)); + proc = npu_procedures[procedure_number]; + assert(proc); + + name = proc->name; + NPU2DEVINF(dev, "Running procedure %s\n", name); + dev->procedure_status = PROCEDURE_INPROGRESS; + dev->procedure_number = procedure_number; + dev->procedure_step = 0; + dev->procedure_data = 0; + dev->procedure_tb = mftb(); + + result = get_procedure_status(dev); + while (!(result & PROCEDURE_COMPLETE)) { + time_wait_ms(1); + result = get_procedure_status(dev); + } + return result; +} + +void npu2_opencapi_bump_ui_lane(struct npu2_dev *dev) +{ + uint64_t reg; + uint64_t status_xscom; + int lane, bit = 7; + + switch (dev->index) { + case 2: + status_xscom = OB0_ODL0_TRAINING_STATUS; + break; + case 3: + status_xscom = OB0_ODL1_TRAINING_STATUS; + break; + case 4: + status_xscom = OB3_ODL1_TRAINING_STATUS; + break; + case 5: + status_xscom = OB3_ODL0_TRAINING_STATUS; + break; + default: + assert(false); + } + xscom_read(dev->npu->chip_id, status_xscom, ®); + reg = GETFIELD(OB_ODL_TRAINING_STATUS_STS_RX_PATTERN_B, reg); + + FOR_EACH_LANE(dev, lane) { + if (reg & (1 << bit--)) + continue; + prlog(PR_TRACE, "OCAPI: bumpui bumping lane %d\n", lane); + for (int i = 0; i < 4; i++) { + phy_write_lane(dev, &NPU2_PHY_RX_PR_BUMP_SL_1UI, lane, 1); + phy_write_lane(dev, &NPU2_PHY_RX_PR_BUMP_SL_1UI, lane, 0); + } + } +} + +void npu2_opencapi_phy_setup(struct npu2_dev *dev) +{ + run_procedure(dev, 4); /* procedure_phy_reset */ + run_procedure(dev, 5); /* procedure_phy_tx_zcal */ + run_procedure(dev, 6); /* procedure_phy_rx_dccal */ +} + +void npu2_opencapi_phy_prbs31(struct npu2_dev *dev) +{ + phy_write(dev, &NPU2_PHY_TX_DRV_DATA_PATTERN_GCRMSG, 0xD); +} |