/* * Copyright (C) Marvell International Ltd. and its affiliates * * SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include "ctrl_pex.h" #include "sys_env_lib.h" int hws_pex_config(const struct serdes_map *serdes_map, u8 count) { u32 pex_idx, tmp, next_busno, first_busno, temp_pex_reg, temp_reg, addr, dev_id, ctrl_mode; enum serdes_type serdes_type; u32 idx; DEBUG_INIT_FULL_S("\n### hws_pex_config ###\n"); for (idx = 0; idx < count; idx++) { serdes_type = serdes_map[idx].serdes_type; /* configuration for PEX only */ if ((serdes_type != PEX0) && (serdes_type != PEX1) && (serdes_type != PEX2) && (serdes_type != PEX3)) continue; if ((serdes_type != PEX0) && ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { /* for PEX by4 - relevant for the first port only */ continue; } pex_idx = serdes_type - PEX0; tmp = reg_read(PEX_CAPABILITIES_REG(pex_idx)); tmp &= ~(0xf << 20); tmp |= (0x4 << 20); reg_write(PEX_CAPABILITIES_REG(pex_idx), tmp); } tmp = reg_read(SOC_CTRL_REG); tmp &= ~0x03; for (idx = 0; idx < count; idx++) { serdes_type = serdes_map[idx].serdes_type; if ((serdes_type != PEX0) && ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { /* for PEX by4 - relevant for the first port only */ continue; } switch (serdes_type) { case PEX0: tmp |= 0x1 << PCIE0_ENABLE_OFFS; break; case PEX1: tmp |= 0x1 << PCIE1_ENABLE_OFFS; break; case PEX2: tmp |= 0x1 << PCIE2_ENABLE_OFFS; break; case PEX3: tmp |= 0x1 << PCIE3_ENABLE_OFFS; break; default: break; } } reg_write(SOC_CTRL_REG, tmp); /* Support gen1/gen2 */ DEBUG_INIT_FULL_S("Support gen1/gen2\n"); next_busno = 0; mdelay(150); for (idx = 0; idx < count; idx++) { serdes_type = serdes_map[idx].serdes_type; DEBUG_INIT_FULL_S(" serdes_type=0x"); DEBUG_INIT_FULL_D(serdes_type, 8); DEBUG_INIT_FULL_S("\n"); DEBUG_INIT_FULL_S(" idx=0x"); DEBUG_INIT_FULL_D(idx, 8); DEBUG_INIT_FULL_S("\n"); /* Configuration for PEX only */ if ((serdes_type != PEX0) && (serdes_type != PEX1) && (serdes_type != PEX2) && (serdes_type != PEX3)) continue; if ((serdes_type != PEX0) && ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { /* for PEX by4 - relevant for the first port only */ continue; } pex_idx = serdes_type - PEX0; tmp = reg_read(PEX_DBG_STATUS_REG(pex_idx)); first_busno = next_busno; if ((tmp & 0x7f) != 0x7e) { DEBUG_INIT_S("PCIe, Idx "); DEBUG_INIT_D(pex_idx, 1); DEBUG_INIT_S(": detected no link\n"); continue; } next_busno++; temp_pex_reg = reg_read((PEX_CFG_DIRECT_ACCESS (pex_idx, PEX_LINK_CAPABILITY_REG))); temp_pex_reg &= 0xf; if (temp_pex_reg != 0x2) continue; temp_reg = (reg_read(PEX_CFG_DIRECT_ACCESS( pex_idx, PEX_LINK_CTRL_STAT_REG)) & 0xf0000) >> 16; /* Check if the link established is GEN1 */ DEBUG_INIT_FULL_S ("Checking if the link established is gen1\n"); if (temp_reg != 0x1) continue; pex_local_bus_num_set(pex_idx, first_busno); pex_local_dev_num_set(pex_idx, 1); DEBUG_INIT_FULL_S("PCIe, Idx "); DEBUG_INIT_FULL_D(pex_idx, 1); DEBUG_INIT_S(":** Link is Gen1, check the EP capability\n"); /* link is Gen1, check the EP capability */ addr = pex_config_read(pex_idx, first_busno, 0, 0, 0x34) & 0xff; DEBUG_INIT_FULL_C("pex_config_read: return addr=0x%x", addr, 4); if (addr == 0xff) { DEBUG_INIT_FULL_C ("pex_config_read: return 0xff -->PCIe (%d): Detected No Link.", pex_idx, 1); continue; } while ((pex_config_read(pex_idx, first_busno, 0, 0, addr) & 0xff) != 0x10) { addr = (pex_config_read(pex_idx, first_busno, 0, 0, addr) & 0xff00) >> 8; } /* Check for Gen2 and above */ if ((pex_config_read(pex_idx, first_busno, 0, 0, addr + 0xc) & 0xf) < 0x2) { DEBUG_INIT_S("PCIe, Idx "); DEBUG_INIT_D(pex_idx, 1); DEBUG_INIT_S(": remains Gen1\n"); continue; } tmp = reg_read(PEX_LINK_CTRL_STATUS2_REG(pex_idx)); DEBUG_RD_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); tmp &= ~(BIT(0) | BIT(1)); tmp |= BIT(1); tmp |= BIT(6); /* Select Deemphasize (-3.5d_b) */ reg_write(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); DEBUG_WR_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); tmp = reg_read(PEX_CTRL_REG(pex_idx)); DEBUG_RD_REG(PEX_CTRL_REG(pex_idx), tmp); tmp |= BIT(10); reg_write(PEX_CTRL_REG(pex_idx), tmp); DEBUG_WR_REG(PEX_CTRL_REG(pex_idx), tmp); /* * We need to wait 10ms before reading the PEX_DBG_STATUS_REG * in order not to read the status of the former state */ mdelay(10); DEBUG_INIT_S("PCIe, Idx "); DEBUG_INIT_D(pex_idx, 1); DEBUG_INIT_S (": Link upgraded to Gen2 based on client cpabilities\n"); } /* Update pex DEVICE ID */ ctrl_mode = sys_env_model_get(); for (idx = 0; idx < count; idx++) { serdes_type = serdes_map[idx].serdes_type; /* configuration for PEX only */ if ((serdes_type != PEX0) && (serdes_type != PEX1) && (serdes_type != PEX2) && (serdes_type != PEX3)) continue; if ((serdes_type != PEX0) && ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { /* for PEX by4 - relevant for the first port only */ continue; } pex_idx = serdes_type - PEX0; dev_id = reg_read(PEX_CFG_DIRECT_ACCESS (pex_idx, PEX_DEVICE_AND_VENDOR_ID)); dev_id &= 0xffff; dev_id |= ((ctrl_mode << 16) & 0xffff0000); reg_write(PEX_CFG_DIRECT_ACCESS (pex_idx, PEX_DEVICE_AND_VENDOR_ID), dev_id); } DEBUG_INIT_FULL_C("Update PEX Device ID ", ctrl_mode, 4); return MV_OK; } int pex_local_bus_num_set(u32 pex_if, u32 bus_num) { u32 pex_status; DEBUG_INIT_FULL_S("\n### pex_local_bus_num_set ###\n"); if (bus_num >= MAX_PEX_BUSSES) { DEBUG_INIT_C("pex_local_bus_num_set: Illegal bus number %d\n", bus_num, 4); return MV_BAD_PARAM; } pex_status = reg_read(PEX_STATUS_REG(pex_if)); pex_status &= ~PXSR_PEX_BUS_NUM_MASK; pex_status |= (bus_num << PXSR_PEX_BUS_NUM_OFFS) & PXSR_PEX_BUS_NUM_MASK; reg_write(PEX_STATUS_REG(pex_if), pex_status); return MV_OK; } int pex_local_dev_num_set(u32 pex_if, u32 dev_num) { u32 pex_status; DEBUG_INIT_FULL_S("\n### pex_local_dev_num_set ###\n"); pex_status = reg_read(PEX_STATUS_REG(pex_if)); pex_status &= ~PXSR_PEX_DEV_NUM_MASK; pex_status |= (dev_num << PXSR_PEX_DEV_NUM_OFFS) & PXSR_PEX_DEV_NUM_MASK; reg_write(PEX_STATUS_REG(pex_if), pex_status); return MV_OK; } /* * pex_config_read - Read from configuration space * * DESCRIPTION: * This function performs a 32 bit read from PEX configuration space. * It supports both type 0 and type 1 of Configuration Transactions * (local and over bridge). In order to read from local bus segment, use * bus number retrieved from pex_local_bus_num_get(). Other bus numbers * will result configuration transaction of type 1 (over bridge). * * INPUT: * pex_if - PEX interface number. * bus - PEX segment bus number. * dev - PEX device number. * func - Function number. * reg_offs - Register offset. * * OUTPUT: * None. * * RETURN: * 32bit register data, 0xffffffff on error */ u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off) { u32 pex_data = 0; u32 local_dev, local_bus; u32 pex_status; pex_status = reg_read(PEX_STATUS_REG(pex_if)); local_dev = ((pex_status & PXSR_PEX_DEV_NUM_MASK) >> PXSR_PEX_DEV_NUM_OFFS); local_bus = ((pex_status & PXSR_PEX_BUS_NUM_MASK) >> PXSR_PEX_BUS_NUM_OFFS); /* * In PCI Express we have only one device number * and this number is the first number we encounter * else that the local_dev * spec pex define return on config read/write on any device */ if (bus == local_bus) { if (local_dev == 0) { /* * if local dev is 0 then the first number we encounter * after 0 is 1 */ if ((dev != 1) && (dev != local_dev)) return MV_ERROR; } else { /* * if local dev is not 0 then the first number we * encounter is 0 */ if ((dev != 0) && (dev != local_dev)) return MV_ERROR; } } /* Creating PEX address to be passed */ pex_data = (bus << PXCAR_BUS_NUM_OFFS); pex_data |= (dev << PXCAR_DEVICE_NUM_OFFS); pex_data |= (func << PXCAR_FUNC_NUM_OFFS); /* Legacy register space */ pex_data |= (reg_off & PXCAR_REG_NUM_MASK); /* Extended register space */ pex_data |= (((reg_off & PXCAR_REAL_EXT_REG_NUM_MASK) >> PXCAR_REAL_EXT_REG_NUM_OFFS) << PXCAR_EXT_REG_NUM_OFFS); pex_data |= PXCAR_CONFIG_EN; /* Write the address to the PEX configuration address register */ reg_write(PEX_CFG_ADDR_REG(pex_if), pex_data); /* * In order to let the PEX controller absorbed the address * of the read transaction we perform a validity check that * the address was written */ if (pex_data != reg_read(PEX_CFG_ADDR_REG(pex_if))) return MV_ERROR; /* Cleaning Master Abort */ reg_bit_set(PEX_CFG_DIRECT_ACCESS(pex_if, PEX_STATUS_AND_COMMAND), PXSAC_MABORT); /* Read the Data returned in the PEX Data register */ pex_data = reg_read(PEX_CFG_DATA_REG(pex_if)); DEBUG_INIT_FULL_C(" --> ", pex_data, 4); return pex_data; }