OSDN Git Service

cnss2: Add support for genoa sdio
authorJayachandran Sreekumaran <jsreekum@codeaurora.org>
Mon, 3 Sep 2018 12:17:39 +0000 (17:47 +0530)
committerJayachandran Sreekumaran <jsreekum@codeaurora.org>
Mon, 14 Oct 2019 06:03:53 +0000 (11:33 +0530)
Add support for genoa sdio

Change-Id: Icf004d954ca0b508830412da6a381a7844f66efe
Signed-off-by: Jayachandran Sreekumaran <jsreekum@codeaurora.org>
drivers/net/wireless/cnss2/Makefile
drivers/net/wireless/cnss2/bus.c
drivers/net/wireless/cnss2/bus.h
drivers/net/wireless/cnss2/main.c
drivers/net/wireless/cnss2/main.h
drivers/net/wireless/cnss2/qmi.c
drivers/net/wireless/cnss2/sdio.c [new file with mode: 0644]
drivers/net/wireless/cnss2/sdio.h [new file with mode: 0644]
include/net/cnss2.h

index a4b177e..b1c8c5e 100644 (file)
@@ -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
index 088593f..e33eb64 100644 (file)
@@ -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);
index 1100fd6..30a93a0 100644 (file)
@@ -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 */
index 1da33c2..f70dfaa 100644 (file)
@@ -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;
        }
 
index 1b7c67b..aa197f0 100644 (file)
@@ -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 {
index 7945f59..489a28e 100644 (file)
@@ -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 (file)
index 0000000..f25a2b0
--- /dev/null
@@ -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 <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <net/cnss2.h>
+#include <linux/qcn_sdio_al.h>
+#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 (file)
index 0000000..1f782fa
--- /dev/null
@@ -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 <linux/qcn_sdio_al.h>
+
+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
index eb6908f..e57b65f 100644 (file)
 
 #include <linux/pci.h>
 #include <linux/usb.h>
+#include <linux/mmc/sdio_func.h>
+#ifdef CONFIG_SDIO_QCN
+#include <linux/qcn_sdio_al.h>
+#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 */