icnss2: Add snapshot of icnss2 driver
This is a snapshot of the ICNSS2 driver and associated files as of msm-4.19 commit bba237ba ("icnss2: Add support to send host SMMU IOVA range to firmware"). Change-Id: Ie779f948b29652f86950e4d6e5609fe5480b7ffe Signed-off-by: Sandeep Singh <sandsing@codeaurora.org>
This commit is contained in:
parent
4027c201fd
commit
13f37630bd
@ -868,4 +868,6 @@ config QCOM_RTIC
|
||||
This option enables QCOM Real Time Integrity Check feature. This
|
||||
will trigger RTIC kernel MP.s (measurement parameters) generation
|
||||
during the kernel build.
|
||||
|
||||
source "drivers/soc/qcom/icnss2/Kconfig"
|
||||
endmenu
|
||||
|
@ -85,3 +85,4 @@ ifdef CONFIG_DEBUG_FS
|
||||
obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o
|
||||
endif
|
||||
obj-$(CONFIG_QTI_SYS_PM_VX) += sys_pm_vx.o
|
||||
obj-$(CONFIG_ICNSS2) += icnss2/
|
||||
|
37
drivers/soc/qcom/icnss2/Kconfig
Normal file
37
drivers/soc/qcom/icnss2/Kconfig
Normal file
@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config ICNSS2
|
||||
tristate "Platform driver for Wi-Fi Module module"
|
||||
select CNSS_UTILS
|
||||
help
|
||||
This module adds support for Q6 integrated WLAN connectivity
|
||||
subsystem with iWCN architecture. This module is responsible for
|
||||
communicating WLAN on/off control messages to FW over QMI channel.
|
||||
It is also responsible for handling WLAN PD restart notifications.
|
||||
|
||||
config ICNSS2_DEBUG
|
||||
bool "ICNSS2 Platform Driver Debug Support"
|
||||
depends on ICNSS2
|
||||
help
|
||||
Say 'Y' here to enable ICNSS driver debug support. Debug support
|
||||
primarily consists of logs consisting of information related to
|
||||
hardware register access and enabling BUG_ON for certain cases to aid
|
||||
the debugging.
|
||||
|
||||
config ICNSS2_QMI
|
||||
bool "ICNSS2 Platform Driver QMI support"
|
||||
depends on ICNSS2
|
||||
help
|
||||
Say 'Y' here to enable ICNSS QMI support. ICNSS driver will use
|
||||
QMI framework to communicate with WLAN FW. It will send coldboot
|
||||
handshake messages to WLAN FW, which includes hardware capabilities
|
||||
and configurations. It also send WLAN on/off control message to FW
|
||||
over QMI channel.
|
||||
|
||||
config CNSS_QCA6750
|
||||
bool "Enable ICNSS QCA6750 chipset specific changes"
|
||||
depends on ICNSS2
|
||||
help
|
||||
This enables the changes from WLAN host driver that are specific to
|
||||
CNSS QCA6750 chipset.
|
||||
These changes are needed to support the new hardware architecture
|
||||
for CNSS QCA6750 chipset.
|
9
drivers/soc/qcom/icnss2/Makefile
Normal file
9
drivers/soc/qcom/icnss2/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_ICNSS2) += icnss2.o
|
||||
|
||||
icnss2-y := main.o
|
||||
icnss2-y += debug.o
|
||||
icnss2-y += power.o
|
||||
icnss2-y += genl.o
|
||||
icnss2-$(CONFIG_ICNSS2_QMI) += qmi.o wlan_firmware_service_v01.o
|
783
drivers/soc/qcom/icnss2/debug.c
Normal file
783
drivers/soc/qcom/icnss2/debug.c
Normal file
@ -0,0 +1,783 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include "main.h"
|
||||
#include "debug.h"
|
||||
#include "qmi.h"
|
||||
#include "power.h"
|
||||
|
||||
void *icnss_ipc_log_context;
|
||||
void *icnss_ipc_log_long_context;
|
||||
|
||||
static ssize_t icnss_regwrite_write(struct file *fp,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct icnss_priv *priv =
|
||||
((struct seq_file *)fp->private_data)->private;
|
||||
char buf[64];
|
||||
char *sptr, *token;
|
||||
unsigned int len = 0;
|
||||
uint32_t reg_offset, mem_type, reg_val;
|
||||
const char *delim = " ";
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state) ||
|
||||
!test_bit(ICNSS_POWER_ON, &priv->state))
|
||||
return -EINVAL;
|
||||
|
||||
len = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, user_buf, len))
|
||||
return -EFAULT;
|
||||
|
||||
buf[len] = '\0';
|
||||
sptr = buf;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (!sptr)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, &mem_type))
|
||||
return -EINVAL;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (!sptr)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, ®_offset))
|
||||
return -EINVAL;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, ®_val))
|
||||
return -EINVAL;
|
||||
|
||||
ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
|
||||
sizeof(uint32_t),
|
||||
(uint8_t *)®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int icnss_regwrite_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct icnss_priv *priv = s->private;
|
||||
|
||||
seq_puts(s, "Usage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state))
|
||||
seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_regwrite_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, icnss_regwrite_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations icnss_regwrite_fops = {
|
||||
.read = seq_read,
|
||||
.write = icnss_regwrite_write,
|
||||
.open = icnss_regwrite_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
static int icnss_regread_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct icnss_priv *priv = s->private;
|
||||
|
||||
mutex_lock(&priv->dev_lock);
|
||||
if (!priv->diag_reg_read_buf) {
|
||||
seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state))
|
||||
seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
|
||||
|
||||
mutex_unlock(&priv->dev_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
|
||||
priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
|
||||
priv->diag_reg_read_len);
|
||||
|
||||
seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
|
||||
priv->diag_reg_read_len, false);
|
||||
|
||||
priv->diag_reg_read_len = 0;
|
||||
kfree(priv->diag_reg_read_buf);
|
||||
priv->diag_reg_read_buf = NULL;
|
||||
mutex_unlock(&priv->dev_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_regread_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, icnss_regread_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t icnss_reg_parse(const char __user *user_buf, size_t count,
|
||||
struct icnss_reg_info *reg_info_ptr)
|
||||
{
|
||||
char buf[64] = {0};
|
||||
char *sptr = NULL, *token = NULL;
|
||||
const char *delim = " ";
|
||||
unsigned int len = 0;
|
||||
|
||||
if (user_buf == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
len = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, user_buf, len))
|
||||
return -EFAULT;
|
||||
|
||||
buf[len] = '\0';
|
||||
sptr = buf;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (!sptr)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, ®_info_ptr->mem_type))
|
||||
return -EINVAL;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (!sptr)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, ®_info_ptr->reg_offset))
|
||||
return -EINVAL;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtou32(token, 0, ®_info_ptr->data_len))
|
||||
return -EINVAL;
|
||||
|
||||
if (reg_info_ptr->data_len == 0 ||
|
||||
reg_info_ptr->data_len > WLFW_MAX_DATA_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct icnss_priv *priv =
|
||||
((struct seq_file *)fp->private_data)->private;
|
||||
uint8_t *reg_buf = NULL;
|
||||
int ret = 0;
|
||||
struct icnss_reg_info reg_info;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state) ||
|
||||
!test_bit(ICNSS_POWER_ON, &priv->state))
|
||||
return -EINVAL;
|
||||
|
||||
ret = icnss_reg_parse(user_buf, count, ®_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&priv->dev_lock);
|
||||
kfree(priv->diag_reg_read_buf);
|
||||
priv->diag_reg_read_buf = NULL;
|
||||
|
||||
reg_buf = kzalloc(reg_info.data_len, GFP_KERNEL);
|
||||
if (!reg_buf) {
|
||||
mutex_unlock(&priv->dev_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = wlfw_athdiag_read_send_sync_msg(priv, reg_info.reg_offset,
|
||||
reg_info.mem_type,
|
||||
reg_info.data_len,
|
||||
reg_buf);
|
||||
if (ret) {
|
||||
kfree(reg_buf);
|
||||
mutex_unlock(&priv->dev_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->diag_reg_read_addr = reg_info.reg_offset;
|
||||
priv->diag_reg_read_mem_type = reg_info.mem_type;
|
||||
priv->diag_reg_read_len = reg_info.data_len;
|
||||
priv->diag_reg_read_buf = reg_buf;
|
||||
mutex_unlock(&priv->dev_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations icnss_regread_fops = {
|
||||
.read = seq_read,
|
||||
.write = icnss_regread_write,
|
||||
.open = icnss_regread_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct icnss_priv *priv =
|
||||
((struct seq_file *)fp->private_data)->private;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = kstrtou32_from_user(buf, count, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ret == 0)
|
||||
memset(&priv->stats, 0, sizeof(priv->stats));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
|
||||
struct icnss_priv *priv)
|
||||
{
|
||||
if (priv->stats.rejuvenate_ind) {
|
||||
seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
|
||||
seq_printf(s, "Number of Rejuvenations: %u\n",
|
||||
priv->stats.rejuvenate_ind);
|
||||
seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
|
||||
priv->cause_for_rejuvenation);
|
||||
seq_printf(s, "Requesting Sub-System: 0x%x\n",
|
||||
priv->requesting_sub_system);
|
||||
seq_printf(s, "Line Number: %u\n",
|
||||
priv->line_number);
|
||||
seq_printf(s, "Function Name: %s\n",
|
||||
priv->function_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
|
||||
seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
|
||||
"Free", "Enable", "Disable");
|
||||
for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
|
||||
seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
|
||||
priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
|
||||
priv->stats.ce_irqs[i].free,
|
||||
priv->stats.ce_irqs[i].enable,
|
||||
priv->stats.ce_irqs[i].disable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_stats_show_capability(struct seq_file *s,
|
||||
struct icnss_priv *priv)
|
||||
{
|
||||
if (test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
|
||||
seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
|
||||
seq_printf(s, "Chip family: 0x%x\n",
|
||||
priv->chip_info.chip_family);
|
||||
seq_printf(s, "Board ID: 0x%x\n", priv->board_id);
|
||||
seq_printf(s, "SOC Info: 0x%x\n", priv->soc_id);
|
||||
seq_printf(s, "Firmware Version: 0x%x\n",
|
||||
priv->fw_version_info.fw_version);
|
||||
seq_printf(s, "Firmware Build Timestamp: %s\n",
|
||||
priv->fw_version_info.fw_build_timestamp);
|
||||
seq_printf(s, "Firmware Build ID: %s\n",
|
||||
priv->fw_build_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_puts(s, "\n<----------------- Events stats ------------------->\n");
|
||||
seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
|
||||
for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
|
||||
seq_printf(s, "%24s %16u %16u\n",
|
||||
icnss_driver_event_to_str(i),
|
||||
priv->stats.events[i].posted,
|
||||
priv->stats.events[i].processed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
|
||||
{
|
||||
enum icnss_driver_state i;
|
||||
int skip = 0;
|
||||
unsigned long state;
|
||||
|
||||
seq_printf(s, "\nState: 0x%lx(", priv->state);
|
||||
for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
|
||||
|
||||
if (!(state & 0x1))
|
||||
continue;
|
||||
|
||||
if (skip++)
|
||||
seq_puts(s, " | ");
|
||||
|
||||
switch (i) {
|
||||
case ICNSS_WLFW_CONNECTED:
|
||||
seq_puts(s, "FW CONN");
|
||||
continue;
|
||||
case ICNSS_POWER_ON:
|
||||
seq_puts(s, "POWER ON");
|
||||
continue;
|
||||
case ICNSS_FW_READY:
|
||||
seq_puts(s, "FW READY");
|
||||
continue;
|
||||
case ICNSS_DRIVER_PROBED:
|
||||
seq_puts(s, "DRIVER PROBED");
|
||||
continue;
|
||||
case ICNSS_FW_TEST_MODE:
|
||||
seq_puts(s, "FW TEST MODE");
|
||||
continue;
|
||||
case ICNSS_PM_SUSPEND:
|
||||
seq_puts(s, "PM SUSPEND");
|
||||
continue;
|
||||
case ICNSS_PM_SUSPEND_NOIRQ:
|
||||
seq_puts(s, "PM SUSPEND NOIRQ");
|
||||
continue;
|
||||
case ICNSS_SSR_REGISTERED:
|
||||
seq_puts(s, "SSR REGISTERED");
|
||||
continue;
|
||||
case ICNSS_PDR_REGISTERED:
|
||||
seq_puts(s, "PDR REGISTERED");
|
||||
continue;
|
||||
case ICNSS_PD_RESTART:
|
||||
seq_puts(s, "PD RESTART");
|
||||
continue;
|
||||
case ICNSS_WLFW_EXISTS:
|
||||
seq_puts(s, "WLAN FW EXISTS");
|
||||
continue;
|
||||
case ICNSS_SHUTDOWN_DONE:
|
||||
seq_puts(s, "SHUTDOWN DONE");
|
||||
continue;
|
||||
case ICNSS_HOST_TRIGGERED_PDR:
|
||||
seq_puts(s, "HOST TRIGGERED PDR");
|
||||
continue;
|
||||
case ICNSS_FW_DOWN:
|
||||
seq_puts(s, "FW DOWN");
|
||||
continue;
|
||||
case ICNSS_DRIVER_UNLOADING:
|
||||
seq_puts(s, "DRIVER UNLOADING");
|
||||
continue;
|
||||
case ICNSS_REJUVENATE:
|
||||
seq_puts(s, "FW REJUVENATE");
|
||||
continue;
|
||||
case ICNSS_MODE_ON:
|
||||
seq_puts(s, "MODE ON DONE");
|
||||
continue;
|
||||
case ICNSS_BLOCK_SHUTDOWN:
|
||||
seq_puts(s, "BLOCK SHUTDOWN");
|
||||
continue;
|
||||
case ICNSS_PDR:
|
||||
seq_puts(s, "PDR TRIGGERED");
|
||||
continue;
|
||||
case ICNSS_DEL_SERVER:
|
||||
seq_puts(s, "DEL SERVER");
|
||||
}
|
||||
|
||||
seq_printf(s, "UNKNOWN-%d", i);
|
||||
}
|
||||
seq_puts(s, ")\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ICNSS_STATS_DUMP(_s, _priv, _x) \
|
||||
seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
|
||||
|
||||
static int icnss_stats_show(struct seq_file *s, void *data)
|
||||
{
|
||||
|
||||
struct icnss_priv *priv = s->private;
|
||||
|
||||
ICNSS_STATS_DUMP(s, priv, ind_register_req);
|
||||
ICNSS_STATS_DUMP(s, priv, ind_register_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, ind_register_err);
|
||||
ICNSS_STATS_DUMP(s, priv, cap_req);
|
||||
ICNSS_STATS_DUMP(s, priv, cap_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, cap_err);
|
||||
ICNSS_STATS_DUMP(s, priv, pin_connect_result);
|
||||
ICNSS_STATS_DUMP(s, priv, cfg_req);
|
||||
ICNSS_STATS_DUMP(s, priv, cfg_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, cfg_req_err);
|
||||
ICNSS_STATS_DUMP(s, priv, mode_req);
|
||||
ICNSS_STATS_DUMP(s, priv, mode_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, mode_req_err);
|
||||
ICNSS_STATS_DUMP(s, priv, ini_req);
|
||||
ICNSS_STATS_DUMP(s, priv, ini_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, ini_req_err);
|
||||
ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
|
||||
ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
|
||||
ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
|
||||
ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
|
||||
|
||||
seq_puts(s, "\n<------------------ PM stats ------------------->\n");
|
||||
ICNSS_STATS_DUMP(s, priv, pm_suspend);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_resume);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_resume_err);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
|
||||
ICNSS_STATS_DUMP(s, priv, pm_relax);
|
||||
|
||||
if (priv->device_id != WCN6750_DEVICE_ID) {
|
||||
seq_puts(s, "\n<------------------ MSA stats ------------------->\n");
|
||||
ICNSS_STATS_DUMP(s, priv, msa_info_req);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_info_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_info_err);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_ready_req);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_ready_err);
|
||||
ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
|
||||
|
||||
seq_puts(s, "\n<------------------ Rejuvenate stats ------------------->\n");
|
||||
ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
|
||||
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
|
||||
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
|
||||
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
|
||||
icnss_stats_show_rejuvenate_info(s, priv);
|
||||
|
||||
}
|
||||
|
||||
icnss_stats_show_irqs(s, priv);
|
||||
|
||||
icnss_stats_show_capability(s, priv);
|
||||
|
||||
icnss_stats_show_events(s, priv);
|
||||
|
||||
icnss_stats_show_state(s, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, icnss_stats_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations icnss_stats_fops = {
|
||||
.read = seq_read,
|
||||
.write = icnss_stats_write,
|
||||
.release = single_release,
|
||||
.open = icnss_stats_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
static int icnss_fw_debug_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct icnss_priv *priv = s->private;
|
||||
|
||||
seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
|
||||
|
||||
seq_puts(s, "\nCMD: test_mode\n");
|
||||
seq_puts(s, " VAL: 0 (Test mode disable)\n");
|
||||
seq_puts(s, " VAL: 1 (WLAN FW test)\n");
|
||||
seq_puts(s, " VAL: 2 (CCPM test)\n");
|
||||
seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
|
||||
seq_puts(s, " VAL: 4 (allow recursive recovery)\n");
|
||||
seq_puts(s, " VAL: 3 (Disallow recursive recovery)\n");
|
||||
|
||||
seq_puts(s, "\nCMD: dynamic_feature_mask\n");
|
||||
seq_puts(s, " VAL: (64 bit feature mask)\n");
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
seq_puts(s, "Machine mode is running, can't run test_mode!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
seq_puts(s, "test_mode is running, can't run test_mode!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
seq_puts(s, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
icnss_pr_err("Test mode not started, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
|
||||
|
||||
ret = icnss_hw_power_off(priv);
|
||||
|
||||
clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_test_mode_fw_test(struct icnss_priv *priv,
|
||||
enum icnss_driver_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(ICNSS_FW_READY, &priv->state)) {
|
||||
icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
|
||||
icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
|
||||
icnss_pr_err("Test mode already started, state: 0x%lx\n",
|
||||
priv->state);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = icnss_hw_power_on(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
set_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
|
||||
if (ret)
|
||||
goto power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
power_off:
|
||||
icnss_hw_power_off(priv);
|
||||
clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t icnss_fw_debug_write(struct file *fp,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct icnss_priv *priv =
|
||||
((struct seq_file *)fp->private_data)->private;
|
||||
char buf[64];
|
||||
char *sptr, *token;
|
||||
unsigned int len = 0;
|
||||
char *cmd;
|
||||
uint64_t val;
|
||||
const char *delim = " ";
|
||||
int ret = 0;
|
||||
|
||||
len = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, user_buf, len))
|
||||
return -EINVAL;
|
||||
|
||||
buf[len] = '\0';
|
||||
sptr = buf;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
if (!sptr)
|
||||
return -EINVAL;
|
||||
cmd = token;
|
||||
|
||||
token = strsep(&sptr, delim);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
if (kstrtou64(token, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(cmd, "test_mode") == 0) {
|
||||
switch (val) {
|
||||
case 0:
|
||||
ret = icnss_test_mode_fw_test_off(priv);
|
||||
break;
|
||||
case 1:
|
||||
ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
|
||||
break;
|
||||
case 2:
|
||||
ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
|
||||
break;
|
||||
case 3:
|
||||
ret = icnss_trigger_recovery(&priv->pdev->dev);
|
||||
break;
|
||||
case 4:
|
||||
icnss_allow_recursive_recovery(&priv->pdev->dev);
|
||||
break;
|
||||
case 5:
|
||||
icnss_disallow_recursive_recovery(&priv->pdev->dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
|
||||
ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int icnss_fw_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, icnss_fw_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations icnss_fw_debug_fops = {
|
||||
.read = seq_read,
|
||||
.write = icnss_fw_debug_write,
|
||||
.release = single_release,
|
||||
.open = icnss_fw_debug_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
#ifdef CONFIG_ICNSS2_DEBUG
|
||||
int icnss_debugfs_create(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *root_dentry;
|
||||
|
||||
root_dentry = debugfs_create_dir("icnss", NULL);
|
||||
|
||||
if (IS_ERR(root_dentry)) {
|
||||
ret = PTR_ERR(root_dentry);
|
||||
icnss_pr_err("Unable to create debugfs %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->root_dentry = root_dentry;
|
||||
|
||||
debugfs_create_file("fw_debug", 0600, root_dentry, priv,
|
||||
&icnss_fw_debug_fops);
|
||||
debugfs_create_file("stats", 0600, root_dentry, priv,
|
||||
&icnss_stats_fops);
|
||||
debugfs_create_file("reg_read", 0600, root_dentry, priv,
|
||||
&icnss_regread_fops);
|
||||
debugfs_create_file("reg_write", 0600, root_dentry, priv,
|
||||
&icnss_regwrite_fops);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int icnss_debugfs_create(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dentry *root_dentry;
|
||||
|
||||
root_dentry = debugfs_create_dir("icnss", NULL);
|
||||
|
||||
if (IS_ERR(root_dentry)) {
|
||||
ret = PTR_ERR(root_dentry);
|
||||
icnss_pr_err("Unable to create debugfs %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->root_dentry = root_dentry;
|
||||
|
||||
debugfs_create_file("stats", 0600, root_dentry, priv,
|
||||
&icnss_stats_fops);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void icnss_debugfs_destroy(struct icnss_priv *priv)
|
||||
{
|
||||
debugfs_remove_recursive(priv->root_dentry);
|
||||
}
|
||||
|
||||
void icnss_debug_init(void)
|
||||
{
|
||||
icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
|
||||
"icnss", 0);
|
||||
if (!icnss_ipc_log_context)
|
||||
icnss_pr_err("Unable to create log context\n");
|
||||
|
||||
icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
|
||||
"icnss_long", 0);
|
||||
if (!icnss_ipc_log_long_context)
|
||||
icnss_pr_err("Unable to create log long context\n");
|
||||
}
|
||||
|
||||
void icnss_debug_deinit(void)
|
||||
{
|
||||
if (icnss_ipc_log_context) {
|
||||
ipc_log_context_destroy(icnss_ipc_log_context);
|
||||
icnss_ipc_log_context = NULL;
|
||||
}
|
||||
|
||||
if (icnss_ipc_log_long_context) {
|
||||
ipc_log_context_destroy(icnss_ipc_log_long_context);
|
||||
icnss_ipc_log_long_context = NULL;
|
||||
}
|
||||
}
|
107
drivers/soc/qcom/icnss2/debug.h
Normal file
107
drivers/soc/qcom/icnss2/debug.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ICNSS_DEBUG_H
|
||||
#define _ICNSS_DEBUG_H
|
||||
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#define NUM_LOG_PAGES 10
|
||||
#define NUM_LOG_LONG_PAGES 4
|
||||
|
||||
extern void *icnss_ipc_log_context;
|
||||
extern void *icnss_ipc_log_long_context;
|
||||
|
||||
#define icnss_ipc_log_string(_x...) \
|
||||
ipc_log_string(icnss_ipc_log_context, _x)
|
||||
|
||||
#define icnss_ipc_log_long_string(_x...) \
|
||||
ipc_log_string(icnss_ipc_log_long_context, _x)
|
||||
|
||||
#define icnss_pr_err(_fmt, ...) do { \
|
||||
printk("%s" pr_fmt(_fmt), KERN_ERR, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define icnss_pr_warn(_fmt, ...) do { \
|
||||
printk("%s" pr_fmt(_fmt), KERN_WARNING, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define icnss_pr_info(_fmt, ...) do { \
|
||||
printk("%s" pr_fmt(_fmt), KERN_INFO, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#if defined(CONFIG_DYNAMIC_DEBUG)
|
||||
#define icnss_pr_dbg(_fmt, ...) do { \
|
||||
pr_debug(_fmt, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string(pr_fmt(_fmt), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define icnss_pr_vdbg(_fmt, ...) do { \
|
||||
pr_debug(_fmt, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_long_string(pr_fmt(_fmt), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#elif defined(DEBUG)
|
||||
#define icnss_pr_dbg(_fmt, ...) do { \
|
||||
printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define icnss_pr_vdbg(_fmt, ...) do { \
|
||||
printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define icnss_pr_dbg(_fmt, ...) do { \
|
||||
no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define icnss_pr_vdbg(_fmt, ...) do { \
|
||||
no_printk("%s" pr_fmt(_fmt), KERN_DEBUG, ##__VA_ARGS__); \
|
||||
icnss_ipc_log_long_string("%s" pr_fmt(_fmt), "", \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ICNSS2_DEBUG
|
||||
#define ICNSS_ASSERT(_condition) do { \
|
||||
if (!(_condition)) { \
|
||||
icnss_pr_err("ASSERT at line %d\n", __LINE__); \
|
||||
BUG(); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define ICNSS_ASSERT(_condition) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define icnss_fatal_err(_fmt, ...) \
|
||||
icnss_pr_err("fatal: "_fmt, ##__VA_ARGS__)
|
||||
|
||||
enum icnss_debug_quirks {
|
||||
HW_ALWAYS_ON,
|
||||
HW_DEBUG_ENABLE,
|
||||
SKIP_QMI,
|
||||
RECOVERY_DISABLE,
|
||||
SSR_ONLY,
|
||||
PDR_ONLY,
|
||||
ENABLE_DAEMON_SUPPORT,
|
||||
FW_REJUVENATE_ENABLE,
|
||||
};
|
||||
|
||||
void icnss_debug_init(void);
|
||||
void icnss_debug_deinit(void);
|
||||
int icnss_debugfs_create(struct icnss_priv *priv);
|
||||
void icnss_debugfs_destroy(struct icnss_priv *priv);
|
||||
#endif /* _ICNSS_DEBUG_H */
|
213
drivers/soc/qcom/icnss2/genl.c
Normal file
213
drivers/soc/qcom/icnss2/genl.c
Normal file
@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) "cnss_genl: " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define ICNSS_GENL_FAMILY_NAME "cnss-genl"
|
||||
#define ICNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp"
|
||||
#define ICNSS_GENL_VERSION 1
|
||||
#define ICNSS_GENL_DATA_LEN_MAX (15 * 1024)
|
||||
#define ICNSS_GENL_STR_LEN_MAX 16
|
||||
|
||||
enum {
|
||||
ICNSS_GENL_ATTR_MSG_UNSPEC,
|
||||
ICNSS_GENL_ATTR_MSG_TYPE,
|
||||
ICNSS_GENL_ATTR_MSG_FILE_NAME,
|
||||
ICNSS_GENL_ATTR_MSG_TOTAL_SIZE,
|
||||
ICNSS_GENL_ATTR_MSG_SEG_ID,
|
||||
ICNSS_GENL_ATTR_MSG_END,
|
||||
ICNSS_GENL_ATTR_MSG_DATA_LEN,
|
||||
ICNSS_GENL_ATTR_MSG_DATA,
|
||||
__ICNSS_GENL_ATTR_MAX,
|
||||
};
|
||||
|
||||
#define ICNSS_GENL_ATTR_MAX (__ICNSS_GENL_ATTR_MAX - 1)
|
||||
|
||||
enum {
|
||||
ICNSS_GENL_CMD_UNSPEC,
|
||||
ICNSS_GENL_CMD_MSG,
|
||||
__ICNSS_GENL_CMD_MAX,
|
||||
};
|
||||
|
||||
#define ICNSS_GENL_CMD_MAX (__ICNSS_GENL_CMD_MAX - 1)
|
||||
|
||||
static struct nla_policy icnss_genl_msg_policy[ICNSS_GENL_ATTR_MAX + 1] = {
|
||||
[ICNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 },
|
||||
[ICNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = ICNSS_GENL_STR_LEN_MAX },
|
||||
[ICNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 },
|
||||
[ICNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 },
|
||||
[ICNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 },
|
||||
[ICNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 },
|
||||
[ICNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY,
|
||||
.len = ICNSS_GENL_DATA_LEN_MAX },
|
||||
};
|
||||
|
||||
static int icnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct genl_ops icnss_genl_ops[] = {
|
||||
{
|
||||
.cmd = ICNSS_GENL_CMD_MSG,
|
||||
.doit = icnss_genl_process_msg,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group icnss_genl_mcast_grp[] = {
|
||||
{
|
||||
.name = ICNSS_GENL_MCAST_GROUP_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family icnss_genl_family = {
|
||||
.id = 0,
|
||||
.hdrsize = 0,
|
||||
.name = ICNSS_GENL_FAMILY_NAME,
|
||||
.version = ICNSS_GENL_VERSION,
|
||||
.maxattr = ICNSS_GENL_ATTR_MAX,
|
||||
.policy = icnss_genl_msg_policy,
|
||||
.module = THIS_MODULE,
|
||||
.ops = icnss_genl_ops,
|
||||
.n_ops = ARRAY_SIZE(icnss_genl_ops),
|
||||
.mcgrps = icnss_genl_mcast_grp,
|
||||
.n_mcgrps = ARRAY_SIZE(icnss_genl_mcast_grp),
|
||||
};
|
||||
|
||||
static int icnss_genl_send_data(u8 type, char *file_name, u32 total_size,
|
||||
u32 seg_id, u8 end, u32 data_len, u8 *msg_buff)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
void *msg_header = NULL;
|
||||
int ret = 0;
|
||||
char filename[ICNSS_GENL_STR_LEN_MAX + 1];
|
||||
|
||||
icnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n",
|
||||
type, file_name, total_size, seg_id, end, data_len);
|
||||
|
||||
if (!file_name)
|
||||
strlcpy(filename, "default", sizeof(filename));
|
||||
else
|
||||
strlcpy(filename, file_name, sizeof(filename));
|
||||
|
||||
skb = genlmsg_new(NLMSG_HDRLEN +
|
||||
nla_total_size(sizeof(type)) +
|
||||
nla_total_size(strlen(filename) + 1) +
|
||||
nla_total_size(sizeof(total_size)) +
|
||||
nla_total_size(sizeof(seg_id)) +
|
||||
nla_total_size(sizeof(end)) +
|
||||
nla_total_size(sizeof(data_len)) +
|
||||
nla_total_size(data_len), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
msg_header = genlmsg_put(skb, 0, 0,
|
||||
&icnss_genl_family, 0,
|
||||
ICNSS_GENL_CMD_MSG);
|
||||
if (!msg_header) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_TYPE, type);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put_string(skb, ICNSS_GENL_ATTR_MSG_FILE_NAME, filename);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_SEG_ID, seg_id);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_END, end);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_DATA_LEN, data_len);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = nla_put(skb, ICNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
genlmsg_end(skb, msg_header);
|
||||
ret = genlmsg_multicast(&icnss_genl_family, skb, 0, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
icnss_pr_err("Fail to send genl msg: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
fail:
|
||||
icnss_pr_err("Fail to generate genl msg: %d\n", ret);
|
||||
if (skb)
|
||||
nlmsg_free(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int icnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 *msg_buff = buff;
|
||||
u32 remaining = total_size;
|
||||
u32 seg_id = 0;
|
||||
u32 data_len = 0;
|
||||
u8 end = 0;
|
||||
u8 retry;
|
||||
|
||||
icnss_pr_dbg("type: %u, total_size: %x\n", type, total_size);
|
||||
|
||||
while (remaining) {
|
||||
if (remaining > ICNSS_GENL_DATA_LEN_MAX) {
|
||||
data_len = ICNSS_GENL_DATA_LEN_MAX;
|
||||
} else {
|
||||
data_len = remaining;
|
||||
end = 1;
|
||||
}
|
||||
|
||||
for (retry = 0; retry < 2; retry++) {
|
||||
ret = icnss_genl_send_data(type, file_name, total_size,
|
||||
seg_id, end, data_len,
|
||||
msg_buff);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
icnss_pr_err("fail to send genl data, ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
remaining -= data_len;
|
||||
msg_buff += data_len;
|
||||
seg_id++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int icnss_genl_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = genl_register_family(&icnss_genl_family);
|
||||
if (ret != 0)
|
||||
icnss_pr_err("genl_register_family fail: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void icnss_genl_exit(void)
|
||||
{
|
||||
genl_unregister_family(&icnss_genl_family);
|
||||
}
|
17
drivers/soc/qcom/icnss2/genl.h
Normal file
17
drivers/soc/qcom/icnss2/genl.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#ifndef __ICNSS_GENL_H__
|
||||
#define __ICNSS_GENL_H__
|
||||
|
||||
enum icnss_genl_msg_type {
|
||||
ICNSS_GENL_MSG_TYPE_UNSPEC,
|
||||
ICNSS_GENL_MSG_TYPE_QDSS,
|
||||
};
|
||||
|
||||
int icnss_genl_init(void);
|
||||
void icnss_genl_exit(void);
|
||||
int icnss_genl_send_msg(void *buff, u8 type,
|
||||
char *file_name, u32 total_size);
|
||||
|
||||
#endif
|
3397
drivers/soc/qcom/icnss2/main.c
Normal file
3397
drivers/soc/qcom/icnss2/main.c
Normal file
File diff suppressed because it is too large
Load Diff
407
drivers/soc/qcom/icnss2/main.h
Normal file
407
drivers/soc/qcom/icnss2/main.h
Normal file
@ -0,0 +1,407 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <linux/adc-tm-clients.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include <soc/qcom/icnss2.h>
|
||||
#include <soc/qcom/service-locator.h>
|
||||
#include <soc/qcom/service-notifier.h>
|
||||
#include "wlan_firmware_service_v01.h"
|
||||
|
||||
#define WCN6750_DEVICE_ID 0x6750
|
||||
#define ADRASTEA_DEVICE_ID 0xabcd
|
||||
#define QMI_WLFW_MAX_NUM_MEM_SEG 32
|
||||
|
||||
extern uint64_t dynamic_feature_mask;
|
||||
|
||||
enum icnss_bdf_type {
|
||||
ICNSS_BDF_BIN,
|
||||
ICNSS_BDF_ELF,
|
||||
ICNSS_BDF_REGDB = 4,
|
||||
ICNSS_BDF_DUMMY = 255,
|
||||
};
|
||||
|
||||
struct icnss_control_params {
|
||||
unsigned long quirks;
|
||||
unsigned int qmi_timeout;
|
||||
unsigned int bdf_type;
|
||||
};
|
||||
|
||||
enum icnss_driver_event_type {
|
||||
ICNSS_DRIVER_EVENT_SERVER_ARRIVE,
|
||||
ICNSS_DRIVER_EVENT_SERVER_EXIT,
|
||||
ICNSS_DRIVER_EVENT_FW_READY_IND,
|
||||
ICNSS_DRIVER_EVENT_REGISTER_DRIVER,
|
||||
ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
|
||||
ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
|
||||
ICNSS_DRIVER_EVENT_FW_EARLY_CRASH_IND,
|
||||
ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
|
||||
ICNSS_DRIVER_EVENT_IDLE_RESTART,
|
||||
ICNSS_DRIVER_EVENT_FW_INIT_DONE_IND,
|
||||
ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
|
||||
ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE,
|
||||
ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
|
||||
ICNSS_DRIVER_EVENT_MAX,
|
||||
};
|
||||
|
||||
enum icnss_soc_wake_event_type {
|
||||
ICNSS_SOC_WAKE_REQUEST_EVENT,
|
||||
ICNSS_SOC_WAKE_RELEASE_EVENT,
|
||||
ICNSS_SOC_WAKE_EVENT_MAX,
|
||||
};
|
||||
|
||||
struct icnss_event_server_arrive_data {
|
||||
unsigned int node;
|
||||
unsigned int port;
|
||||
};
|
||||
|
||||
struct icnss_event_pd_service_down_data {
|
||||
bool crashed;
|
||||
bool fw_rejuvenate;
|
||||
};
|
||||
|
||||
struct icnss_driver_event {
|
||||
struct list_head list;
|
||||
enum icnss_driver_event_type type;
|
||||
bool sync;
|
||||
struct completion complete;
|
||||
int ret;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct icnss_soc_wake_event {
|
||||
struct list_head list;
|
||||
enum icnss_soc_wake_event_type type;
|
||||
bool sync;
|
||||
struct completion complete;
|
||||
int ret;
|
||||
void *data;
|
||||
};
|
||||
|
||||
enum icnss_driver_state {
|
||||
ICNSS_WLFW_CONNECTED,
|
||||
ICNSS_POWER_ON,
|
||||
ICNSS_FW_READY,
|
||||
ICNSS_DRIVER_PROBED,
|
||||
ICNSS_FW_TEST_MODE,
|
||||
ICNSS_PM_SUSPEND,
|
||||
ICNSS_PM_SUSPEND_NOIRQ,
|
||||
ICNSS_SSR_REGISTERED,
|
||||
ICNSS_PDR_REGISTERED,
|
||||
ICNSS_PD_RESTART,
|
||||
ICNSS_WLFW_EXISTS,
|
||||
ICNSS_SHUTDOWN_DONE,
|
||||
ICNSS_HOST_TRIGGERED_PDR,
|
||||
ICNSS_FW_DOWN,
|
||||
ICNSS_DRIVER_UNLOADING,
|
||||
ICNSS_REJUVENATE,
|
||||
ICNSS_MODE_ON,
|
||||
ICNSS_BLOCK_SHUTDOWN,
|
||||
ICNSS_PDR,
|
||||
ICNSS_DEL_SERVER,
|
||||
};
|
||||
|
||||
struct ce_irq_list {
|
||||
int irq;
|
||||
irqreturn_t (*handler)(int irq, void *priv);
|
||||
};
|
||||
|
||||
struct icnss_vreg_cfg {
|
||||
const char *name;
|
||||
u32 min_uv;
|
||||
u32 max_uv;
|
||||
u32 load_ua;
|
||||
u32 delay_us;
|
||||
u32 need_unvote;
|
||||
bool required;
|
||||
};
|
||||
|
||||
struct icnss_vreg_info {
|
||||
struct list_head list;
|
||||
struct regulator *reg;
|
||||
struct icnss_vreg_cfg cfg;
|
||||
u32 enabled;
|
||||
};
|
||||
|
||||
enum icnss_vreg_type {
|
||||
ICNSS_VREG_PRIM,
|
||||
};
|
||||
struct icnss_clk_cfg {
|
||||
const char *name;
|
||||
u32 freq;
|
||||
u32 required;
|
||||
};
|
||||
|
||||
struct icnss_clk_info {
|
||||
struct list_head list;
|
||||
struct clk *clk;
|
||||
struct icnss_clk_cfg cfg;
|
||||
u32 enabled;
|
||||
};
|
||||
|
||||
struct icnss_fw_mem {
|
||||
size_t size;
|
||||
void *va;
|
||||
phys_addr_t pa;
|
||||
u8 valid;
|
||||
u32 type;
|
||||
unsigned long attrs;
|
||||
};
|
||||
|
||||
struct icnss_stats {
|
||||
struct {
|
||||
uint32_t posted;
|
||||
uint32_t processed;
|
||||
} events[ICNSS_DRIVER_EVENT_MAX];
|
||||
|
||||
struct {
|
||||
u32 posted;
|
||||
u32 processed;
|
||||
} soc_wake_events[ICNSS_SOC_WAKE_EVENT_MAX];
|
||||
|
||||
struct {
|
||||
uint32_t request;
|
||||
uint32_t free;
|
||||
uint32_t enable;
|
||||
uint32_t disable;
|
||||
} ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
|
||||
|
||||
struct {
|
||||
uint32_t pdr_fw_crash;
|
||||
uint32_t pdr_host_error;
|
||||
uint32_t root_pd_crash;
|
||||
uint32_t root_pd_shutdown;
|
||||
} recovery;
|
||||
|
||||
uint32_t pm_suspend;
|
||||
uint32_t pm_suspend_err;
|
||||
uint32_t pm_resume;
|
||||
uint32_t pm_resume_err;
|
||||
uint32_t pm_suspend_noirq;
|
||||
uint32_t pm_suspend_noirq_err;
|
||||
uint32_t pm_resume_noirq;
|
||||
uint32_t pm_resume_noirq_err;
|
||||
uint32_t pm_stay_awake;
|
||||
uint32_t pm_relax;
|
||||
|
||||
uint32_t ind_register_req;
|
||||
uint32_t ind_register_resp;
|
||||
uint32_t ind_register_err;
|
||||
uint32_t msa_info_req;
|
||||
uint32_t msa_info_resp;
|
||||
uint32_t msa_info_err;
|
||||
uint32_t msa_ready_req;
|
||||
uint32_t msa_ready_resp;
|
||||
uint32_t msa_ready_err;
|
||||
uint32_t msa_ready_ind;
|
||||
uint32_t cap_req;
|
||||
uint32_t cap_resp;
|
||||
uint32_t cap_err;
|
||||
uint32_t pin_connect_result;
|
||||
uint32_t cfg_req;
|
||||
uint32_t cfg_resp;
|
||||
uint32_t cfg_req_err;
|
||||
uint32_t mode_req;
|
||||
uint32_t mode_resp;
|
||||
uint32_t mode_req_err;
|
||||
uint32_t ini_req;
|
||||
uint32_t ini_resp;
|
||||
uint32_t ini_req_err;
|
||||
u32 rejuvenate_ind;
|
||||
uint32_t rejuvenate_ack_req;
|
||||
uint32_t rejuvenate_ack_resp;
|
||||
uint32_t rejuvenate_ack_err;
|
||||
uint32_t vbatt_req;
|
||||
uint32_t vbatt_resp;
|
||||
uint32_t vbatt_req_err;
|
||||
uint32_t device_info_req;
|
||||
uint32_t device_info_resp;
|
||||
uint32_t device_info_err;
|
||||
u32 exit_power_save_req;
|
||||
u32 exit_power_save_resp;
|
||||
u32 exit_power_save_err;
|
||||
u32 soc_wake_req;
|
||||
u32 soc_wake_resp;
|
||||
u32 soc_wake_err;
|
||||
};
|
||||
|
||||
#define WLFW_MAX_TIMESTAMP_LEN 32
|
||||
#define WLFW_MAX_BUILD_ID_LEN 128
|
||||
#define WLFW_MAX_NUM_MEMORY_REGIONS 2
|
||||
#define WLFW_FUNCTION_NAME_LEN 129
|
||||
#define WLFW_MAX_DATA_SIZE 6144
|
||||
#define WLFW_MAX_STR_LEN 16
|
||||
#define WLFW_MAX_NUM_CE 12
|
||||
#define WLFW_MAX_NUM_SVC 24
|
||||
#define WLFW_MAX_NUM_SHADOW_REG 24
|
||||
#define WLFW_MAX_HANG_EVENT_DATA_SIZE 400
|
||||
|
||||
struct service_notifier_context {
|
||||
void *handle;
|
||||
uint32_t instance_id;
|
||||
char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
|
||||
};
|
||||
|
||||
struct wlfw_rf_chip_info {
|
||||
uint32_t chip_id;
|
||||
uint32_t chip_family;
|
||||
};
|
||||
|
||||
struct wlfw_rf_board_info {
|
||||
uint32_t board_id;
|
||||
};
|
||||
|
||||
struct wlfw_fw_version_info {
|
||||
uint32_t fw_version;
|
||||
char fw_build_timestamp[WLFW_MAX_TIMESTAMP_LEN + 1];
|
||||
};
|
||||
|
||||
struct icnss_mem_region_info {
|
||||
uint64_t reg_addr;
|
||||
uint32_t size;
|
||||
uint8_t secure_flag;
|
||||
};
|
||||
|
||||
struct icnss_msi_user {
|
||||
char *name;
|
||||
int num_vectors;
|
||||
u32 base_vector;
|
||||
};
|
||||
|
||||
struct icnss_msi_config {
|
||||
int total_vectors;
|
||||
int total_users;
|
||||
struct icnss_msi_user *users;
|
||||
};
|
||||
|
||||
struct icnss_priv {
|
||||
uint32_t magic;
|
||||
struct platform_device *pdev;
|
||||
struct icnss_driver_ops *ops;
|
||||
struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
|
||||
struct list_head vreg_list;
|
||||
struct list_head clk_list;
|
||||
unsigned long device_id;
|
||||
struct icnss_msi_config *msi_config;
|
||||
u32 msi_base_data;
|
||||
struct icnss_control_params ctrl_params;
|
||||
u8 cal_done;
|
||||
u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
|
||||
u32 srng_irqs[IWCN_MAX_IRQ_REGISTRATIONS];
|
||||
phys_addr_t mem_base_pa;
|
||||
void __iomem *mem_base_va;
|
||||
u32 mem_base_size;
|
||||
struct iommu_domain *iommu_domain;
|
||||
dma_addr_t smmu_iova_start;
|
||||
size_t smmu_iova_len;
|
||||
dma_addr_t smmu_iova_ipa_start;
|
||||
dma_addr_t smmu_iova_ipa_current;
|
||||
size_t smmu_iova_ipa_len;
|
||||
struct qmi_handle qmi;
|
||||
struct list_head event_list;
|
||||
struct list_head soc_wake_msg_list;
|
||||
spinlock_t event_lock;
|
||||
spinlock_t soc_wake_msg_lock;
|
||||
struct work_struct event_work;
|
||||
struct work_struct fw_recv_msg_work;
|
||||
struct work_struct soc_wake_msg_work;
|
||||
struct workqueue_struct *event_wq;
|
||||
struct workqueue_struct *soc_wake_wq;
|
||||
phys_addr_t msa_pa;
|
||||
phys_addr_t msi_addr_pa;
|
||||
dma_addr_t msi_addr_iova;
|
||||
uint32_t msa_mem_size;
|
||||
void *msa_va;
|
||||
unsigned long state;
|
||||
struct wlfw_rf_chip_info chip_info;
|
||||
uint32_t board_id;
|
||||
uint32_t soc_id;
|
||||
struct wlfw_fw_version_info fw_version_info;
|
||||
char fw_build_id[WLFW_MAX_BUILD_ID_LEN + 1];
|
||||
u32 pwr_pin_result;
|
||||
u32 phy_io_pin_result;
|
||||
u32 rf_pin_result;
|
||||
uint32_t nr_mem_region;
|
||||
struct icnss_mem_region_info
|
||||
mem_region[WLFW_MAX_NUM_MEMORY_REGIONS];
|
||||
struct dentry *root_dentry;
|
||||
spinlock_t on_off_lock;
|
||||
struct icnss_stats stats;
|
||||
struct work_struct service_notifier_work;
|
||||
struct service_notifier_context *service_notifier;
|
||||
struct notifier_block service_notifier_nb;
|
||||
int total_domains;
|
||||
struct notifier_block get_service_nb;
|
||||
void *modem_notify_handler;
|
||||
struct notifier_block modem_ssr_nb;
|
||||
uint32_t diag_reg_read_addr;
|
||||
uint32_t diag_reg_read_mem_type;
|
||||
uint32_t diag_reg_read_len;
|
||||
uint8_t *diag_reg_read_buf;
|
||||
atomic_t pm_count;
|
||||
struct ramdump_device *msa0_dump_dev;
|
||||
bool force_err_fatal;
|
||||
bool allow_recursive_recovery;
|
||||
bool early_crash_ind;
|
||||
u8 cause_for_rejuvenation;
|
||||
u8 requesting_sub_system;
|
||||
u16 line_number;
|
||||
struct mutex dev_lock;
|
||||
uint32_t fw_error_fatal_irq;
|
||||
uint32_t fw_early_crash_irq;
|
||||
struct completion unblock_shutdown;
|
||||
struct adc_tm_param vph_monitor_params;
|
||||
struct adc_tm_chip *adc_tm_dev;
|
||||
struct iio_channel *channel;
|
||||
uint64_t vph_pwr;
|
||||
bool vbatt_supported;
|
||||
char function_name[WLFW_FUNCTION_NAME_LEN + 1];
|
||||
bool is_ssr;
|
||||
struct kobject *icnss_kobject;
|
||||
atomic_t is_shutdown;
|
||||
u32 qdss_mem_seg_len;
|
||||
struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG];
|
||||
void *get_info_cb_ctx;
|
||||
int (*get_info_cb)(void *ctx, void *event, int event_len);
|
||||
atomic_t soc_wake_ref_count;
|
||||
struct thermal_cooling_device *tcdev;
|
||||
unsigned long curr_thermal_state;
|
||||
unsigned long max_thermal_state;
|
||||
phys_addr_t hang_event_data_pa;
|
||||
void __iomem *hang_event_data_va;
|
||||
uint16_t hang_event_data_len;
|
||||
void *hang_event_data;
|
||||
};
|
||||
|
||||
struct icnss_reg_info {
|
||||
uint32_t mem_type;
|
||||
uint32_t reg_offset;
|
||||
uint32_t data_len;
|
||||
};
|
||||
|
||||
char *icnss_driver_event_to_str(enum icnss_driver_event_type type);
|
||||
int icnss_call_driver_uevent(struct icnss_priv *priv,
|
||||
enum icnss_uevent uevent, void *data);
|
||||
int icnss_driver_event_post(struct icnss_priv *priv,
|
||||
enum icnss_driver_event_type type,
|
||||
u32 flags, void *data);
|
||||
void icnss_allow_recursive_recovery(struct device *dev);
|
||||
void icnss_disallow_recursive_recovery(struct device *dev);
|
||||
char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type);
|
||||
int icnss_soc_wake_event_post(struct icnss_priv *priv,
|
||||
enum icnss_soc_wake_event_type type,
|
||||
u32 flags, void *data);
|
||||
int icnss_get_iova(struct icnss_priv *priv, u64 *addr, u64 *size);
|
||||
int icnss_get_iova_ipa(struct icnss_priv *priv, u64 *addr, u64 *size);
|
||||
#endif
|
||||
|
802
drivers/soc/qcom/icnss2/power.c
Normal file
802
drivers/soc/qcom/icnss2/power.c
Normal file
@ -0,0 +1,802 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <soc/qcom/cmd-db.h>
|
||||
#include "main.h"
|
||||
#include "qmi.h"
|
||||
#include "debug.h"
|
||||
|
||||
static struct icnss_vreg_cfg icnss_wcn6750_vreg_list[] = {
|
||||
{"vdd-cx-mx", 824000, 952000, 0, 0, 0, false},
|
||||
{"vdd-1.8-xo", 1872000, 1872000, 0, 0, 0, false},
|
||||
{"vdd-1.3-rfa", 1256000, 1352000, 0, 0, 0, false},
|
||||
};
|
||||
|
||||
static struct icnss_vreg_cfg icnss_adrestea_vreg_list[] = {
|
||||
{"vdd-cx-mx", 752000, 752000, 0, 0, 0, false},
|
||||
{"vdd-1.8-xo", 1800000, 1800000, 0, 0, 0, false},
|
||||
{"vdd-1.3-rfa", 1304000, 1304000, 0, 0, 0, false},
|
||||
{"vdd-3.3-ch1", 3312000, 3312000, 0, 0, 0, false},
|
||||
{"vdd-3.3-ch0", 3312000, 3312000, 0, 0, 0, false},
|
||||
};
|
||||
|
||||
static struct icnss_clk_cfg icnss_clk_list[] = {
|
||||
{"rf_clk", 0, 0},
|
||||
};
|
||||
|
||||
static struct icnss_clk_cfg icnss_adrestea_clk_list[] = {
|
||||
{"cxo_ref_clk_pin", 0, 0},
|
||||
};
|
||||
|
||||
#define ICNSS_VREG_LIST_SIZE ARRAY_SIZE(icnss_wcn6750_vreg_list)
|
||||
#define ICNSS_VREG_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_vreg_list)
|
||||
#define ICNSS_CLK_LIST_SIZE ARRAY_SIZE(icnss_clk_list)
|
||||
#define ICNSS_CLK_ADRESTEA_LIST_SIZE ARRAY_SIZE(icnss_adrestea_clk_list)
|
||||
|
||||
#define MAX_PROP_SIZE 32
|
||||
#define ICNSS_THRESHOLD_HIGH 3600000
|
||||
#define ICNSS_THRESHOLD_LOW 3450000
|
||||
#define ICNSS_THRESHOLD_GUARD 20000
|
||||
|
||||
static int icnss_get_vreg_single(struct icnss_priv *priv,
|
||||
struct icnss_vreg_info *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = NULL;
|
||||
struct regulator *reg = NULL;
|
||||
const __be32 *prop = NULL;
|
||||
char prop_name[MAX_PROP_SIZE] = {0};
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
dev = &priv->pdev->dev;
|
||||
|
||||
reg = devm_regulator_get_optional(dev, vreg->cfg.name);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
if (ret == -ENODEV) {
|
||||
return ret;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
icnss_pr_info("EPROBE_DEFER for regulator: %s\n",
|
||||
vreg->cfg.name);
|
||||
goto out;
|
||||
} else if (priv->device_id == ADRASTEA_DEVICE_ID) {
|
||||
if (vreg->cfg.required) {
|
||||
icnss_pr_err("Regulator %s doesn't exist: %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
goto out;
|
||||
} else {
|
||||
icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
icnss_pr_err("Failed to get regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
vreg->reg = reg;
|
||||
|
||||
snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-config",
|
||||
vreg->cfg.name);
|
||||
|
||||
prop = of_get_property(dev->of_node, prop_name, &len);
|
||||
|
||||
icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
|
||||
prop_name, len);
|
||||
|
||||
if (!prop || len < (2 * sizeof(__be32))) {
|
||||
icnss_pr_dbg("Property %s %s, use default\n", prop_name,
|
||||
prop ? "invalid format" : "doesn't exist");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; (i * sizeof(__be32)) < len; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
vreg->cfg.min_uv = be32_to_cpup(&prop[0]);
|
||||
break;
|
||||
case 1:
|
||||
vreg->cfg.max_uv = be32_to_cpup(&prop[1]);
|
||||
break;
|
||||
case 2:
|
||||
vreg->cfg.load_ua = be32_to_cpup(&prop[2]);
|
||||
break;
|
||||
case 3:
|
||||
vreg->cfg.delay_us = be32_to_cpup(&prop[3]);
|
||||
break;
|
||||
case 4:
|
||||
if (priv->device_id == WCN6750_DEVICE_ID)
|
||||
vreg->cfg.need_unvote = be32_to_cpup(&prop[4]);
|
||||
else
|
||||
vreg->cfg.need_unvote = 0;
|
||||
break;
|
||||
default:
|
||||
icnss_pr_dbg("Property %s, ignoring value at %d\n",
|
||||
prop_name, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
icnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u, need_unvote: %u\n",
|
||||
vreg->cfg.name, vreg->cfg.min_uv,
|
||||
vreg->cfg.max_uv, vreg->cfg.load_ua,
|
||||
vreg->cfg.delay_us, vreg->cfg.need_unvote);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_vreg_on_single(struct icnss_vreg_info *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (vreg->enabled) {
|
||||
icnss_pr_dbg("Regulator %s is already enabled\n",
|
||||
vreg->cfg.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Regulator %s is being enabled\n", vreg->cfg.name);
|
||||
|
||||
if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
|
||||
ret = regulator_set_voltage(vreg->reg,
|
||||
vreg->cfg.min_uv,
|
||||
vreg->cfg.max_uv);
|
||||
|
||||
if (ret) {
|
||||
icnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n",
|
||||
vreg->cfg.name, vreg->cfg.min_uv,
|
||||
vreg->cfg.max_uv, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (vreg->cfg.load_ua) {
|
||||
ret = regulator_set_load(vreg->reg,
|
||||
vreg->cfg.load_ua);
|
||||
|
||||
if (ret < 0) {
|
||||
icnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n",
|
||||
vreg->cfg.name, vreg->cfg.load_ua,
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (vreg->cfg.delay_us)
|
||||
udelay(vreg->cfg.delay_us);
|
||||
|
||||
ret = regulator_enable(vreg->reg);
|
||||
if (ret) {
|
||||
icnss_pr_err("Failed to enable regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vreg->enabled = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_vreg_unvote_single(struct icnss_vreg_info *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!vreg->enabled) {
|
||||
icnss_pr_dbg("Regulator %s is already disabled\n",
|
||||
vreg->cfg.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Removing vote for Regulator %s\n", vreg->cfg.name);
|
||||
|
||||
if (vreg->cfg.load_ua) {
|
||||
ret = regulator_set_load(vreg->reg, 0);
|
||||
if (ret < 0)
|
||||
icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
}
|
||||
|
||||
if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
|
||||
ret = regulator_set_voltage(vreg->reg, 0,
|
||||
vreg->cfg.max_uv);
|
||||
if (ret)
|
||||
icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_vreg_off_single(struct icnss_vreg_info *vreg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!vreg->enabled) {
|
||||
icnss_pr_dbg("Regulator %s is already disabled\n",
|
||||
vreg->cfg.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Regulator %s is being disabled\n",
|
||||
vreg->cfg.name);
|
||||
|
||||
ret = regulator_disable(vreg->reg);
|
||||
if (ret)
|
||||
icnss_pr_err("Failed to disable regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
|
||||
if (vreg->cfg.load_ua) {
|
||||
ret = regulator_set_load(vreg->reg, 0);
|
||||
if (ret < 0)
|
||||
icnss_pr_err("Failed to set load for regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
}
|
||||
|
||||
if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) {
|
||||
ret = regulator_set_voltage(vreg->reg, 0,
|
||||
vreg->cfg.max_uv);
|
||||
if (ret)
|
||||
icnss_pr_err("Failed to set voltage for regulator %s, err = %d\n",
|
||||
vreg->cfg.name, ret);
|
||||
}
|
||||
vreg->enabled = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct icnss_vreg_cfg *get_vreg_list(u32 *vreg_list_size,
|
||||
unsigned long device_id)
|
||||
{
|
||||
switch (device_id) {
|
||||
case WCN6750_DEVICE_ID:
|
||||
*vreg_list_size = ICNSS_VREG_LIST_SIZE;
|
||||
return icnss_wcn6750_vreg_list;
|
||||
|
||||
case ADRASTEA_DEVICE_ID:
|
||||
*vreg_list_size = ICNSS_VREG_ADRESTEA_LIST_SIZE;
|
||||
return icnss_adrestea_vreg_list;
|
||||
|
||||
default:
|
||||
icnss_pr_err("Unsupported device_id 0x%x\n", device_id);
|
||||
*vreg_list_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int icnss_get_vreg(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
struct icnss_vreg_info *vreg;
|
||||
struct icnss_vreg_cfg *vreg_cfg = NULL;
|
||||
struct list_head *vreg_list = &priv->vreg_list;
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
u32 vreg_list_size = 0;
|
||||
|
||||
vreg_cfg = get_vreg_list(&vreg_list_size, priv->device_id);
|
||||
if (!vreg_cfg)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < vreg_list_size; i++) {
|
||||
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
|
||||
if (!vreg)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&vreg->cfg, &vreg_cfg[i], sizeof(vreg->cfg));
|
||||
ret = icnss_get_vreg_single(priv, vreg);
|
||||
if (ret != 0) {
|
||||
if (ret == -ENODEV)
|
||||
continue;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
list_add_tail(&vreg->list, vreg_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void icnss_put_vreg(struct icnss_priv *priv)
|
||||
{
|
||||
struct list_head *vreg_list = &priv->vreg_list;
|
||||
struct icnss_vreg_info *vreg = NULL;
|
||||
|
||||
while (!list_empty(vreg_list)) {
|
||||
vreg = list_first_entry(vreg_list,
|
||||
struct icnss_vreg_info, list);
|
||||
list_del(&vreg->list);
|
||||
}
|
||||
}
|
||||
|
||||
static int icnss_vreg_on(struct icnss_priv *priv)
|
||||
{
|
||||
struct list_head *vreg_list = &priv->vreg_list;
|
||||
struct icnss_vreg_info *vreg = NULL;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(vreg, vreg_list, list) {
|
||||
if (IS_ERR_OR_NULL(vreg->reg))
|
||||
continue;
|
||||
ret = icnss_vreg_on_single(vreg);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry_continue_reverse(vreg, vreg_list, list) {
|
||||
if (IS_ERR_OR_NULL(vreg->reg) || !vreg->enabled)
|
||||
continue;
|
||||
|
||||
icnss_vreg_off_single(vreg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_vreg_off(struct icnss_priv *priv)
|
||||
{
|
||||
struct list_head *vreg_list = &priv->vreg_list;
|
||||
struct icnss_vreg_info *vreg = NULL;
|
||||
|
||||
list_for_each_entry_reverse(vreg, vreg_list, list) {
|
||||
if (IS_ERR_OR_NULL(vreg->reg))
|
||||
continue;
|
||||
|
||||
icnss_vreg_off_single(vreg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icnss_vreg_unvote(struct icnss_priv *priv)
|
||||
{
|
||||
struct list_head *vreg_list = &priv->vreg_list;
|
||||
struct icnss_vreg_info *vreg = NULL;
|
||||
|
||||
list_for_each_entry_reverse(vreg, vreg_list, list) {
|
||||
if (IS_ERR_OR_NULL(vreg->reg))
|
||||
continue;
|
||||
|
||||
if (vreg->cfg.need_unvote)
|
||||
icnss_vreg_unvote_single(vreg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icnss_get_clk_single(struct icnss_priv *priv,
|
||||
struct icnss_clk_info *clk_info)
|
||||
{
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = devm_clk_get(dev, clk_info->cfg.name);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
if (clk_info->cfg.required)
|
||||
icnss_pr_err("Failed to get clock %s, err = %d\n",
|
||||
clk_info->cfg.name, ret);
|
||||
else
|
||||
icnss_pr_dbg("Failed to get optional clock %s, err = %d\n",
|
||||
clk_info->cfg.name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_info->clk = clk;
|
||||
icnss_pr_dbg("Got clock: %s, freq: %u\n",
|
||||
clk_info->cfg.name, clk_info->cfg.freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_clk_on_single(struct icnss_clk_info *clk_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (clk_info->enabled) {
|
||||
icnss_pr_dbg("Clock %s is already enabled\n",
|
||||
clk_info->cfg.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Clock %s is being enabled\n", clk_info->cfg.name);
|
||||
|
||||
if (clk_info->cfg.freq) {
|
||||
ret = clk_set_rate(clk_info->clk, clk_info->cfg.freq);
|
||||
if (ret) {
|
||||
icnss_pr_err("Failed to set frequency %u for clock %s, err = %d\n",
|
||||
clk_info->cfg.freq, clk_info->cfg.name,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk_info->clk);
|
||||
if (ret) {
|
||||
icnss_pr_err("Failed to enable clock %s, err = %d\n",
|
||||
clk_info->cfg.name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_info->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icnss_clk_off_single(struct icnss_clk_info *clk_info)
|
||||
{
|
||||
if (!clk_info->enabled) {
|
||||
icnss_pr_dbg("Clock %s is already disabled\n",
|
||||
clk_info->cfg.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Clock %s is being disabled\n", clk_info->cfg.name);
|
||||
|
||||
clk_disable_unprepare(clk_info->clk);
|
||||
clk_info->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icnss_get_clk(struct icnss_priv *priv)
|
||||
{
|
||||
struct device *dev;
|
||||
struct list_head *clk_list;
|
||||
struct icnss_clk_info *clk_info;
|
||||
struct icnss_clk_cfg *clk_cfg;
|
||||
int ret, i;
|
||||
u32 clk_list_size = 0;
|
||||
|
||||
if (!priv)
|
||||
return -ENODEV;
|
||||
|
||||
dev = &priv->pdev->dev;
|
||||
clk_list = &priv->clk_list;
|
||||
|
||||
if (priv->device_id == ADRASTEA_DEVICE_ID) {
|
||||
clk_cfg = icnss_adrestea_clk_list;
|
||||
clk_list_size = ICNSS_CLK_ADRESTEA_LIST_SIZE;
|
||||
} else if (priv->device_id == WCN6750_DEVICE_ID) {
|
||||
clk_cfg = icnss_clk_list;
|
||||
clk_list_size = ICNSS_CLK_LIST_SIZE;
|
||||
}
|
||||
|
||||
if (!list_empty(clk_list)) {
|
||||
icnss_pr_dbg("Clocks have already been updated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_list_size; i++) {
|
||||
clk_info = devm_kzalloc(dev, sizeof(*clk_info), GFP_KERNEL);
|
||||
if (!clk_info) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(&clk_info->cfg, &clk_cfg[i],
|
||||
sizeof(clk_info->cfg));
|
||||
ret = icnss_get_clk_single(priv, clk_info);
|
||||
if (ret != 0) {
|
||||
if (clk_info->cfg.required)
|
||||
goto cleanup;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
list_add_tail(&clk_info->list, clk_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
while (!list_empty(clk_list)) {
|
||||
clk_info = list_first_entry(clk_list, struct icnss_clk_info,
|
||||
list);
|
||||
list_del(&clk_info->list);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void icnss_put_clk(struct icnss_priv *priv)
|
||||
{
|
||||
struct device *dev;
|
||||
struct list_head *clk_list;
|
||||
struct icnss_clk_info *clk_info;
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
dev = &priv->pdev->dev;
|
||||
clk_list = &priv->clk_list;
|
||||
|
||||
while (!list_empty(clk_list)) {
|
||||
clk_info = list_first_entry(clk_list, struct icnss_clk_info,
|
||||
list);
|
||||
list_del(&clk_info->list);
|
||||
}
|
||||
}
|
||||
|
||||
static int icnss_clk_on(struct list_head *clk_list)
|
||||
{
|
||||
struct icnss_clk_info *clk_info;
|
||||
int ret = 0;
|
||||
|
||||
list_for_each_entry(clk_info, clk_list, list) {
|
||||
if (IS_ERR_OR_NULL(clk_info->clk))
|
||||
continue;
|
||||
ret = icnss_clk_on_single(clk_info);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry_continue_reverse(clk_info, clk_list, list) {
|
||||
if (IS_ERR_OR_NULL(clk_info->clk))
|
||||
continue;
|
||||
|
||||
icnss_clk_off_single(clk_info);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icnss_clk_off(struct list_head *clk_list)
|
||||
{
|
||||
struct icnss_clk_info *clk_info;
|
||||
|
||||
list_for_each_entry_reverse(clk_info, clk_list, list) {
|
||||
if (IS_ERR_OR_NULL(clk_info->clk))
|
||||
continue;
|
||||
|
||||
icnss_clk_off_single(clk_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icnss_hw_power_on(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
|
||||
|
||||
spin_lock(&priv->on_off_lock);
|
||||
if (test_bit(ICNSS_POWER_ON, &priv->state)) {
|
||||
spin_unlock(&priv->on_off_lock);
|
||||
return ret;
|
||||
}
|
||||
set_bit(ICNSS_POWER_ON, &priv->state);
|
||||
spin_unlock(&priv->on_off_lock);
|
||||
|
||||
ret = icnss_vreg_on(priv);
|
||||
if (ret) {
|
||||
icnss_pr_err("Failed to turn on vreg, err = %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = icnss_clk_on(&priv->clk_list);
|
||||
if (ret)
|
||||
goto vreg_off;
|
||||
|
||||
return ret;
|
||||
|
||||
vreg_off:
|
||||
icnss_vreg_off(priv);
|
||||
out:
|
||||
clear_bit(ICNSS_POWER_ON, &priv->state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int icnss_hw_power_off(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (test_bit(HW_ALWAYS_ON, &priv->ctrl_params.quirks))
|
||||
return 0;
|
||||
|
||||
if (test_bit(ICNSS_FW_DOWN, &priv->state))
|
||||
return 0;
|
||||
|
||||
icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
|
||||
|
||||
spin_lock(&priv->on_off_lock);
|
||||
if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
|
||||
spin_unlock(&priv->on_off_lock);
|
||||
return ret;
|
||||
}
|
||||
clear_bit(ICNSS_POWER_ON, &priv->state);
|
||||
spin_unlock(&priv->on_off_lock);
|
||||
|
||||
icnss_clk_off(&priv->clk_list);
|
||||
|
||||
ret = icnss_vreg_off(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int icnss_power_on(struct device *dev)
|
||||
{
|
||||
struct icnss_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!priv) {
|
||||
icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
|
||||
dev, priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Power On: 0x%lx\n", priv->state);
|
||||
|
||||
return icnss_hw_power_on(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(icnss_power_on);
|
||||
|
||||
int icnss_power_off(struct device *dev)
|
||||
{
|
||||
struct icnss_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!priv) {
|
||||
icnss_pr_err("Invalid drvdata: dev %pK, data %pK\n",
|
||||
dev, priv);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icnss_pr_dbg("Power Off: 0x%lx\n", priv->state);
|
||||
|
||||
return icnss_hw_power_off(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(icnss_power_off);
|
||||
|
||||
void icnss_put_resources(struct icnss_priv *priv)
|
||||
{
|
||||
icnss_put_clk(priv);
|
||||
icnss_put_vreg(priv);
|
||||
}
|
||||
|
||||
static int icnss_get_phone_power(struct icnss_priv *priv, uint64_t *result_uv)
|
||||
{
|
||||
int ret = 0;
|
||||
int result;
|
||||
|
||||
if (!priv->channel) {
|
||||
icnss_pr_err("Channel doesn't exists\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = iio_read_channel_processed(priv->channel, &result);
|
||||
if (ret < 0) {
|
||||
icnss_pr_err("Error reading channel, ret = %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*result_uv = (uint64_t)result;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void icnss_vph_notify(enum adc_tm_state state, void *ctx)
|
||||
{
|
||||
struct icnss_priv *priv = ctx;
|
||||
u64 vph_pwr = 0;
|
||||
u64 vph_pwr_prev;
|
||||
int ret = 0;
|
||||
bool update = true;
|
||||
|
||||
if (!priv) {
|
||||
icnss_pr_err("Priv pointer is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vph_pwr_prev = priv->vph_pwr;
|
||||
|
||||
ret = icnss_get_phone_power(priv, &vph_pwr);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (vph_pwr < ICNSS_THRESHOLD_LOW) {
|
||||
if (vph_pwr_prev < ICNSS_THRESHOLD_LOW)
|
||||
update = false;
|
||||
priv->vph_monitor_params.state_request =
|
||||
ADC_TM_HIGH_THR_ENABLE;
|
||||
priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_LOW +
|
||||
ICNSS_THRESHOLD_GUARD;
|
||||
priv->vph_monitor_params.low_thr = 0;
|
||||
} else if (vph_pwr > ICNSS_THRESHOLD_HIGH) {
|
||||
if (vph_pwr_prev > ICNSS_THRESHOLD_HIGH)
|
||||
update = false;
|
||||
priv->vph_monitor_params.state_request =
|
||||
ADC_TM_LOW_THR_ENABLE;
|
||||
priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_HIGH -
|
||||
ICNSS_THRESHOLD_GUARD;
|
||||
priv->vph_monitor_params.high_thr = 0;
|
||||
} else {
|
||||
if (vph_pwr_prev > ICNSS_THRESHOLD_LOW &&
|
||||
vph_pwr_prev < ICNSS_THRESHOLD_HIGH)
|
||||
update = false;
|
||||
priv->vph_monitor_params.state_request =
|
||||
ADC_TM_HIGH_LOW_THR_ENABLE;
|
||||
priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
|
||||
priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
|
||||
}
|
||||
|
||||
priv->vph_pwr = vph_pwr;
|
||||
|
||||
if (update) {
|
||||
icnss_send_vbatt_update(priv, vph_pwr);
|
||||
icnss_pr_dbg("set low threshold to %d, high threshold to %d Phone power=%llu\n",
|
||||
priv->vph_monitor_params.low_thr,
|
||||
priv->vph_monitor_params.high_thr, vph_pwr);
|
||||
}
|
||||
|
||||
ret = adc_tm_channel_measure(priv->adc_tm_dev,
|
||||
&priv->vph_monitor_params);
|
||||
if (ret)
|
||||
icnss_pr_err("TM channel setup failed %d\n", ret);
|
||||
}
|
||||
|
||||
static int icnss_setup_vph_monitor(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!priv->adc_tm_dev) {
|
||||
icnss_pr_err("ADC TM handler is NULL\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->vph_monitor_params.low_thr = ICNSS_THRESHOLD_LOW;
|
||||
priv->vph_monitor_params.high_thr = ICNSS_THRESHOLD_HIGH;
|
||||
priv->vph_monitor_params.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
|
||||
priv->vph_monitor_params.channel = ADC5_VBAT_SNS;
|
||||
priv->vph_monitor_params.btm_ctx = priv;
|
||||
priv->vph_monitor_params.threshold_notification = &icnss_vph_notify;
|
||||
icnss_pr_dbg("Set low threshold to %d, high threshold to %d\n",
|
||||
priv->vph_monitor_params.low_thr,
|
||||
priv->vph_monitor_params.high_thr);
|
||||
|
||||
ret = adc_tm_channel_measure(priv->adc_tm_dev,
|
||||
&priv->vph_monitor_params);
|
||||
if (ret)
|
||||
icnss_pr_err("TM channel setup failed %d\n", ret);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int icnss_init_vph_monitor(struct icnss_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = icnss_get_phone_power(priv, &priv->vph_pwr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
icnss_pr_dbg("Phone power=%llu\n", priv->vph_pwr);
|
||||
|
||||
icnss_send_vbatt_update(priv, priv->vph_pwr);
|
||||
|
||||
ret = icnss_setup_vph_monitor(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
out:
|
||||
return ret;
|
||||
}
|
19
drivers/soc/qcom/icnss2/power.h
Normal file
19
drivers/soc/qcom/icnss2/power.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ICNSS_POWER_H__
|
||||
#define __ICNSS_POWER_H__
|
||||
|
||||
int icnss_hw_power_on(struct icnss_priv *priv);
|
||||
int icnss_hw_power_off(struct icnss_priv *priv);
|
||||
int icnss_get_clk(struct icnss_priv *priv);
|
||||
int icnss_get_vreg(struct icnss_priv *priv);
|
||||
int icnss_init_vph_monitor(struct icnss_priv *priv);
|
||||
void icnss_put_resources(struct icnss_priv *priv);
|
||||
void icnss_put_vreg(struct icnss_priv *priv);
|
||||
void icnss_put_clk(struct icnss_priv *priv);
|
||||
int icnss_vreg_unvote(struct icnss_priv *priv);
|
||||
|
||||
#endif
|
2446
drivers/soc/qcom/icnss2/qmi.c
Normal file
2446
drivers/soc/qcom/icnss2/qmi.c
Normal file
File diff suppressed because it is too large
Load Diff
190
drivers/soc/qcom/icnss2/qmi.h
Normal file
190
drivers/soc/qcom/icnss2/qmi.h
Normal file
@ -0,0 +1,190 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ICNSS_QMI_H__
|
||||
#define __ICNSS_QMI_H__
|
||||
|
||||
#define QDSS_TRACE_SEG_LEN_MAX 32
|
||||
#define QDSS_TRACE_FILE_NAME_MAX 16
|
||||
|
||||
struct icnss_mem_seg {
|
||||
u64 addr;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct icnss_qmi_event_qdss_trace_save_data {
|
||||
u32 total_size;
|
||||
u32 mem_seg_len;
|
||||
struct icnss_mem_seg mem_seg[QDSS_TRACE_SEG_LEN_MAX];
|
||||
char file_name[QDSS_TRACE_FILE_NAME_MAX + 1];
|
||||
};
|
||||
|
||||
#ifndef CONFIG_ICNSS2_QMI
|
||||
|
||||
static inline int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int icnss_connect_to_fw_server(struct icnss_priv *priv,
|
||||
void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_cap_send_sync_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_dynamic_feature_mask_send_sync_msg(
|
||||
struct icnss_priv *priv, uint64_t dynamic_feature_mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int icnss_clear_server(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void icnss_ignore_fw_timeout(bool ignore) {}
|
||||
static int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_ini_send_sync_msg(struct icnss_priv *priv,
|
||||
uint8_t fw_log_mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
|
||||
uint32_t offset, uint32_t mem_type,
|
||||
uint32_t data_len, uint8_t *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
|
||||
uint32_t offset, uint32_t mem_type,
|
||||
uint32_t data_len, uint8_t *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
|
||||
enum icnss_driver_mode mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int wlfw_host_cap_send_sync(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
|
||||
struct icnss_wlan_enable_cfg *config,
|
||||
enum icnss_driver_mode mode,
|
||||
const char *host_version)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int icnss_register_fw_service(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void icnss_unregister_fw_service(struct icnss_priv *priv) {}
|
||||
static inline int icnss_send_vbatt_update(struct icnss_priv *priv,
|
||||
uint64_t voltage_uv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wlfw_device_info_send_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
|
||||
enum wlfw_driver_mode_enum_v01 mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wlfw_exit_power_save_send_msg(struct icnss_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type,
|
||||
void *cmd, int cmd_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wlfw_send_soc_wake_msg(struct icnss_priv *priv,
|
||||
enum wlfw_soc_wake_enum_v01 type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv);
|
||||
int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data);
|
||||
int wlfw_msa_mem_info_send_sync_msg(struct icnss_priv *priv);
|
||||
int wlfw_msa_ready_send_sync_msg(struct icnss_priv *priv);
|
||||
int wlfw_cap_send_sync_msg(struct icnss_priv *priv);
|
||||
int icnss_qmi_pin_connect_result_ind(struct icnss_priv *priv,
|
||||
void *msg, unsigned int msg_len);
|
||||
int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
|
||||
uint64_t dynamic_feature_mask);
|
||||
int icnss_clear_server(struct icnss_priv *priv);
|
||||
int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv);
|
||||
void icnss_ignore_fw_timeout(bool ignore);
|
||||
int wlfw_send_modem_shutdown_msg(struct icnss_priv *priv);
|
||||
int wlfw_ini_send_sync_msg(struct icnss_priv *priv, uint8_t fw_log_mode);
|
||||
int wlfw_athdiag_read_send_sync_msg(struct icnss_priv *priv,
|
||||
uint32_t offset, uint32_t mem_type,
|
||||
uint32_t data_len, uint8_t *data);
|
||||
int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
|
||||
uint32_t offset, uint32_t mem_type,
|
||||
uint32_t data_len, uint8_t *data);
|
||||
int icnss_send_wlan_enable_to_fw(struct icnss_priv *priv,
|
||||
struct icnss_wlan_enable_cfg *config,
|
||||
enum icnss_driver_mode mode,
|
||||
const char *host_version);
|
||||
int icnss_send_wlan_disable_to_fw(struct icnss_priv *priv);
|
||||
int icnss_register_fw_service(struct icnss_priv *priv);
|
||||
void icnss_unregister_fw_service(struct icnss_priv *priv);
|
||||
int icnss_send_vbatt_update(struct icnss_priv *priv, uint64_t voltage_uv);
|
||||
int wlfw_host_cap_send_sync(struct icnss_priv *priv);
|
||||
int wlfw_device_info_send_msg(struct icnss_priv *priv);
|
||||
int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv,
|
||||
enum wlfw_driver_mode_enum_v01 mode);
|
||||
int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type);
|
||||
int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv);
|
||||
int wlfw_exit_power_save_send_msg(struct icnss_priv *priv);
|
||||
int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type,
|
||||
void *cmd, int cmd_len);
|
||||
int wlfw_send_soc_wake_msg(struct icnss_priv *priv,
|
||||
enum wlfw_soc_wake_enum_v01 type);
|
||||
#endif
|
||||
|
||||
#endif /* __ICNSS_QMI_H__*/
|
4443
drivers/soc/qcom/icnss2/wlan_firmware_service_v01.c
Normal file
4443
drivers/soc/qcom/icnss2/wlan_firmware_service_v01.c
Normal file
File diff suppressed because it is too large
Load Diff
1158
drivers/soc/qcom/icnss2/wlan_firmware_service_v01.h
Normal file
1158
drivers/soc/qcom/icnss2/wlan_firmware_service_v01.h
Normal file
File diff suppressed because it is too large
Load Diff
186
include/soc/qcom/icnss2.h
Normal file
186
include/soc/qcom/icnss2.h
Normal file
@ -0,0 +1,186 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#ifndef _ICNSS_WLAN_H_
|
||||
#define _ICNSS_WLAN_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#define ICNSS_MAX_IRQ_REGISTRATIONS 12
|
||||
#define IWCN_MAX_IRQ_REGISTRATIONS 32
|
||||
#define ICNSS_MAX_TIMESTAMP_LEN 32
|
||||
|
||||
#ifndef ICNSS_API_WITH_DEV
|
||||
#define ICNSS_API_WITH_DEV
|
||||
#endif
|
||||
|
||||
enum icnss_uevent {
|
||||
ICNSS_UEVENT_FW_CRASHED,
|
||||
ICNSS_UEVENT_FW_DOWN,
|
||||
ICNSS_UEVENT_HANG_DATA,
|
||||
};
|
||||
|
||||
struct icnss_uevent_hang_data {
|
||||
void *hang_event_data;
|
||||
uint16_t hang_event_data_len;
|
||||
};
|
||||
|
||||
struct icnss_uevent_fw_down_data {
|
||||
bool crashed;
|
||||
};
|
||||
|
||||
struct icnss_uevent_data {
|
||||
enum icnss_uevent uevent;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct icnss_driver_ops {
|
||||
char *name;
|
||||
unsigned long drv_state;
|
||||
struct device_driver driver;
|
||||
int (*probe)(struct device *dev);
|
||||
void (*remove)(struct device *dev);
|
||||
void (*shutdown)(struct device *dev);
|
||||
int (*reinit)(struct device *dev);
|
||||
void (*crash_shutdown)(void *pdev);
|
||||
int (*pm_suspend)(struct device *dev);
|
||||
int (*pm_resume)(struct device *dev);
|
||||
int (*suspend_noirq)(struct device *dev);
|
||||
int (*resume_noirq)(struct device *dev);
|
||||
int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent);
|
||||
int (*idle_shutdown)(struct device *dev);
|
||||
int (*idle_restart)(struct device *dev);
|
||||
int (*set_therm_state)(struct device *dev, unsigned long thermal_state);
|
||||
};
|
||||
|
||||
|
||||
struct ce_tgt_pipe_cfg {
|
||||
u32 pipe_num;
|
||||
u32 pipe_dir;
|
||||
u32 nentries;
|
||||
u32 nbytes_max;
|
||||
u32 flags;
|
||||
u32 reserved;
|
||||
};
|
||||
|
||||
struct ce_svc_pipe_cfg {
|
||||
u32 service_id;
|
||||
u32 pipe_dir;
|
||||
u32 pipe_num;
|
||||
};
|
||||
|
||||
struct icnss_shadow_reg_cfg {
|
||||
u16 ce_id;
|
||||
u16 reg_offset;
|
||||
};
|
||||
|
||||
struct icnss_shadow_reg_v2_cfg {
|
||||
u32 addr;
|
||||
};
|
||||
|
||||
struct icnss_rri_over_ddr_cfg {
|
||||
u32 base_addr_low;
|
||||
u32 base_addr_high;
|
||||
};
|
||||
/* CE configuration to target */
|
||||
struct icnss_wlan_enable_cfg {
|
||||
u32 num_ce_tgt_cfg;
|
||||
struct ce_tgt_pipe_cfg *ce_tgt_cfg;
|
||||
u32 num_ce_svc_pipe_cfg;
|
||||
struct ce_svc_pipe_cfg *ce_svc_cfg;
|
||||
u32 num_shadow_reg_cfg;
|
||||
struct icnss_shadow_reg_cfg *shadow_reg_cfg;
|
||||
u32 num_shadow_reg_v2_cfg;
|
||||
struct icnss_shadow_reg_v2_cfg *shadow_reg_v2_cfg;
|
||||
bool rri_over_ddr_cfg_valid;
|
||||
struct icnss_rri_over_ddr_cfg rri_over_ddr_cfg;
|
||||
};
|
||||
|
||||
/* driver modes */
|
||||
enum icnss_driver_mode {
|
||||
ICNSS_MISSION,
|
||||
ICNSS_FTM,
|
||||
ICNSS_EPPING,
|
||||
ICNSS_WALTEST,
|
||||
ICNSS_OFF,
|
||||
ICNSS_CCPM,
|
||||
ICNSS_QVIT,
|
||||
ICNSS_CALIBRATION,
|
||||
};
|
||||
|
||||
struct icnss_soc_info {
|
||||
void __iomem *v_addr;
|
||||
phys_addr_t p_addr;
|
||||
uint32_t chip_id;
|
||||
uint32_t chip_family;
|
||||
uint32_t board_id;
|
||||
uint32_t soc_id;
|
||||
uint32_t fw_version;
|
||||
char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1];
|
||||
};
|
||||
|
||||
#define icnss_register_driver(ops) \
|
||||
__icnss_register_driver(ops, THIS_MODULE, KBUILD_MODNAME)
|
||||
extern int __icnss_register_driver(struct icnss_driver_ops *ops,
|
||||
struct module *owner, const char *mod_name);
|
||||
|
||||
extern int icnss_unregister_driver(struct icnss_driver_ops *ops);
|
||||
|
||||
extern int icnss_wlan_enable(struct device *dev,
|
||||
struct icnss_wlan_enable_cfg *config,
|
||||
enum icnss_driver_mode mode,
|
||||
const char *host_version);
|
||||
extern int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode);
|
||||
extern void icnss_enable_irq(struct device *dev, unsigned int ce_id);
|
||||
extern void icnss_disable_irq(struct device *dev, unsigned int ce_id);
|
||||
extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info);
|
||||
extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx);
|
||||
extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id,
|
||||
irqreturn_t (*handler)(int, void *),
|
||||
unsigned long flags, const char *name, void *ctx);
|
||||
extern int icnss_get_ce_id(struct device *dev, int irq);
|
||||
extern int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode);
|
||||
extern int icnss_athdiag_read(struct device *dev, uint32_t offset,
|
||||
uint32_t mem_type, uint32_t data_len,
|
||||
uint8_t *output);
|
||||
extern int icnss_athdiag_write(struct device *dev, uint32_t offset,
|
||||
uint32_t mem_type, uint32_t data_len,
|
||||
uint8_t *input);
|
||||
extern int icnss_get_irq(struct device *dev, int ce_id);
|
||||
extern int icnss_power_on(struct device *dev);
|
||||
extern int icnss_power_off(struct device *dev);
|
||||
extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev);
|
||||
extern struct iommu_domain *icnss_smmu_get_domain(struct device *dev);
|
||||
extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
|
||||
uint32_t *iova_addr, size_t size);
|
||||
extern int icnss_smmu_unmap(struct device *dev,
|
||||
uint32_t iova_addr, size_t size);
|
||||
extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
|
||||
extern bool icnss_is_qmi_disable(struct device *dev);
|
||||
extern bool icnss_is_fw_ready(void);
|
||||
extern bool icnss_is_fw_down(void);
|
||||
extern bool icnss_is_rejuvenate(void);
|
||||
extern int icnss_trigger_recovery(struct device *dev);
|
||||
extern void icnss_block_shutdown(bool status);
|
||||
extern bool icnss_is_pdr(void);
|
||||
extern int icnss_idle_restart(struct device *dev);
|
||||
extern int icnss_idle_shutdown(struct device *dev);
|
||||
extern int icnss_get_user_msi_assignment(struct device *dev, char *user_name,
|
||||
int *num_vectors, u32 *user_base_data,
|
||||
u32 *base_vector);
|
||||
extern int icnss_get_msi_irq(struct device *dev, unsigned int vector);
|
||||
extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low,
|
||||
u32 *msi_addr_high);
|
||||
extern int icnss_qmi_send(struct device *dev, int type, void *cmd,
|
||||
int cmd_len, void *cb_ctx,
|
||||
int (*cb)(void *ctx, void *event, int event_len));
|
||||
extern int icnss_force_wake_request(struct device *dev);
|
||||
extern int icnss_force_wake_release(struct device *dev);
|
||||
extern int icnss_is_device_awake(struct device *dev);
|
||||
extern int icnss_thermal_register(struct device *dev, unsigned long max_state);
|
||||
extern void icnss_thermal_unregister(struct device *dev);
|
||||
extern int icnss_get_curr_therm_state(struct device *dev,
|
||||
unsigned long *thermal_state);
|
||||
#endif /* _ICNSS_WLAN_H_ */
|
Loading…
Reference in New Issue
Block a user