diff options
-rw-r--r-- | drivers/net/wireless/cnss2/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/bus.c | 164 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/bus.h | 39 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/main.c | 105 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/main.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/pci.c | 82 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/pci.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/qmi.c | 5 |
8 files changed, 304 insertions, 96 deletions
diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index b49d0898178b..318076f23213 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_CNSS2) += cnss2.o cnss2-y := main.o +cnss2-y += bus.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c new file mode 100644 index 000000000000..83809c2038c0 --- /dev/null +++ b/drivers/net/wireless/cnss2/bus.c @@ -0,0 +1,164 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "bus.h" +#include "debug.h" +#include "pci.h" + +enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) +{ + if (!dev) + return CNSS_BUS_NONE; + + if (!dev->bus) + return CNSS_BUS_NONE; + + if (memcmp(dev->bus->name, "pci", 3) == 0) + return CNSS_BUS_PCI; + else + return CNSS_BUS_NONE; +} + +enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id) +{ + switch (device_id) { + case QCA6174_DEVICE_ID: + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + return CNSS_BUS_PCI; + default: + cnss_pr_err("Unknown device_id: 0x%lx\n", device_id); + return CNSS_BUS_NONE; + } +} + +int cnss_bus_init(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_init(plat_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_deinit(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + cnss_pci_deinit(plat_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + +int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_load_m3(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_alloc_fw_mem(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_get_wake_msi(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_force_fw_assert_hdlr(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_fw_boot_timeout_hdlr(unsigned long data) +{ + struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data; + + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_fw_boot_timeout_hdlr(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + +void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_collect_dump_info(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h new file mode 100644 index 000000000000..40400db6e896 --- /dev/null +++ b/drivers/net/wireless/cnss2/bus.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CNSS_BUS_H +#define _CNSS_BUS_H + +#include "main.h" + +#define QCA6174_VENDOR_ID 0x168C +#define QCA6174_DEVICE_ID 0x003E +#define QCA6174_REV_ID_OFFSET 0x08 +#define QCA6174_REV3_VERSION 0x5020000 +#define QCA6174_REV3_2_VERSION 0x5030000 +#define QCA6290_VENDOR_ID 0x17CB +#define QCA6290_DEVICE_ID 0x1100 +#define QCA6290_EMULATION_VENDOR_ID 0x168C +#define QCA6290_EMULATION_DEVICE_ID 0xABCD + +enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev); +enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id); +int cnss_bus_init(struct cnss_plat_data *plat_priv); +void cnss_bus_deinit(struct cnss_plat_data *plat_priv); +int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); +int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv); +u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv); +int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv); +void cnss_bus_fw_boot_timeout_hdlr(unsigned long data); +void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv); + +#endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 5183f3de7c9b..165dd5625a8f 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -23,6 +23,7 @@ #include <soc/qcom/subsystem_notif.h> #include "main.h" +#include "bus.h" #include "debug.h" #include "pci.h" @@ -37,7 +38,6 @@ #define FW_READY_TIMEOUT 20000 #define FW_ASSERT_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 -#define WAKE_MSI_NAME "WAKE" static struct cnss_plat_data *plat_env; @@ -87,20 +87,6 @@ struct cnss_driver_event { void *data; }; -static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) -{ - if (!dev) - return CNSS_BUS_NONE; - - if (!dev->bus) - return CNSS_BUS_NONE; - - if (memcmp(dev->bus->name, "pci", 3) == 0) - return CNSS_BUS_PCI; - else - return CNSS_BUS_NONE; -} - static void cnss_set_plat_priv(struct platform_device *plat_dev, struct cnss_plat_data *plat_priv) { @@ -274,23 +260,6 @@ int cnss_get_platform_cap(struct device *dev, struct cnss_platform_cap *cap) } EXPORT_SYMBOL(cnss_get_platform_cap); -int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) -{ - int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); - void *bus_priv = cnss_bus_dev_to_bus_priv(dev); - - if (!plat_priv) - return -ENODEV; - - ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa); - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL(cnss_get_soc_info); - void cnss_request_pm_qos(struct device *dev, u32 qos_val) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); @@ -506,24 +475,6 @@ int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) } EXPORT_SYMBOL(cnss_set_fw_log_mode); -u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - int ret, num_vectors; - u32 user_base_data, base_vector; - - ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, - WAKE_MSI_NAME, &num_vectors, - &user_base_data, &base_vector); - - if (ret) { - cnss_pr_err("WAKE MSI is not valid\n"); - return 0; - } - - return user_base_data; -} - static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -541,7 +492,7 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; - ret = cnss_pci_load_m3(plat_priv->bus_priv); + ret = cnss_bus_load_m3(plat_priv); if (ret) goto out; @@ -1483,7 +1434,7 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, cnss_pr_err("Failed to complete RDDM, err = %d\n", ret); break; } - cnss_pci_collect_dump_info(pci_priv); + cnss_bus_collect_dump_info(plat_priv); break; case CNSS_REASON_DEFAULT: case CNSS_REASON_TIMEOUT: @@ -1590,28 +1541,6 @@ void cnss_schedule_recovery(struct device *dev, } EXPORT_SYMBOL(cnss_schedule_recovery); -static int cnss_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - int ret; - - ret = cnss_pci_set_mhi_state(plat_priv->bus_priv, - CNSS_MHI_TRIGGER_RDDM); - if (ret) { - cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_DEFAULT); - return 0; - } - - if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); - } - - return 0; -} - int cnss_force_fw_assert(struct device *dev) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); @@ -1639,17 +1568,6 @@ int cnss_force_fw_assert(struct device *dev) } EXPORT_SYMBOL(cnss_force_fw_assert); -void fw_boot_timeout(unsigned long data) -{ - struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - cnss_pr_err("Timeout waiting for FW ready indication!\n"); - - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_TIMEOUT); -} - static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) { @@ -1746,7 +1664,7 @@ static void cnss_driver_event_work(struct work_struct *work) ret = cnss_wlfw_server_exit(plat_priv); break; case CNSS_DRIVER_EVENT_REQUEST_MEM: - ret = cnss_pci_alloc_fw_mem(plat_priv->bus_priv); + ret = cnss_bus_alloc_fw_mem(plat_priv); if (ret) break; ret = cnss_wlfw_respond_mem_send_sync(plat_priv); @@ -1775,7 +1693,7 @@ static void cnss_driver_event_work(struct work_struct *work) event->data); break; case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: - ret = cnss_force_fw_assert_hdlr(plat_priv); + ret = cnss_bus_force_fw_assert_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_POWER_UP: ret = cnss_power_up_hdlr(plat_priv); @@ -2232,6 +2150,7 @@ static int cnss_probe(struct platform_device *plat_dev) plat_priv->plat_dev = plat_dev; plat_priv->device_id = device_id->driver_data; + plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id); cnss_set_plat_priv(plat_dev, plat_priv); platform_set_drvdata(plat_dev, plat_priv); @@ -2244,14 +2163,14 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto free_res; - ret = cnss_pci_init(plat_priv); + ret = cnss_bus_init(plat_priv); if (ret) goto power_off; } ret = cnss_register_esoc(plat_priv); if (ret) - goto deinit_pci; + goto deinit_bus; ret = cnss_register_bus_scale(plat_priv); if (ret) @@ -2274,7 +2193,7 @@ static int cnss_probe(struct platform_device *plat_dev) goto deinit_qmi; setup_timer(&plat_priv->fw_boot_timer, - fw_boot_timeout, (unsigned long)plat_priv); + cnss_bus_fw_boot_timeout_hdlr, (unsigned long)plat_priv); register_pm_notifier(&cnss_pm_notifier); @@ -2300,9 +2219,9 @@ unreg_bus_scale: cnss_unregister_bus_scale(plat_priv); unreg_esoc: cnss_unregister_esoc(plat_priv); -deinit_pci: +deinit_bus: if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) - cnss_pci_deinit(plat_priv); + cnss_bus_deinit(plat_priv); power_off: if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) cnss_power_off_device(plat_priv); @@ -2329,7 +2248,7 @@ static int cnss_remove(struct platform_device *plat_dev) cnss_remove_sysfs(plat_priv); cnss_unregister_bus_scale(plat_priv); cnss_unregister_esoc(plat_priv); - cnss_pci_deinit(plat_priv); + cnss_bus_deinit(plat_priv); cnss_put_resources(plat_priv); platform_set_drvdata(plat_dev, NULL); plat_env = NULL; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index a36281cb560f..7efbb27aee7c 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -167,6 +167,7 @@ struct cnss_pin_connect_result { struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; + enum cnss_dev_bus_type bus_type; struct cnss_vreg_info *vreg_info; struct cnss_pinctrl_info pinctrl_info; struct cnss_subsys_info subsys_info; diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 2356caa3af78..3f2aef84662c 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -16,8 +16,10 @@ #include <linux/msi.h> #include <linux/of.h> #include <linux/pm_runtime.h> +#include <linux/memblock.h> #include "main.h" +#include "bus.h" #include "debug.h" #include "pci.h" @@ -40,10 +42,15 @@ #endif #define MHI_NODE_NAME "qcom,mhi" +#define MHI_MSI_NAME "MHI" #define MAX_M3_FILE_NAME_LENGTH 13 #define DEFAULT_M3_FILE_NAME "m3.bin" +#define WAKE_MSI_NAME "WAKE" + +#define FW_ASSERT_TIMEOUT 5000 + #ifdef CONFIG_PCI_MSM static DEFINE_SPINLOCK(pci_link_down_lock); #endif @@ -178,7 +185,6 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) goto out; } - pci_set_master(pci_priv->pci_dev); if (pci_priv->pci_link_down_ind) @@ -841,6 +847,63 @@ static void cnss_pci_free_m3_mem(struct cnss_pci_data *pci_priv) m3_mem->size = 0; } +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + int ret; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + ret = cnss_pci_set_mhi_state(pci_priv, + CNSS_MHI_TRIGGER_RDDM); + if (ret) { + cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_DEFAULT); + return 0; + } + + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); + } + + return 0; +} + +void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) +{ + if (!pci_priv) + return; + + cnss_pr_err("Timeout waiting for FW ready indication\n"); + + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_TIMEOUT); +} + +int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + void *bus_priv = cnss_bus_dev_to_bus_priv(dev); + + if (!plat_priv) + return -ENODEV; + + ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(cnss_get_soc_info); + int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va, phys_addr_t *pa) { @@ -991,6 +1054,23 @@ void cnss_get_msi_address(struct device *dev, u32 *msi_addr_low, } EXPORT_SYMBOL(cnss_get_msi_address); +u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv) +{ + int ret, num_vectors; + u32 user_base_data, base_vector; + + ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, + WAKE_MSI_NAME, &num_vectors, + &user_base_data, &base_vector); + + if (ret) { + cnss_pr_err("WAKE MSI is not valid\n"); + return 0; + } + + return user_base_data; +} + #ifdef CONFIG_PCI_MSM static inline int cnss_pci_set_dma_mask(struct pci_dev *pci_dev) { diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index a00ca61972f0..b644d1e138e4 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -152,5 +152,8 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv); void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); int cnss_pm_request_resume(struct cnss_pci_data *pci_priv); +u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv); +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv); +void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv); #endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index cc4583996d1c..669816c84e37 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -15,8 +15,9 @@ #include <linux/qmi_encdec.h> #include <soc/qcom/msm_qmi_interface.h> -#include "main.h" +#include "bus.h" #include "debug.h" +#include "main.h" #include "qmi.h" #define WLFW_SERVICE_INS_ID_V01 1 @@ -163,7 +164,7 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) req.num_clients = daemon_support ? 2 : 1; cnss_pr_dbg("Number of clients is %d\n", req.num_clients); - req.wake_msi = cnss_get_wake_msi(plat_priv); + req.wake_msi = cnss_bus_get_wake_irq(plat_priv); if (req.wake_msi) { cnss_pr_dbg("WAKE MSI base data is %d\n", req.wake_msi); req.wake_msi_valid = 1; |