diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index a4b177e7a981..b1c8c5e6461e 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -5,6 +5,7 @@ cnss2-y += bus.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += usb.o +cnss2-$(CONFIG_SDIO_QCN) += sdio.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 088593f9ae1c..e33eb647d9bb 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -14,6 +14,7 @@ #include "debug.h" #include "pci.h" #include "usb.h" +#include "sdio.h" enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) { @@ -27,6 +28,8 @@ enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) return CNSS_BUS_PCI; else if (memcmp(dev->bus->name, "usb", 3) == 0) return CNSS_BUS_USB; + else if (memcmp(dev->bus->name, "sdio", 4) == 0) + return CNSS_BUS_SDIO; else return CNSS_BUS_NONE; } @@ -44,20 +47,14 @@ enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id) case QCN7605_VER20_STANDALONE_DEVICE_ID: case QCN7605_VER20_COMPOSITE_DEVICE_ID: return CNSS_BUS_USB; + case QCN7605_SDIO_DEVICE_ID: + return CNSS_BUS_SDIO; 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; -} - void *cnss_bus_dev_to_bus_priv(struct device *dev) { if (!dev) @@ -89,6 +86,8 @@ struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev) return cnss_pci_priv_to_plat_priv(bus_priv); case CNSS_BUS_USB: return cnss_usb_priv_to_plat_priv(bus_priv); + case CNSS_BUS_SDIO: + return cnss_get_plat_priv(NULL); default: return NULL; } @@ -104,6 +103,8 @@ int cnss_bus_init(struct cnss_plat_data *plat_priv) return cnss_pci_init(plat_priv); case CNSS_BUS_USB: return cnss_usb_init(plat_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_init(plat_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -119,13 +120,18 @@ void cnss_bus_deinit(struct cnss_plat_data *plat_priv) switch (plat_priv->bus_type) { case CNSS_BUS_PCI: cnss_pci_deinit(plat_priv); + break; case CNSS_BUS_USB: cnss_usb_deinit(plat_priv); + break; + case CNSS_BUS_SDIO: + cnss_sdio_deinit(plat_priv); + break; default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); - return; } + return; } int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) @@ -200,6 +206,8 @@ void cnss_bus_fw_boot_timeout_hdlr(unsigned long data) return cnss_pci_fw_boot_timeout_hdlr(plat_priv->bus_priv); case CNSS_BUS_USB: return cnss_usb_fw_boot_timeout_hdlr(plat_priv->bus_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_fw_boot_timeout_hdlr(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -240,6 +248,8 @@ int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv) return cnss_pci_call_driver_probe(plat_priv->bus_priv); case CNSS_BUS_USB: return cnss_usb_call_driver_probe(plat_priv->bus_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_call_driver_probe(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -257,6 +267,8 @@ int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv) return cnss_pci_call_driver_remove(plat_priv->bus_priv); case CNSS_BUS_USB: return cnss_usb_call_driver_remove(plat_priv->bus_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_call_driver_remove(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -274,6 +286,8 @@ int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv) return cnss_pci_dev_powerup(plat_priv->bus_priv); case CNSS_BUS_USB: return cnss_usb_dev_powerup(plat_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_dev_powerup(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -291,6 +305,8 @@ int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv) return cnss_pci_dev_shutdown(plat_priv->bus_priv); case CNSS_BUS_USB: return 0; + case CNSS_BUS_SDIO: + return cnss_sdio_dev_shutdown(plat_priv->bus_priv); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -338,6 +354,9 @@ int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) 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); + case CNSS_BUS_SDIO: + return cnss_sdio_register_driver_hdlr(plat_priv->bus_priv, + data); default: cnss_pr_err("Unsupported bus type: %d\n", plat_priv->bus_type); @@ -355,6 +374,8 @@ int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) return cnss_pci_unregister_driver_hdlr(plat_priv->bus_priv); case CNSS_BUS_USB: return cnss_usb_unregister_driver_hdlr(plat_priv->bus_priv); + case CNSS_BUS_SDIO: + return cnss_sdio_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 1100fd66482a..30a93a06549f 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -26,6 +26,8 @@ #define QCA6290_EMULATION_DEVICE_ID 0xABCD #define QCN7605_VENDOR_ID 0x17CB #define QCN7605_DEVICE_ID 0x1102 +#define QCN7605_SDIO_VENDOR_ID 0x70 +#define QCN7605_SDIO_DEVICE_ID 0x400B #define QCN7605_USB_VENDOR_ID 0x05C6 #define QCN7605_COMPOSITE_DEVICE_ID QCN7605_COMPOSITE_PRODUCT_ID @@ -62,5 +64,4 @@ 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); #endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 1da33c258d4a..f70dfaa45c0f 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -248,7 +248,7 @@ int cnss_wlan_enable(struct device *dev, enum cnss_driver_mode mode, const char *host_version) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); struct wlfw_wlan_cfg_req_msg_v01 req; u32 i, ce_id, num_vectors, user_base_data, base_vector; int ret = 0; @@ -259,7 +259,8 @@ int cnss_wlan_enable(struct device *dev, if (qmi_bypass) return 0; - if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB) + if (plat_priv->bus_type == CNSS_BUS_USB || + plat_priv->bus_type == CNSS_BUS_SDIO) goto skip_cfg; if (!config || !host_version) { @@ -364,7 +365,7 @@ EXPORT_SYMBOL(cnss_wlan_enable); int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) { - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; @@ -1234,7 +1235,8 @@ static int cnss_wlfw_server_arrive_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; - if (!cnss_bus_req_mem_ind_valid(plat_priv)) { + if (plat_priv->bus_type == CNSS_BUS_USB || + plat_priv->bus_type == CNSS_BUS_SDIO) { ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); if (ret) goto out; @@ -1254,7 +1256,7 @@ static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) if (test_bit(CNSS_DEV_REMOVED, &plat_priv->driver_state)) pwr_up_reqd = true; - if (pwr_up_reqd || plat_priv->bus_type != CNSS_BUS_USB) + if (pwr_up_reqd || plat_priv->bus_type == CNSS_BUS_PCI) ret = cnss_bus_dev_powerup(plat_priv); if (ret) @@ -1413,6 +1415,7 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv) case QCN7605_COMPOSITE_DEVICE_ID: case QCN7605_VER20_STANDALONE_DEVICE_ID: case QCN7605_VER20_COMPOSITE_DEVICE_ID: + case QCN7605_SDIO_DEVICE_ID: subsys_info->subsys_desc.name = "QCN7605"; break; default: @@ -1749,6 +1752,18 @@ static ssize_t cnss_fs_ready_store(struct device *dev, return count; } +#ifdef CONFIG_SDIO_QCN +static void cnss_set_card_state(bool state) +{ + qcn_sdio_card_state(state); +} +#else +static void cnss_set_card_state(bool state) +{ + /* no op */ +} +#endif + static DEVICE_ATTR(fs_ready, 0220, NULL, cnss_fs_ready_store); static ssize_t cnss_wl_pwr_on(struct device *dev, @@ -1769,12 +1784,14 @@ static ssize_t cnss_wl_pwr_on(struct device *dev, timeout = cnss_get_qmi_timeout(); if (pwr_state) { cnss_power_on_device(plat_priv); + cnss_set_card_state(true); if (timeout) { mod_timer(&plat_priv->fw_boot_timer, jiffies + msecs_to_jiffies(timeout)); } } else { cnss_power_off_device(plat_priv); + cnss_set_card_state(false); del_timer(&plat_priv->fw_boot_timer); } return count; @@ -1867,6 +1884,7 @@ static const struct platform_device_id cnss_platform_id_table[] = { { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, { .name = "qcn7605", .driver_data = QCN7605_DEVICE_ID, }, + { .name = "qcn7605_sdio", .driver_data = QCN7605_SDIO_DEVICE_ID, }, }; static const struct of_device_id cnss_of_match_table[] = { @@ -1876,6 +1894,12 @@ static const struct of_device_id cnss_of_match_table[] = { { .compatible = "qcom,cnss-qca6290", .data = (void *)&cnss_platform_id_table[1]}, + { + .compatible = "qcom,cnss", + .data = (void *)&cnss_platform_id_table[2]}, + { + .compatible = "qcom,cnss-sdio", + .data = (void *)&cnss_platform_id_table[3]}, { }, }; MODULE_DEVICE_TABLE(of, cnss_of_match_table); @@ -1926,7 +1950,9 @@ static int cnss_probe(struct platform_device *plat_dev) goto free_res; ret = cnss_bus_init(plat_priv); - if (ret) + if (ret == -EPROBE_DEFER) + goto free_res; + else if (ret) goto power_off; } diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 1b7c67b90795..aa197f0684d5 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -36,6 +36,7 @@ enum cnss_dev_bus_type { CNSS_BUS_NONE = -1, CNSS_BUS_PCI, CNSS_BUS_USB, + CNSS_BUS_SDIO, }; struct cnss_vreg_info { diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index 7945f59439f7..489a28e478bd 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -797,7 +797,8 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) plat_priv->device_id == QCN7605_COMPOSITE_DEVICE_ID || plat_priv->device_id == QCN7605_STANDALONE_DEVICE_ID || plat_priv->device_id == QCN7605_VER20_STANDALONE_DEVICE_ID || - plat_priv->device_id == QCN7605_VER20_COMPOSITE_DEVICE_ID) + plat_priv->device_id == QCN7605_VER20_COMPOSITE_DEVICE_ID || + plat_priv->device_id == QCN7605_SDIO_DEVICE_ID) bdf_type = CNSS_BDF_BIN; if (plat_priv->board_info.board_id == 0xFF) { diff --git a/drivers/net/wireless/cnss2/sdio.c b/drivers/net/wireless/cnss2/sdio.c new file mode 100644 index 000000000000..f25a2b0aaaa8 --- /dev/null +++ b/drivers/net/wireless/cnss2/sdio.c @@ -0,0 +1,410 @@ +/* Copyright (c) 2015-2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include "main.h" +#include "sdio.h" +#include "debug.h" +#include "bus.h" + +int cnss_sdio_call_driver_probe(struct cnss_sdio_data *sdio_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = sdio_priv->plat_priv; + + if ((!sdio_priv->al_client_handle) || + (!sdio_priv->al_client_handle->func)) { + ret = -ENODEV; + goto out; + } + + if (!sdio_priv->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 = sdio_priv->ops->reinit(sdio_priv->al_client_handle->func, + sdio_priv->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 = sdio_priv->ops->probe(sdio_priv->al_client_handle->func, + sdio_priv->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_sdio_call_driver_remove(struct cnss_sdio_data *sdio_priv) +{ + struct cnss_plat_data *plat_priv = sdio_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 (!sdio_priv->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)) { + cnss_pr_dbg("Recovery set after driver probed.Call shutdown\n"); + sdio_priv->ops->shutdown(sdio_priv->al_client_handle->func); + } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + cnss_pr_dbg("driver_ops->remove\n"); + sdio_priv->ops->remove(sdio_priv->al_client_handle->func); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + return 0; +} + +/** + * cnss_sdio_wlan_register_driver() - cnss wlan register API + * @driver: sdio wlan driver interface from wlan driver. + * + * wlan sdio function driver uses this API to register callback + * functions to cnss_sido platform driver. The callback will + * be invoked by corresponding wrapper function of this cnss + * platform driver. + */ +int cnss_sdio_wlan_register_driver(struct cnss_sdio_wlan_driver *driver_ops) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_sdio_data *cnss_info; + int ret = 0; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + cnss_info = plat_priv->bus_priv; + if ((!cnss_info) || + (!cnss_info->al_client_handle) || + (!cnss_info->al_client_handle->func)) { + cnss_pr_err("cnss_info is NULL\n"); + return -ENODEV; + } + + if (cnss_info->ops) { + cnss_pr_err("Driver has already registered\n"); + return -EEXIST; + } + cnss_info->ops = driver_ops; + + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_REGISTER_DRIVER, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, + driver_ops); + return ret; +} +EXPORT_SYMBOL(cnss_sdio_wlan_register_driver); + +/** + * cnss_sdio_wlan_unregister_driver() - cnss wlan unregister API + * @driver: sdio wlan driver interface from wlan driver. + * + * wlan sdio function driver uses this API to detach it from cnss_sido + * platform driver. + */ +void cnss_sdio_wlan_unregister_driver(struct cnss_sdio_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_sdio_wlan_unregister_driver); + +struct sdio_al_client_handle *cnss_sdio_wlan_get_sdio_al_client_handle( + struct sdio_func *func) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_sdio_data *cnss_info = plat_priv->bus_priv; + + return cnss_info->al_client_handle; +} +EXPORT_SYMBOL(cnss_sdio_wlan_get_sdio_al_client_handle); + +struct sdio_al_channel_handle *cnss_sdio_wlan_register_sdio_al_channel( + struct sdio_al_channel_data *channel_data) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_sdio_data *cnss_info = plat_priv->bus_priv; + + return sdio_al_register_channel(cnss_info->al_client_handle, + channel_data); +} +EXPORT_SYMBOL(cnss_sdio_wlan_register_sdio_al_channel); + +void cnss_sdio_wlan_unregister_sdio_al_channel( + struct sdio_al_channel_handle *ch_handle) +{ + sdio_al_deregister_channel(ch_handle); +} +EXPORT_SYMBOL(cnss_sdio_wlan_unregister_sdio_al_channel); + +int cnss_sdio_register_driver_hdlr(struct cnss_sdio_data *cnss_info, + void *data) +{ + struct cnss_plat_data *plat_priv = cnss_info->plat_priv; + int ret = 0; + unsigned int timeout; + + set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + if (test_bit(CNSS_FW_READY, &plat_priv->driver_state)) { + cnss_pr_info("CNSS SDIO driver register in FW_Ready state"); + cnss_sdio_call_driver_probe(cnss_info); + } else if ((*cnss_get_qmi_bypass()) && + (cnss_info->al_client_handle->func)) { + cnss_pr_info("qmi bypass enabled"); + cnss_sdio_call_driver_probe(cnss_info); + } else { + cnss_pr_info("Wait for FW_Ready"); + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", + ret); + return ret; + } + + qcn_sdio_card_state(true); + timeout = cnss_get_qmi_timeout(); + if (timeout) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout << 1)); + } + } + return 0; +} + +int cnss_sdio_unregister_driver_hdlr(struct cnss_sdio_data *cnss_info) +{ + struct cnss_plat_data *plat_priv = cnss_info->plat_priv; + + set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + cnss_sdio_call_driver_remove(cnss_info); + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); + qcn_sdio_card_state(false); + cnss_power_off_device(plat_priv); + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + + cnss_info->ops = NULL; + return 0; +} + +int cnss_sdio_dev_powerup(struct cnss_sdio_data *cnss_info) +{ + struct cnss_plat_data *plat_priv = cnss_info->plat_priv; + int ret = 0; + unsigned int timeout; + + switch (plat_priv->device_id) { + case QCN7605_SDIO_DEVICE_ID: + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", + ret); + goto out; + } + + qcn_sdio_card_state(true); + timeout = cnss_get_qmi_timeout(); + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout >> 1)); + cnss_set_pin_connect_status(plat_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%lx\n", + plat_priv->device_id); + ret = -ENODEV; + } +out: + return ret; +} + +int cnss_sdio_dev_shutdown(struct cnss_sdio_data *cnss_info) +{ + struct cnss_plat_data *plat_priv = cnss_info->plat_priv; + + cnss_sdio_call_driver_remove(cnss_info); + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); + /*qcn_sdio_card_state(false);*/ + cnss_power_off_device(plat_priv); + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + + return 0; +} + +void cnss_sdio_fw_boot_timeout_hdlr(void *bus_priv) +{ + cnss_pr_err("Timeout waiting for FW ready indication\n"); +} + +static int cnss_sdio_probe(struct sdio_al_client_handle *pal_cli_handle) +{ + struct cnss_sdio_data *sdio_info = pal_cli_handle->client_priv; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct sdio_device_id *device_id; + + device_id = devm_kzalloc(&plat_priv->plat_dev->dev, + sizeof(struct sdio_device_id), + GFP_KERNEL); + device_id->class = pal_cli_handle->func->class; + device_id->vendor = pal_cli_handle->func->vendor; + device_id->device = pal_cli_handle->func->device; + sdio_info->device_id = device_id; + + if (pal_cli_handle->func) + cnss_pr_info("CNSS SDIO AL Probe for device Id: 0x%x\n", + pal_cli_handle->func->device); + clear_bit(CNSS_DEV_REMOVED, &plat_priv->driver_state); + plat_priv->device_id = pal_cli_handle->func->device; + cnss_register_subsys(sdio_info->plat_priv); + return 0; +} + +static int cnss_sdio_remove(struct sdio_al_client_handle *pal_cli_handle) +{ + struct cnss_sdio_data *sdio_info = pal_cli_handle->client_priv; + struct cnss_plat_data *plat_priv = sdio_info->plat_priv; + + if (pal_cli_handle->func) + cnss_pr_err( + "SDIO AL remove for device Id: 0x%x in driver state %lu\n", + pal_cli_handle->func->device, + plat_priv->driver_state); + + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); + set_bit(CNSS_DEV_REMOVED, &plat_priv->driver_state); + if (sdio_info->ops && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { + cnss_pr_err("Triggering driver_ops remove\n"); + sdio_info->ops->update_status( + sdio_info->al_client_handle->func, + CNSS_FW_DOWN); + sdio_info->ops->remove(sdio_info->al_client_handle->func); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + cnss_unregister_subsys(plat_priv); + devm_kfree(&plat_priv->plat_dev->dev, (void *)sdio_info->device_id); + + return 0; +} + +static void cnss_sdio_pm(struct sdio_al_client_handle *pal_cli_handle, + enum sdio_al_lpm_event event) +{ + struct cnss_sdio_data *sdio_info = pal_cli_handle->client_priv; + struct sdio_func *func = sdio_info->al_client_handle->func; + + if (!sdio_info->ops) { + cnss_pr_err("Ignore LPM event\n"); + return; + } + + if (event == LPM_ENTER) { + cnss_pr_info("Entering LPM\n"); + sdio_info->ops->suspend(&func->dev); + } else { + cnss_pr_info("Exiting LPM\n"); + sdio_info->ops->resume(&func->dev); + } +} + +struct sdio_al_client_data al_cli_data = { + .name = "SDIO_AL_CLIENT_WLAN", + .probe = cnss_sdio_probe, + .remove = cnss_sdio_remove, + .lpm_notify_cb = cnss_sdio_pm, +}; + +int cnss_sdio_init(struct cnss_plat_data *plat_priv) +{ + struct cnss_sdio_data *sdio_info; + struct sdio_al_client_handle *al_client_handle; + int ret = 0; + + if (sdio_al_is_ready()) { + cnss_pr_err("sdio_al not ready, defer probe\n"); + ret = -EPROBE_DEFER; + goto out; + } + + al_client_handle = sdio_al_register_client(&al_cli_data); + if (!al_client_handle) { + cnss_pr_err("sdio al registration failed!\n"); + ret = -ENODEV; + goto out; + } + sdio_info = devm_kzalloc(&plat_priv->plat_dev->dev, sizeof(*sdio_info), + GFP_KERNEL); + if (!sdio_info) { + ret = -ENOMEM; + goto out; + } + al_client_handle->client_priv = sdio_info; + sdio_info->al_client_handle = al_client_handle; + sdio_info->plat_priv = plat_priv; + plat_priv->bus_priv = sdio_info; + +out: + return ret; +} + +int cnss_sdio_deinit(struct cnss_plat_data *plat_priv) +{ + struct cnss_sdio_data *sdio_info = plat_priv->bus_priv; + + sdio_al_deregister_client(sdio_info->al_client_handle); + return 0; +} diff --git a/drivers/net/wireless/cnss2/sdio.h b/drivers/net/wireless/cnss2/sdio.h new file mode 100644 index 000000000000..1f782fa61f79 --- /dev/null +++ b/drivers/net/wireless/cnss2/sdio.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2016-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_SDIO_H +#define _CNSS_SDIO_H + +#include "main.h" +#ifdef CONFIG_SDIO_QCN +#include + +struct cnss_sdio_data { + struct cnss_plat_data *plat_priv; + struct sdio_al_client_handle *al_client_handle; + struct cnss_sdio_wlan_driver *ops; + struct sdio_device_id *device_id; + void *client_priv; +}; + +int cnss_sdio_init(struct cnss_plat_data *plat_priv); +int cnss_sdio_deinit(struct cnss_plat_data *plat_priv); +int cnss_sdio_register_driver_hdlr(struct cnss_sdio_data *sdio_info, + void *data); +int cnss_sdio_unregister_driver_hdlr(struct cnss_sdio_data *sdio_info); +int cnss_sdio_dev_powerup(struct cnss_sdio_data *cnss_info); +int cnss_sdio_dev_shutdown(struct cnss_sdio_data *cnss_info); +int cnss_sdio_call_driver_probe(struct cnss_sdio_data *sdio_priv); +int cnss_sdio_call_driver_remove(struct cnss_sdio_data *sdio_priv); +void cnss_sdio_fw_boot_timeout_hdlr(void *bus_priv); +#else +inline int cnss_sdio_init(void *plat_priv) +{ + return -EINVAL; +} + +inline int cnss_sdio_deinit(void *plat_priv) +{ + return -EINVAL; +} + +inline int cnss_sdio_register_driver_hdlr(void *sdio_info, + void *data) +{ + return -EINVAL; +} + +inline int cnss_sdio_unregister_driver_hdlr(void *sdio_info) +{ + return -EINVAL; +} + +inline int cnss_sdio_dev_powerup(void *cnss_info) +{ + return -EINVAL; +} + +inline int cnss_sdio_dev_shutdown(void *cnss_info) +{ + return -EINVAL; +} + +inline int cnss_sdio_call_driver_probe(void *sdio_priv) +{ + return -EINVAL; +} + +inline int cnss_sdio_call_driver_remove(void *sdio_priv) +{ + return -EINVAL; +} + +inline void cnss_sdio_fw_boot_timeout_hdlr(void *bus_priv) +{ + /* no op */ +} +#endif + +#endif diff --git a/include/net/cnss2.h b/include/net/cnss2.h index eb6908feb7ef..e57b65f97f0f 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -15,6 +15,10 @@ #include #include +#include +#ifdef CONFIG_SDIO_QCN +#include +#endif #define CNSS_MAX_FILE_NAME 20 #define CNSS_MAX_TIMESTAMP_LEN 32 @@ -111,6 +115,21 @@ struct cnss_usb_wlan_driver { const struct usb_device_id *id_table; }; +#ifdef CONFIG_SDIO_QCN +struct cnss_sdio_wlan_driver { + const char *name; + const struct sdio_device_id *id_table; + int (*probe)(struct sdio_func *, const struct sdio_device_id *); + void (*remove)(struct sdio_func *); + int (*reinit)(struct sdio_func *, const struct sdio_device_id *); + void (*shutdown)(struct sdio_func *); + void (*crash_shutdown)(struct sdio_func *); + int (*suspend)(struct device *); + int (*resume)(struct device *); + void (*update_status)(struct sdio_func *, uint32_t status); +}; +#endif + enum cnss_driver_status { CNSS_UNINITIALIZED, CNSS_INITIALIZED, @@ -251,5 +270,42 @@ 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); extern int cnss_usb_is_device_down(struct device *dev); +#ifdef CONFIG_SDIO_QCN +extern int cnss_sdio_wlan_register_driver(struct cnss_sdio_wlan_driver * + driver_ops); +extern void cnss_sdio_wlan_unregister_driver(struct cnss_sdio_wlan_driver * + driver_ops); +extern struct sdio_al_client_handle *cnss_sdio_wlan_get_sdio_al_client_handle( + struct sdio_func *func); +extern struct sdio_al_channel_handle *cnss_sdio_wlan_register_sdio_al_channel( + struct sdio_al_channel_data *channel_data); +extern void cnss_sdio_wlan_unregister_sdio_al_channel( + struct sdio_al_channel_handle *ch_handle); +#else +extern inline int cnss_sdio_wlan_register_driver(void *driver_ops) +{ + return 0; +} + +extern inline void cnss_sdio_wlan_unregister_driver(void *driver_ops) +{ + +} + +extern inline void *cnss_sdio_wlan_get_sdio_al_client_handle(void *func) +{ + return NULL; +} + +extern inline void *cnss_sdio_wlan_register_sdio_al_channel(void *channel_data) +{ + return NULL; +} + +extern inline void cnss_sdio_wlan_unregister_sdio_al_channel(void *ch_handle) +{ + +} +#endif #endif /* _NET_CNSS2_H */