summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2019-10-14 04:48:43 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2019-10-14 04:48:42 -0700
commitc65b9585249986588b205fbed6eda20924423e72 (patch)
treeb87f3b9444b200d10fd08b2f29a6051cc7a68667 /drivers
parent4b981b7bf74d71edaf1f666a10d4ae8ea2eadc9a (diff)
parente5b2b112b3d9bc67afc37d1dd70d9bb0ac1b8b01 (diff)
Merge "cnss2: Add support for genoa sdio"
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/cnss2/Makefile1
-rw-r--r--drivers/net/wireless/cnss2/bus.c39
-rw-r--r--drivers/net/wireless/cnss2/bus.h3
-rw-r--r--drivers/net/wireless/cnss2/main.c38
-rw-r--r--drivers/net/wireless/cnss2/main.h1
-rw-r--r--drivers/net/wireless/cnss2/qmi.c3
-rw-r--r--drivers/net/wireless/cnss2/sdio.c410
-rw-r--r--drivers/net/wireless/cnss2/sdio.h86
8 files changed, 564 insertions, 17 deletions
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 baa469ad2f6a..4507f57f5bd8 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -282,7 +282,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;
@@ -293,7 +293,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) {
@@ -398,7 +399,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;
@@ -1288,7 +1289,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;
@@ -1308,7 +1310,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)
@@ -1467,6 +1469,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:
@@ -1803,6 +1806,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,
@@ -1823,12 +1838,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;
@@ -1921,6 +1938,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[] = {
@@ -1930,6 +1948,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);
@@ -1980,7 +2004,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 524a1fa0dcf4..969ff00b85ae 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -37,6 +37,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 <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
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 <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