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:
Jayachandran Sreekumaran 2018-09-03 17:47:39 +05:30
parent 2a61e30d44
commit e5b2b112b3
9 changed files with 620 additions and 17 deletions

View 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

View 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);

View 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 */

View 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;
}

View 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 {

View 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) {

View 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;
}

View 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

View file

@ -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 */