cnss2: Add support for genoa sdio
Add support for genoa sdio Change-Id: Icf004d954ca0b508830412da6a381a7844f66efe Signed-off-by: Jayachandran Sreekumaran <jsreekum@codeaurora.org>
This commit is contained in:
parent
2a61e30d44
commit
e5b2b112b3
9 changed files with 620 additions and 17 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
410
drivers/net/wireless/cnss2/sdio.c
Normal file
410
drivers/net/wireless/cnss2/sdio.c
Normal file
|
@ -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;
|
||||
}
|
86
drivers/net/wireless/cnss2/sdio.h
Normal file
86
drivers/net/wireless/cnss2/sdio.h
Normal file
|
@ -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
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
#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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue