|
@@ -35,13 +35,14 @@
|
|
|
|
|
|
#define PCI_DMA_MASK_32_BIT DMA_BIT_MASK(32)
|
|
|
#define PCI_DMA_MASK_36_BIT DMA_BIT_MASK(36)
|
|
|
-#define PCI_DMA_MASK_64_BIT DMA_BIT_MASK(64)
|
|
|
+#define PCI_DMA_MASK_64_BIT ~0ULL
|
|
|
|
|
|
#define MHI_NODE_NAME "qcom,mhi"
|
|
|
#define MHI_MSI_NAME "MHI"
|
|
|
|
|
|
#define QCA6390_PATH_PREFIX "qca6390/"
|
|
|
#define QCA6490_PATH_PREFIX "qca6490/"
|
|
|
+#define QCN7605_PATH_PREFIX "qcn7605/"
|
|
|
#define KIWI_PATH_PREFIX "kiwi/"
|
|
|
#define MANGO_PATH_PREFIX "mango/"
|
|
|
#define PEACH_PATH_PREFIX "peach/"
|
|
@@ -68,6 +69,8 @@
|
|
|
#define CNSS_256KB_SIZE 0x40000
|
|
|
#define DEVICE_RDDM_COOKIE 0xCAFECACE
|
|
|
|
|
|
+static bool cnss_driver_registered;
|
|
|
+
|
|
|
static DEFINE_SPINLOCK(pci_link_down_lock);
|
|
|
static DEFINE_SPINLOCK(pci_reg_window_lock);
|
|
|
static DEFINE_SPINLOCK(time_sync_lock);
|
|
@@ -82,6 +85,8 @@ static DEFINE_SPINLOCK(time_sync_lock);
|
|
|
#define FORCE_WAKE_DELAY_MAX_US 6000
|
|
|
#define FORCE_WAKE_DELAY_TIMEOUT_US 60000
|
|
|
|
|
|
+#define REG_RETRY_MAX_TIMES 3
|
|
|
+
|
|
|
#define MHI_SUSPEND_RETRY_MAX_TIMES 3
|
|
|
#define MHI_SUSPEND_RETRY_DELAY_US 5000
|
|
|
|
|
@@ -244,6 +249,93 @@ static const struct mhi_channel_config cnss_mhi_channels[] = {
|
|
|
#endif
|
|
|
};
|
|
|
|
|
|
+static const struct mhi_channel_config cnss_mhi_channels_genoa[] = {
|
|
|
+ {
|
|
|
+ .num = 0,
|
|
|
+ .name = "LOOPBACK",
|
|
|
+ .num_elements = 32,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_TO_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .num = 1,
|
|
|
+ .name = "LOOPBACK",
|
|
|
+ .num_elements = 32,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_FROM_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .num = 4,
|
|
|
+ .name = "DIAG",
|
|
|
+ .num_elements = 64,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_TO_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .num = 5,
|
|
|
+ .name = "DIAG",
|
|
|
+ .num_elements = 64,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_FROM_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .num = 16,
|
|
|
+ .name = "IPCR",
|
|
|
+ .num_elements = 64,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_TO_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .num = 17,
|
|
|
+ .name = "IPCR",
|
|
|
+ .num_elements = 64,
|
|
|
+ .event_ring = 1,
|
|
|
+ .dir = DMA_FROM_DEVICE,
|
|
|
+ .ee_mask = 0x4,
|
|
|
+ .pollcfg = 0,
|
|
|
+ .doorbell = MHI_DB_BRST_DISABLE,
|
|
|
+ .lpm_notify = false,
|
|
|
+ .offload_channel = false,
|
|
|
+ .doorbell_mode_switch = false,
|
|
|
+ .auto_queue = true,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
|
|
|
static struct mhi_event_config cnss_mhi_events[] = {
|
|
|
#else
|
|
@@ -322,6 +414,20 @@ static const struct mhi_controller_config cnss_mhi_config_default = {
|
|
|
.m2_no_db = true,
|
|
|
};
|
|
|
|
|
|
+static const struct mhi_controller_config cnss_mhi_config_genoa = {
|
|
|
+ .max_channels = 32,
|
|
|
+ .timeout_ms = 10000,
|
|
|
+ .use_bounce_buf = false,
|
|
|
+ .buf_len = 0x8000,
|
|
|
+ .num_channels = ARRAY_SIZE(cnss_mhi_channels_genoa),
|
|
|
+ .ch_cfg = cnss_mhi_channels_genoa,
|
|
|
+ .num_events = ARRAY_SIZE(cnss_mhi_events) -
|
|
|
+ CNSS_MHI_SATELLITE_EVT_COUNT,
|
|
|
+ .event_cfg = cnss_mhi_events,
|
|
|
+ .m2_no_db = true,
|
|
|
+ .bhie_offset = 0x0324,
|
|
|
+};
|
|
|
+
|
|
|
static const struct mhi_controller_config cnss_mhi_config_no_satellite = {
|
|
|
.max_channels = 32,
|
|
|
.timeout_ms = 10000,
|
|
@@ -667,6 +773,9 @@ static struct cnss_print_optimize print_optimize;
|
|
|
#define SYSPM_REG_SIZE ARRAY_SIZE(syspm_reg_access_seq)
|
|
|
|
|
|
static int cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv);
|
|
|
+static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev);
|
|
|
+static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev);
|
|
|
+
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_MHI_BUS_MISC)
|
|
|
static void cnss_mhi_debug_reg_dump(struct cnss_pci_data *pci_priv)
|
|
@@ -826,6 +935,9 @@ static void cnss_pci_select_window(struct cnss_pci_data *pci_priv, u32 offset)
|
|
|
QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET);
|
|
|
}
|
|
|
|
|
|
+ if (plat_priv->device_id == QCN7605_DEVICE_ID)
|
|
|
+ window_enable = QCN7605_WINDOW_ENABLE_BIT | window;
|
|
|
+
|
|
|
if (window != pci_priv->remap_window) {
|
|
|
pci_priv->remap_window = window;
|
|
|
cnss_pr_dbg("Config PCIe remap window register to 0x%x\n",
|
|
@@ -1809,6 +1921,18 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
|
|
|
+static int cnss_mhi_pm_force_resume(struct cnss_pci_data *pci_priv)
|
|
|
+{
|
|
|
+ return mhi_pm_resume_force(pci_priv->mhi_ctrl);
|
|
|
+}
|
|
|
+#else
|
|
|
+static int cnss_mhi_pm_force_resume(struct cnss_pci_data *pci_priv)
|
|
|
+{
|
|
|
+ return mhi_pm_resume(pci_priv->mhi_ctrl);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
static int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv,
|
|
|
enum cnss_mhi_state mhi_state)
|
|
|
{
|
|
@@ -1881,7 +2005,10 @@ retry_mhi_suspend:
|
|
|
ret = cnss_mhi_pm_fast_resume(pci_priv, true);
|
|
|
cnss_pci_allow_l1(&pci_priv->pci_dev->dev);
|
|
|
} else {
|
|
|
- ret = mhi_pm_resume(pci_priv->mhi_ctrl);
|
|
|
+ if (pci_priv->device_id == QCA6390_DEVICE_ID)
|
|
|
+ ret = cnss_mhi_pm_force_resume(pci_priv);
|
|
|
+ else
|
|
|
+ ret = mhi_pm_resume(pci_priv->mhi_ctrl);
|
|
|
}
|
|
|
mutex_unlock(&pci_priv->mhi_ctrl->pm_mutex);
|
|
|
break;
|
|
@@ -1933,11 +2060,158 @@ static int cnss_pci_config_msi_data(struct cnss_pci_data *pci_priv)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
|
|
|
+#define PLC_PCIE_NAME_LEN 14
|
|
|
+
|
|
|
+static struct cnss_plat_data *
|
|
|
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
|
|
|
+{
|
|
|
+ int plat_env_count = cnss_get_plat_env_count();
|
|
|
+ struct cnss_plat_data *plat_env;
|
|
|
+ struct cnss_pci_data *pci_priv;
|
|
|
+ int i = 0;
|
|
|
+
|
|
|
+ if (!driver_ops) {
|
|
|
+ cnss_pr_err("No cnss driver\n");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < plat_env_count; i++) {
|
|
|
+ plat_env = cnss_get_plat_env(i);
|
|
|
+ if (!plat_env)
|
|
|
+ continue;
|
|
|
+ if (driver_ops->name && plat_env->pld_bus_ops_name) {
|
|
|
+ /* driver_ops->name = PLD_PCIE_OPS_NAME
|
|
|
+ * #ifdef MULTI_IF_NAME
|
|
|
+ * #define PLD_PCIE_OPS_NAME "pld_pcie_" MULTI_IF_NAME
|
|
|
+ * #else
|
|
|
+ * #define PLD_PCIE_OPS_NAME "pld_pcie"
|
|
|
+ * #endif
|
|
|
+ */
|
|
|
+ if (memcmp(driver_ops->name,
|
|
|
+ plat_env->pld_bus_ops_name,
|
|
|
+ PLC_PCIE_NAME_LEN) == 0)
|
|
|
+ return plat_env;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cnss_pr_err("Invalid cnss driver name from ko %s\n", driver_ops->name);
|
|
|
+ /* in the dual wlan card case, the pld_bus_ops_name from dts
|
|
|
+ * and driver_ops-> name from ko should match, otherwise
|
|
|
+ * wlanhost driver don't know which plat_env it can use;
|
|
|
+ * if doesn't find the match one, then get first available
|
|
|
+ * instance insteadly.
|
|
|
+ */
|
|
|
+
|
|
|
+ for (i = 0; i < plat_env_count; i++) {
|
|
|
+ plat_env = cnss_get_plat_env(i);
|
|
|
+
|
|
|
+ if (!plat_env)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ pci_priv = plat_env->bus_priv;
|
|
|
+ if (!pci_priv) {
|
|
|
+ cnss_pr_err("pci_priv is NULL\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (driver_ops == pci_priv->driver_ops)
|
|
|
+ return plat_env;
|
|
|
+ }
|
|
|
+ /* Doesn't find the existing instance,
|
|
|
+ * so return the fist empty instance
|
|
|
+ */
|
|
|
+ for (i = 0; i < plat_env_count; i++) {
|
|
|
+ plat_env = cnss_get_plat_env(i);
|
|
|
+
|
|
|
+ if (!plat_env)
|
|
|
+ continue;
|
|
|
+ pci_priv = plat_env->bus_priv;
|
|
|
+ if (!pci_priv) {
|
|
|
+ cnss_pr_err("pci_priv is NULL\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pci_priv->driver_ops)
|
|
|
+ return plat_env;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ u32 scratch = QCA6390_PCIE_SOC_PCIE_REG_PCIE_SCRATCH_2_SOC_PCIE_REG;
|
|
|
+ struct cnss_plat_data *plat_priv;
|
|
|
+
|
|
|
+ if (!pci_priv) {
|
|
|
+ cnss_pr_err("pci_priv is NULL\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ plat_priv = pci_priv->plat_priv;
|
|
|
+ /**
|
|
|
+ * in the single wlan chipset case, plat_priv->qrtr_node_id always is 0,
|
|
|
+ * wlan fw will use the hardcode 7 as the qrtr node id.
|
|
|
+ * in the dual Hastings case, we will read qrtr node id
|
|
|
+ * from device tree and pass to get plat_priv->qrtr_node_id,
|
|
|
+ * which always is not zero. And then store this new value
|
|
|
+ * to pcie register, wlan fw will read out this qrtr node id
|
|
|
+ * from this register and overwrite to the hardcode one
|
|
|
+ * while do initialization for ipc router.
|
|
|
+ * without this change, two Hastings will use the same
|
|
|
+ * qrtr node instance id, which will mess up qmi message
|
|
|
+ * exchange. According to qrtr spec, every node should
|
|
|
+ * have unique qrtr node id
|
|
|
+ */
|
|
|
+ if (plat_priv->device_id == QCA6390_DEVICE_ID &&
|
|
|
+ plat_priv->qrtr_node_id) {
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ cnss_pr_dbg("write 0x%x to SCRATCH REG\n",
|
|
|
+ plat_priv->qrtr_node_id);
|
|
|
+ ret = cnss_pci_reg_write(pci_priv, scratch,
|
|
|
+ plat_priv->qrtr_node_id);
|
|
|
+ if (ret) {
|
|
|
+ cnss_pr_err("Failed to write register offset 0x%x, err = %d\n",
|
|
|
+ scratch, ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = cnss_pci_reg_read(pci_priv, scratch, &val);
|
|
|
+ if (ret) {
|
|
|
+ cnss_pr_err("Failed to read SCRATCH REG");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (val != plat_priv->qrtr_node_id) {
|
|
|
+ cnss_pr_err("qrtr node id write to register doesn't match with readout value");
|
|
|
+ return -ERANGE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#else
|
|
|
+static struct cnss_plat_data *
|
|
|
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
|
|
|
+{
|
|
|
+ return cnss_bus_dev_to_plat_priv(NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
struct cnss_plat_data *plat_priv;
|
|
|
unsigned int timeout = 0;
|
|
|
+ int retry = 0;
|
|
|
|
|
|
if (!pci_priv) {
|
|
|
cnss_pr_err("pci_priv is NULL\n");
|
|
@@ -1963,6 +2237,15 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
|
|
|
else /* For perf builds the timeout is 10 (default) * 3 seconds */
|
|
|
pci_priv->mhi_ctrl->timeout_ms *= 3;
|
|
|
|
|
|
+retry:
|
|
|
+ ret = cnss_pci_store_qrtr_node_id(pci_priv);
|
|
|
+ if (ret) {
|
|
|
+ if (retry++ < REG_RETRY_MAX_TIMES)
|
|
|
+ goto retry;
|
|
|
+ else
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
/* Start the timer to dump MHI/PBL/SBL debug data periodically */
|
|
|
mod_timer(&pci_priv->boot_debug_timer,
|
|
|
jiffies + msecs_to_jiffies(BOOT_DEBUG_TIMEOUT_MS));
|
|
@@ -2312,6 +2595,25 @@ static void cnss_pci_stop_time_sync_update(struct cnss_pci_data *pci_priv)
|
|
|
cancel_delayed_work_sync(&pci_priv->time_sync_work);
|
|
|
}
|
|
|
|
|
|
+int cnss_pci_set_therm_cdev_state(struct cnss_pci_data *pci_priv,
|
|
|
+ unsigned long thermal_state,
|
|
|
+ int tcdev_id)
|
|
|
+{
|
|
|
+ if (!pci_priv) {
|
|
|
+ cnss_pr_err("pci_priv is NULL!\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pci_priv->driver_ops || !pci_priv->driver_ops->set_therm_cdev_state) {
|
|
|
+ cnss_pr_err("driver_ops or set_therm_cdev_state is NULL\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return pci_priv->driver_ops->set_therm_cdev_state(pci_priv->pci_dev,
|
|
|
+ thermal_state,
|
|
|
+ tcdev_id);
|
|
|
+}
|
|
|
+
|
|
|
int cnss_pci_update_time_sync_period(struct cnss_pci_data *pci_priv,
|
|
|
unsigned int time_sync_period)
|
|
|
{
|
|
@@ -2844,7 +3146,9 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv)
|
|
|
test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) {
|
|
|
del_timer(&pci_priv->dev_rddm_timer);
|
|
|
cnss_pci_collect_dump_info(pci_priv, false);
|
|
|
- CNSS_ASSERT(0);
|
|
|
+
|
|
|
+ if (!plat_priv->recovery_enabled)
|
|
|
+ CNSS_ASSERT(0);
|
|
|
}
|
|
|
|
|
|
if (!cnss_is_device_powered_on(plat_priv)) {
|
|
@@ -2937,6 +3241,7 @@ int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv)
|
|
|
break;
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
@@ -2967,6 +3272,7 @@ int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv)
|
|
|
break;
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
@@ -2997,6 +3303,7 @@ int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv)
|
|
|
break;
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
@@ -3027,6 +3334,7 @@ int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv)
|
|
|
break;
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
@@ -3111,7 +3419,7 @@ reg_driver:
|
|
|
int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
|
|
|
+ struct cnss_plat_data *plat_priv;
|
|
|
struct cnss_pci_data *pci_priv;
|
|
|
const struct pci_device_id *id_table = driver_ops->id_table;
|
|
|
unsigned int timeout;
|
|
@@ -3121,6 +3429,8 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
+ plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
|
|
|
+
|
|
|
if (!plat_priv) {
|
|
|
cnss_pr_buf("plat_priv is not ready for register driver\n");
|
|
|
return -EAGAIN;
|
|
@@ -3216,10 +3526,11 @@ EXPORT_SYMBOL(cnss_wlan_register_driver);
|
|
|
|
|
|
void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops)
|
|
|
{
|
|
|
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
|
|
|
+ struct cnss_plat_data *plat_priv;
|
|
|
int ret = 0;
|
|
|
unsigned int timeout;
|
|
|
|
|
|
+ plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
|
|
|
if (!plat_priv) {
|
|
|
cnss_pr_err("plat_priv is NULL\n");
|
|
|
return;
|
|
@@ -3426,7 +3737,8 @@ out:
|
|
|
static int cnss_pci_suspend(struct device *dev)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
- struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev));
|
|
|
+ struct pci_dev *pci_dev = to_pci_dev(dev);
|
|
|
+ struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
|
|
|
struct cnss_plat_data *plat_priv;
|
|
|
|
|
|
if (!pci_priv)
|
|
@@ -3439,6 +3751,25 @@ static int cnss_pci_suspend(struct device *dev)
|
|
|
if (!cnss_is_device_powered_on(plat_priv))
|
|
|
goto out;
|
|
|
|
|
|
+ /* No mhi state bit set if only finish pcie enumeration,
|
|
|
+ * so test_bit is not applicable to check if it is INIT state.
|
|
|
+ */
|
|
|
+ if (pci_priv->mhi_state == CNSS_MHI_INIT) {
|
|
|
+ bool suspend = cnss_should_suspend_pwroff(pci_dev);
|
|
|
+
|
|
|
+ /* Do PCI link suspend and power off in the LPM case
|
|
|
+ * if chipset didn't do that after pcie enumeration.
|
|
|
+ */
|
|
|
+ if (!suspend) {
|
|
|
+ ret = cnss_suspend_pci_link(pci_priv);
|
|
|
+ if (ret)
|
|
|
+ cnss_pr_err("Failed to suspend PCI link, err = %d\n",
|
|
|
+ ret);
|
|
|
+ cnss_power_off_device(plat_priv);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (!test_bit(DISABLE_DRV, &plat_priv->ctrl_params.quirks) &&
|
|
|
pci_priv->drv_supported) {
|
|
|
pci_priv->drv_connected_last =
|
|
@@ -4614,6 +4945,19 @@ int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info)
|
|
|
}
|
|
|
EXPORT_SYMBOL(cnss_get_soc_info);
|
|
|
|
|
|
+int cnss_pci_get_user_msi_assignment(struct cnss_pci_data *pci_priv,
|
|
|
+ char *user_name,
|
|
|
+ int *num_vectors,
|
|
|
+ u32 *user_base_data,
|
|
|
+ u32 *base_vector)
|
|
|
+{
|
|
|
+ return cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev,
|
|
|
+ user_name,
|
|
|
+ num_vectors,
|
|
|
+ user_base_data,
|
|
|
+ base_vector);
|
|
|
+}
|
|
|
+
|
|
|
static int cnss_pci_enable_msi(struct cnss_pci_data *pci_priv)
|
|
|
{
|
|
|
int ret = 0;
|
|
@@ -4852,6 +5196,9 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv)
|
|
|
case PEACH_DEVICE_ID:
|
|
|
pci_priv->dma_bit_mask = PCI_DMA_MASK_36_BIT;
|
|
|
break;
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
+ pci_priv->dma_bit_mask = PCI_DMA_MASK_64_BIT;
|
|
|
+ break;
|
|
|
default:
|
|
|
pci_priv->dma_bit_mask = PCI_DMA_MASK_32_BIT;
|
|
|
break;
|
|
@@ -5483,6 +5830,10 @@ void cnss_pci_add_fw_prefix_name(struct cnss_pci_data *pci_priv,
|
|
|
}
|
|
|
|
|
|
switch (pci_priv->device_id) {
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
+ scnprintf(prefix_name, MAX_FIRMWARE_NAME_LEN,
|
|
|
+ QCN7605_PATH_PREFIX "%s", name);
|
|
|
+ break;
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
scnprintf(prefix_name, MAX_FIRMWARE_NAME_LEN,
|
|
|
QCA6390_PATH_PREFIX "%s", name);
|
|
@@ -5897,6 +6248,21 @@ exit:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static bool cnss_is_tme_supported(struct cnss_pci_data *pci_priv)
|
|
|
+{
|
|
|
+ if (!pci_priv) {
|
|
|
+ cnss_pr_dbg("pci_priv is NULL");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (pci_priv->device_id) {
|
|
|
+ case PEACH_DEVICE_ID:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
|
|
|
{
|
|
|
int ret = 0;
|
|
@@ -5907,6 +6273,10 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
|
|
|
const struct mhi_controller_config *cnss_mhi_config =
|
|
|
&cnss_mhi_config_default;
|
|
|
|
|
|
+ ret = cnss_qmi_init(plat_priv);
|
|
|
+ if (ret)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
if (pci_priv->device_id == QCA6174_DEVICE_ID)
|
|
|
return 0;
|
|
|
|
|
@@ -5957,7 +6327,12 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
|
|
|
mhi_ctrl->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size;
|
|
|
if (!mhi_ctrl->rddm_size)
|
|
|
mhi_ctrl->rddm_size = RAMDUMP_SIZE_DEFAULT;
|
|
|
- mhi_ctrl->sbl_size = SZ_512K;
|
|
|
+
|
|
|
+ if (plat_priv->device_id == QCN7605_DEVICE_ID)
|
|
|
+ mhi_ctrl->sbl_size = SZ_256K;
|
|
|
+ else
|
|
|
+ mhi_ctrl->sbl_size = SZ_512K;
|
|
|
+
|
|
|
mhi_ctrl->seg_len = SZ_512K;
|
|
|
mhi_ctrl->fbc_download = true;
|
|
|
|
|
@@ -5967,9 +6342,15 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
|
|
|
|
|
|
/* Satellite config only supported on KIWI V2 and later chipset */
|
|
|
if (plat_priv->device_id <= QCA6490_DEVICE_ID ||
|
|
|
- (plat_priv->device_id == KIWI_DEVICE_ID &&
|
|
|
- plat_priv->device_version.major_version == 1))
|
|
|
- cnss_mhi_config = &cnss_mhi_config_no_satellite;
|
|
|
+ (plat_priv->device_id == KIWI_DEVICE_ID &&
|
|
|
+ plat_priv->device_version.major_version == 1)) {
|
|
|
+ if (plat_priv->device_id == QCN7605_DEVICE_ID)
|
|
|
+ cnss_mhi_config = &cnss_mhi_config_genoa;
|
|
|
+ else
|
|
|
+ cnss_mhi_config = &cnss_mhi_config_no_satellite;
|
|
|
+ }
|
|
|
+
|
|
|
+ mhi_ctrl->tme_supported_image = cnss_is_tme_supported(pci_priv);
|
|
|
|
|
|
ret = mhi_register_controller(mhi_ctrl, cnss_mhi_config);
|
|
|
if (ret) {
|
|
@@ -6150,6 +6531,57 @@ static void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
|
|
|
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ /* in the dual wlan card case, if call pci_register_driver after
|
|
|
+ * finishing the first pcie device enumeration, it will cause
|
|
|
+ * the cnss_pci_probe called in advance with the second wlan card,
|
|
|
+ * and the sequence like this:
|
|
|
+ * enter msm_pcie_enumerate -> pci_bus_add_devices -> cnss_pci_probe
|
|
|
+ * -> exit msm_pcie_enumerate.
|
|
|
+ * But the correct sequence we expected is like this:
|
|
|
+ * enter msm_pcie_enumerate -> pci_bus_add_devices ->
|
|
|
+ * exit msm_pcie_enumerate -> cnss_pci_probe.
|
|
|
+ * And this unexpected sequence will make the second wlan card do
|
|
|
+ * pcie link suspend while the pcie enumeration not finished.
|
|
|
+ * So need to add below logical to avoid doing pcie link suspend
|
|
|
+ * if the enumeration has not finish.
|
|
|
+ */
|
|
|
+ plat_priv->enumerate_done = true;
|
|
|
+
|
|
|
+ /* Now enumeration is finished, try to suspend PCIe link */
|
|
|
+ if (plat_priv->bus_priv) {
|
|
|
+ struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
|
|
|
+ struct pci_dev *pci_dev = pci_priv->pci_dev;
|
|
|
+
|
|
|
+ switch (pci_dev->device) {
|
|
|
+ case QCA6390_DEVICE_ID:
|
|
|
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv,
|
|
|
+ false,
|
|
|
+ true,
|
|
|
+ false);
|
|
|
+
|
|
|
+ cnss_pci_suspend_pwroff(pci_dev);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ cnss_pr_err("Unknown PCI device found: 0x%x\n",
|
|
|
+ pci_dev->device);
|
|
|
+ ret = -ENODEV;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#else
|
|
|
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/* Setting to use this cnss_pm_domain ops will let PM framework override the
|
|
|
* ops from dev->bus->pm which is pci_dev_pm_ops from pci-driver.c. This ops
|
|
|
* has to take care everything device driver needed which is currently done
|
|
@@ -6238,10 +6670,13 @@ static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev)
|
|
|
static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev)
|
|
|
{
|
|
|
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
|
|
|
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
|
|
|
+ int rc_num = pci_dev->bus->domain_nr;
|
|
|
+ struct cnss_plat_data *plat_priv;
|
|
|
int ret = 0;
|
|
|
bool suspend_pwroff = cnss_should_suspend_pwroff(pci_dev);
|
|
|
|
|
|
+ plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
|
|
|
+
|
|
|
if (suspend_pwroff) {
|
|
|
ret = cnss_suspend_pci_link(pci_priv);
|
|
|
if (ret)
|
|
@@ -6259,11 +6694,17 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
struct cnss_pci_data *pci_priv;
|
|
|
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
|
|
|
struct device *dev = &pci_dev->dev;
|
|
|
+ int rc_num = pci_dev->bus->domain_nr;
|
|
|
+ struct cnss_plat_data *plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
|
|
|
|
|
|
- cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
|
|
|
- id->vendor, pci_dev->device);
|
|
|
+ cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x rc_num %d\n",
|
|
|
+ id->vendor, pci_dev->device, rc_num);
|
|
|
+ if (!plat_priv) {
|
|
|
+ cnss_pr_err("Find match plat_priv with rc number failure\n");
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
pci_priv = devm_kzalloc(dev, sizeof(*pci_priv), GFP_KERNEL);
|
|
|
if (!pci_priv) {
|
|
@@ -6332,11 +6773,16 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
|
|
|
break;
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
|
case PEACH_DEVICE_ID:
|
|
|
- cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
|
|
|
+ if ((cnss_is_dual_wlan_enabled() &&
|
|
|
+ plat_priv->enumerate_done) || !cnss_is_dual_wlan_enabled())
|
|
|
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false,
|
|
|
+ false);
|
|
|
+
|
|
|
timer_setup(&pci_priv->dev_rddm_timer,
|
|
|
cnss_dev_rddm_timeout_hdlr, 0);
|
|
|
timer_setup(&pci_priv->boot_debug_timer,
|
|
@@ -6357,7 +6803,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
|
|
|
cnss_pci_config_regs(pci_priv);
|
|
|
if (EMULATION_HW)
|
|
|
goto out;
|
|
|
+ if (cnss_is_dual_wlan_enabled() && !plat_priv->enumerate_done)
|
|
|
+ goto probe_done;
|
|
|
cnss_pci_suspend_pwroff(pci_dev);
|
|
|
+
|
|
|
+probe_done:
|
|
|
set_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state);
|
|
|
|
|
|
return 0;
|
|
@@ -6397,6 +6847,7 @@ static void cnss_pci_remove(struct pci_dev *pci_dev)
|
|
|
switch (pci_dev->device) {
|
|
|
case QCA6290_DEVICE_ID:
|
|
|
case QCA6390_DEVICE_ID:
|
|
|
+ case QCN7605_DEVICE_ID:
|
|
|
case QCA6490_DEVICE_ID:
|
|
|
case KIWI_DEVICE_ID:
|
|
|
case MANGO_DEVICE_ID:
|
|
@@ -6427,6 +6878,7 @@ static const struct pci_device_id cnss_pci_id_table[] = {
|
|
|
{ QCA6174_VENDOR_ID, QCA6174_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
{ QCA6290_VENDOR_ID, QCA6290_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
{ QCA6390_VENDOR_ID, QCA6390_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
+ { QCN7605_VENDOR_ID, QCN7605_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
{ QCA6490_VENDOR_ID, QCA6490_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
{ KIWI_VENDOR_ID, KIWI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
|
{ MANGO_VENDOR_ID, MANGO_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID },
|
|
@@ -6443,7 +6895,7 @@ static const struct dev_pm_ops cnss_pm_ops = {
|
|
|
cnss_pci_runtime_idle)
|
|
|
};
|
|
|
|
|
|
-struct pci_driver cnss_pci_driver = {
|
|
|
+static struct pci_driver cnss_pci_driver = {
|
|
|
.name = "cnss_pci",
|
|
|
.id_table = cnss_pci_id_table,
|
|
|
.probe = cnss_pci_probe,
|
|
@@ -6515,17 +6967,25 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- ret = pci_register_driver(&cnss_pci_driver);
|
|
|
+ ret = cnss_try_suspend(plat_priv);
|
|
|
if (ret) {
|
|
|
- cnss_pr_err("Failed to register to PCI framework, err = %d\n",
|
|
|
- ret);
|
|
|
+ cnss_pr_err("Failed to suspend, ret: %d\n", ret);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (!plat_priv->bus_priv) {
|
|
|
- cnss_pr_err("Failed to probe PCI driver\n");
|
|
|
- ret = -ENODEV;
|
|
|
- goto unreg_pci;
|
|
|
+ if (!cnss_driver_registered) {
|
|
|
+ ret = pci_register_driver(&cnss_pci_driver);
|
|
|
+ if (ret) {
|
|
|
+ cnss_pr_err("Failed to register to PCI framework, err = %d\n",
|
|
|
+ ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (!plat_priv->bus_priv) {
|
|
|
+ cnss_pr_err("Failed to probe PCI driver\n");
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto unreg_pci;
|
|
|
+ }
|
|
|
+ cnss_driver_registered = true;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -6538,5 +6998,8 @@ out:
|
|
|
|
|
|
void cnss_pci_deinit(struct cnss_plat_data *plat_priv)
|
|
|
{
|
|
|
- pci_unregister_driver(&cnss_pci_driver);
|
|
|
+ if (cnss_driver_registered) {
|
|
|
+ pci_unregister_driver(&cnss_pci_driver);
|
|
|
+ cnss_driver_registered = false;
|
|
|
+ }
|
|
|
}
|