/* Update Tx credit maximum update interval */
val = rockchip_pcie_read(rockchip, PCIE_CORE_TXCREDIT_CFG1);
val &= ~PCIE_CORE_TXCREDIT_CFG1_MUI_MASK;
val |= PCIE_CORE_TXCREDIT_CFG1_MUI_ENCODE(24000); /* ns */
rockchip_pcie_write(rockchip, val, PCIE_CORE_TXCREDIT_CFG1);
}
staticint rockchip_pcie_valid_device(struct rockchip_pcie *rockchip, struct pci_bus *bus, int dev)
{ /* * Access only one slot on each root port. * Do not read more than one device on the bus directly attached * to RC's downstream side.
*/ if (pci_is_root_bus(bus) || pci_is_root_bus(bus->parent)) return dev == 0;
/* * N.B. This read/modify/write isn't safe in general because it can * corrupt RW1C bits in adjacent registers. But the hardware * doesn't support smaller writes.
*/
tmp = readl(addr) & mask;
tmp |= val << ((where & 0x3) * 8);
writel(tmp, addr);
return PCIBIOS_SUCCESSFUL;
}
staticint rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val)
{ void __iomem *addr;
/* * Set RC's captured slot power limit and scale if * vpcie3v3 available. The default values are both zero * which means the software should set these two according * to the actual power supply.
*/
curr = regulator_get_current_limit(rockchip->vpcie3v3); if (curr <= 0) return;
scale = 3; /* 0.001x */
curr = curr / 1000; /* convert to mA */
power = (curr * 3300) / 1000; /* milliwatt */ while (power > FIELD_MAX(PCI_EXP_DEVCAP_PWR_VAL)) { if (!scale) {
dev_warn(rockchip->dev, "invalid power supply\n"); return;
}
scale--;
power = power / 10;
}
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_DEVCAP);
status |= FIELD_PREP(PCI_EXP_DEVCAP_PWR_VAL, power);
status |= FIELD_PREP(PCI_EXP_DEVCAP_PWR_SCL, scale);
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_DEVCAP);
}
/** * rockchip_pcie_host_init_port - Initialize hardware * @rockchip: PCIe port information
*/ staticint rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
{ struct device *dev = rockchip->dev; int err, i = MAX_LANE_NUM;
u32 status;
err = rockchip_pcie_init_port(rockchip); if (err) return err;
/* Fix the transmitted FTS count desired to exit from L0s. */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1);
status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) |
(PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT);
rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1);
rockchip_pcie_set_power_limit(rockchip);
/* Set RC's clock architecture as common clock */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
status |= PCI_EXP_LNKSTA_SLC << 16;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
/* Set RC's RCB to 128 */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
status |= PCI_EXP_LNKCTL_RCB;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
/* Enable Gen1 training */
rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE,
PCIE_CLIENT_CONFIG);
/* 500ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1,
status, PCIE_LINK_UP(status), 20,
500 * USEC_PER_MSEC); if (err) {
dev_err(dev, "PCIe link training gen1 timeout!\n"); goto err_power_off_phy;
}
if (rockchip->link_gen == 2) { /* * Enable retrain for gen2. This should be configured only after * gen1 finished.
*/
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
status &= ~PCI_EXP_LNKCTL2_TLS;
status |= PCI_EXP_LNKCTL2_TLS_5_0GT;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
status |= PCI_EXP_LNKCTL_RL;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
status, PCIE_LINK_IS_GEN2(status), 20,
500 * USEC_PER_MSEC); if (err)
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
}
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
PCIE_CORE_PL_CONF_LANE_SHIFT);
dev_dbg(dev, "current link width is x%d\n", status);
/* Power off unused lane(s) */
rockchip->lanes_map = rockchip_pcie_lane_map(rockchip); for (i = 0; i < MAX_LANE_NUM; i++) { if (!(rockchip->lanes_map & BIT(i))) {
dev_dbg(dev, "idling lane %d\n", i);
phy_power_off(rockchip->phys[i]);
}
}
/* Clear THP cap's next cap pointer to remove L1 substate cap */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_THP_CAP);
status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP);
/* Clear L0s from RC's link cap */ if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) {
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCAP);
status &= ~PCI_EXP_LNKCAP_ASPM_L0S;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCAP);
}
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_DEVCTL);
status &= ~PCI_EXP_DEVCTL_PAYLOAD;
status |= PCI_EXP_DEVCTL_PAYLOAD_256B;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_DEVCTL);
return 0;
err_power_off_phy: while (i--)
phy_power_off(rockchip->phys[i]);
i = MAX_LANE_NUM; while (i--)
phy_exit(rockchip->phys[i]); return err;
}
/** * rockchip_pcie_parse_host_dt - Parse Device Tree * @rockchip: PCIe port information * * Return: '0' on success and error value on failure
*/ staticint rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip)
{ struct device *dev = rockchip->dev; int err;
err = rockchip_pcie_parse_dt(rockchip); if (err) return err;
rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v"); if (IS_ERR(rockchip->vpcie12v)) { if (PTR_ERR(rockchip->vpcie12v) != -ENODEV) return PTR_ERR(rockchip->vpcie12v);
dev_info(dev, "no vpcie12v regulator found\n");
}
rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); if (IS_ERR(rockchip->vpcie3v3)) { if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV) return PTR_ERR(rockchip->vpcie3v3);
dev_info(dev, "no vpcie3v3 regulator found\n");
}
rockchip->vpcie1v8 = devm_regulator_get(dev, "vpcie1v8"); if (IS_ERR(rockchip->vpcie1v8)) return PTR_ERR(rockchip->vpcie1v8);
rockchip->vpcie0v9 = devm_regulator_get(dev, "vpcie0v9"); if (IS_ERR(rockchip->vpcie0v9)) return PTR_ERR(rockchip->vpcie0v9);
/* read LTSSM and wait for falling into L2 link state */
err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
value, PCIE_LINK_IS_L2(value), 20,
jiffies_to_usecs(5 * HZ)); if (err) {
dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n"); return err;
}
/* disable core and cli int since we don't need to ack PME_ACK */
rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) |
PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK);
rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK);
ret = rockchip_pcie_wait_l2(rockchip); if (ret) {
rockchip_pcie_enable_interrupts(rockchip); return ret;
}
if (!IS_ERR(rockchip->vpcie12v))
regulator_disable(rockchip->vpcie12v); if (!IS_ERR(rockchip->vpcie3v3))
regulator_disable(rockchip->vpcie3v3);
regulator_disable(rockchip->vpcie1v8);
regulator_disable(rockchip->vpcie0v9);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.