diff options
author | Rajasekaran Kalidoss <rkalidos@codeaurora.org> | 2018-06-06 09:05:41 +0530 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2018-10-16 12:05:55 -0700 |
commit | 616ea9d08bebd6f0c581806d051c5562f1d50565 (patch) | |
tree | 6d37ecff6bff222b90b1b0c850aaf2557b7e561a | |
parent | 3a799fd24db3939fd49ad0190dfd6c9fdfeb84b0 (diff) |
cnss2: Add support for USB transport
Changes in state machine and boot flow to support usb bus
based behaviour. Handler functions added to support USB
boot flow.
Change-Id: I51c4751c8d8669a8b54ae692ae3ec1bb32d1e7be
Signed-off-by: Rajasekaran Kalidoss <rkalidos@codeaurora.org>
-rw-r--r-- | drivers/net/wireless/cnss2/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/bus.c | 40 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/bus.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/main.c | 53 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/main.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/qmi.c | 19 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/qmi.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/usb.c | 361 | ||||
-rw-r--r-- | drivers/net/wireless/cnss2/usb.h | 66 | ||||
-rw-r--r-- | include/net/cnss2.h | 20 |
10 files changed, 572 insertions, 5 deletions
diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index 318076f23213..a4b177e7a981 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -4,6 +4,7 @@ cnss2-y := main.o cnss2-y += bus.o cnss2-y += debug.o cnss2-y += pci.o +cnss2-y += usb.o cnss2-y += power.o cnss2-y += qmi.o cnss2-y += wlan_firmware_service_v01.o diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index c0810df81bfc..17da90259084 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -13,6 +13,7 @@ #include "bus.h" #include "debug.h" #include "pci.h" +#include "usb.h" enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) { @@ -36,12 +37,33 @@ enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id) case QCA6290_DEVICE_ID: case QCN7605_DEVICE_ID: return CNSS_BUS_PCI; + case QCN7605_COMPOSITE_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + return CNSS_BUS_USB; default: cnss_pr_err("Unknown device_id: 0x%lx\n", device_id); return CNSS_BUS_NONE; } } +bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv) +{ + if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB) + return false; + else + return true; +} + +bool cnss_bus_dev_cal_rep_valid(struct cnss_plat_data *plat_priv) +{ + bool ret = false; + + if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB) + ret = true; + + return ret; +} + void *cnss_bus_dev_to_bus_priv(struct device *dev) { if (!dev) @@ -69,6 +91,8 @@ struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev) switch (cnss_get_dev_bus_type(dev)) { case CNSS_BUS_PCI: return cnss_pci_priv_to_plat_priv(bus_priv); + case CNSS_BUS_USB: + return cnss_usb_priv_to_plat_priv(bus_priv); default: return NULL; } @@ -82,6 +106,8 @@ int cnss_bus_init(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_init(plat_priv); + case CNSS_BUS_USB: + return cnss_usb_init(plat_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -97,6 +123,8 @@ void cnss_bus_deinit(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: cnss_pci_deinit(plat_priv); + case CNSS_BUS_USB: + cnss_usb_deinit(plat_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -212,6 +240,8 @@ int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_call_driver_probe(plat_priv->bus_priv); + case CNSS_BUS_USB: + return cnss_usb_call_driver_probe(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -227,6 +257,8 @@ int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_call_driver_remove(plat_priv->bus_priv); + case CNSS_BUS_USB: + return cnss_usb_call_driver_remove(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -242,6 +274,8 @@ int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_dev_powerup(plat_priv->bus_priv); + case CNSS_BUS_USB: + return 0; default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -257,6 +291,8 @@ int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_dev_shutdown(plat_priv->bus_priv); + case CNSS_BUS_USB: + return 0; default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -302,6 +338,8 @@ int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_register_driver_hdlr(plat_priv->bus_priv, data); + case CNSS_BUS_USB: + return cnss_usb_register_driver_hdlr(plat_priv->bus_priv, data); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -317,6 +355,8 @@ int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: return cnss_pci_unregister_driver_hdlr(plat_priv->bus_priv); + case CNSS_BUS_USB: + return cnss_usb_unregister_driver_hdlr(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index bd32a94e5146..415f1ae6cfed 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -27,6 +27,13 @@ #define QCN7605_VENDOR_ID 0x17CB #define QCN7605_DEVICE_ID 0x1102 +#define QCN7605_USB_VENDOR_ID 0x05C6 +#define QCN7605_COMPOSITE_DEVICE_ID QCN7605_COMPOSITE_PRODUCT_ID +#define QCN7605_STANDALONE_DEVICE_ID QCN7605_STANDALONE_PRODUCT_ID + +#define QCN7605_STANDALONE_PRODUCT_ID 0x9900 +#define QCN7605_COMPOSITE_PRODUCT_ID 0x9901 + 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); void *cnss_bus_dev_to_bus_priv(struct device *dev); @@ -50,4 +57,6 @@ int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv); int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv, int modem_current_status); int cnss_bus_recovery_update_status(struct cnss_plat_data *plat_priv); +bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv); +bool cnss_bus_dev_cal_rep_valid(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 ac9d8e7d08ad..9c1b29fc6e27 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -559,6 +559,15 @@ out: return ret; } +static int cnss_cal_update_hdlr(struct cnss_plat_data *plat_priv) +{ + /* QCN7605 store the cal data sent by FW to calDB memory area + * get out of this after complete data is uploaded. FW is expected + * to send cal done + */ + return 0; +} + static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) { switch (type) { @@ -576,6 +585,10 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) return "COLD_BOOT_CAL_START"; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: return "COLD_BOOT_CAL_DONE"; + case CNSS_DRIVER_EVENT_CAL_UPDATE: + return "COLD_BOOT_CAL_DATA_UPDATE"; + case CNSS_DRIVER_EVENT_CAL_DOWNLOAD: + return "COLD_BOOT_CAL_DATA_DOWNLOAD"; case CNSS_DRIVER_EVENT_REGISTER_DRIVER: return "REGISTER_DRIVER"; case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER: @@ -1127,6 +1140,30 @@ int cnss_force_fw_assert(struct device *dev) } EXPORT_SYMBOL(cnss_force_fw_assert); +static int cnss_wlfw_server_arrive_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret; + + ret = cnss_wlfw_server_arrive(plat_priv); + if (ret) + goto out; + + if (!cnss_bus_req_mem_ind_valid(plat_priv)) { + ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); + if (ret) + goto out; + + ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); + if (ret) + goto out; + /*cnss driver sends meta data report and waits for FW_READY*/ + if (cnss_bus_dev_cal_rep_valid(plat_priv)) + ret = cnss_wlfw_cal_report_send_sync(plat_priv); + } +out: + return ret; +} + static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1146,9 +1183,9 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) if (plat_priv->device_id == QCN7605_DEVICE_ID) goto skip_shutdown; cnss_bus_dev_shutdown(plat_priv); + skip_shutdown: clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); - return 0; } @@ -1194,7 +1231,7 @@ static void cnss_driver_event_work(struct work_struct *work) switch (event->type) { case CNSS_DRIVER_EVENT_SERVER_ARRIVE: - ret = cnss_wlfw_server_arrive(plat_priv); + ret = cnss_wlfw_server_arrive_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_SERVER_EXIT: ret = cnss_wlfw_server_exit(plat_priv); @@ -1214,6 +1251,9 @@ static void cnss_driver_event_work(struct work_struct *work) case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START: ret = cnss_cold_boot_cal_start_hdlr(plat_priv); break; + case CNSS_DRIVER_EVENT_CAL_UPDATE: + ret = cnss_cal_update_hdlr(plat_priv); + break; case CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE: ret = cnss_cold_boot_cal_done_hdlr(plat_priv); break; @@ -1278,6 +1318,8 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv) subsys_info->subsys_desc.name = "QCA6290"; break; case QCN7605_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + case QCN7605_COMPOSITE_DEVICE_ID: subsys_info->subsys_desc.name = "QCN7605"; break; default: @@ -1498,6 +1540,10 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv) case QCN7605_DEVICE_ID: ret = cnss_register_ramdump_v2(plat_priv); break; + case QCN7605_COMPOSITE_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + break; + default: cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); ret = -ENODEV; @@ -1516,6 +1562,9 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv) case QCA6290_DEVICE_ID: cnss_unregister_ramdump_v2(plat_priv); break; + case QCN7605_COMPOSITE_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + break; default: cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); break; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 9dc64e016d82..ec67d31ea945 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -33,6 +33,7 @@ enum cnss_dev_bus_type { CNSS_BUS_NONE = -1, CNSS_BUS_PCI, + CNSS_BUS_USB, }; struct cnss_vreg_info { @@ -124,6 +125,8 @@ enum cnss_driver_event_type { CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, CNSS_DRIVER_EVENT_POWER_UP, CNSS_DRIVER_EVENT_POWER_DOWN, + CNSS_DRIVER_EVENT_CAL_UPDATE, + CNSS_DRIVER_EVENT_CAL_DOWNLOAD, CNSS_DRIVER_EVENT_MAX, }; @@ -245,4 +248,5 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv); bool *cnss_get_qmi_bypass(void); +bool is_qcn7605_device(u16 device_id); #endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index 669816c84e37..fb2841360265 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -238,6 +238,10 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) req.fw_init_done_enable = 1; req.pin_connect_result_enable_valid = 1; req.pin_connect_result_enable = 1; + req.initiate_cal_download_enable_valid = 1; + req.initiate_cal_download_enable = 1; + req.initiate_cal_update_enable_valid = 1; + req.initiate_cal_update_enable = 1; req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; @@ -269,6 +273,13 @@ out: return ret; } +static int cnss_qmi_initiate_cal_update_ind_hdlr( + struct cnss_plat_data *plat_priv, + void *msg, unsigned int msg_len) +{ + return 0; +} + static int cnss_wlfw_request_mem_ind_hdlr(struct cnss_plat_data *plat_priv, void *msg, unsigned int msg_len) { @@ -941,6 +952,11 @@ out: return ret; } +int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv) +{ + return 0; +} + static void cnss_wlfw_clnt_ind(struct qmi_handle *handle, unsigned int msg_id, void *msg, unsigned int msg_len, void *ind_cb_priv) @@ -977,6 +993,9 @@ static void cnss_wlfw_clnt_ind(struct qmi_handle *handle, case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01: cnss_qmi_pin_result_ind_hdlr(plat_priv, msg, msg_len); break; + case QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01: + cnss_qmi_initiate_cal_update_ind_hdlr(plat_priv, msg, msg_len); + break; default: cnss_pr_err("Invalid QMI WLFW indication, msg_id: 0x%x\n", msg_id); diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h index 70d8d404c48e..c6a1e6733505 100644 --- a/drivers/net/wireless/cnss2/qmi.h +++ b/drivers/net/wireless/cnss2/qmi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-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 @@ -37,5 +37,5 @@ int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv, u32 data_len, u8 *data); int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, u8 fw_log_mode); - +int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv); #endif /* _CNSS_QMI_H */ diff --git a/drivers/net/wireless/cnss2/usb.c b/drivers/net/wireless/cnss2/usb.c new file mode 100644 index 000000000000..6cf5aaea1b37 --- /dev/null +++ b/drivers/net/wireless/cnss2/usb.c @@ -0,0 +1,361 @@ +/* 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 "main.h" +#include "bus.h" +#include "debug.h" +#include "usb.h" + +int cnss_usb_dev_powerup(struct cnss_usb_data *usb_priv) +{ + int ret = 0; + + if (!usb_priv) { + cnss_pr_err("usb_priv is NULL\n"); + return -ENODEV; + } + return ret; +} + +int cnss_usb_wlan_register_driver(struct cnss_usb_wlan_driver *driver_ops) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_usb_data *usb_priv; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + usb_priv = plat_priv->bus_priv; + if (!usb_priv) { + cnss_pr_err("usb_priv is NULL\n"); + return -ENODEV; + } + + if (usb_priv->driver_ops) { + cnss_pr_err("Driver has already registered\n"); + return -EEXIST; + } + + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_REGISTER_DRIVER, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, + driver_ops); + return ret; +} +EXPORT_SYMBOL(cnss_usb_wlan_register_driver); + +void cnss_usb_wlan_unregister_driver(struct cnss_usb_wlan_driver *driver_ops) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return; + } + + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); +} +EXPORT_SYMBOL(cnss_usb_wlan_unregister_driver); + +int cnss_usb_register_driver_hdlr(struct cnss_usb_data *usb_priv, + void *data) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = usb_priv->plat_priv; + + set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + usb_priv->driver_ops = data; + + ret = cnss_bus_call_driver_probe(plat_priv); + + return ret; +} + +int cnss_usb_unregister_driver_hdlr(struct cnss_usb_data *usb_priv) +{ + struct cnss_plat_data *plat_priv = usb_priv->plat_priv; + + set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + cnss_usb_dev_shutdown(usb_priv); + usb_priv->driver_ops = NULL; + + return 0; +} + +int cnss_usb_dev_shutdown(struct cnss_usb_data *usb_priv) +{ + int ret = 0; + + if (!usb_priv) { + cnss_pr_err("usb_priv is NULL\n"); + return -ENODEV; + } + + switch (usb_priv->device_id) { + case QCN7605_COMPOSITE_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + break; + default: + cnss_pr_err("Unknown device_id found: 0x%x\n", + usb_priv->device_id); + ret = -ENODEV; + } + return ret; +} + +int cnss_usb_call_driver_probe(struct cnss_usb_data *usb_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = usb_priv->plat_priv; + + if (!usb_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + ret = -EINVAL; + goto out; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { + ret = usb_priv->driver_ops->reinit(usb_priv->usb_intf, + usb_priv->usb_device_id); + if (ret) { + cnss_pr_err("Failed to reinit host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { + ret = usb_priv->driver_ops->probe(usb_priv->usb_intf, + usb_priv->usb_device_id); + if (ret) { + cnss_pr_err("Failed to probe host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; + +out: + return ret; +} + +int cnss_usb_call_driver_remove(struct cnss_usb_data *usb_priv) +{ + struct cnss_plat_data *plat_priv = usb_priv->plat_priv; + + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || + test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { + cnss_pr_dbg("Skip driver remove\n"); + return 0; + } + + if (!usb_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + return -EINVAL; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { + usb_priv->driver_ops->shutdown(usb_priv->usb_intf); + } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + usb_priv->driver_ops->remove(usb_priv->usb_intf); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; +} + +static struct usb_driver cnss_usb_driver; +#define QCN7605_WLAN_INTERFACE_NUM 0x0000 + +static int cnss_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ret = 0; + struct cnss_usb_data *usb_priv; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct usb_device *usb_dev; + unsigned short bcd_device; + + cnss_pr_dbg("USB probe, vendor ID: 0x%x, product ID: 0x%x\n", + id->idVendor, id->idProduct); + + usb_dev = interface_to_usbdev(interface); + usb_priv = devm_kzalloc(&usb_dev->dev, sizeof(*usb_priv), + GFP_KERNEL); + if (!usb_priv) { + ret = -ENOMEM; + goto out; + } + + if (interface->cur_altsetting->desc.bInterfaceNumber == + QCN7605_WLAN_INTERFACE_NUM) { + if (usb_driver_claim_interface(&cnss_usb_driver, + interface, + NULL)) { + ret = -ENODEV; + goto reset_priv; + } + } + bcd_device = le16_to_cpu(usb_dev->descriptor.bcdDevice); + usb_priv->plat_priv = plat_priv; + usb_priv->usb_intf = interface; + usb_priv->usb_device_id = id; + usb_priv->device_id = id->idProduct; + usb_priv->target_version = bcd_device; + cnss_set_usb_priv(interface, usb_priv); + plat_priv->device_id = usb_priv->device_id; + plat_priv->bus_priv = usb_priv; + + /*increment the ref count of usb dev structure*/ + usb_get_dev(usb_dev); + + ret = cnss_register_subsys(plat_priv); + if (ret) + goto reset_ctx; + + ret = cnss_register_ramdump(plat_priv); + if (ret) + goto unregister_subsys; + + switch (usb_priv->device_id) { + case QCN7605_COMPOSITE_DEVICE_ID: + case QCN7605_STANDALONE_DEVICE_ID: + break; + default: + cnss_pr_err("Unknown USB device found: 0x%x\n", + usb_priv->device_id); + ret = -ENODEV; + goto unregister_ramdump; + } + + return 0; + +unregister_ramdump: + cnss_unregister_ramdump(plat_priv); +unregister_subsys: + cnss_unregister_subsys(plat_priv); +reset_ctx: + plat_priv->bus_priv = NULL; +reset_priv: + devm_kfree(&usb_dev->dev, usb_priv); +out: + return ret; +} + +static void cnss_usb_remove(struct usb_interface *interface) +{ + struct usb_device *usb_dev; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_usb_data *usb_priv = plat_priv->bus_priv; + + usb_priv->plat_priv = NULL; + plat_priv->bus_priv = NULL; + usb_dev = interface_to_usbdev(interface); + usb_put_dev(usb_dev); + devm_kfree(&usb_dev->dev, usb_priv); +} + +static int cnss_usb_suspend(struct usb_interface *interface, pm_message_t state) +{ + int ret = 0; + struct cnss_usb_data *usb_priv; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + usb_priv = plat_priv->bus_priv; + if (!usb_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + ret = -EINVAL; + goto out; + } + ret = usb_priv->driver_ops->suspend(usb_priv->usb_intf, + state); +out: + return ret; +} + +static int cnss_usb_resume(struct usb_interface *interface) +{ + int ret = 0; + struct cnss_usb_data *usb_priv; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + usb_priv = plat_priv->bus_priv; + if (!usb_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + ret = -EINVAL; + goto out; + } + ret = usb_priv->driver_ops->resume(usb_priv->usb_intf); + +out: + return ret; +} + +static int cnss_usb_reset_resume(struct usb_interface *interface) +{ + return 0; +} + +static struct usb_device_id cnss_usb_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QCN7605_USB_VENDOR_ID, + QCN7605_COMPOSITE_PRODUCT_ID, + QCN7605_WLAN_INTERFACE_NUM, + 0xFF, 0xFF) }, + { USB_DEVICE_AND_INTERFACE_INFO(QCN7605_USB_VENDOR_ID, + QCN7605_STANDALONE_PRODUCT_ID, + QCN7605_WLAN_INTERFACE_NUM, + 0xFF, 0xFF) }, + {} /* Terminating entry */ +}; + +static struct usb_driver cnss_usb_driver = { + .name = "cnss_usb", + .id_table = cnss_usb_id_table, + .probe = cnss_usb_probe, + .disconnect = cnss_usb_remove, + .suspend = cnss_usb_suspend, + .resume = cnss_usb_resume, + .reset_resume = cnss_usb_reset_resume, + .supports_autosuspend = true, +}; + +int cnss_usb_init(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + ret = usb_register(&cnss_usb_driver); + if (ret) { + cnss_pr_err("Failed to register to Linux USB framework, err = %d\n", + ret); + goto out; + } + + return 0; +out: + return ret; +} + +void cnss_usb_deinit(struct cnss_plat_data *plat_priv) +{ + usb_deregister(&cnss_usb_driver); +} diff --git a/drivers/net/wireless/cnss2/usb.h b/drivers/net/wireless/cnss2/usb.h new file mode 100644 index 000000000000..b285dc27daa9 --- /dev/null +++ b/drivers/net/wireless/cnss2/usb.h @@ -0,0 +1,66 @@ +/* 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_USB_H +#define _CNSS_USB_H + +#include <linux/usb.h> + +#include "main.h" + +struct cnss_usb_data { + struct usb_interface *usb_intf; + struct cnss_plat_data *plat_priv; + const struct usb_device_id *usb_device_id; + u16 device_id; /*USB PID*/ + u16 target_version; /* [QCN7605] - from bcdDevice*/ + struct cnss_usb_wlan_driver *driver_ops; +}; + +static inline void cnss_set_usb_priv(struct usb_interface *usb_intf, void *data) +{ + usb_set_intfdata(usb_intf, data); +} + +static inline struct cnss_usb_data *cnss_get_usb_priv(struct usb_interface + *usb_intf) +{ + return usb_get_intfdata(usb_intf); +} + +static inline struct cnss_plat_data *cnss_usb_priv_to_plat_priv(void *bus_priv) +{ + struct cnss_usb_data *usb_priv = bus_priv; + + return usb_priv->plat_priv; +} + +int cnss_usb_init(struct cnss_plat_data *plat_priv); +void cnss_usb_deinit(struct cnss_plat_data *plat_priv); +void cnss_usb_collect_dump_info(struct cnss_usb_data *usb_priv, bool in_panic); +void cnss_usb_clear_dump_info(struct cnss_usb_data *usb_priv); +int cnss_usb_force_fw_assert_hdlr(struct cnss_usb_data *usb_priv); +void cnss_usb_fw_boot_timeout_hdlr(struct cnss_usb_data *usb_priv); +int cnss_usb_call_driver_probe(struct cnss_usb_data *usb_priv); +int cnss_usb_call_driver_remove(struct cnss_usb_data *usb_priv); +int cnss_usb_dev_powerup(struct cnss_usb_data *usb_priv); +int cnss_usb_dev_shutdown(struct cnss_usb_data *usb_priv); +int cnss_usb_dev_crash_shutdown(struct cnss_usb_data *usb_priv); +int cnss_usb_dev_ramdump(struct cnss_usb_data *usb_priv); + +int cnss_usb_register_driver_hdlr(struct cnss_usb_data *usb_priv, void *data); + +int cnss_usb_unregister_driver_hdlr(struct cnss_usb_data *usb_priv); +int cnss_usb_call_driver_modem_status(struct cnss_usb_data *usb_priv, + int modem_current_status); + +#endif /* _CNSS_USB_H */ diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 7ca407f6b606..8dc7fc44aa02 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -14,6 +14,7 @@ #define _NET_CNSS2_H #include <linux/pci.h> +#include <linux/usb.h> #define CNSS_MAX_FILE_NAME 20 #define CNSS_MAX_TIMESTAMP_LEN 32 @@ -83,6 +84,21 @@ struct cnss_wlan_driver { const struct pci_device_id *id_table; }; +struct cnss_usb_wlan_driver { + char *name; + int (*probe)(struct usb_interface *pintf, const struct usb_device_id + *id); + void (*remove)(struct usb_interface *pintf); + int (*reinit)(struct usb_interface *pintf, const struct usb_device_id + *id); + void (*shutdown)(struct usb_interface *pintf); + void (*crash_shutdown)(struct usb_interface *pintf); + int (*suspend)(struct usb_interface *pintf, pm_message_t state); + int (*resume)(struct usb_interface *pintf); + int (*reset_resume)(struct usb_interface *pintf); + const struct usb_device_id *id_table; +}; + enum cnss_driver_status { CNSS_UNINITIALIZED, CNSS_INITIALIZED, @@ -215,5 +231,7 @@ extern int cnss_athdiag_write(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *input); extern int cnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode); - +extern int cnss_usb_wlan_register_driver(struct cnss_usb_wlan_driver *driver); +extern void cnss_usb_wlan_unregister_driver(struct cnss_usb_wlan_driver * + driver); #endif /* _NET_CNSS2_H */ |