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:
Sandeep Singh 2020-07-01 19:32:53 +05:30
parent 4027c201fd
commit 13f37630bd
17 changed files with 14217 additions and 0 deletions

View File

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

View File

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

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

View 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

View 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, &reg_offset))
return -EINVAL;
token = strsep(&sptr, delim);
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &reg_val))
return -EINVAL;
ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
sizeof(uint32_t),
(uint8_t *)&reg_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, &reg_info_ptr->mem_type))
return -EINVAL;
token = strsep(&sptr, delim);
if (!token)
return -EINVAL;
if (!sptr)
return -EINVAL;
if (kstrtou32(token, 0, &reg_info_ptr->reg_offset))
return -EINVAL;
token = strsep(&sptr, delim);
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &reg_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, &reg_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;
}
}

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

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

View 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

File diff suppressed because it is too large Load Diff

View 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

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

View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

186
include/soc/qcom/icnss2.h Normal file
View 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_ */