diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index 8fec768057ad..368d0f26c0f7 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -700,6 +700,7 @@ struct msm_pcie_dev_t { uint32_t smmu_sid_base; uint32_t link_check_max_count; uint32_t target_link_speed; + uint32_t dt_target_link_speed; uint32_t current_link_speed; uint32_t n_fts; uint32_t ep_latency; @@ -5740,9 +5741,11 @@ static int msm_pcie_probe(struct platform_device *pdev) pcie_dev->rc_idx, pcie_dev->link_check_max_count); of_property_read_u32(of_node, "qcom,target-link-speed", - &pcie_dev->target_link_speed); + &pcie_dev->dt_target_link_speed); PCIE_DBG(pcie_dev, "PCIe: RC%d: target-link-speed: 0x%x.\n", - pcie_dev->rc_idx, pcie_dev->target_link_speed); + pcie_dev->rc_idx, pcie_dev->dt_target_link_speed); + + pcie_dev->target_link_speed = pcie_dev->dt_target_link_speed; of_property_read_u32(of_node, "qcom,n-fts", &pcie_dev->n_fts); PCIE_DBG(pcie_dev, "n-fts: 0x%x.\n", pcie_dev->n_fts); @@ -6192,6 +6195,55 @@ static void msm_pcie_poll_for_l0_from_l0s(struct msm_pcie_dev_t *dev) pci_walk_bus(dev->dev->bus, msm_pcie_read_devid_all, dev); } +int msm_pcie_set_target_link_speed(u32 rc_idx, u32 target_link_speed) +{ + struct msm_pcie_dev_t *pcie_dev; + + if (rc_idx >= MAX_RC_NUM) { + pr_err("PCIe: invalid rc index %u\n", rc_idx); + return -EINVAL; + } + + pcie_dev = &msm_pcie_dev[rc_idx]; + + if (!pcie_dev->drv_ready) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: has not been successfully probed yet\n", + pcie_dev->rc_idx); + return -EPROBE_DEFER; + } + + /* + * Reject the request if it exceeds what PCIe RC is capable or if + * it's greater than what was specified in DT (if present) + */ + if (target_link_speed > pcie_dev->bw_gen_max || + (pcie_dev->dt_target_link_speed && + target_link_speed > pcie_dev->dt_target_link_speed)) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: invalid target link speed: %d\n", + pcie_dev->rc_idx, target_link_speed); + return -EINVAL; + } + + pcie_dev->target_link_speed = target_link_speed; + + /* + * The request 0 will reset maximum GEN speed to default. Default will + * be devicetree specified GEN speed if present else it will be whatever + * the PCIe root complex is capable of. + */ + if (!target_link_speed) + pcie_dev->target_link_speed = pcie_dev->dt_target_link_speed ? + pcie_dev->dt_target_link_speed : pcie_dev->bw_gen_max; + + PCIE_DBG(pcie_dev, "PCIe: RC%d: target_link_speed is now: 0x%x.\n", + pcie_dev->rc_idx, pcie_dev->target_link_speed); + + return 0; +} +EXPORT_SYMBOL(msm_pcie_set_target_link_speed); + int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed, u16 target_link_width) { diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h index 9e88e0a7781d..d9bce5df0767 100644 --- a/include/linux/msm_pcie.h +++ b/include/linux/msm_pcie.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.*/ +/* Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.*/ #ifndef __MSM_PCIE_H #define __MSM_PCIE_H @@ -64,6 +64,24 @@ int msm_msi_init(struct device *dev); #if IS_ENABLED(CONFIG_PCI_MSM) +/** + * msm_pcie_set_target_link_speed - sets the upper bound of GEN speed PCIe can + * link up with + * @rc_idx: root complex port number that endpoint is connected to + * @target_link_speed: new target link speed PCIe can link up with + * + * Provide PCIe clients the option to control upper bound of GEN speed PCIe + * can link up with. Clients may choose only GEN speed within root complex's + * controller capability or up to what is defined in devicetree, + * qcom,target-link-speed. + * + * Client may also pass 0 for target_link_speed to have PCIe root complex + * reset and use the default TLS. + * + * Return 0 on success, negative value on error + */ +int msm_pcie_set_target_link_speed(u32 rc_idx, u32 target_link_speed); + /** * msm_pcie_allow_l1 - allow PCIe link to re-enter L1 * @pci_dev: client's pci device structure @@ -221,6 +239,12 @@ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, return -ENODEV; } +static inline int msm_pcie_set_target_link_speed(u32 rc_idx, + u32 target_link_speed) +{ + return -ENODEV; +} + static inline void msm_pcie_allow_l1(struct pci_dev *pci_dev) { }