diff --git a/cnss2/Makefile b/cnss2/Makefile index a2b48e94d5..16e4b18609 100644 --- a/cnss2/Makefile +++ b/cnss2/Makefile @@ -15,4 +15,5 @@ cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o cnss2-y += genl.o +cnss2-$(CONFIG_PCI_MSM) += pci_qcom.o cnss2-$(CONFIG_CNSS2_QMI) += qmi.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.o diff --git a/cnss2/pci.c b/cnss2/pci.c index fd5cdedc72..ceec634b3b 100644 --- a/cnss2/pci.c +++ b/cnss2/pci.c @@ -4,7 +4,6 @@ * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. */ -#include #include #include #include @@ -13,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +20,7 @@ #include "bus.h" #include "debug.h" #include "pci.h" +#include "pci_platform.h" #include "reg.h" #define PCI_LINK_UP 1 @@ -30,8 +29,6 @@ #define SAVE_PCI_CONFIG_SPACE 1 #define RESTORE_PCI_CONFIG_SPACE 0 -#define PM_OPTIONS_DEFAULT 0 - #define PCI_BAR_NUM 0 #define PCI_INVALID_READ(val) ((val) == U32_MAX) @@ -80,9 +77,6 @@ static DEFINE_SPINLOCK(time_sync_lock); #define FORCE_WAKE_DELAY_MAX_US 6000 #define FORCE_WAKE_DELAY_TIMEOUT_US 60000 -#define LINK_TRAINING_RETRY_MAX_TIMES 3 -#define LINK_TRAINING_RETRY_DELAY_MS 500 - #define MHI_SUSPEND_RETRY_MAX_TIMES 3 #define MHI_SUSPEND_RETRY_DELAY_US 5000 @@ -635,232 +629,6 @@ static struct cnss_misc_reg syspm_reg_access_seq[] = { #define WLAON_REG_SIZE ARRAY_SIZE(wlaon_reg_access_seq) #define SYSPM_REG_SIZE ARRAY_SIZE(syspm_reg_access_seq) -#if IS_ENABLED(CONFIG_PCI_MSM) -/** - * _cnss_pci_enumerate() - Enumerate PCIe endpoints - * @plat_priv: driver platform context pointer - * @rc_num: root complex index that an endpoint connects to - * - * This function shall call corresponding PCIe root complex driver APIs - * to power on root complex and enumerate the endpoint connected to it. - * - * Return: 0 for success, negative value for error - */ -static int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) -{ - return msm_pcie_enumerate(rc_num); -} - -/** - * cnss_pci_assert_perst() - Assert PCIe PERST GPIO - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to assert PCIe PERST GPIO. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_assert_perst(struct cnss_pci_data *pci_priv) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - - return msm_pcie_pm_control(MSM_PCIE_HANDLE_LINKDOWN, - pci_dev->bus->number, pci_dev, NULL, - PM_OPTIONS_DEFAULT); -} - -/** - * cnss_pci_disable_pc() - Disable PCIe link power collapse from RC driver - * @pci_priv: driver PCI bus context pointer - * @vote: value to indicate disable (true) or enable (false) - * - * This function shall call corresponding PCIe root complex driver APIs - * to disable PCIe power collapse. The purpose of this API is to avoid - * root complex driver still controlling PCIe link from callbacks of - * system suspend/resume. Device driver itself should take full control - * of the link in such cases. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_disable_pc(struct cnss_pci_data *pci_priv, bool vote) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - - return msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC : - MSM_PCIE_ENABLE_PC, - pci_dev->bus->number, pci_dev, NULL, - PM_OPTIONS_DEFAULT); -} - -/** - * cnss_pci_set_link_bandwidth() - Update number of lanes and speed of - * PCIe link - * @pci_priv: driver PCI bus context pointer - * @link_speed: PCIe link gen speed - * @link_width: number of lanes for PCIe link - * - * This function shall call corresponding PCIe root complex driver APIs - * to update number of lanes and speed of the link. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv, - u16 link_speed, u16 link_width) -{ - return msm_pcie_set_link_bandwidth(pci_priv->pci_dev, - link_speed, link_width); -} - -/** - * cnss_pci_set_max_link_speed() - Set the maximum speed PCIe can link up with - * @pci_priv: driver PCI bus context pointer - * @rc_num: root complex index that an endpoint connects to - * @link_speed: PCIe link gen speed - * - * This function shall call corresponding PCIe root complex driver APIs - * to update the maximum speed that PCIe can link up with. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_set_max_link_speed(struct cnss_pci_data *pci_priv, - u32 rc_num, u16 link_speed) -{ - return msm_pcie_set_target_link_speed(rc_num, link_speed, false); -} - -/** - * _cnss_pci_prevent_l1() - Prevent PCIe L1 and L1 sub-states - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to prevent PCIe link enter L1 and L1 sub-states. The APIs should also - * bring link out of L1 or L1 sub-states if any and avoid synchronization - * issues if any. - * - * Return: 0 for success, negative value for error - */ -static int _cnss_pci_prevent_l1(struct cnss_pci_data *pci_priv) -{ - return msm_pcie_prevent_l1(pci_priv->pci_dev); -} - -/** - * _cnss_pci_allow_l1() - Allow PCIe L1 and L1 sub-states - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to allow PCIe link enter L1 and L1 sub-states. The APIs should avoid - * synchronization issues if any. - * - * Return: 0 for success, negative value for error - */ -static void _cnss_pci_allow_l1(struct cnss_pci_data *pci_priv) -{ - msm_pcie_allow_l1(pci_priv->pci_dev); -} - -/** - * cnss_pci_set_link_up() - Power on or resume PCIe link - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to Power on or resume PCIe link. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_set_link_up(struct cnss_pci_data *pci_priv) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - enum msm_pcie_pm_opt pm_ops = MSM_PCIE_RESUME; - u32 pm_options = PM_OPTIONS_DEFAULT; - int ret; - - ret = msm_pcie_pm_control(pm_ops, pci_dev->bus->number, pci_dev, - NULL, pm_options); - if (ret) - cnss_pr_err("Failed to resume PCI link with default option, err = %d\n", - ret); - - return ret; -} - -/** - * cnss_pci_set_link_down() - Power off or suspend PCIe link - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to power off or suspend PCIe link. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_set_link_down(struct cnss_pci_data *pci_priv) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - enum msm_pcie_pm_opt pm_ops; - u32 pm_options = PM_OPTIONS_DEFAULT; - int ret; - - if (pci_priv->drv_connected_last) { - cnss_pr_vdbg("Use PCIe DRV suspend\n"); - pm_ops = MSM_PCIE_DRV_SUSPEND; - } else { - pm_ops = MSM_PCIE_SUSPEND; - } - - ret = msm_pcie_pm_control(pm_ops, pci_dev->bus->number, pci_dev, - NULL, pm_options); - if (ret) - cnss_pr_err("Failed to suspend PCI link with default option, err = %d\n", - ret); - - return ret; -} -#else -static int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) -{ - return -EOPNOTSUPP; -} - -static int cnss_pci_assert_perst(struct cnss_pci_data *pci_priv) -{ - return -EOPNOTSUPP; -} - -static int cnss_pci_disable_pc(struct cnss_pci_data *pci_priv, bool vote) -{ - return 0; -} - -static int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv, - u16 link_speed, u16 link_width) -{ - return 0; -} - -static int cnss_pci_set_max_link_speed(struct cnss_pci_data *pci_priv, - u32 rc_num, u16 link_speed) -{ - return 0; -} - -static int _cnss_pci_prevent_l1(struct cnss_pci_data *pci_priv) -{ - return 0; -} - -static void _cnss_pci_allow_l1(struct cnss_pci_data *pci_priv) {} - -static int cnss_pci_set_link_up(struct cnss_pci_data *pci_priv) -{ - return 0; -} - -static int cnss_pci_set_link_down(struct cnss_pci_data *pci_priv) -{ - return 0; -} -#endif /* CONFIG_PCI_MSM */ - #if IS_ENABLED(CONFIG_MHI_BUS_MISC) static void cnss_mhi_debug_reg_dump(struct cnss_pci_data *pci_priv) { @@ -1341,79 +1109,6 @@ static int cnss_pci_get_link_status(struct cnss_pci_data *pci_priv) return 0; } -static int cnss_set_pci_link_status(struct cnss_pci_data *pci_priv, - enum pci_link_status status) -{ - u16 link_speed, link_width = pci_priv->def_link_width; - u16 one_lane = PCI_EXP_LNKSTA_NLW_X1 >> PCI_EXP_LNKSTA_NLW_SHIFT; - int ret; - - cnss_pr_vdbg("Set PCI link status to: %u\n", status); - - switch (status) { - case PCI_GEN1: - link_speed = PCI_EXP_LNKSTA_CLS_2_5GB; - if (!link_width) - link_width = one_lane; - break; - case PCI_GEN2: - link_speed = PCI_EXP_LNKSTA_CLS_5_0GB; - if (!link_width) - link_width = one_lane; - break; - case PCI_DEF: - link_speed = pci_priv->def_link_speed; - if (!link_speed || !link_width) { - cnss_pr_err("PCI link speed or width is not valid\n"); - return -EINVAL; - } - break; - default: - cnss_pr_err("Unknown PCI link status config: %u\n", status); - return -EINVAL; - } - - ret = cnss_pci_set_link_bandwidth(pci_priv, link_speed, link_width); - if (!ret) - pci_priv->cur_link_speed = link_speed; - - return ret; -} - -static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) -{ - int ret = 0, retry = 0; - - cnss_pr_vdbg("%s PCI link\n", link_up ? "Resuming" : "Suspending"); - - if (link_up) { -retry: - ret = cnss_pci_set_link_up(pci_priv); - if (ret && retry++ < LINK_TRAINING_RETRY_MAX_TIMES) { - cnss_pr_dbg("Retry PCI link training #%d\n", retry); - if (pci_priv->pci_link_down_ind) - msleep(LINK_TRAINING_RETRY_DELAY_MS * retry); - goto retry; - } - } else { - /* Since DRV suspend cannot be done in Gen 3, set it to - * Gen 2 if current link speed is larger than Gen 2. - */ - if (pci_priv->drv_connected_last && - pci_priv->cur_link_speed > PCI_EXP_LNKSTA_CLS_5_0GB) - cnss_set_pci_link_status(pci_priv, PCI_GEN2); - - ret = cnss_pci_set_link_down(pci_priv); - } - - if (pci_priv->drv_connected_last) { - if ((link_up && !ret) || (!link_up && ret)) - cnss_set_pci_link_status(pci_priv, PCI_DEF); - } - - return ret; -} - static void cnss_pci_soc_scratch_reg_dump(struct cnss_pci_data *pci_priv) { u32 reg_offset, val; @@ -1569,61 +1264,6 @@ int cnss_pci_recover_link_down(struct cnss_pci_data *pci_priv) return 0; } -int cnss_pci_prevent_l1(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); - int ret; - - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL\n"); - return -ENODEV; - } - - if (pci_priv->pci_link_state == PCI_LINK_DOWN) { - cnss_pr_dbg("PCIe link is in suspend state\n"); - return -EIO; - } - - if (pci_priv->pci_link_down_ind) { - cnss_pr_err("PCIe link is down\n"); - return -EIO; - } - - ret = _cnss_pci_prevent_l1(pci_priv); - if (ret == -EIO) { - cnss_pr_err("Failed to prevent PCIe L1, considered as link down\n"); - cnss_pci_link_down(dev); - } - - return ret; -} -EXPORT_SYMBOL(cnss_pci_prevent_l1); - -void cnss_pci_allow_l1(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); - - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL\n"); - return; - } - - if (pci_priv->pci_link_state == PCI_LINK_DOWN) { - cnss_pr_dbg("PCIe link is in suspend state\n"); - return; - } - - if (pci_priv->pci_link_down_ind) { - cnss_pr_err("PCIe link is down\n"); - return; - } - - _cnss_pci_allow_l1(pci_priv); -} -EXPORT_SYMBOL(cnss_pci_allow_l1); - static void cnss_pci_update_link_event(struct cnss_pci_data *pci_priv, enum cnss_bus_event_type type, void *data) @@ -1635,7 +1275,7 @@ static void cnss_pci_update_link_event(struct cnss_pci_data *pci_priv, cnss_pci_call_driver_uevent(pci_priv, CNSS_BUS_EVENT, &bus_event); } -static void cnss_pci_handle_linkdown(struct cnss_pci_data *pci_priv) +void cnss_pci_handle_linkdown(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; struct pci_dev *pci_dev = pci_priv->pci_dev; @@ -2099,56 +1739,6 @@ out: return ret; } -#if IS_ENABLED(CONFIG_PCI_MSM) -/** - * cnss_wlan_adsp_pc_enable: Control ADSP power collapse setup - * @dev: Platform driver pci private data structure - * @control: Power collapse enable / disable - * - * This function controls ADSP power collapse (PC). It must be called - * based on wlan state. ADSP power collapse during wlan RTPM suspend state - * results in delay during periodic QMI stats PCI link up/down. This delay - * causes additional power consumption. - * Introduced in SM8350. - * - * Result: 0 Success. negative error codes. - */ -static int cnss_wlan_adsp_pc_enable(struct cnss_pci_data *pci_priv, - bool control) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - int ret = 0; - u32 pm_options = PM_OPTIONS_DEFAULT; - struct cnss_plat_data *plat_priv = pci_priv->plat_priv; - - if (plat_priv->adsp_pc_enabled == control) { - cnss_pr_dbg("ADSP power collapse already %s\n", - control ? "Enabled" : "Disabled"); - return 0; - } - - if (control) - pm_options &= ~MSM_PCIE_CONFIG_NO_DRV_PC; - else - pm_options |= MSM_PCIE_CONFIG_NO_DRV_PC; - - ret = msm_pcie_pm_control(MSM_PCIE_DRV_PC_CTRL, pci_dev->bus->number, - pci_dev, NULL, pm_options); - if (ret) - return ret; - - cnss_pr_dbg("%s ADSP power collapse\n", control ? "Enable" : "Disable"); - plat_priv->adsp_pc_enabled = control; - return 0; -} -#else -static int cnss_wlan_adsp_pc_enable(struct cnss_pci_data *pci_priv, - bool control) -{ - return 0; -} -#endif - int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -3410,157 +3000,6 @@ int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv) return 0; } -#if IS_ENABLED(CONFIG_PCI_MSM) -static bool cnss_pci_is_drv_supported(struct cnss_pci_data *pci_priv) -{ - struct pci_dev *root_port = pcie_find_root_port(pci_priv->pci_dev); - struct cnss_plat_data *plat_priv = pci_priv->plat_priv; - struct device_node *root_of_node; - bool drv_supported = false; - - if (!root_port) { - cnss_pr_err("PCIe DRV is not supported as root port is null\n"); - pci_priv->drv_supported = false; - return drv_supported; - } - - root_of_node = root_port->dev.of_node; - - if (root_of_node->parent) { - drv_supported = of_property_read_bool(root_of_node->parent, - "qcom,drv-supported") || - of_property_read_bool(root_of_node->parent, - "qcom,drv-name"); - } - - cnss_pr_dbg("PCIe DRV is %s\n", - drv_supported ? "supported" : "not supported"); - pci_priv->drv_supported = drv_supported; - - if (drv_supported) { - plat_priv->cap.cap_flag |= CNSS_HAS_DRV_SUPPORT; - cnss_set_feature_list(plat_priv, CNSS_DRV_SUPPORT_V01); - } - - return drv_supported; -} - -static void cnss_pci_event_cb(struct msm_pcie_notify *notify) -{ - struct pci_dev *pci_dev; - struct cnss_pci_data *pci_priv; - struct device *dev; - struct cnss_plat_data *plat_priv = NULL; - int ret = 0; - - if (!notify) - return; - - pci_dev = notify->user; - if (!pci_dev) - return; - - pci_priv = cnss_get_pci_priv(pci_dev); - if (!pci_priv) - return; - dev = &pci_priv->pci_dev->dev; - - switch (notify->event) { - case MSM_PCIE_EVENT_LINK_RECOVER: - cnss_pr_dbg("PCI link recover callback\n"); - - plat_priv = pci_priv->plat_priv; - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL\n"); - return; - } - - plat_priv->ctrl_params.quirks |= BIT(LINK_DOWN_SELF_RECOVERY); - - ret = msm_pcie_pm_control(MSM_PCIE_HANDLE_LINKDOWN, - pci_dev->bus->number, pci_dev, NULL, - PM_OPTIONS_DEFAULT); - if (ret) - cnss_pci_handle_linkdown(pci_priv); - break; - case MSM_PCIE_EVENT_LINKDOWN: - cnss_pr_dbg("PCI link down event callback\n"); - cnss_pci_handle_linkdown(pci_priv); - break; - case MSM_PCIE_EVENT_WAKEUP: - if ((cnss_pci_get_monitor_wake_intr(pci_priv) && - cnss_pci_get_auto_suspended(pci_priv)) || - dev->power.runtime_status == RPM_SUSPENDING) { - cnss_pci_set_monitor_wake_intr(pci_priv, false); - cnss_pci_pm_request_resume(pci_priv); - } - break; - case MSM_PCIE_EVENT_DRV_CONNECT: - cnss_pr_dbg("DRV subsystem is connected\n"); - cnss_pci_set_drv_connected(pci_priv, 1); - break; - case MSM_PCIE_EVENT_DRV_DISCONNECT: - cnss_pr_dbg("DRV subsystem is disconnected\n"); - if (cnss_pci_get_auto_suspended(pci_priv)) - cnss_pci_pm_request_resume(pci_priv); - cnss_pci_set_drv_connected(pci_priv, 0); - break; - default: - cnss_pr_err("Received invalid PCI event: %d\n", notify->event); - } -} - -/** - * cnss_reg_pci_event() - Register for PCIe events - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding PCIe root complex driver APIs - * to register for PCIe events like link down or WAKE GPIO toggling etc. - * The events should be based on PCIe root complex driver's capability. - * - * Return: 0 for success, negative value for error - */ -static int cnss_reg_pci_event(struct cnss_pci_data *pci_priv) -{ - int ret = 0; - struct msm_pcie_register_event *pci_event; - - pci_event = &pci_priv->msm_pci_event; - pci_event->events = MSM_PCIE_EVENT_LINK_RECOVER | - MSM_PCIE_EVENT_LINKDOWN | - MSM_PCIE_EVENT_WAKEUP; - - if (cnss_pci_is_drv_supported(pci_priv)) - pci_event->events = pci_event->events | - MSM_PCIE_EVENT_DRV_CONNECT | - MSM_PCIE_EVENT_DRV_DISCONNECT; - - pci_event->user = pci_priv->pci_dev; - pci_event->mode = MSM_PCIE_TRIGGER_CALLBACK; - pci_event->callback = cnss_pci_event_cb; - pci_event->options = MSM_PCIE_CONFIG_NO_RECOVERY; - - ret = msm_pcie_register_event(pci_event); - if (ret) - cnss_pr_err("Failed to register MSM PCI event, err = %d\n", - ret); - - return ret; -} - -static void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) -{ - msm_pcie_deregister_event(&pci_priv->msm_pci_event); -} -#else -static int cnss_reg_pci_event(struct cnss_pci_data *pci_priv) -{ - return 0; -} - -static void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) {} -#endif - static int cnss_pci_suspend_driver(struct cnss_pci_data *pci_priv) { struct pci_dev *pci_dev = pci_priv->pci_dev; @@ -4559,86 +3998,6 @@ void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) CNSS_REASON_TIMEOUT); } -static int cnss_pci_smmu_fault_handler(struct iommu_domain *domain, - struct device *dev, unsigned long iova, - int flags, void *handler_token) -{ - struct cnss_pci_data *pci_priv = handler_token; - - cnss_fatal_err("SMMU fault happened with IOVA 0x%lx\n", iova); - - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL\n"); - return -ENODEV; - } - - cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); - cnss_force_fw_assert(&pci_priv->pci_dev->dev); - - /* IOMMU driver requires -ENOSYS to print debug info. */ - return -ENOSYS; -} - -static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) -{ - struct pci_dev *pci_dev = pci_priv->pci_dev; - struct cnss_plat_data *plat_priv = pci_priv->plat_priv; - struct device_node *of_node; - struct resource *res; - const char *iommu_dma_type; - u32 addr_win[2]; - int ret = 0; - - of_node = of_parse_phandle(pci_dev->dev.of_node, "qcom,iommu-group", 0); - if (!of_node) - return ret; - - cnss_pr_dbg("Initializing SMMU\n"); - - pci_priv->iommu_domain = iommu_get_domain_for_dev(&pci_dev->dev); - ret = of_property_read_string(of_node, "qcom,iommu-dma", - &iommu_dma_type); - if (!ret && !strcmp("fastmap", iommu_dma_type)) { - cnss_pr_dbg("Enabling SMMU S1 stage\n"); - pci_priv->smmu_s1_enable = true; - iommu_set_fault_handler(pci_priv->iommu_domain, - cnss_pci_smmu_fault_handler, pci_priv); - } - - ret = of_property_read_u32_array(of_node, "qcom,iommu-dma-addr-pool", - addr_win, ARRAY_SIZE(addr_win)); - if (ret) { - cnss_pr_err("Invalid SMMU size window, err = %d\n", ret); - of_node_put(of_node); - return ret; - } - - pci_priv->smmu_iova_start = addr_win[0]; - pci_priv->smmu_iova_len = addr_win[1]; - cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: 0x%zx\n", - &pci_priv->smmu_iova_start, - pci_priv->smmu_iova_len); - - res = platform_get_resource_byname(plat_priv->plat_dev, IORESOURCE_MEM, - "smmu_iova_ipa"); - if (res) { - pci_priv->smmu_iova_ipa_start = res->start; - pci_priv->smmu_iova_ipa_current = res->start; - pci_priv->smmu_iova_ipa_len = resource_size(res); - cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: 0x%zx\n", - &pci_priv->smmu_iova_ipa_start, - pci_priv->smmu_iova_ipa_len); - } - - pci_priv->iommu_geometry = of_property_read_bool(of_node, - "qcom,iommu-geometry"); - cnss_pr_dbg("iommu_geometry: %d\n", pci_priv->iommu_geometry); - - of_node_put(of_node); - - return 0; -} - static void cnss_pci_deinit_smmu(struct cnss_pci_data *pci_priv) { pci_priv->iommu_domain = NULL; @@ -4823,24 +4182,6 @@ int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) } EXPORT_SYMBOL(cnss_get_soc_info); -static struct cnss_msi_config msi_config = { - .total_vectors = 32, - .total_users = 4, - .users = (struct cnss_msi_user[]) { - { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, - { .name = "CE", .num_vectors = 10, .base_vector = 3 }, - { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, - { .name = "DP", .num_vectors = 18, .base_vector = 14 }, - }, -}; - -static int cnss_pci_get_msi_assignment(struct cnss_pci_data *pci_priv) -{ - pci_priv->msi_config = &msi_config; - - return 0; -} - static int cnss_pci_enable_msi(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -6062,6 +5403,11 @@ static void cnss_pci_config_regs(struct cnss_pci_data *pci_priv) } #if !IS_ENABLED(CONFIG_ARCH_QCOM) +static int cnss_pci_of_reserved_mem_device_init(struct cnss_pci_data *pci_priv) +{ + return 0; +} + static irqreturn_t cnss_pci_wake_handler(int irq, void *data) { struct cnss_pci_data *pci_priv = data; @@ -6162,53 +5508,6 @@ static void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv) free_irq(pci_priv->wake_irq, pci_priv); gpio_free(pci_priv->wake_gpio); } -#else -static int cnss_pci_wake_gpio_init(struct cnss_pci_data *pci_priv) -{ - return 0; -} - -static void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv) -{ -} -#endif - -#if IS_ENABLED(CONFIG_ARCH_QCOM) -/** - * cnss_pci_of_reserved_mem_device_init() - Assign reserved memory region - * to given PCI device - * @pci_priv: driver PCI bus context pointer - * - * This function shall call corresponding of_reserved_mem_device* API to - * assign reserved memory region to PCI device based on where the memory is - * defined and attached to (platform device of_node or PCI device of_node) - * in device tree. - * - * Return: 0 for success, negative value for error - */ -static int cnss_pci_of_reserved_mem_device_init(struct cnss_pci_data *pci_priv) -{ - struct device *dev_pci = &pci_priv->pci_dev->dev; - int ret; - - /* Use of_reserved_mem_device_init_by_idx() if reserved memory is - * attached to platform device of_node. - */ - ret = of_reserved_mem_device_init(dev_pci); - if (ret) - cnss_pr_err("Failed to init reserved mem device, err = %d\n", - ret); - if (dev_pci->cma_area) - cnss_pr_dbg("CMA area is %s\n", - cma_get_name(dev_pci->cma_area)); - - return ret; -} -#else -static int cnss_pci_of_reserved_mem_device_init(struct cnss_pci_data *pci_priv) -{ - return 0; -} #endif /* Setting to use this cnss_pm_domain ops will let PM framework override the diff --git a/cnss2/pci.h b/cnss2/pci.h index b6e4e0156d..7901a930e4 100644 --- a/cnss2/pci.h +++ b/cnss2/pci.h @@ -7,6 +7,7 @@ #ifndef _CNSS_PCI_H #define _CNSS_PCI_H +#include #include #include #if IS_ENABLED(CONFIG_MHI_BUS_MISC) @@ -15,10 +16,16 @@ #if IS_ENABLED(CONFIG_PCI_MSM) #include #endif +#include #include #include "main.h" +#define PM_OPTIONS_DEFAULT 0 +#define PCI_LINK_DOWN 0 +#define LINK_TRAINING_RETRY_MAX_TIMES 3 +#define LINK_TRAINING_RETRY_DELAY_MS 500 + enum cnss_mhi_state { CNSS_MHI_INIT, CNSS_MHI_DEINIT, @@ -263,5 +270,6 @@ int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset, int cnss_pci_get_iova(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size); int cnss_pci_get_iova_ipa(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size); +void cnss_pci_handle_linkdown(struct cnss_pci_data *pci_priv); #endif /* _CNSS_PCI_H */ diff --git a/cnss2/pci_platform.h b/cnss2/pci_platform.h new file mode 100644 index 0000000000..6c452a9b43 --- /dev/null +++ b/cnss2/pci_platform.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ + +#ifndef _CNSS_PCI_PLATFORM_H +#define _CNSS_PCI_PLATFORM_H + +#include "pci.h" + +#if IS_ENABLED(CONFIG_PCI_MSM) +/** + * _cnss_pci_enumerate() - Enumerate PCIe endpoints + * @plat_priv: driver platform context pointer + * @rc_num: root complex index that an endpoint connects to + * + * This function shall call corresponding PCIe root complex driver APIs + * to power on root complex and enumerate the endpoint connected to it. + * + * Return: 0 for success, negative value for error + */ +int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num); + +/** + * cnss_pci_assert_perst() - Assert PCIe PERST GPIO + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to assert PCIe PERST GPIO. + * + * Return: 0 for success, negative value for error + */ +int cnss_pci_assert_perst(struct cnss_pci_data *pci_priv); + +/** + * cnss_pci_disable_pc() - Disable PCIe link power collapse from RC driver + * @pci_priv: driver PCI bus context pointer + * @vote: value to indicate disable (true) or enable (false) + * + * This function shall call corresponding PCIe root complex driver APIs + * to disable PCIe power collapse. The purpose of this API is to avoid + * root complex driver still controlling PCIe link from callbacks of + * system suspend/resume. Device driver itself should take full control + * of the link in such cases. + * + * Return: 0 for success, negative value for error + */ +int cnss_pci_disable_pc(struct cnss_pci_data *pci_priv, bool vote); + +/** + * cnss_pci_set_link_bandwidth() - Update number of lanes and speed of + * PCIe link + * @pci_priv: driver PCI bus context pointer + * @link_speed: PCIe link gen speed + * @link_width: number of lanes for PCIe link + * + * This function shall call corresponding PCIe root complex driver APIs + * to update number of lanes and speed of the link. + * + * Return: 0 for success, negative value for error + */ +int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv, + u16 link_speed, u16 link_width); + +/** + * cnss_pci_set_max_link_speed() - Set the maximum speed PCIe can link up with + * @pci_priv: driver PCI bus context pointer + * @rc_num: root complex index that an endpoint connects to + * @link_speed: PCIe link gen speed + * + * This function shall call corresponding PCIe root complex driver APIs + * to update the maximum speed that PCIe can link up with. + * + * Return: 0 for success, negative value for error + */ +int cnss_pci_set_max_link_speed(struct cnss_pci_data *pci_priv, + u32 rc_num, u16 link_speed); + +/** + * cnss_reg_pci_event() - Register for PCIe events + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to register for PCIe events like link down or WAKE GPIO toggling etc. + * The events should be based on PCIe root complex driver's capability. + * + * Return: 0 for success, negative value for error + */ +int cnss_reg_pci_event(struct cnss_pci_data *pci_priv); +void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv); + +/** + * cnss_wlan_adsp_pc_enable: Control ADSP power collapse setup + * @dev: Platform driver pci private data structure + * @control: Power collapse enable / disable + * + * This function controls ADSP power collapse (PC). It must be called + * based on wlan state. ADSP power collapse during wlan RTPM suspend state + * results in delay during periodic QMI stats PCI link up/down. This delay + * causes additional power consumption. + * + * Result: 0 Success. negative error codes. + */ +int cnss_wlan_adsp_pc_enable(struct cnss_pci_data *pci_priv, + bool control); +int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up); +int cnss_pci_prevent_l1(struct device *dev); +void cnss_pci_allow_l1(struct device *dev); +int cnss_pci_get_msi_assignment(struct cnss_pci_data *pci_priv); +int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv); +bool cnss_pci_is_drv_supported(struct cnss_pci_data *pci_priv); +/** + * _cnss_pci_get_reg_dump() - Dump PCIe RC registers for debug + * @pci_priv: driver PCI bus context pointer + * @buf: destination buffer pointer + * @len: length of the buffer + * + * This function shall call corresponding PCIe root complex driver API + * to dump PCIe RC registers for debug purpose. + * + * Return: 0 for success, negative value for error + */ +int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, + u8 *buf, u32 len); +#else +int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) +{ + return -EOPNOTSUPP; +} + +int cnss_pci_assert_perst(struct cnss_pci_data *pci_priv) +{ + return -EOPNOTSUPP; +} + +int cnss_pci_disable_pc(struct cnss_pci_data *pci_priv, bool vote) +{ + return 0; +} + +int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv, + u16 link_speed, u16 link_width) +{ + return 0; +} + +int cnss_pci_set_max_link_speed(struct cnss_pci_data *pci_priv, + u32 rc_num, u16 link_speed) +{ + return 0; +} + +int cnss_reg_pci_event(struct cnss_pci_data *pci_priv) +{ + return 0; +} + +void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) {} + +int cnss_wlan_adsp_pc_enable(struct cnss_pci_data *pci_priv, bool control) +{ + return 0; +} + +int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) +{ + return 0; +} + +int cnss_pci_prevent_l1(struct device *dev) +{ + return 0; +} +EXPORT_SYMBOL(cnss_pci_prevent_l1); + +void cnss_pci_allow_l1(struct device *dev) +{ +} +EXPORT_SYMBOL(cnss_pci_allow_l1); + +int cnss_pci_get_msi_assignment(struct cnss_pci_data *pci_priv) +{ + return 0; +} + +int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) +{ + return 0; +} + +int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, + u8 *buf, u32 len) +{ + return 0; +} + +bool cnss_pci_is_drv_supported(struct cnss_pci_data *pci_priv) +{ + return false; +} +#endif /* CONFIG_PCI_MSM */ + +#if IS_ENABLED(CONFIG_ARCH_QCOM) +int cnss_pci_of_reserved_mem_device_init(struct cnss_pci_data *pci_priv); +int cnss_pci_wake_gpio_init(struct cnss_pci_data *pci_priv); +void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv); +#endif /* CONFIG_ARCH_QCOM */ +#endif /* _CNSS_PCI_PLATFORM_H*/ diff --git a/cnss2/pci_qcom.c b/cnss2/pci_qcom.c new file mode 100644 index 0000000000..dda3845b50 --- /dev/null +++ b/cnss2/pci_qcom.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ + +#include "pci_platform.h" +#include "debug.h" + +static struct cnss_msi_config msi_config = { + .total_vectors = 32, + .total_users = 4, + .users = (struct cnss_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, +}; + +int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) +{ + return msm_pcie_enumerate(rc_num); +} + +int cnss_pci_assert_perst(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + + return msm_pcie_pm_control(MSM_PCIE_HANDLE_LINKDOWN, + pci_dev->bus->number, pci_dev, NULL, + PM_OPTIONS_DEFAULT); +} + +int cnss_pci_disable_pc(struct cnss_pci_data *pci_priv, bool vote) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + + return msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC : + MSM_PCIE_ENABLE_PC, + pci_dev->bus->number, pci_dev, NULL, + PM_OPTIONS_DEFAULT); +} + +int cnss_pci_set_link_bandwidth(struct cnss_pci_data *pci_priv, + u16 link_speed, u16 link_width) +{ + return msm_pcie_set_link_bandwidth(pci_priv->pci_dev, + link_speed, link_width); +} + +int cnss_pci_set_max_link_speed(struct cnss_pci_data *pci_priv, + u32 rc_num, u16 link_speed) +{ + return msm_pcie_set_target_link_speed(rc_num, link_speed, false); +} + +/** + * _cnss_pci_prevent_l1() - Prevent PCIe L1 and L1 sub-states + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to prevent PCIe link enter L1 and L1 sub-states. The APIs should also + * bring link out of L1 or L1 sub-states if any and avoid synchronization + * issues if any. + * + * Return: 0 for success, negative value for error + */ +static int _cnss_pci_prevent_l1(struct cnss_pci_data *pci_priv) +{ + return msm_pcie_prevent_l1(pci_priv->pci_dev); +} + +/** + * _cnss_pci_allow_l1() - Allow PCIe L1 and L1 sub-states + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to allow PCIe link enter L1 and L1 sub-states. The APIs should avoid + * synchronization issues if any. + * + * Return: 0 for success, negative value for error + */ +static void _cnss_pci_allow_l1(struct cnss_pci_data *pci_priv) +{ + msm_pcie_allow_l1(pci_priv->pci_dev); +} + +/** + * cnss_pci_set_link_up() - Power on or resume PCIe link + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to Power on or resume PCIe link. + * + * Return: 0 for success, negative value for error + */ +static int cnss_pci_set_link_up(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + enum msm_pcie_pm_opt pm_ops = MSM_PCIE_RESUME; + u32 pm_options = PM_OPTIONS_DEFAULT; + int ret; + + ret = msm_pcie_pm_control(pm_ops, pci_dev->bus->number, pci_dev, + NULL, pm_options); + if (ret) + cnss_pr_err("Failed to resume PCI link with default option, err = %d\n", + ret); + + return ret; +} + +/** + * cnss_pci_set_link_down() - Power off or suspend PCIe link + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding PCIe root complex driver APIs + * to power off or suspend PCIe link. + * + * Return: 0 for success, negative value for error + */ +static int cnss_pci_set_link_down(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + enum msm_pcie_pm_opt pm_ops; + u32 pm_options = PM_OPTIONS_DEFAULT; + int ret; + + if (pci_priv->drv_connected_last) { + cnss_pr_vdbg("Use PCIe DRV suspend\n"); + pm_ops = MSM_PCIE_DRV_SUSPEND; + } else { + pm_ops = MSM_PCIE_SUSPEND; + } + + ret = msm_pcie_pm_control(pm_ops, pci_dev->bus->number, pci_dev, + NULL, pm_options); + if (ret) + cnss_pr_err("Failed to suspend PCI link with default option, err = %d\n", + ret); + + return ret; +} + +bool cnss_pci_is_drv_supported(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *root_port = pcie_find_root_port(pci_priv->pci_dev); + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct device_node *root_of_node; + bool drv_supported = false; + + if (!root_port) { + cnss_pr_err("PCIe DRV is not supported as root port is null\n"); + pci_priv->drv_supported = false; + return drv_supported; + } + + root_of_node = root_port->dev.of_node; + + if (root_of_node->parent) { + drv_supported = of_property_read_bool(root_of_node->parent, + "qcom,drv-supported") || + of_property_read_bool(root_of_node->parent, + "qcom,drv-name"); + } + + cnss_pr_dbg("PCIe DRV is %s\n", + drv_supported ? "supported" : "not supported"); + pci_priv->drv_supported = drv_supported; + + if (drv_supported) { + plat_priv->cap.cap_flag |= CNSS_HAS_DRV_SUPPORT; + cnss_set_feature_list(plat_priv, CNSS_DRV_SUPPORT_V01); + } + + return drv_supported; +} + +static void cnss_pci_event_cb(struct msm_pcie_notify *notify) +{ + struct pci_dev *pci_dev; + struct cnss_pci_data *pci_priv; + struct device *dev; + struct cnss_plat_data *plat_priv = NULL; + int ret = 0; + + if (!notify) + return; + + pci_dev = notify->user; + if (!pci_dev) + return; + + pci_priv = cnss_get_pci_priv(pci_dev); + if (!pci_priv) + return; + dev = &pci_priv->pci_dev->dev; + + switch (notify->event) { + case MSM_PCIE_EVENT_LINK_RECOVER: + cnss_pr_dbg("PCI link recover callback\n"); + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return; + } + + plat_priv->ctrl_params.quirks |= BIT(LINK_DOWN_SELF_RECOVERY); + + ret = msm_pcie_pm_control(MSM_PCIE_HANDLE_LINKDOWN, + pci_dev->bus->number, pci_dev, NULL, + PM_OPTIONS_DEFAULT); + if (ret) + cnss_pci_handle_linkdown(pci_priv); + break; + case MSM_PCIE_EVENT_LINKDOWN: + cnss_pr_dbg("PCI link down event callback\n"); + cnss_pci_handle_linkdown(pci_priv); + break; + case MSM_PCIE_EVENT_WAKEUP: + if ((cnss_pci_get_monitor_wake_intr(pci_priv) && + cnss_pci_get_auto_suspended(pci_priv)) || + dev->power.runtime_status == RPM_SUSPENDING) { + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_pm_request_resume(pci_priv); + } + break; + case MSM_PCIE_EVENT_DRV_CONNECT: + cnss_pr_dbg("DRV subsystem is connected\n"); + cnss_pci_set_drv_connected(pci_priv, 1); + break; + case MSM_PCIE_EVENT_DRV_DISCONNECT: + cnss_pr_dbg("DRV subsystem is disconnected\n"); + if (cnss_pci_get_auto_suspended(pci_priv)) + cnss_pci_pm_request_resume(pci_priv); + cnss_pci_set_drv_connected(pci_priv, 0); + break; + default: + cnss_pr_err("Received invalid PCI event: %d\n", notify->event); + } +} + +int cnss_reg_pci_event(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct msm_pcie_register_event *pci_event; + + pci_event = &pci_priv->msm_pci_event; + pci_event->events = MSM_PCIE_EVENT_LINK_RECOVER | + MSM_PCIE_EVENT_LINKDOWN | + MSM_PCIE_EVENT_WAKEUP; + + if (cnss_pci_is_drv_supported(pci_priv)) + pci_event->events = pci_event->events | + MSM_PCIE_EVENT_DRV_CONNECT | + MSM_PCIE_EVENT_DRV_DISCONNECT; + + pci_event->user = pci_priv->pci_dev; + pci_event->mode = MSM_PCIE_TRIGGER_CALLBACK; + pci_event->callback = cnss_pci_event_cb; + pci_event->options = MSM_PCIE_CONFIG_NO_RECOVERY; + + ret = msm_pcie_register_event(pci_event); + if (ret) + cnss_pr_err("Failed to register MSM PCI event, err = %d\n", + ret); + + return ret; +} + +void cnss_dereg_pci_event(struct cnss_pci_data *pci_priv) +{ + msm_pcie_deregister_event(&pci_priv->msm_pci_event); +} + +int cnss_wlan_adsp_pc_enable(struct cnss_pci_data *pci_priv, + bool control) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + int ret = 0; + u32 pm_options = PM_OPTIONS_DEFAULT; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + if (plat_priv->adsp_pc_enabled == control) { + cnss_pr_dbg("ADSP power collapse already %s\n", + control ? "Enabled" : "Disabled"); + return 0; + } + + if (control) + pm_options &= ~MSM_PCIE_CONFIG_NO_DRV_PC; + else + pm_options |= MSM_PCIE_CONFIG_NO_DRV_PC; + + ret = msm_pcie_pm_control(MSM_PCIE_DRV_PC_CTRL, pci_dev->bus->number, + pci_dev, NULL, pm_options); + if (ret) + return ret; + + cnss_pr_dbg("%s ADSP power collapse\n", control ? "Enable" : "Disable"); + plat_priv->adsp_pc_enabled = control; + return 0; +} + +static int cnss_set_pci_link_status(struct cnss_pci_data *pci_priv, + enum pci_link_status status) +{ + u16 link_speed, link_width = pci_priv->def_link_width; + u16 one_lane = PCI_EXP_LNKSTA_NLW_X1 >> PCI_EXP_LNKSTA_NLW_SHIFT; + int ret; + + cnss_pr_vdbg("Set PCI link status to: %u\n", status); + + switch (status) { + case PCI_GEN1: + link_speed = PCI_EXP_LNKSTA_CLS_2_5GB; + if (!link_width) + link_width = one_lane; + break; + case PCI_GEN2: + link_speed = PCI_EXP_LNKSTA_CLS_5_0GB; + if (!link_width) + link_width = one_lane; + break; + case PCI_DEF: + link_speed = pci_priv->def_link_speed; + if (!link_speed || !link_width) { + cnss_pr_err("PCI link speed or width is not valid\n"); + return -EINVAL; + } + break; + default: + cnss_pr_err("Unknown PCI link status config: %u\n", status); + return -EINVAL; + } + + ret = cnss_pci_set_link_bandwidth(pci_priv, link_speed, link_width); + if (!ret) + pci_priv->cur_link_speed = link_speed; + + return ret; +} + +int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) +{ + int ret = 0, retry = 0; + + cnss_pr_vdbg("%s PCI link\n", link_up ? "Resuming" : "Suspending"); + + if (link_up) { +retry: + ret = cnss_pci_set_link_up(pci_priv); + if (ret && retry++ < LINK_TRAINING_RETRY_MAX_TIMES) { + cnss_pr_dbg("Retry PCI link training #%d\n", retry); + if (pci_priv->pci_link_down_ind) + msleep(LINK_TRAINING_RETRY_DELAY_MS * retry); + goto retry; + } + } else { + /* Since DRV suspend cannot be done in Gen 3, set it to + * Gen 2 if current link speed is larger than Gen 2. + */ + if (pci_priv->drv_connected_last && + pci_priv->cur_link_speed > PCI_EXP_LNKSTA_CLS_5_0GB) + cnss_set_pci_link_status(pci_priv, PCI_GEN2); + + ret = cnss_pci_set_link_down(pci_priv); + } + + if (pci_priv->drv_connected_last) { + if ((link_up && !ret) || (!link_up && ret)) + cnss_set_pci_link_status(pci_priv, PCI_DEF); + } + + return ret; +} + +int cnss_pci_prevent_l1(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + int ret; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + if (pci_priv->pci_link_state == PCI_LINK_DOWN) { + cnss_pr_dbg("PCIe link is in suspend state\n"); + return -EIO; + } + + if (pci_priv->pci_link_down_ind) { + cnss_pr_err("PCIe link is down\n"); + return -EIO; + } + + ret = _cnss_pci_prevent_l1(pci_priv); + if (ret == -EIO) { + cnss_pr_err("Failed to prevent PCIe L1, considered as link down\n"); + cnss_pci_link_down(dev); + } + + return ret; +} +EXPORT_SYMBOL(cnss_pci_prevent_l1); + +void cnss_pci_allow_l1(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return; + } + + if (pci_priv->pci_link_state == PCI_LINK_DOWN) { + cnss_pr_dbg("PCIe link is in suspend state\n"); + return; + } + + if (pci_priv->pci_link_down_ind) { + cnss_pr_err("PCIe link is down\n"); + return; + } + + _cnss_pci_allow_l1(pci_priv); +} +EXPORT_SYMBOL(cnss_pci_allow_l1); + +int cnss_pci_get_msi_assignment(struct cnss_pci_data *pci_priv) +{ + pci_priv->msi_config = &msi_config; + + return 0; +} + +static int cnss_pci_smmu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, + int flags, void *handler_token) +{ + struct cnss_pci_data *pci_priv = handler_token; + + cnss_fatal_err("SMMU fault happened with IOVA 0x%lx\n", iova); + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); + cnss_force_fw_assert(&pci_priv->pci_dev->dev); + + /* IOMMU driver requires -ENOSYS to print debug info. */ + return -ENOSYS; +} + +int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) +{ + struct pci_dev *pci_dev = pci_priv->pci_dev; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct device_node *of_node; + struct resource *res; + const char *iommu_dma_type; + u32 addr_win[2]; + int ret = 0; + + of_node = of_parse_phandle(pci_dev->dev.of_node, "qcom,iommu-group", 0); + if (!of_node) + return ret; + + cnss_pr_dbg("Initializing SMMU\n"); + + pci_priv->iommu_domain = iommu_get_domain_for_dev(&pci_dev->dev); + ret = of_property_read_string(of_node, "qcom,iommu-dma", + &iommu_dma_type); + if (!ret && !strcmp("fastmap", iommu_dma_type)) { + cnss_pr_dbg("Enabling SMMU S1 stage\n"); + pci_priv->smmu_s1_enable = true; + iommu_set_fault_handler(pci_priv->iommu_domain, + cnss_pci_smmu_fault_handler, pci_priv); + } + + ret = of_property_read_u32_array(of_node, "qcom,iommu-dma-addr-pool", + addr_win, ARRAY_SIZE(addr_win)); + if (ret) { + cnss_pr_err("Invalid SMMU size window, err = %d\n", ret); + of_node_put(of_node); + return ret; + } + + pci_priv->smmu_iova_start = addr_win[0]; + pci_priv->smmu_iova_len = addr_win[1]; + cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: 0x%zx\n", + &pci_priv->smmu_iova_start, + pci_priv->smmu_iova_len); + + res = platform_get_resource_byname(plat_priv->plat_dev, IORESOURCE_MEM, + "smmu_iova_ipa"); + if (res) { + pci_priv->smmu_iova_ipa_start = res->start; + pci_priv->smmu_iova_ipa_current = res->start; + pci_priv->smmu_iova_ipa_len = resource_size(res); + cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: 0x%zx\n", + &pci_priv->smmu_iova_ipa_start, + pci_priv->smmu_iova_ipa_len); + } + + pci_priv->iommu_geometry = of_property_read_bool(of_node, + "qcom,iommu-geometry"); + cnss_pr_dbg("iommu_geometry: %d\n", pci_priv->iommu_geometry); + + of_node_put(of_node); + + return 0; +} + +int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, + u8 *buf, u32 len) +{ + return 0; +} + +#if IS_ENABLED(CONFIG_ARCH_QCOM) +/** + * cnss_pci_of_reserved_mem_device_init() - Assign reserved memory region + * to given PCI device + * @pci_priv: driver PCI bus context pointer + * + * This function shall call corresponding of_reserved_mem_device* API to + * assign reserved memory region to PCI device based on where the memory is + * defined and attached to (platform device of_node or PCI device of_node) + * in device tree. + * + * Return: 0 for success, negative value for error + */ +int cnss_pci_of_reserved_mem_device_init(struct cnss_pci_data *pci_priv) +{ + struct device *dev_pci = &pci_priv->pci_dev->dev; + int ret; + + /* Use of_reserved_mem_device_init_by_idx() if reserved memory is + * attached to platform device of_node. + */ + ret = of_reserved_mem_device_init(dev_pci); + if (ret) + cnss_pr_err("Failed to init reserved mem device, err = %d\n", + ret); + if (dev_pci->cma_area) + cnss_pr_dbg("CMA area is %s\n", + cma_get_name(dev_pci->cma_area)); + + return ret; +} + +int cnss_pci_wake_gpio_init(struct cnss_pci_data *pci_priv) +{ + return 0; +} + +void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv) +{ +} +#endif +