wlan_platform: Bring initial files for CNSS family drivers

Bring CNSS family drivers from msm-5.10 kernel as of commit
cc3bc4b888af (cnss2: Fix a few switch statement fallthrough
issues) to WLAN platform project. Fix SPDX-License format for
a few files as well.

drivers/net/wireless/cnss* -> .
include/net/cnss* -> inc/

Above shows how directories and header files are relocated.

Change-Id: If8fd40a35c9fdbeb1aa76a8aac5fdb1fc1c7e786
This commit is contained in:
Yue Ma
2021-10-06 18:50:15 -07:00
parent 40d31cc7a5
commit 5b29459b17
41 changed files with 28664 additions and 0 deletions

107
cnss2/Kconfig Normal file
View File

@@ -0,0 +1,107 @@
# SPDX-License-Identifier: GPL-2.0-only
config CNSS2
tristate "CNSS2 Platform Driver for Wi-Fi Module"
depends on !CNSS && PCI_MSM
select CNSS_PLAT_IPC_QMI_SVC
help
This module adds the support for Connectivity Subsystem (CNSS) used
for PCIe based Wi-Fi devices with QCA6174/QCA6290 chipsets.
This driver also adds support to integrate WLAN module to subsystem
restart framework.
config CNSS2_DEBUG
bool "CNSS2 Platform Driver Debug Support"
depends on CNSS2
help
This option is to enable CNSS2 platform driver debug support which
primarily includes providing additional verbose logs for certain
features, enabling kernel panic for certain cases to aid the
debugging, and enabling any other debug mechanisms.
config CNSS2_QMI
bool "CNSS2 Platform Driver QMI support"
select CNSS_QMI_SVC
depends on CNSS2
help
CNSS2 platform driver uses QMI framework to communicate with WLAN
firmware. It sends and receives boot handshake messages to WLAN
firmware, which includes hardware and software capabilities and
configurations. It also sends WLAN on/off control message to
firmware over QMI channel.
config CNSS_ASYNC
bool "Enable/disable CNSS platform driver asynchronous probe"
depends on CNSS2
help
If enabled, CNSS platform driver would do asynchronous probe.
Using asynchronous probe will allow CNSS platform driver to
probe in parallel with other device drivers and will help to
reduce kernel boot time.
config BUS_AUTO_SUSPEND
bool "Enable/Disable Runtime PM support for PCIe based WLAN Drivers"
depends on CNSS2
depends on PCI
help
Runtime Power Management is supported for PCIe based WLAN Drivers.
The features enable cld wlan driver to suspend pcie bus when APPS
is awake based on the driver inactivity with the Firmware.
The Feature uses runtime power management framework from kernel to
track bus access clients and to synchronize the driver activity
during system pm.
This config flag controls the feature per target based. The feature
requires CNSS driver support.
config CNSS_QCA6290
bool "Enable CNSS QCA6290 chipset specific changes"
depends on CNSS2
help
This enables the changes from WLAN host driver that are specific to
CNSS QCA6290 chipset.
These changes are needed to support the new hardware architecture
for CNSS QCA6290 chipset.
config CNSS_QCA6390
bool "Enable CNSS QCA6390 chipset specific changes"
depends on CNSS2
help
This enables the changes from WLAN host driver that are specific to
CNSS QCA6390 chipset.
These changes are needed to support the new hardware architecture
for CNSS QCA6390 chipset.
config CNSS_EMULATION
bool "Enable specific changes for emulation hardware"
depends on CNSS2
help
This enables the changes from WLAN drivers that are specific to
emulation hardware.
These changes are needed for WLAN drivers to support and meet the
requirement of emulation hardware.
config CNSS_QCA6490
bool "Enable CNSS QCA6490 chipset specific changes"
depends on CNSS2
help
This enables the changes from WLAN host driver that are specific to
CNSS QCA6490 chipset.
These changes are needed to support the new hardware architecture
for CNSS QCA6490 chipset.
config CNSS_WCN7850
bool "Enable CNSS WCN7850 chipset specific changes"
depends on CNSS2
help
This enables the changes from WLAN host driver that are specific to
CNSS WCN7850 chipset.
These changes are needed to support the new hardware architecture
for CNSS WCN7850 chipset.
config CNSS_REQ_FW_DIRECT
bool "Enable request_firmware_direct for firmware or configuration file"
depends on CNSS2
help
This enables calling request_firmware_direct for firmware or
configuration file to avoid 60s timeout while search file under user
space failure.

12
cnss2/Makefile Normal file
View File

@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CNSS2) += cnss2.o
ccflags-y += -I$(srctree)/drivers/net/wireless/cnss_utils/
cnss2-y := main.o
cnss2-y += bus.o
cnss2-y += debug.o
cnss2-y += pci.o
cnss2-y += power.o
cnss2-y += genl.o
cnss2-$(CONFIG_CNSS2_QMI) += qmi.o coexistence_service_v01.o ip_multimedia_subsystem_private_service_v01.o

564
cnss2/bus.c Normal file
View File

@@ -0,0 +1,564 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. */
#include "bus.h"
#include "debug.h"
#include "pci.h"
enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev)
{
if (!dev)
return CNSS_BUS_NONE;
if (!dev->bus)
return CNSS_BUS_NONE;
if (memcmp(dev->bus->name, "pci", 3) == 0)
return CNSS_BUS_PCI;
else
return CNSS_BUS_NONE;
}
enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id)
{
switch (device_id) {
case QCA6174_DEVICE_ID:
case QCA6290_DEVICE_ID:
case QCA6390_DEVICE_ID:
case QCA6490_DEVICE_ID:
case WCN7850_DEVICE_ID:
return CNSS_BUS_PCI;
default:
cnss_pr_err("Unknown device_id: 0x%lx\n", device_id);
return CNSS_BUS_NONE;
}
}
void *cnss_bus_dev_to_bus_priv(struct device *dev)
{
if (!dev)
return NULL;
switch (cnss_get_dev_bus_type(dev)) {
case CNSS_BUS_PCI:
return cnss_get_pci_priv(to_pci_dev(dev));
default:
return NULL;
}
}
struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev)
{
void *bus_priv;
if (!dev)
return cnss_get_plat_priv(NULL);
bus_priv = cnss_bus_dev_to_bus_priv(dev);
if (!bus_priv)
return NULL;
switch (cnss_get_dev_bus_type(dev)) {
case CNSS_BUS_PCI:
return cnss_pci_priv_to_plat_priv(bus_priv);
default:
return NULL;
}
}
int cnss_bus_init(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_init(plat_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
void cnss_bus_deinit(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_deinit(plat_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv,
char *prefix_name, char *name)
{
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_add_fw_prefix_name(plat_priv->bus_priv,
prefix_name, name);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
int cnss_bus_load_m3(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_load_m3(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_alloc_fw_mem(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_alloc_qdss_mem(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
cnss_pci_free_qdss_mem(plat_priv->bus_priv);
return;
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_get_wake_msi(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_force_fw_assert_hdlr(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_qmi_send_get(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_qmi_send_put(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t)
{
struct cnss_plat_data *plat_priv =
from_timer(plat_priv, t, fw_boot_timer);
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_fw_boot_timeout_hdlr(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv, bool in_panic)
{
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_collect_dump_info(plat_priv->bus_priv,
in_panic);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
void cnss_bus_device_crashed(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_device_crashed(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return;
}
}
int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_call_driver_probe(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_call_driver_remove(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dev_powerup(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dev_shutdown(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dev_crash_shutdown(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dev_ramdump(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_register_driver_hdlr(plat_priv->bus_priv, data);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_unregister_driver_hdlr(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv,
int modem_current_status)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_call_driver_modem_status(plat_priv->bus_priv,
modem_current_status);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_update_status(struct cnss_plat_data *plat_priv,
enum cnss_driver_status status)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_update_status(plat_priv->bus_priv, status);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_update_uevent(struct cnss_plat_data *plat_priv,
enum cnss_driver_status status, void *data)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_call_driver_uevent(plat_priv->bus_priv,
status, data);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_is_device_down(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pcie_is_device_down(plat_priv->bus_priv);
default:
cnss_pr_dbg("Unsupported bus type: %d\n",
plat_priv->bus_type);
return 0;
}
}
int cnss_bus_check_link_status(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_check_link_status(plat_priv->bus_priv);
default:
cnss_pr_dbg("Unsupported bus type: %d\n",
plat_priv->bus_type);
return 0;
}
}
int cnss_bus_recover_link_down(struct cnss_plat_data *plat_priv)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_recover_link_down(plat_priv->bus_priv);
default:
cnss_pr_dbg("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset,
u32 *val, bool raw_access)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_debug_reg_read(plat_priv->bus_priv, offset,
val, raw_access);
default:
cnss_pr_dbg("Unsupported bus type: %d\n",
plat_priv->bus_type);
return 0;
}
}
int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset,
u32 val, bool raw_access)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_debug_reg_write(plat_priv->bus_priv, offset,
val, raw_access);
default:
cnss_pr_dbg("Unsupported bus type: %d\n",
plat_priv->bus_type);
return 0;
}
}
int cnss_bus_get_iova(struct cnss_plat_data *plat_priv, u64 *addr, u64 *size)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_get_iova(plat_priv->bus_priv, addr, size);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_get_iova_ipa(struct cnss_plat_data *plat_priv, u64 *addr,
u64 *size)
{
if (!plat_priv)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_get_iova_ipa(plat_priv->bus_priv, addr, size);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}

67
cnss2/bus.h Normal file
View File

@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_BUS_H
#define _CNSS_BUS_H
#include "main.h"
#define QCA6174_VENDOR_ID 0x168C
#define QCA6174_DEVICE_ID 0x003E
#define QCA6174_REV_ID_OFFSET 0x08
#define QCA6174_REV3_VERSION 0x5020000
#define QCA6174_REV3_2_VERSION 0x5030000
#define QCA6290_VENDOR_ID 0x17CB
#define QCA6290_DEVICE_ID 0x1100
#define QCA6390_VENDOR_ID 0x17CB
#define QCA6390_DEVICE_ID 0x1101
#define QCA6490_VENDOR_ID 0x17CB
#define QCA6490_DEVICE_ID 0x1103
#define WCN7850_VENDOR_ID 0x17CB
#define WCN7850_DEVICE_ID 0x1107
enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev);
enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id);
void *cnss_bus_dev_to_bus_priv(struct device *dev);
struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev);
int cnss_bus_init(struct cnss_plat_data *plat_priv);
void cnss_bus_deinit(struct cnss_plat_data *plat_priv);
void cnss_bus_add_fw_prefix_name(struct cnss_plat_data *plat_priv,
char *prefix_name, char *name);
int cnss_bus_load_m3(struct cnss_plat_data *plat_priv);
int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv);
int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv);
void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv);
u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv);
int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv);
int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv);
int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv);
void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t);
void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv,
bool in_panic);
void cnss_bus_device_crashed(struct cnss_plat_data *plat_priv);
int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv);
int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv);
int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv);
int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv);
int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv);
int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv);
int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data);
int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv);
int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv,
int modem_current_status);
int cnss_bus_update_status(struct cnss_plat_data *plat_priv,
enum cnss_driver_status status);
int cnss_bus_update_uevent(struct cnss_plat_data *plat_priv,
enum cnss_driver_status status, void *data);
int cnss_bus_is_device_down(struct cnss_plat_data *plat_priv);
int cnss_bus_check_link_status(struct cnss_plat_data *plat_priv);
int cnss_bus_recover_link_down(struct cnss_plat_data *plat_priv);
int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset,
u32 *val, bool raw_access);
int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset,
u32 val, bool raw_access);
int cnss_bus_get_iova(struct cnss_plat_data *plat_priv, u64 *addr, u64 *size);
int cnss_bus_get_iova_ipa(struct cnss_plat_data *plat_priv, u64 *addr,
u64 *size);
#endif /* _CNSS_BUS_H */

View File

@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
#include <linux/soc/qcom/qmi.h>
#include "coexistence_service_v01.h"
struct qmi_elem_info coex_antenna_switch_to_wlan_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
coex_antenna_switch_to_wlan_req_msg_v01,
antenna),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info coex_antenna_switch_to_wlan_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
coex_antenna_switch_to_wlan_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
coex_antenna_switch_to_wlan_resp_msg_v01,
grant_valid),
},
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
coex_antenna_switch_to_wlan_resp_msg_v01,
grant),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info coex_antenna_switch_to_mdm_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
coex_antenna_switch_to_mdm_req_msg_v01,
antenna),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info coex_antenna_switch_to_mdm_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
coex_antenna_switch_to_mdm_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};

View File

@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2019, The Linux Foundation. All rights reserved. */
#ifndef COEXISTENCE_SERVICE_V01_H
#define COEXISTENCE_SERVICE_V01_H
#define COEX_SERVICE_ID_V01 0x22
#define COEX_SERVICE_VERS_V01 0x01
#define COEX_SERVICE_MAX_MSG_LEN 8204
#define QMI_COEX_SWITCH_ANTENNA_TO_WLAN_RESP_V01 0x0042
#define QMI_COEX_SWITCH_ANTENNA_TO_WLAN_REQ_V01 0x0042
#define QMI_COEX_SWITCH_ANTENNA_TO_MDM_RESP_V01 0x0042
#define QMI_COEX_SWITCH_ANTENNA_TO_MDM_REQ_V01 0x0042
#define COEX_ANTENNA_BAND_2GHZ_CHAIN0_V01 ((u64)0x0000000000000001ULL)
#define COEX_ANTENNA_BAND_2GHZ_CHAIN1_V01 ((u64)0x0000000000000002ULL)
#define COEX_ANTENNA_BAND_5GHZ_CHAIN0_V01 ((u64)0x0000000000000004ULL)
#define COEX_ANTENNA_BAND_5GHZ_CHAIN1_V01 ((u64)0x0000000000000008ULL)
struct coex_antenna_switch_to_wlan_req_msg_v01 {
u64 antenna;
};
#define COEX_ANTENNA_SWITCH_TO_WLAN_REQ_MSG_V01_MAX_MSG_LEN 11
extern struct qmi_elem_info coex_antenna_switch_to_wlan_req_msg_v01_ei[];
struct coex_antenna_switch_to_wlan_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 grant_valid;
u64 grant;
};
#define COEX_ANTENNA_SWITCH_TO_WLAN_RESP_MSG_V01_MAX_MSG_LEN 18
extern struct qmi_elem_info coex_antenna_switch_to_wlan_resp_msg_v01_ei[];
struct coex_antenna_switch_to_mdm_req_msg_v01 {
u64 antenna;
};
#define COEX_ANTENNA_SWITCH_TO_MDM_REQ_MSG_V01_MAX_MSG_LEN 11
extern struct qmi_elem_info coex_antenna_switch_to_mdm_req_msg_v01_ei[];
struct coex_antenna_switch_to_mdm_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define COEX_ANTENNA_SWITCH_TO_MDM_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info coex_antenna_switch_to_mdm_resp_msg_v01_ei[];
#endif

973
cnss2/debug.c Normal file
View File

@@ -0,0 +1,973 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include "main.h"
#include "bus.h"
#include "debug.h"
#include "pci.h"
#define MMIO_REG_ACCESS_MEM_TYPE 0xFF
#define MMIO_REG_RAW_ACCESS_MEM_TYPE 0xFE
#if IS_ENABLED(CONFIG_IPC_LOGGING)
void *cnss_ipc_log_context;
void *cnss_ipc_log_long_context;
#endif
static int cnss_pin_connect_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *cnss_priv = s->private;
seq_puts(s, "Pin connect results\n");
seq_printf(s, "FW power pin result: %04x\n",
cnss_priv->pin_result.fw_pwr_pin_result);
seq_printf(s, "FW PHY IO pin result: %04x\n",
cnss_priv->pin_result.fw_phy_io_pin_result);
seq_printf(s, "FW RF pin result: %04x\n",
cnss_priv->pin_result.fw_rf_pin_result);
seq_printf(s, "Host pin result: %04x\n",
cnss_priv->pin_result.host_pin_result);
seq_puts(s, "\n");
return 0;
}
static int cnss_pin_connect_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_pin_connect_show, inode->i_private);
}
static const struct file_operations cnss_pin_connect_fops = {
.read = seq_read,
.release = single_release,
.open = cnss_pin_connect_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static int cnss_stats_show_state(struct seq_file *s,
struct cnss_plat_data *plat_priv)
{
enum cnss_driver_state i;
int skip = 0;
unsigned long state;
seq_printf(s, "\nState: 0x%lx(", plat_priv->driver_state);
for (i = 0, state = plat_priv->driver_state; state != 0;
state >>= 1, i++) {
if (!(state & 0x1))
continue;
if (skip++)
seq_puts(s, " | ");
switch (i) {
case CNSS_QMI_WLFW_CONNECTED:
seq_puts(s, "QMI_WLFW_CONNECTED");
continue;
case CNSS_FW_MEM_READY:
seq_puts(s, "FW_MEM_READY");
continue;
case CNSS_FW_READY:
seq_puts(s, "FW_READY");
continue;
case CNSS_IN_COLD_BOOT_CAL:
seq_puts(s, "IN_COLD_BOOT_CAL");
continue;
case CNSS_DRIVER_LOADING:
seq_puts(s, "DRIVER_LOADING");
continue;
case CNSS_DRIVER_UNLOADING:
seq_puts(s, "DRIVER_UNLOADING");
continue;
case CNSS_DRIVER_IDLE_RESTART:
seq_puts(s, "IDLE_RESTART");
continue;
case CNSS_DRIVER_IDLE_SHUTDOWN:
seq_puts(s, "IDLE_SHUTDOWN");
continue;
case CNSS_DRIVER_PROBED:
seq_puts(s, "DRIVER_PROBED");
continue;
case CNSS_DRIVER_RECOVERY:
seq_puts(s, "DRIVER_RECOVERY");
continue;
case CNSS_FW_BOOT_RECOVERY:
seq_puts(s, "FW_BOOT_RECOVERY");
continue;
case CNSS_DEV_ERR_NOTIFY:
seq_puts(s, "DEV_ERR");
continue;
case CNSS_DRIVER_DEBUG:
seq_puts(s, "DRIVER_DEBUG");
continue;
case CNSS_COEX_CONNECTED:
seq_puts(s, "COEX_CONNECTED");
continue;
case CNSS_IMS_CONNECTED:
seq_puts(s, "IMS_CONNECTED");
continue;
case CNSS_IN_SUSPEND_RESUME:
seq_puts(s, "IN_SUSPEND_RESUME");
continue;
case CNSS_IN_REBOOT:
seq_puts(s, "IN_REBOOT");
continue;
case CNSS_COLD_BOOT_CAL_DONE:
seq_puts(s, "COLD_BOOT_CAL_DONE");
continue;
case CNSS_IN_PANIC:
seq_puts(s, "IN_PANIC");
continue;
case CNSS_QMI_DEL_SERVER:
seq_puts(s, "DEL_SERVER_IN_PROGRESS");
continue;
case CNSS_QMI_DMS_CONNECTED:
seq_puts(s, "DMS_CONNECTED");
continue;
case CNSS_DAEMON_CONNECTED:
seq_puts(s, "DAEMON_CONNECTED");
continue;
case CNSS_PCI_PROBE_DONE:
seq_puts(s, "PCI PROBE DONE");
continue;
}
seq_printf(s, "UNKNOWN-%d", i);
}
seq_puts(s, ")\n");
return 0;
}
static int cnss_stats_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *plat_priv = s->private;
cnss_stats_show_state(s, plat_priv);
return 0;
}
static int cnss_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_stats_show, inode->i_private);
}
static const struct file_operations cnss_stats_fops = {
.read = seq_read,
.release = single_release,
.open = cnss_stats_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static ssize_t cnss_dev_boot_debug_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
struct cnss_pci_data *pci_priv;
char buf[64];
char *cmd;
unsigned int len = 0;
int ret = 0;
if (!plat_priv)
return -ENODEV;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
cmd = buf;
cnss_pr_dbg("Received dev_boot debug command: %s\n", cmd);
if (sysfs_streq(cmd, "on")) {
ret = cnss_power_on_device(plat_priv);
} else if (sysfs_streq(cmd, "off")) {
cnss_power_off_device(plat_priv);
} else if (sysfs_streq(cmd, "enumerate")) {
ret = cnss_pci_init(plat_priv);
} else if (sysfs_streq(cmd, "powerup")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
ret = cnss_driver_event_post(plat_priv,
CNSS_DRIVER_EVENT_POWER_UP,
CNSS_EVENT_SYNC, NULL);
} else if (sysfs_streq(cmd, "shutdown")) {
ret = cnss_driver_event_post(plat_priv,
CNSS_DRIVER_EVENT_POWER_DOWN,
0, NULL);
clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
} else {
pci_priv = plat_priv->bus_priv;
if (!pci_priv)
return -ENODEV;
if (sysfs_streq(cmd, "download")) {
set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
ret = cnss_pci_start_mhi(pci_priv);
} else if (sysfs_streq(cmd, "linkup")) {
ret = cnss_resume_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "linkdown")) {
ret = cnss_suspend_pci_link(pci_priv);
} else if (sysfs_streq(cmd, "assert")) {
cnss_pr_info("FW Assert triggered for debug\n");
ret = cnss_force_fw_assert(&pci_priv->pci_dev->dev);
} else if (sysfs_streq(cmd, "set_cbc_done")) {
cnss_pr_dbg("Force set cold boot cal done status\n");
set_bit(CNSS_COLD_BOOT_CAL_DONE,
&plat_priv->driver_state);
} else {
cnss_pr_err("Device boot debugfs command is invalid\n");
ret = -EINVAL;
}
}
if (ret < 0)
return ret;
return count;
}
static int cnss_dev_boot_debug_show(struct seq_file *s, void *data)
{
seq_puts(s, "\nUsage: echo <action> > <debugfs_path>/cnss/dev_boot\n");
seq_puts(s, "<action> can be one of below:\n");
seq_puts(s, "on: turn on device power, assert WLAN_EN\n");
seq_puts(s, "off: de-assert WLAN_EN, turn off device power\n");
seq_puts(s, "enumerate: de-assert PERST, enumerate PCIe\n");
seq_puts(s, "download: download FW and do QMI handshake with FW\n");
seq_puts(s, "linkup: bring up PCIe link\n");
seq_puts(s, "linkdown: bring down PCIe link\n");
seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n");
seq_puts(s, "shutdown: full power off sequence to shutdown device\n");
seq_puts(s, "assert: trigger firmware assert\n");
seq_puts(s, "set_cbc_done: Set cold boot calibration done status\n");
return 0;
}
static int cnss_dev_boot_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_dev_boot_debug_show, inode->i_private);
}
static const struct file_operations cnss_dev_boot_debug_fops = {
.read = seq_read,
.write = cnss_dev_boot_debug_write,
.release = single_release,
.open = cnss_dev_boot_debug_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static int cnss_reg_read_debug_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *plat_priv = s->private;
mutex_lock(&plat_priv->dev_lock);
if (!plat_priv->diag_reg_read_buf) {
seq_puts(s, "\nUsage: echo <mem_type> <offset> <data_len> > <debugfs_path>/cnss/reg_read\n");
seq_puts(s, "Use mem_type = 0xff for register read by IO access, data_len will be ignored\n");
seq_puts(s, "Use mem_type = 0xfe for register read by raw IO access which skips sanity checks, data_len will be ignored\n");
seq_puts(s, "Use other mem_type for register read by QMI\n");
mutex_unlock(&plat_priv->dev_lock);
return 0;
}
seq_printf(s, "\nRegister read, address: 0x%x memory type: 0x%x length: 0x%x\n\n",
plat_priv->diag_reg_read_addr,
plat_priv->diag_reg_read_mem_type,
plat_priv->diag_reg_read_len);
seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4,
plat_priv->diag_reg_read_buf,
plat_priv->diag_reg_read_len, false);
plat_priv->diag_reg_read_len = 0;
kfree(plat_priv->diag_reg_read_buf);
plat_priv->diag_reg_read_buf = NULL;
mutex_unlock(&plat_priv->dev_lock);
return 0;
}
static ssize_t cnss_reg_read_debug_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
char buf[64];
char *sptr, *token;
unsigned int len = 0;
u32 reg_offset, mem_type;
u32 data_len = 0, reg_val = 0;
u8 *reg_buf = NULL;
const char *delim = " ";
int ret = 0;
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, &data_len))
return -EINVAL;
if (mem_type == MMIO_REG_ACCESS_MEM_TYPE ||
mem_type == MMIO_REG_RAW_ACCESS_MEM_TYPE) {
ret = cnss_bus_debug_reg_read(plat_priv, reg_offset, &reg_val,
mem_type ==
MMIO_REG_RAW_ACCESS_MEM_TYPE);
if (ret)
return ret;
cnss_pr_dbg("Read 0x%x from register offset 0x%x\n", reg_val,
reg_offset);
return count;
}
if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
cnss_pr_err("Firmware is not ready yet\n");
return -EINVAL;
}
mutex_lock(&plat_priv->dev_lock);
kfree(plat_priv->diag_reg_read_buf);
plat_priv->diag_reg_read_buf = NULL;
reg_buf = kzalloc(data_len, GFP_KERNEL);
if (!reg_buf) {
mutex_unlock(&plat_priv->dev_lock);
return -ENOMEM;
}
ret = cnss_wlfw_athdiag_read_send_sync(plat_priv, reg_offset,
mem_type, data_len,
reg_buf);
if (ret) {
kfree(reg_buf);
mutex_unlock(&plat_priv->dev_lock);
return ret;
}
plat_priv->diag_reg_read_addr = reg_offset;
plat_priv->diag_reg_read_mem_type = mem_type;
plat_priv->diag_reg_read_len = data_len;
plat_priv->diag_reg_read_buf = reg_buf;
mutex_unlock(&plat_priv->dev_lock);
return count;
}
static int cnss_reg_read_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_reg_read_debug_show, inode->i_private);
}
static const struct file_operations cnss_reg_read_debug_fops = {
.read = seq_read,
.write = cnss_reg_read_debug_write,
.open = cnss_reg_read_debug_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static int cnss_reg_write_debug_show(struct seq_file *s, void *data)
{
seq_puts(s, "\nUsage: echo <mem_type> <offset> <reg_val> > <debugfs_path>/cnss/reg_write\n");
seq_puts(s, "Use mem_type = 0xff for register write by IO access\n");
seq_puts(s, "Use mem_type = 0xfe for register write by raw IO access which skips sanity checks\n");
seq_puts(s, "Use other mem_type for register write by QMI\n");
return 0;
}
static ssize_t cnss_reg_write_debug_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
char buf[64];
char *sptr, *token;
unsigned int len = 0;
u32 reg_offset, mem_type, reg_val;
const char *delim = " ";
int ret = 0;
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;
if (mem_type == MMIO_REG_ACCESS_MEM_TYPE ||
mem_type == MMIO_REG_RAW_ACCESS_MEM_TYPE) {
ret = cnss_bus_debug_reg_write(plat_priv, reg_offset, reg_val,
mem_type ==
MMIO_REG_RAW_ACCESS_MEM_TYPE);
if (ret)
return ret;
cnss_pr_dbg("Wrote 0x%x to register offset 0x%x\n", reg_val,
reg_offset);
return count;
}
if (!test_bit(CNSS_FW_READY, &plat_priv->driver_state)) {
cnss_pr_err("Firmware is not ready yet\n");
return -EINVAL;
}
ret = cnss_wlfw_athdiag_write_send_sync(plat_priv, reg_offset, mem_type,
sizeof(u32),
(u8 *)&reg_val);
if (ret)
return ret;
return count;
}
static int cnss_reg_write_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_reg_write_debug_show, inode->i_private);
}
static const struct file_operations cnss_reg_write_debug_fops = {
.read = seq_read,
.write = cnss_reg_write_debug_write,
.open = cnss_reg_write_debug_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static ssize_t cnss_runtime_pm_debug_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
struct cnss_pci_data *pci_priv;
char buf[64];
char *cmd;
unsigned int len = 0;
int ret = 0;
if (!plat_priv)
return -ENODEV;
pci_priv = plat_priv->bus_priv;
if (!pci_priv)
return -ENODEV;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
cmd = buf;
if (sysfs_streq(cmd, "usage_count")) {
cnss_pci_pm_runtime_show_usage_count(pci_priv);
} else if (sysfs_streq(cmd, "request_resume")) {
ret = cnss_pci_pm_request_resume(pci_priv);
} else if (sysfs_streq(cmd, "resume")) {
ret = cnss_pci_pm_runtime_resume(pci_priv);
} else if (sysfs_streq(cmd, "get")) {
ret = cnss_pci_pm_runtime_get(pci_priv, RTPM_ID_CNSS);
} else if (sysfs_streq(cmd, "get_noresume")) {
cnss_pci_pm_runtime_get_noresume(pci_priv, RTPM_ID_CNSS);
} else if (sysfs_streq(cmd, "put_autosuspend")) {
ret = cnss_pci_pm_runtime_put_autosuspend(pci_priv,
RTPM_ID_CNSS);
} else if (sysfs_streq(cmd, "put_noidle")) {
cnss_pci_pm_runtime_put_noidle(pci_priv, RTPM_ID_CNSS);
} else if (sysfs_streq(cmd, "mark_last_busy")) {
cnss_pci_pm_runtime_mark_last_busy(pci_priv);
} else if (sysfs_streq(cmd, "resume_bus")) {
cnss_pci_resume_bus(pci_priv);
} else if (sysfs_streq(cmd, "suspend_bus")) {
cnss_pci_suspend_bus(pci_priv);
} else {
cnss_pr_err("Runtime PM debugfs command is invalid\n");
ret = -EINVAL;
}
if (ret < 0)
return ret;
return count;
}
static int cnss_runtime_pm_debug_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *plat_priv = s->private;
struct cnss_pci_data *pci_priv;
int i;
if (!plat_priv)
return -ENODEV;
pci_priv = plat_priv->bus_priv;
if (!pci_priv)
return -ENODEV;
seq_puts(s, "\nUsage: echo <action> > <debugfs_path>/cnss/runtime_pm\n");
seq_puts(s, "<action> can be one of below:\n");
seq_puts(s, "usage_count: get runtime PM usage count\n");
seq_puts(s, "reques_resume: do async runtime PM resume\n");
seq_puts(s, "resume: do sync runtime PM resume\n");
seq_puts(s, "get: do runtime PM get\n");
seq_puts(s, "get_noresume: do runtime PM get noresume\n");
seq_puts(s, "put_noidle: do runtime PM put noidle\n");
seq_puts(s, "put_autosuspend: do runtime PM put autosuspend\n");
seq_puts(s, "mark_last_busy: do runtime PM mark last busy\n");
seq_puts(s, "resume_bus: do bus resume only\n");
seq_puts(s, "suspend_bus: do bus suspend only\n");
seq_puts(s, "\nStats:\n");
seq_printf(s, "%s: %u\n", "get count",
atomic_read(&pci_priv->pm_stats.runtime_get));
seq_printf(s, "%s: %u\n", "put count",
atomic_read(&pci_priv->pm_stats.runtime_put));
seq_printf(s, "%-10s%-10s%-10s%-15s%-15s\n",
"id:", "get", "put", "get time(us)", "put time(us)");
for (i = 0; i < RTPM_ID_MAX; i++) {
seq_printf(s, "%d%-9s", i, ":");
seq_printf(s, "%-10d",
atomic_read(&pci_priv->pm_stats.runtime_get_id[i]));
seq_printf(s, "%-10d",
atomic_read(&pci_priv->pm_stats.runtime_put_id[i]));
seq_printf(s, "%-15llu",
pci_priv->pm_stats.runtime_get_timestamp_id[i]);
seq_printf(s, "%-15llu\n",
pci_priv->pm_stats.runtime_put_timestamp_id[i]);
}
return 0;
}
static int cnss_runtime_pm_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_runtime_pm_debug_show, inode->i_private);
}
static const struct file_operations cnss_runtime_pm_debug_fops = {
.read = seq_read,
.write = cnss_runtime_pm_debug_write,
.open = cnss_runtime_pm_debug_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static ssize_t cnss_control_params_debug_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
char buf[64];
char *sptr, *token;
char *cmd;
u32 val;
unsigned int len = 0;
const char *delim = " ";
if (!plat_priv)
return -ENODEV;
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;
cmd = token;
token = strsep(&sptr, delim);
if (!token)
return -EINVAL;
if (kstrtou32(token, 0, &val))
return -EINVAL;
if (strcmp(cmd, "quirks") == 0)
plat_priv->ctrl_params.quirks = val;
else if (strcmp(cmd, "mhi_timeout") == 0)
plat_priv->ctrl_params.mhi_timeout = val;
else if (strcmp(cmd, "mhi_m2_timeout") == 0)
plat_priv->ctrl_params.mhi_m2_timeout = val;
else if (strcmp(cmd, "qmi_timeout") == 0)
plat_priv->ctrl_params.qmi_timeout = val;
else if (strcmp(cmd, "bdf_type") == 0)
plat_priv->ctrl_params.bdf_type = val;
else if (strcmp(cmd, "time_sync_period") == 0)
plat_priv->ctrl_params.time_sync_period = val;
else
return -EINVAL;
return count;
}
static int cnss_show_quirks_state(struct seq_file *s,
struct cnss_plat_data *plat_priv)
{
enum cnss_debug_quirks i;
int skip = 0;
unsigned long state;
seq_printf(s, "quirks: 0x%lx (", plat_priv->ctrl_params.quirks);
for (i = 0, state = plat_priv->ctrl_params.quirks;
state != 0; state >>= 1, i++) {
if (!(state & 0x1))
continue;
if (skip++)
seq_puts(s, " | ");
switch (i) {
case LINK_DOWN_SELF_RECOVERY:
seq_puts(s, "LINK_DOWN_SELF_RECOVERY");
continue;
case SKIP_DEVICE_BOOT:
seq_puts(s, "SKIP_DEVICE_BOOT");
continue;
case USE_CORE_ONLY_FW:
seq_puts(s, "USE_CORE_ONLY_FW");
continue;
case SKIP_RECOVERY:
seq_puts(s, "SKIP_RECOVERY");
continue;
case QMI_BYPASS:
seq_puts(s, "QMI_BYPASS");
continue;
case ENABLE_WALTEST:
seq_puts(s, "WALTEST");
continue;
case ENABLE_PCI_LINK_DOWN_PANIC:
seq_puts(s, "PCI_LINK_DOWN_PANIC");
continue;
case FBC_BYPASS:
seq_puts(s, "FBC_BYPASS");
continue;
case ENABLE_DAEMON_SUPPORT:
seq_puts(s, "DAEMON_SUPPORT");
continue;
case DISABLE_DRV:
seq_puts(s, "DISABLE_DRV");
continue;
case DISABLE_IO_COHERENCY:
seq_puts(s, "DISABLE_IO_COHERENCY");
continue;
case IGNORE_PCI_LINK_FAILURE:
seq_puts(s, "IGNORE_PCI_LINK_FAILURE");
continue;
case DISABLE_TIME_SYNC:
seq_puts(s, "DISABLE_TIME_SYNC");
continue;
}
seq_printf(s, "UNKNOWN-%d", i);
}
seq_puts(s, ")\n");
return 0;
}
static int cnss_control_params_debug_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *cnss_priv = s->private;
seq_puts(s, "\nUsage: echo <params_name> <value> > <debugfs_path>/cnss/control_params\n");
seq_puts(s, "<params_name> can be one of below:\n");
seq_puts(s, "quirks: Debug quirks for driver\n");
seq_puts(s, "mhi_timeout: Timeout for MHI operation in milliseconds\n");
seq_puts(s, "qmi_timeout: Timeout for QMI message in milliseconds\n");
seq_puts(s, "bdf_type: Type of board data file to be downloaded\n");
seq_puts(s, "time_sync_period: Time period to do time sync with device in milliseconds\n");
seq_puts(s, "\nCurrent value:\n");
cnss_show_quirks_state(s, cnss_priv);
seq_printf(s, "mhi_timeout: %u\n", cnss_priv->ctrl_params.mhi_timeout);
seq_printf(s, "mhi_m2_timeout: %u\n",
cnss_priv->ctrl_params.mhi_m2_timeout);
seq_printf(s, "qmi_timeout: %u\n", cnss_priv->ctrl_params.qmi_timeout);
seq_printf(s, "bdf_type: %u\n", cnss_priv->ctrl_params.bdf_type);
seq_printf(s, "time_sync_period: %u\n",
cnss_priv->ctrl_params.time_sync_period);
return 0;
}
static int cnss_control_params_debug_open(struct inode *inode,
struct file *file)
{
return single_open(file, cnss_control_params_debug_show,
inode->i_private);
}
static const struct file_operations cnss_control_params_debug_fops = {
.read = seq_read,
.write = cnss_control_params_debug_write,
.open = cnss_control_params_debug_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static ssize_t cnss_dynamic_feature_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_plat_data *plat_priv =
((struct seq_file *)fp->private_data)->private;
int ret = 0;
u64 val;
ret = kstrtou64_from_user(user_buf, count, 0, &val);
if (ret)
return ret;
plat_priv->dynamic_feature = val;
ret = cnss_wlfw_dynamic_feature_mask_send_sync(plat_priv);
if (ret < 0)
return ret;
return count;
}
static int cnss_dynamic_feature_show(struct seq_file *s, void *data)
{
struct cnss_plat_data *cnss_priv = s->private;
seq_printf(s, "dynamic_feature: 0x%llx\n", cnss_priv->dynamic_feature);
return 0;
}
static int cnss_dynamic_feature_open(struct inode *inode,
struct file *file)
{
return single_open(file, cnss_dynamic_feature_show,
inode->i_private);
}
static const struct file_operations cnss_dynamic_feature_fops = {
.read = seq_read,
.write = cnss_dynamic_feature_write,
.open = cnss_dynamic_feature_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
#ifdef CONFIG_DEBUG_FS
#ifdef CONFIG_CNSS2_DEBUG
static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
{
struct dentry *root_dentry = plat_priv->root_dentry;
debugfs_create_file("dev_boot", 0600, root_dentry, plat_priv,
&cnss_dev_boot_debug_fops);
debugfs_create_file("reg_read", 0600, root_dentry, plat_priv,
&cnss_reg_read_debug_fops);
debugfs_create_file("reg_write", 0600, root_dentry, plat_priv,
&cnss_reg_write_debug_fops);
debugfs_create_file("runtime_pm", 0600, root_dentry, plat_priv,
&cnss_runtime_pm_debug_fops);
debugfs_create_file("control_params", 0600, root_dentry, plat_priv,
&cnss_control_params_debug_fops);
debugfs_create_file("dynamic_feature", 0600, root_dentry, plat_priv,
&cnss_dynamic_feature_fops);
return 0;
}
#else
static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv)
{
return 0;
}
#endif
int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct dentry *root_dentry;
root_dentry = debugfs_create_dir("cnss", 0);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
cnss_pr_err("Unable to create debugfs %d\n", ret);
goto out;
}
plat_priv->root_dentry = root_dentry;
debugfs_create_file("pin_connect_result", 0644, root_dentry, plat_priv,
&cnss_pin_connect_fops);
debugfs_create_file("stats", 0644, root_dentry, plat_priv,
&cnss_stats_fops);
cnss_create_debug_only_node(plat_priv);
out:
return ret;
}
void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv)
{
debugfs_remove_recursive(plat_priv->root_dentry);
}
#else
int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
{
plat_priv->root_dentry = NULL;
return 0;
}
void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv)
{
}
#endif
#if IS_ENABLED(CONFIG_IPC_LOGGING)
void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
const char *log_level, char *fmt, ...)
{
struct va_format vaf;
va_list va_args;
va_start(va_args, fmt);
vaf.fmt = fmt;
vaf.va = &va_args;
if (log_level)
printk("%scnss: %pV", log_level, &vaf);
ipc_log_string(log_ctx, "[%s] %s: %pV", process, fn, &vaf);
va_end(va_args);
}
static int cnss_ipc_logging_init(void)
{
cnss_ipc_log_context = ipc_log_context_create(CNSS_IPC_LOG_PAGES,
"cnss", 0);
if (!cnss_ipc_log_context) {
cnss_pr_err("Unable to create IPC log context\n");
return -EINVAL;
}
cnss_ipc_log_long_context = ipc_log_context_create(CNSS_IPC_LOG_PAGES,
"cnss-long", 0);
if (!cnss_ipc_log_long_context) {
cnss_pr_err("Unable to create IPC long log context\n");
ipc_log_context_destroy(cnss_ipc_log_context);
return -EINVAL;
}
return 0;
}
static void cnss_ipc_logging_deinit(void)
{
if (cnss_ipc_log_long_context) {
ipc_log_context_destroy(cnss_ipc_log_long_context);
cnss_ipc_log_long_context = NULL;
}
if (cnss_ipc_log_context) {
ipc_log_context_destroy(cnss_ipc_log_context);
cnss_ipc_log_context = NULL;
}
}
#else
static int cnss_ipc_logging_init(void) { return 0; }
static void cnss_ipc_logging_deinit(void) {}
void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
const char *log_level, char *fmt, ...)
{
struct va_format vaf;
va_list va_args;
va_start(va_args, fmt);
vaf.fmt = fmt;
vaf.va = &va_args;
if (log_level)
printk("%scnss: %pV", log_level, &vaf);
va_end(va_args);
}
#endif
int cnss_debug_init(void)
{
return cnss_ipc_logging_init();
}
void cnss_debug_deinit(void)
{
cnss_ipc_logging_deinit();
}

87
cnss2/debug.h Normal file
View File

@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_DEBUG_H
#define _CNSS_DEBUG_H
#include <linux/printk.h>
#if IS_ENABLED(CONFIG_IPC_LOGGING)
#include <linux/ipc_logging.h>
#include <asm/current.h>
extern void *cnss_ipc_log_context;
extern void *cnss_ipc_log_long_context;
#ifdef CONFIG_CNSS2_DEBUG
#define CNSS_IPC_LOG_PAGES 100
#else
#define CNSS_IPC_LOG_PAGES 50
#endif
#define cnss_debug_log_print(_x...) \
cnss_debug_ipc_log_print(cnss_ipc_log_context, _x)
#define cnss_debug_log_long_print(_x...) \
cnss_debug_ipc_log_print(cnss_ipc_log_long_context, _x)
#else
#define cnss_debug_log_print(_x...) \
cnss_debug_ipc_log_print((void *)NULL, _x)
#define cnss_debug_log_long_print(_x...) \
cnss_debug_ipc_log_print((void *)NULL, _x)
#endif
#define proc_name (in_irq() ? "irq" : \
(in_softirq() ? "soft_irq" : current->comm))
#define cnss_pr_err(_fmt, ...) \
cnss_debug_log_print(proc_name, __func__, \
KERN_ERR, _fmt, ##__VA_ARGS__)
#define cnss_pr_warn(_fmt, ...) \
cnss_debug_log_print(proc_name, __func__, \
KERN_WARNING, _fmt, ##__VA_ARGS__)
#define cnss_pr_info(_fmt, ...) \
cnss_debug_log_print(proc_name, __func__, \
KERN_INFO, _fmt, ##__VA_ARGS__)
#define cnss_pr_dbg(_fmt, ...) \
cnss_debug_log_print(proc_name, __func__, \
KERN_DEBUG, _fmt, ##__VA_ARGS__)
#define cnss_pr_vdbg(_fmt, ...) \
cnss_debug_log_long_print(proc_name, __func__, \
KERN_DEBUG, _fmt, ##__VA_ARGS__)
#define cnss_pr_buf(_fmt, ...) \
cnss_debug_log_long_print(proc_name, __func__, \
NULL, _fmt, ##__VA_ARGS__)
#ifdef CONFIG_CNSS2_DEBUG
#define CNSS_ASSERT(_condition) do { \
if (!(_condition)) { \
cnss_pr_err("ASSERT at line %d\n", \
__LINE__); \
BUG(); \
} \
} while (0)
#else
#define CNSS_ASSERT(_condition) do { \
if (!(_condition)) { \
cnss_pr_err("ASSERT at line %d\n", \
__LINE__); \
WARN_ON(1); \
} \
} while (0)
#endif
#define cnss_fatal_err(_fmt, ...) \
cnss_pr_err("fatal: " _fmt, ##__VA_ARGS__)
int cnss_debug_init(void);
void cnss_debug_deinit(void);
int cnss_debugfs_create(struct cnss_plat_data *plat_priv);
void cnss_debugfs_destroy(struct cnss_plat_data *plat_priv);
void cnss_debug_ipc_log_print(void *log_ctx, char *process, const char *fn,
const char *log_level, char *fmt, ...);
#endif /* _CNSS_DEBUG_H */

213
cnss2/genl.c Normal file
View File

@@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019, 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 CNSS_GENL_FAMILY_NAME "cnss-genl"
#define CNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp"
#define CNSS_GENL_VERSION 1
#define CNSS_GENL_DATA_LEN_MAX (15 * 1024)
#define CNSS_GENL_STR_LEN_MAX 16
enum {
CNSS_GENL_ATTR_MSG_UNSPEC,
CNSS_GENL_ATTR_MSG_TYPE,
CNSS_GENL_ATTR_MSG_FILE_NAME,
CNSS_GENL_ATTR_MSG_TOTAL_SIZE,
CNSS_GENL_ATTR_MSG_SEG_ID,
CNSS_GENL_ATTR_MSG_END,
CNSS_GENL_ATTR_MSG_DATA_LEN,
CNSS_GENL_ATTR_MSG_DATA,
__CNSS_GENL_ATTR_MAX,
};
#define CNSS_GENL_ATTR_MAX (__CNSS_GENL_ATTR_MAX - 1)
enum {
CNSS_GENL_CMD_UNSPEC,
CNSS_GENL_CMD_MSG,
__CNSS_GENL_CMD_MAX,
};
#define CNSS_GENL_CMD_MAX (__CNSS_GENL_CMD_MAX - 1)
static struct nla_policy cnss_genl_msg_policy[CNSS_GENL_ATTR_MAX + 1] = {
[CNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 },
[CNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING,
.len = CNSS_GENL_STR_LEN_MAX },
[CNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 },
[CNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 },
[CNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 },
[CNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 },
[CNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY,
.len = CNSS_GENL_DATA_LEN_MAX },
};
static int cnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info)
{
return 0;
}
static struct genl_ops cnss_genl_ops[] = {
{
.cmd = CNSS_GENL_CMD_MSG,
.doit = cnss_genl_process_msg,
},
};
static struct genl_multicast_group cnss_genl_mcast_grp[] = {
{
.name = CNSS_GENL_MCAST_GROUP_NAME,
},
};
static struct genl_family cnss_genl_family = {
.id = 0,
.hdrsize = 0,
.name = CNSS_GENL_FAMILY_NAME,
.version = CNSS_GENL_VERSION,
.maxattr = CNSS_GENL_ATTR_MAX,
.policy = cnss_genl_msg_policy,
.module = THIS_MODULE,
.ops = cnss_genl_ops,
.n_ops = ARRAY_SIZE(cnss_genl_ops),
.mcgrps = cnss_genl_mcast_grp,
.n_mcgrps = ARRAY_SIZE(cnss_genl_mcast_grp),
};
static int cnss_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[CNSS_GENL_STR_LEN_MAX + 1];
cnss_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,
&cnss_genl_family, 0,
CNSS_GENL_CMD_MSG);
if (!msg_header) {
ret = -ENOMEM;
goto fail;
}
ret = nla_put_u8(skb, CNSS_GENL_ATTR_MSG_TYPE, type);
if (ret < 0)
goto fail;
ret = nla_put_string(skb, CNSS_GENL_ATTR_MSG_FILE_NAME, filename);
if (ret < 0)
goto fail;
ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size);
if (ret < 0)
goto fail;
ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_SEG_ID, seg_id);
if (ret < 0)
goto fail;
ret = nla_put_u8(skb, CNSS_GENL_ATTR_MSG_END, end);
if (ret < 0)
goto fail;
ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_DATA_LEN, data_len);
if (ret < 0)
goto fail;
ret = nla_put(skb, CNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff);
if (ret < 0)
goto fail;
genlmsg_end(skb, msg_header);
ret = genlmsg_multicast(&cnss_genl_family, skb, 0, 0, GFP_KERNEL);
if (ret < 0)
cnss_pr_err("Fail to send genl msg: %d\n", ret);
return ret;
fail:
cnss_pr_err("Fail to generate genl msg: %d\n", ret);
if (skb)
nlmsg_free(skb);
return ret;
}
int cnss_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;
cnss_pr_dbg("type: %u, total_size: %x\n", type, total_size);
while (remaining) {
if (remaining > CNSS_GENL_DATA_LEN_MAX) {
data_len = CNSS_GENL_DATA_LEN_MAX;
} else {
data_len = remaining;
end = 1;
}
for (retry = 0; retry < 2; retry++) {
ret = cnss_genl_send_data(type, file_name, total_size,
seg_id, end, data_len,
msg_buff);
if (ret >= 0)
break;
msleep(100);
}
if (ret < 0) {
cnss_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 cnss_genl_init(void)
{
int ret = 0;
ret = genl_register_family(&cnss_genl_family);
if (ret != 0)
cnss_pr_err("genl_register_family fail: %d\n", ret);
return ret;
}
void cnss_genl_exit(void)
{
genl_unregister_family(&cnss_genl_family);
}

17
cnss2/genl.h Normal file
View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#ifndef __CNSS_GENL_H__
#define __CNSS_GENL_H__
enum cnss_genl_msg_type {
CNSS_GENL_MSG_TYPE_UNSPEC,
CNSS_GENL_MSG_TYPE_QDSS,
};
int cnss_genl_init(void);
void cnss_genl_exit(void);
int cnss_genl_send_msg(void *buff, u8 type,
char *file_name, u32 total_size);
#endif

View File

@@ -0,0 +1,450 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
#include "ip_multimedia_subsystem_private_service_v01.h"
static struct qmi_elem_info ims_private_service_header_value_v01_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = IMS_PRIVATE_SERVICE_HEADER_STR_LEN_V01 + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset =
offsetof(struct ims_private_service_header_value_v01, header),
},
{
.data_type = QMI_STRING,
.elem_len = IMS_PRIVATE_SERVICE_HEADER_STR_LEN_V01 + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0,
.offset =
offsetof(struct ims_private_service_header_value_v01, value),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info
ims_private_service_subscribe_for_indications_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
ims_private_service_subscribe_for_indications_req_msg_v01,
mt_invite_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
ims_private_service_subscribe_for_indications_req_msg_v01,
mt_invite),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct
ims_private_service_subscribe_for_indications_req_msg_v01,
wfc_call_status_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset = offsetof(struct
ims_private_service_subscribe_for_indications_req_msg_v01,
wfc_call_status),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info
ims_private_service_subscribe_for_indications_rsp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
ims_private_service_subscribe_for_indications_rsp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info ims_private_service_mt_invite_ind_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum ims_subscription_type_enum_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
subscription_type),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
iccid_valid),
},
{
.data_type = QMI_STRING,
.elem_len = IMS_PRIVATE_SERVICE_MAX_ICCID_LEN_V01 + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
iccid),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
header_value_list_valid),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
header_value_list_len),
},
{
.data_type = QMI_STRUCT,
.elem_len = IMS_PRIVATE_SERVICE_MAX_MT_INVITE_HEADERS_V01,
.elem_size =
sizeof(struct ims_private_service_header_value_v01),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct ims_private_service_mt_invite_ind_msg_v01,
header_value_list),
.ei_array = ims_private_service_header_value_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info ims_private_service_wfc_call_status_ind_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
wfc_call_active),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
all_wfc_calls_held_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
all_wfc_calls_held),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
is_wfc_emergency_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
is_wfc_emergency),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
twt_ims_start_valid),
},
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
twt_ims_start),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x13,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
twt_ims_int_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x13,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
twt_ims_int),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x14,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
media_quality_valid),
},
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum wfc_media_quality_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x14,
.offset =
offsetof(struct ims_private_service_wfc_call_status_ind_msg_v01,
media_quality),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info
ims_private_service_wfc_call_twt_config_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_start_valid),
},
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_start),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_int_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x11,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_int),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_upo_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x12,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_upo),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x13,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_sp_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x13,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_sp),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x14,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_dl_valid),
},
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x14,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_dl),
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x15,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_config_changed_valid),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x15,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_req_msg_v01,
twt_sta_config_changed),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
struct qmi_elem_info
ims_private_service_wfc_call_twt_config_rsp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset =
offsetof(struct
ims_private_service_wfc_call_twt_config_rsp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};

View File

@@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
#ifndef IP_MULTIMEDIA_SUBSYSTEM_PRIVATE_SERVICE_V01_H
#define IP_MULTIMEDIA_SUBSYSTEM_PRIVATE_SERVICE_V01_H
#include <linux/soc/qcom/qmi.h>
#define IMSPRIVATE_SERVICE_ID_V01 0x4D
#define IMSPRIVATE_SERVICE_VERS_V01 0x01
#define QMI_IMS_PRIVATE_SERVICE_SUBSCRIBE_FOR_INDICATIONS_RSP_V01 0x003E
#define QMI_IMS_PRIVATE_SERVICE_WFC_CALL_STATUS_IND_V01 0x0040
#define QMI_IMS_PRIVATE_SERVICE_SUBSCRIBE_FOR_INDICATIONS_REQ_V01 0x003E
#define QMI_IMS_PRIVATE_SERVICE_MT_INVITE_IND_V01 0x003F
#define QMI_IMS_PRIVATE_SERVICE_WFC_CALL_TWT_CONFIG_RSP_V01 0x0041
#define QMI_IMS_PRIVATE_SERVICE_WFC_CALL_TWT_CONFIG_REQ_V01 0x0041
#define IMS_PRIVATE_SERVICE_MAX_MT_INVITE_HEADERS_V01 15
#define IMS_PRIVATE_SERVICE_HEADER_STR_LEN_V01 1024
#define IMS_PRIVATE_SERVICE_MAX_ICCID_LEN_V01 21
enum ims_common_resp_enum_v01 {
IMS_COMMON_RESP_ENUM_MIN_VAL_V01 = INT_MIN,
IMS_COMMON_MSG_NO_ERR_V01 = 0,
IMS_COMMON_MSG_IMS_NOT_READY_V01 = 1,
IMS_COMMON_MSG_FILE_NOT_AVAILABLE_V01 = 2,
IMS_COMMON_MSG_READ_FAILED_V01 = 3,
IMS_COMMON_MSG_WRITE_FAILED_V01 = 4,
IMS_COMMON_MSG_OTHER_INTERNAL_ERR_V01 = 5,
IMS_COMMON_RESP_ENUM_MAX_VAL_V01 = INT_MAX,
};
enum ims_subscription_type_enum_v01 {
IMS_SUBSCRIPTION_TYPE_ENUM_MIN_VAL_V01 = INT_MIN,
IMS_SUBSCRIPTION_TYPE_NONE_V01 = -1,
IMS_SUBSCRIPTION_TYPE_PRIMARY_V01 = 0,
IMS_SUBSCRIPTION_TYPE_SECONDARY_V01 = 1,
IMS_SUBSCRIPTION_TYPE_TERTIARY_V01 = 2,
IMS_SUBSCRIPTION_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
};
enum wfc_media_quality_v01 {
WFC_MEDIA_QUALITY_MIN_VAL_V01 = INT_MIN,
WFC_MEDIA_QUAL_NOT_AVAILABLE_V01 = 0,
WFC_MEDIA_QUAL_BAD_V01 = 1,
WFC_MEDIA_QUAL_GOOD_V01 = 2,
WFC_MEDIA_QUAL_EXCELLENT_V01 = 3,
WFC_MEDIA_QUALITY_MAX_VAL_V01 = INT_MAX,
};
struct ims_private_service_header_value_v01 {
char header[IMS_PRIVATE_SERVICE_HEADER_STR_LEN_V01 + 1];
char value[IMS_PRIVATE_SERVICE_HEADER_STR_LEN_V01 + 1];
};
struct ims_private_service_subscribe_for_indications_req_msg_v01 {
u8 mt_invite_valid;
u8 mt_invite;
u8 wfc_call_status_valid;
u8 wfc_call_status;
};
#define IMS_PRIVATE_SERVICE_SUBSCRIBE_FOR_INDICATIONS_REQ_MSG_V01_MAX_MSG_LEN 8
extern struct qmi_elem_info
ims_private_service_subscribe_for_indications_req_msg_v01_ei[];
struct ims_private_service_subscribe_for_indications_rsp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define IMS_PRIVATE_SERVICE_SUBSCRIBE_FOR_INDICATIONS_RSP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info
ims_private_service_subscribe_for_indications_rsp_msg_v01_ei[];
struct ims_private_service_mt_invite_ind_msg_v01 {
enum ims_subscription_type_enum_v01 subscription_type;
u8 iccid_valid;
char iccid[IMS_PRIVATE_SERVICE_MAX_ICCID_LEN_V01 + 1];
u8 header_value_list_valid;
u32 header_value_list_len;
struct ims_private_service_header_value_v01
header_value_list[IMS_PRIVATE_SERVICE_MAX_MT_INVITE_HEADERS_V01];
};
#define IMS_PRIVATE_SERVICE_MT_INVITE_IND_MSG_V01_MAX_MSG_LEN 30815
extern struct qmi_elem_info ims_private_service_mt_invite_ind_msg_v01_ei[];
struct ims_private_service_wfc_call_status_ind_msg_v01 {
u8 wfc_call_active;
u8 all_wfc_calls_held_valid;
u8 all_wfc_calls_held;
u8 is_wfc_emergency_valid;
u8 is_wfc_emergency;
u8 twt_ims_start_valid;
u64 twt_ims_start;
u8 twt_ims_int_valid;
u16 twt_ims_int;
u8 media_quality_valid;
enum wfc_media_quality_v01 media_quality;
};
#define IMS_PRIVATE_SERVICE_WFC_CALL_STATUS_IND_MSG_V01_MAX_MSG_LEN 35
extern struct qmi_elem_info
ims_private_service_wfc_call_status_ind_msg_v01_ei[];
struct ims_private_service_wfc_call_twt_config_req_msg_v01 {
u8 twt_sta_start_valid;
u64 twt_sta_start;
u8 twt_sta_int_valid;
u16 twt_sta_int;
u8 twt_sta_upo_valid;
u16 twt_sta_upo;
u8 twt_sta_sp_valid;
u16 twt_sta_sp;
u8 twt_sta_dl_valid;
u16 twt_sta_dl;
u8 twt_sta_config_changed_valid;
u8 twt_sta_config_changed;
};
#define IMS_PRIVATE_SERVICE_WFC_CALL_TWT_CONFIG_REQ_MSG_V01_MAX_MSG_LEN 35
extern struct qmi_elem_info
ims_private_service_wfc_call_twt_config_req_msg_v01_ei[];
struct ims_private_service_wfc_call_twt_config_rsp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define IMS_PRIVATE_SERVICE_WFC_CALL_TWT_CONFIG_RSP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info
ims_private_service_wfc_call_twt_config_rsp_msg_v01_ei[];
#endif

3477
cnss2/main.c Normal file

File diff suppressed because it is too large Load Diff

594
cnss2/main.h Normal file
View File

@@ -0,0 +1,594 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_MAIN_H
#define _CNSS_MAIN_H
#if IS_ENABLED(CONFIG_ARM) || IS_ENABLED(CONFIG_ARM64)
#include <asm/arch_timer.h>
#endif
#if IS_ENABLED(CONFIG_ESOC)
#include <linux/esoc_client.h>
#endif
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#if IS_ENABLED(CONFIG_INTERCONNECT)
#include <linux/interconnect.h>
#endif
#include <linux/mailbox_client.h>
#include <linux/pm_qos.h>
#include <linux/platform_device.h>
#include <linux/time64.h>
#include <net/cnss2.h>
#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
#include <soc/qcom/memory_dump.h>
#endif
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) || \
IS_ENABLED(CONFIG_QCOM_RAMDUMP)
#include <soc/qcom/qcom_ramdump.h>
#endif
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART)
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
#endif
#include "qmi.h"
#define MAX_NO_OF_MAC_ADDR 4
#define QMI_WLFW_MAX_TIMESTAMP_LEN 32
#define QMI_WLFW_MAX_NUM_MEM_SEG 32
#define QMI_WLFW_MAX_BUILD_ID_LEN 128
#define CNSS_RDDM_TIMEOUT_MS 20000
#define RECOVERY_TIMEOUT 60000
#define WLAN_WD_TIMEOUT_MS 60000
#define WLAN_COLD_BOOT_CAL_TIMEOUT 60000
#define WLAN_MISSION_MODE_TIMEOUT 30000
#define TIME_CLOCK_FREQ_HZ 19200000
#define CNSS_RAMDUMP_MAGIC 0x574C414E
#define CNSS_RAMDUMP_VERSION 0
#define MAX_FIRMWARE_NAME_LEN 40
#define FW_V2_NUMBER 2
#define POWER_ON_RETRY_MAX_TIMES 3
#define POWER_ON_RETRY_DELAY_MS 200
#define CNSS_EVENT_SYNC BIT(0)
#define CNSS_EVENT_UNINTERRUPTIBLE BIT(1)
#define CNSS_EVENT_UNKILLABLE BIT(2)
#define CNSS_EVENT_SYNC_UNINTERRUPTIBLE (CNSS_EVENT_SYNC | \
CNSS_EVENT_UNINTERRUPTIBLE)
#define CNSS_EVENT_SYNC_UNKILLABLE (CNSS_EVENT_SYNC | CNSS_EVENT_UNKILLABLE)
enum cnss_dev_bus_type {
CNSS_BUS_NONE = -1,
CNSS_BUS_PCI,
};
struct cnss_vreg_cfg {
const char *name;
u32 min_uv;
u32 max_uv;
u32 load_ua;
u32 delay_us;
u32 need_unvote;
};
struct cnss_vreg_info {
struct list_head list;
struct regulator *reg;
struct cnss_vreg_cfg cfg;
u32 enabled;
};
enum cnss_vreg_type {
CNSS_VREG_PRIM,
};
struct cnss_clk_cfg {
const char *name;
u32 freq;
u32 required;
};
struct cnss_clk_info {
struct list_head list;
struct clk *clk;
struct cnss_clk_cfg cfg;
u32 enabled;
};
struct cnss_pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *bootstrap_active;
struct pinctrl_state *wlan_en_active;
struct pinctrl_state *wlan_en_sleep;
int bt_en_gpio;
int xo_clk_gpio; /*qca6490 only */
};
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART)
struct cnss_subsys_info {
struct subsys_device *subsys_device;
struct subsys_desc subsys_desc;
void *subsys_handle;
};
#endif
struct cnss_ramdump_info {
void *ramdump_dev;
unsigned long ramdump_size;
void *ramdump_va;
phys_addr_t ramdump_pa;
#if IS_ENABLED(CONFIG_QCOM_MEMORY_DUMP_V2)
struct msm_dump_data dump_data;
#endif
};
struct cnss_dump_seg {
unsigned long address;
void *v_address;
unsigned long size;
u32 type;
};
struct cnss_dump_data {
u32 version;
u32 magic;
char name[32];
phys_addr_t paddr;
int nentries;
u32 seg_version;
};
struct cnss_ramdump_info_v2 {
void *ramdump_dev;
unsigned long ramdump_size;
void *dump_data_vaddr;
u8 dump_data_valid;
struct cnss_dump_data dump_data;
};
#if IS_ENABLED(CONFIG_ESOC)
struct cnss_esoc_info {
struct esoc_desc *esoc_desc;
u8 notify_modem_status;
void *modem_notify_handler;
int modem_current_status;
};
#endif
#if IS_ENABLED(CONFIG_INTERCONNECT)
/**
* struct cnss_bus_bw_cfg - Interconnect vote data
* @avg_bw: Vote for average bandwidth
* @peak_bw: Vote for peak bandwidth
*/
struct cnss_bus_bw_cfg {
u32 avg_bw;
u32 peak_bw;
};
/* Number of bw votes (avg, peak) entries that ICC requires */
#define CNSS_ICC_VOTE_MAX 2
/**
* struct cnss_bus_bw_info - Bus bandwidth config for interconnect path
* @list: Kernel linked list
* @icc_name: Name of interconnect path as defined in Device tree
* @icc_path: Interconnect path data structure
* @cfg_table: Interconnect vote data for average and peak bandwidth
*/
struct cnss_bus_bw_info {
struct list_head list;
const char *icc_name;
struct icc_path *icc_path;
struct cnss_bus_bw_cfg *cfg_table;
};
#endif
/**
* struct cnss_interconnect_cfg - CNSS platform interconnect config
* @list_head: List of interconnect path bandwidth configs
* @path_count: Count of interconnect path configured in device tree
* @current_bw_vote: WLAN driver provided bandwidth vote
* @bus_bw_cfg_count: Number of bandwidth configs for voting. It is the array
* size of struct cnss_bus_bw_info.cfg_table
*/
struct cnss_interconnect_cfg {
struct list_head list_head;
u32 path_count;
int current_bw_vote;
u32 bus_bw_cfg_count;
};
struct cnss_fw_mem {
size_t size;
void *va;
phys_addr_t pa;
u8 valid;
u32 type;
unsigned long attrs;
};
struct wlfw_rf_chip_info {
u32 chip_id;
u32 chip_family;
};
struct wlfw_rf_board_info {
u32 board_id;
};
struct wlfw_soc_info {
u32 soc_id;
};
struct wlfw_fw_version_info {
u32 fw_version;
char fw_build_timestamp[QMI_WLFW_MAX_TIMESTAMP_LEN + 1];
};
enum cnss_mem_type {
CNSS_MEM_TYPE_MSA,
CNSS_MEM_TYPE_DDR,
CNSS_MEM_BDF,
CNSS_MEM_M3,
CNSS_MEM_CAL_V01,
CNSS_MEM_DPD_V01,
};
enum cnss_fw_dump_type {
CNSS_FW_IMAGE,
CNSS_FW_RDDM,
CNSS_FW_REMOTE_HEAP,
CNSS_FW_DUMP_TYPE_MAX,
};
struct cnss_dump_entry {
u32 type;
u32 entry_start;
u32 entry_num;
};
struct cnss_dump_meta_info {
u32 magic;
u32 version;
u32 chipset;
u32 total_entries;
struct cnss_dump_entry entry[CNSS_FW_DUMP_TYPE_MAX];
};
enum cnss_driver_event_type {
CNSS_DRIVER_EVENT_SERVER_ARRIVE,
CNSS_DRIVER_EVENT_SERVER_EXIT,
CNSS_DRIVER_EVENT_REQUEST_MEM,
CNSS_DRIVER_EVENT_FW_MEM_READY,
CNSS_DRIVER_EVENT_FW_READY,
CNSS_DRIVER_EVENT_COLD_BOOT_CAL_START,
CNSS_DRIVER_EVENT_COLD_BOOT_CAL_DONE,
CNSS_DRIVER_EVENT_REGISTER_DRIVER,
CNSS_DRIVER_EVENT_UNREGISTER_DRIVER,
CNSS_DRIVER_EVENT_RECOVERY,
CNSS_DRIVER_EVENT_FORCE_FW_ASSERT,
CNSS_DRIVER_EVENT_POWER_UP,
CNSS_DRIVER_EVENT_POWER_DOWN,
CNSS_DRIVER_EVENT_IDLE_RESTART,
CNSS_DRIVER_EVENT_IDLE_SHUTDOWN,
CNSS_DRIVER_EVENT_IMS_WFC_CALL_IND,
CNSS_DRIVER_EVENT_WLFW_TWT_CFG_IND,
CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
CNSS_DRIVER_EVENT_FW_MEM_FILE_SAVE,
CNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA,
CNSS_DRIVER_EVENT_MAX,
};
enum cnss_driver_state {
CNSS_QMI_WLFW_CONNECTED = 0,
CNSS_FW_MEM_READY,
CNSS_FW_READY,
CNSS_IN_COLD_BOOT_CAL,
CNSS_DRIVER_LOADING,
CNSS_DRIVER_UNLOADING = 5,
CNSS_DRIVER_IDLE_RESTART,
CNSS_DRIVER_IDLE_SHUTDOWN,
CNSS_DRIVER_PROBED,
CNSS_DRIVER_RECOVERY,
CNSS_FW_BOOT_RECOVERY = 10,
CNSS_DEV_ERR_NOTIFY,
CNSS_DRIVER_DEBUG,
CNSS_COEX_CONNECTED,
CNSS_IMS_CONNECTED,
CNSS_IN_SUSPEND_RESUME = 15,
CNSS_IN_REBOOT,
CNSS_COLD_BOOT_CAL_DONE,
CNSS_IN_PANIC,
CNSS_QMI_DEL_SERVER,
CNSS_QMI_DMS_CONNECTED = 20,
CNSS_DAEMON_CONNECTED,
CNSS_PCI_PROBE_DONE,
};
struct cnss_recovery_data {
enum cnss_recovery_reason reason;
};
enum cnss_pins {
CNSS_WLAN_EN,
CNSS_PCIE_TXP,
CNSS_PCIE_TXN,
CNSS_PCIE_RXP,
CNSS_PCIE_RXN,
CNSS_PCIE_REFCLKP,
CNSS_PCIE_REFCLKN,
CNSS_PCIE_RST,
CNSS_PCIE_WAKE,
};
struct cnss_pin_connect_result {
u32 fw_pwr_pin_result;
u32 fw_phy_io_pin_result;
u32 fw_rf_pin_result;
u32 host_pin_result;
};
enum cnss_debug_quirks {
LINK_DOWN_SELF_RECOVERY,
SKIP_DEVICE_BOOT,
USE_CORE_ONLY_FW,
SKIP_RECOVERY,
QMI_BYPASS,
ENABLE_WALTEST,
ENABLE_PCI_LINK_DOWN_PANIC,
FBC_BYPASS,
ENABLE_DAEMON_SUPPORT,
DISABLE_DRV,
DISABLE_IO_COHERENCY,
IGNORE_PCI_LINK_FAILURE,
DISABLE_TIME_SYNC,
};
enum cnss_bdf_type {
CNSS_BDF_BIN,
CNSS_BDF_ELF,
CNSS_BDF_REGDB = 4,
CNSS_BDF_HDS = 6,
};
enum cnss_cal_status {
CNSS_CAL_DONE,
CNSS_CAL_TIMEOUT,
CNSS_CAL_FAILURE,
};
struct cnss_cal_info {
enum cnss_cal_status cal_status;
};
struct cnss_control_params {
unsigned long quirks;
unsigned int mhi_timeout;
unsigned int mhi_m2_timeout;
unsigned int qmi_timeout;
unsigned int bdf_type;
unsigned int time_sync_period;
};
struct cnss_tcs_info {
resource_size_t cmd_base_addr;
void __iomem *cmd_base_addr_io;
};
struct cnss_cpr_info {
resource_size_t tcs_cmd_data_addr;
void __iomem *tcs_cmd_data_addr_io;
u32 cpr_pmic_addr;
u32 voltage;
};
enum cnss_ce_index {
CNSS_CE_00,
CNSS_CE_01,
CNSS_CE_02,
CNSS_CE_03,
CNSS_CE_04,
CNSS_CE_05,
CNSS_CE_06,
CNSS_CE_07,
CNSS_CE_08,
CNSS_CE_09,
CNSS_CE_10,
CNSS_CE_11,
CNSS_CE_COMMON,
};
struct cnss_dms_data {
u32 mac_valid;
u8 mac[QMI_WLFW_MAC_ADDR_SIZE_V01];
};
enum cnss_timeout_type {
CNSS_TIMEOUT_QMI,
CNSS_TIMEOUT_POWER_UP,
CNSS_TIMEOUT_IDLE_RESTART,
CNSS_TIMEOUT_CALIBRATION,
CNSS_TIMEOUT_WLAN_WATCHDOG,
CNSS_TIMEOUT_RDDM,
CNSS_TIMEOUT_RECOVERY,
CNSS_TIMEOUT_DAEMON_CONNECTION,
};
struct cnss_plat_data {
struct platform_device *plat_dev;
void *bus_priv;
enum cnss_dev_bus_type bus_type;
struct list_head vreg_list;
struct list_head clk_list;
struct cnss_pinctrl_info pinctrl_info;
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART)
struct cnss_subsys_info subsys_info;
#endif
struct cnss_ramdump_info ramdump_info;
struct cnss_ramdump_info_v2 ramdump_info_v2;
#if IS_ENABLED(CONFIG_ESOC)
struct cnss_esoc_info esoc_info;
#endif
struct cnss_interconnect_cfg icc;
struct notifier_block modem_nb;
struct notifier_block reboot_nb;
struct notifier_block panic_nb;
struct cnss_platform_cap cap;
struct pm_qos_request qos_request;
struct cnss_device_version device_version;
u32 rc_num;
unsigned long device_id;
enum cnss_driver_status driver_status;
u32 recovery_count;
u8 recovery_enabled;
u8 hds_enabled;
unsigned long driver_state;
struct list_head event_list;
spinlock_t event_lock; /* spinlock for driver work event handling */
struct work_struct event_work;
struct workqueue_struct *event_wq;
struct work_struct recovery_work;
struct delayed_work wlan_reg_driver_work;
struct qmi_handle qmi_wlfw;
struct qmi_handle qmi_dms;
struct wlfw_rf_chip_info chip_info;
struct wlfw_rf_board_info board_info;
struct wlfw_soc_info soc_info;
struct wlfw_fw_version_info fw_version_info;
struct cnss_dev_mem_info dev_mem_info[CNSS_MAX_DEV_MEM_NUM];
char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN + 1];
u32 otp_version;
u32 fw_mem_seg_len;
struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG];
struct cnss_fw_mem m3_mem;
struct cnss_fw_mem *cal_mem;
u64 cal_time;
bool cbc_file_download;
u32 cal_file_size;
struct completion daemon_connected;
u32 qdss_mem_seg_len;
struct cnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG];
u32 *qdss_reg;
struct cnss_pin_connect_result pin_result;
struct dentry *root_dentry;
atomic_t pm_count;
struct timer_list fw_boot_timer;
struct completion power_up_complete;
struct completion cal_complete;
struct mutex dev_lock; /* mutex for register access through debugfs */
struct mutex driver_ops_lock; /* mutex for external driver ops */
u32 device_freq_hz;
u32 diag_reg_read_addr;
u32 diag_reg_read_mem_type;
u32 diag_reg_read_len;
u8 *diag_reg_read_buf;
u8 cal_done;
u8 powered_on;
u8 use_fw_path_with_prefix;
char firmware_name[MAX_FIRMWARE_NAME_LEN];
char fw_fallback_name[MAX_FIRMWARE_NAME_LEN];
struct completion rddm_complete;
struct completion recovery_complete;
struct cnss_control_params ctrl_params;
struct cnss_cpr_info cpr_info;
u64 antenna;
u64 grant;
struct qmi_handle coex_qmi;
struct qmi_handle ims_qmi;
struct qmi_txn txn;
struct wakeup_source *recovery_ws;
u64 dynamic_feature;
void *get_info_cb_ctx;
int (*get_info_cb)(void *ctx, void *event, int event_len);
bool cbc_enabled;
u8 use_pm_domain;
u8 use_nv_mac;
u8 set_wlaon_pwr_ctrl;
struct cnss_tcs_info tcs_info;
bool fw_pcie_gen_switch;
u8 pcie_gen_speed;
struct cnss_dms_data dms;
int power_up_error;
u32 hw_trc_override;
struct mbox_client mbox_client_data;
struct mbox_chan *mbox_chan;
const char *vreg_ol_cpr, *vreg_ipa;
bool adsp_pc_enabled;
u64 feature_list;
};
#if IS_ENABLED(CONFIG_ARCH_QCOM)
static inline u64 cnss_get_host_timestamp(struct cnss_plat_data *plat_priv)
{
u64 ticks = __arch_counter_get_cntvct();
do_div(ticks, TIME_CLOCK_FREQ_HZ / 100000);
return ticks * 10;
}
#else
static inline u64 cnss_get_host_timestamp(struct cnss_plat_data *plat_priv)
{
struct timespec64 ts;
ktime_get_ts64(&ts);
return (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
}
#endif
struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
void cnss_pm_stay_awake(struct cnss_plat_data *plat_priv);
void cnss_pm_relax(struct cnss_plat_data *plat_priv);
int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
enum cnss_driver_event_type type,
u32 flags, void *data);
int cnss_get_vreg_type(struct cnss_plat_data *plat_priv,
enum cnss_vreg_type type);
void cnss_put_vreg_type(struct cnss_plat_data *plat_priv,
enum cnss_vreg_type type);
int cnss_vreg_on_type(struct cnss_plat_data *plat_priv,
enum cnss_vreg_type type);
int cnss_vreg_off_type(struct cnss_plat_data *plat_priv,
enum cnss_vreg_type type);
int cnss_get_clk(struct cnss_plat_data *plat_priv);
void cnss_put_clk(struct cnss_plat_data *plat_priv);
int cnss_vreg_unvote_type(struct cnss_plat_data *plat_priv,
enum cnss_vreg_type type);
int cnss_get_pinctrl(struct cnss_plat_data *plat_priv);
int cnss_power_on_device(struct cnss_plat_data *plat_priv);
void cnss_power_off_device(struct cnss_plat_data *plat_priv);
bool cnss_is_device_powered_on(struct cnss_plat_data *plat_priv);
int cnss_register_subsys(struct cnss_plat_data *plat_priv);
void cnss_unregister_subsys(struct cnss_plat_data *plat_priv);
int cnss_register_ramdump(struct cnss_plat_data *plat_priv);
void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv);
int cnss_do_ramdump(struct cnss_plat_data *plat_priv);
int cnss_do_elf_ramdump(struct cnss_plat_data *plat_priv);
void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv);
int cnss_get_cpr_info(struct cnss_plat_data *plat_priv);
int cnss_update_cpr_info(struct cnss_plat_data *plat_priv);
int cnss_va_to_pa(struct device *dev, size_t size, void *va, dma_addr_t dma,
phys_addr_t *pa, unsigned long attrs);
int cnss_minidump_add_region(struct cnss_plat_data *plat_priv,
enum cnss_fw_dump_type type, int seg_no,
void *va, phys_addr_t pa, size_t size);
int cnss_minidump_remove_region(struct cnss_plat_data *plat_priv,
enum cnss_fw_dump_type type, int seg_no,
void *va, phys_addr_t pa, size_t size);
int cnss_enable_int_pow_amp_vreg(struct cnss_plat_data *plat_priv);
int cnss_get_tcs_info(struct cnss_plat_data *plat_priv);
unsigned int cnss_get_timeout(struct cnss_plat_data *plat_priv,
enum cnss_timeout_type);
int cnss_aop_mbox_init(struct cnss_plat_data *plat_priv);
int cnss_request_firmware_direct(struct cnss_plat_data *plat_priv,
const struct firmware **fw_entry,
const char *filename);
int cnss_set_feature_list(struct cnss_plat_data *plat_priv,
enum cnss_feature_v01 feature);
int cnss_get_feature_list(struct cnss_plat_data *plat_priv,
u64 *feature_list);
#endif /* _CNSS_MAIN_H */

6190
cnss2/pci.c Normal file

File diff suppressed because it is too large Load Diff

263
cnss2/pci.h Normal file
View File

@@ -0,0 +1,263 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_PCI_H
#define _CNSS_PCI_H
#include <linux/iommu.h>
#include <linux/mhi.h>
#if IS_ENABLED(CONFIG_MHI_BUS_MISC)
#include <linux/mhi_misc.h>
#endif
#if IS_ENABLED(CONFIG_PCI_MSM)
#include <linux/msm_pcie.h>
#endif
#include <linux/pci.h>
#include "main.h"
enum cnss_mhi_state {
CNSS_MHI_INIT,
CNSS_MHI_DEINIT,
CNSS_MHI_POWER_ON,
CNSS_MHI_POWERING_OFF,
CNSS_MHI_POWER_OFF,
CNSS_MHI_FORCE_POWER_OFF,
CNSS_MHI_SUSPEND,
CNSS_MHI_RESUME,
CNSS_MHI_TRIGGER_RDDM,
CNSS_MHI_RDDM,
CNSS_MHI_RDDM_DONE,
};
enum pci_link_status {
PCI_GEN1,
PCI_GEN2,
PCI_DEF,
};
enum cnss_rtpm_id {
RTPM_ID_CNSS,
RTPM_ID_MHI,
RTPM_ID_MAX,
};
enum cnss_pci_reg_dev_mask {
REG_MASK_QCA6390,
REG_MASK_QCA6490,
REG_MASK_WCN7850,
};
struct cnss_msi_user {
char *name;
int num_vectors;
u32 base_vector;
};
struct cnss_msi_config {
int total_vectors;
int total_users;
struct cnss_msi_user *users;
};
struct cnss_pci_reg {
char *name;
u32 offset;
};
struct cnss_pci_debug_reg {
u32 offset;
u32 val;
};
struct cnss_misc_reg {
unsigned long dev_mask;
u8 wr;
u32 offset;
u32 val;
};
struct cnss_pm_stats {
atomic_t runtime_get;
atomic_t runtime_put;
atomic_t runtime_get_id[RTPM_ID_MAX];
atomic_t runtime_put_id[RTPM_ID_MAX];
u64 runtime_get_timestamp_id[RTPM_ID_MAX];
u64 runtime_put_timestamp_id[RTPM_ID_MAX];
};
struct cnss_pci_data {
struct pci_dev *pci_dev;
struct cnss_plat_data *plat_priv;
const struct pci_device_id *pci_device_id;
u32 device_id;
u16 revision_id;
u64 dma_bit_mask;
struct cnss_wlan_driver *driver_ops;
u8 pci_link_state;
u8 pci_link_down_ind;
struct pci_saved_state *saved_state;
struct pci_saved_state *default_state;
#if IS_ENABLED(CONFIG_PCI_MSM)
struct msm_pcie_register_event msm_pci_event;
#endif
struct cnss_pm_stats pm_stats;
atomic_t auto_suspended;
atomic_t drv_connected;
u8 drv_connected_last;
u32 qmi_send_usage_count;
u16 def_link_speed;
u16 def_link_width;
u16 cur_link_speed;
int wake_gpio;
int wake_irq;
u32 wake_counter;
u8 monitor_wake_intr;
struct iommu_domain *iommu_domain;
u8 smmu_s1_enable;
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;
void __iomem *bar;
struct cnss_msi_config *msi_config;
u32 msi_ep_base_data;
struct mhi_controller *mhi_ctrl;
unsigned long mhi_state;
u32 remap_window;
struct timer_list dev_rddm_timer;
struct timer_list boot_debug_timer;
struct delayed_work time_sync_work;
u8 disable_pc;
struct mutex bus_lock; /* mutex for suspend and resume bus */
struct cnss_pci_debug_reg *debug_reg;
struct cnss_misc_reg *wcss_reg;
struct cnss_misc_reg *pcie_reg;
struct cnss_misc_reg *wlaon_reg;
struct cnss_misc_reg *syspm_reg;
unsigned long misc_reg_dev_mask;
u8 iommu_geometry;
bool drv_supported;
};
static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data)
{
pci_set_drvdata(pci_dev, data);
}
static inline struct cnss_pci_data *cnss_get_pci_priv(struct pci_dev *pci_dev)
{
return pci_get_drvdata(pci_dev);
}
static inline struct cnss_plat_data *cnss_pci_priv_to_plat_priv(void *bus_priv)
{
struct cnss_pci_data *pci_priv = bus_priv;
return pci_priv->plat_priv;
}
static inline void cnss_pci_set_monitor_wake_intr(void *bus_priv, bool val)
{
struct cnss_pci_data *pci_priv = bus_priv;
pci_priv->monitor_wake_intr = val;
}
static inline bool cnss_pci_get_monitor_wake_intr(void *bus_priv)
{
struct cnss_pci_data *pci_priv = bus_priv;
return pci_priv->monitor_wake_intr;
}
static inline void cnss_pci_set_auto_suspended(void *bus_priv, int val)
{
struct cnss_pci_data *pci_priv = bus_priv;
atomic_set(&pci_priv->auto_suspended, val);
}
static inline int cnss_pci_get_auto_suspended(void *bus_priv)
{
struct cnss_pci_data *pci_priv = bus_priv;
return atomic_read(&pci_priv->auto_suspended);
}
static inline void cnss_pci_set_drv_connected(void *bus_priv, int val)
{
struct cnss_pci_data *pci_priv = bus_priv;
atomic_set(&pci_priv->drv_connected, val);
}
static inline int cnss_pci_get_drv_connected(void *bus_priv)
{
struct cnss_pci_data *pci_priv = bus_priv;
return atomic_read(&pci_priv->drv_connected);
}
int cnss_pci_check_link_status(struct cnss_pci_data *pci_priv);
int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv);
int cnss_resume_pci_link(struct cnss_pci_data *pci_priv);
int cnss_pci_recover_link_down(struct cnss_pci_data *pci_priv);
int cnss_pci_init(struct cnss_plat_data *plat_priv);
void cnss_pci_deinit(struct cnss_plat_data *plat_priv);
void cnss_pci_add_fw_prefix_name(struct cnss_pci_data *pci_priv,
char *prefix_name, char *name);
int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv);
int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv);
void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv);
int cnss_pci_load_m3(struct cnss_pci_data *pci_priv);
int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv);
void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic);
void cnss_pci_device_crashed(struct cnss_pci_data *pci_priv);
void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv);
u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv);
int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv);
int cnss_pci_qmi_send_get(struct cnss_pci_data *pci_priv);
int cnss_pci_qmi_send_put(struct cnss_pci_data *pci_priv);
void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv);
int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv);
int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv);
int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv);
int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv);
int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv);
int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv);
int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv, void *data);
int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv);
int cnss_pci_call_driver_modem_status(struct cnss_pci_data *pci_priv,
int modem_current_status);
void cnss_pci_pm_runtime_show_usage_count(struct cnss_pci_data *pci_priv);
int cnss_pci_pm_request_resume(struct cnss_pci_data *pci_priv);
int cnss_pci_pm_runtime_resume(struct cnss_pci_data *pci_priv);
int cnss_pci_pm_runtime_get(struct cnss_pci_data *pci_priv,
enum cnss_rtpm_id id);
int cnss_pci_pm_runtime_get_sync(struct cnss_pci_data *pci_priv,
enum cnss_rtpm_id id);
void cnss_pci_pm_runtime_get_noresume(struct cnss_pci_data *pci_priv,
enum cnss_rtpm_id id);
int cnss_pci_pm_runtime_put_autosuspend(struct cnss_pci_data *pci_priv,
enum cnss_rtpm_id id);
void cnss_pci_pm_runtime_put_noidle(struct cnss_pci_data *pci_priv,
enum cnss_rtpm_id id);
void cnss_pci_pm_runtime_mark_last_busy(struct cnss_pci_data *pci_priv);
int cnss_pci_update_status(struct cnss_pci_data *pci_priv,
enum cnss_driver_status status);
int cnss_pci_call_driver_uevent(struct cnss_pci_data *pci_priv,
enum cnss_driver_status status, void *data);
int cnss_pcie_is_device_down(struct cnss_pci_data *pci_priv);
int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv);
int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv);
int cnss_pci_debug_reg_read(struct cnss_pci_data *pci_priv, u32 offset,
u32 *val, bool raw_access);
int cnss_pci_debug_reg_write(struct cnss_pci_data *pci_priv, u32 offset,
u32 val, bool raw_access);
int cnss_pci_get_iova(struct cnss_pci_data *pci_priv, u64 *addr, u64 *size);
int cnss_pci_get_iova_ipa(struct cnss_pci_data *pci_priv, u64 *addr,
u64 *size);
#endif /* _CNSS_PCI_H */

1283
cnss2/power.c Normal file

File diff suppressed because it is too large Load Diff

3448
cnss2/qmi.c Normal file

File diff suppressed because it is too large Load Diff

316
cnss2/qmi.h Normal file
View File

@@ -0,0 +1,316 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_QMI_H
#define _CNSS_QMI_H
#include "wlan_firmware_service_v01.h"
struct cnss_plat_data;
struct cnss_qmi_event_server_arrive_data {
unsigned int node;
unsigned int port;
};
struct cnss_mem_seg {
u64 addr;
u32 size;
};
struct cnss_qmi_event_fw_mem_file_save_data {
u32 total_size;
u32 mem_seg_len;
enum wlfw_mem_type_enum_v01 mem_type;
struct cnss_mem_seg mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
char file_name[QMI_WLFW_MAX_STR_LEN_V01 + 1];
};
#ifdef CONFIG_CNSS2_QMI
#include "coexistence_service_v01.h"
#include "ip_multimedia_subsystem_private_service_v01.h"
#include "device_management_service_v01.h"
int cnss_qmi_init(struct cnss_plat_data *plat_priv);
void cnss_qmi_deinit(struct cnss_plat_data *plat_priv);
unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv);
int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv, void *data);
int cnss_wlfw_server_exit(struct cnss_plat_data *plat_priv);
int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
u32 bdf_type);
int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv,
enum cnss_driver_mode mode);
int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv,
struct cnss_wlan_enable_cfg *config,
const char *host_version);
int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv,
u32 offset, u32 mem_type,
u32 data_len, u8 *data);
int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv,
u32 offset, u32 mem_type,
u32 data_len, u8 *data);
int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv,
u8 fw_log_mode);
int cnss_wlfw_antenna_switch_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_antenna_grant_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_dynamic_feature_mask_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type,
void *cmd, int cmd_len);
int cnss_process_wfc_call_ind_event(struct cnss_plat_data *plat_priv,
void *data);
int cnss_process_twt_cfg_ind_event(struct cnss_plat_data *plat_priv,
void *data);
int cnss_register_coex_service(struct cnss_plat_data *plat_priv);
void cnss_unregister_coex_service(struct cnss_plat_data *plat_priv);
int coex_antenna_switch_to_wlan_send_sync_msg(struct cnss_plat_data *plat_priv);
int coex_antenna_switch_to_mdm_send_sync_msg(struct cnss_plat_data *plat_priv);
int cnss_wlfw_qdss_trace_mem_info_send_sync(struct cnss_plat_data *plat_priv);
int cnss_register_ims_service(struct cnss_plat_data *plat_priv);
void cnss_unregister_ims_service(struct cnss_plat_data *plat_priv);
int cnss_wlfw_send_pcie_gen_speed_sync(struct cnss_plat_data *plat_priv);
void cnss_ignore_qmi_failure(bool ignore);
int cnss_qmi_get_dms_mac(struct cnss_plat_data *plat_priv);
int cnss_wlfw_wlan_mac_req_send_sync(struct cnss_plat_data *plat_priv,
u8 *mac, u32 mac_len);
int cnss_dms_init(struct cnss_plat_data *plat_priv);
void cnss_dms_deinit(struct cnss_plat_data *plat_priv);
int cnss_wlfw_qdss_dnld_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_qdss_data_send_sync(struct cnss_plat_data *plat_priv, char *file_name,
u32 total_size);
int wlfw_qdss_trace_start(struct cnss_plat_data *plat_priv);
int wlfw_qdss_trace_stop(struct cnss_plat_data *plat_priv, unsigned long long option);
int cnss_wlfw_cal_report_req_send_sync(struct cnss_plat_data *plat_priv,
u32 cal_file_download_size);
#else
#define QMI_WLFW_TIMEOUT_MS 10000
static inline int cnss_qmi_init(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline void cnss_qmi_deinit(struct cnss_plat_data *plat_priv)
{
}
static inline
unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv)
{
return QMI_WLFW_TIMEOUT_MS;
}
static inline int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv,
void *data)
{
return 0;
}
static inline int cnss_wlfw_server_exit(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv,
u32 bdf_type)
{
return 0;
}
static inline int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv,
enum cnss_driver_mode mode)
{
return 0;
}
static inline
int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv,
struct cnss_wlan_enable_cfg *config,
const char *host_version)
{
return 0;
}
static inline
int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv,
u32 offset, u32 mem_type,
u32 data_len, u8 *data)
{
return 0;
}
static inline
int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv,
u32 offset, u32 mem_type,
u32 data_len, u8 *data)
{
return 0;
}
static inline
int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv,
u8 fw_log_mode)
{
return 0;
}
static inline
int cnss_wlfw_antenna_switch_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_antenna_grant_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_dynamic_feature_mask_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type,
void *cmd, int cmd_len)
{
return 0;
}
static inline
int cnss_process_wfc_call_ind_event(struct cnss_plat_data *plat_priv,
void *data)
{
return 0;
}
static inline
int cnss_process_twt_cfg_ind_event(struct cnss_plat_data *plat_priv,
void *data)
{
return 0;
}
static inline
int cnss_register_coex_service(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
void cnss_unregister_coex_service(struct cnss_plat_data *plat_priv) {}
static inline
int coex_antenna_switch_to_wlan_send_sync_msg(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int coex_antenna_switch_to_mdm_send_sync_msg(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_wlfw_qdss_trace_mem_info_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
int cnss_register_ims_service(struct cnss_plat_data *plat_priv)
{
return 0;
}
static inline
void cnss_unregister_ims_service(struct cnss_plat_data *plat_priv) {}
static inline
int cnss_wlfw_send_pcie_gen_speed_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
void cnss_ignore_qmi_failure(bool ignore) {};
static inline int cnss_qmi_get_dms_mac(struct cnss_plat_data *plat_priv)
{
return 0;
}
int cnss_wlfw_wlan_mac_req_send_sync(struct cnss_plat_data *plat_priv,
u8 *mac, u32 mac_len)
{
return 0;
}
static inline int cnss_dms_init(struct cnss_plat_data *plat_priv)
{
return 0;
}
int cnss_wlfw_qdss_dnld_send_sync(struct cnss_plat_data *plat_priv)
{
return 0;
}
int cnss_wlfw_qdss_data_send_sync(struct cnss_plat_data *plat_priv, char *file_name,
u32 total_size)
{
return 0;
}
static inline void cnss_dms_deinit(struct cnss_plat_data *plat_priv) {}
int wlfw_qdss_trace_start(struct cnss_plat_data *plat_priv)
{
return 0;
}
int wlfw_qdss_trace_stop(struct cnss_plat_data *plat_priv, unsigned long long option)
{
return 0;
}
static inline
int cnss_wlfw_cal_report_req_send_sync(struct cnss_plat_data *plat_priv,
u32 cal_file_download_size)
{
return 0;
}
#endif /* CONFIG_CNSS2_QMI */
#ifdef CONFIG_CNSS2_DEBUG
static inline u32 cnss_get_host_build_type(void)
{
return QMI_HOST_BUILD_TYPE_PRIMARY_V01;
}
#else
static inline u32 cnss_get_host_build_type(void)
{
return QMI_HOST_BUILD_TYPE_SECONDARY_V01;
}
#endif
#endif /* _CNSS_QMI_H */

338
cnss2/reg.h Normal file
View File

@@ -0,0 +1,338 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_REG_H
#define _CNSS_REG_H
#define QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET 0x310C
#define QCA6390_CE_SRC_RING_REG_BASE 0xA00000
#define QCA6390_CE_DST_RING_REG_BASE 0xA01000
#define QCA6390_CE_COMMON_REG_BASE 0xA18000
#define QCA6490_CE_SRC_RING_REG_BASE 0x1B80000
#define QCA6490_CE_DST_RING_REG_BASE 0x1B81000
#define QCA6490_CE_COMMON_REG_BASE 0x1B98000
#define CE_SRC_RING_BASE_LSB_OFFSET 0x0
#define CE_SRC_RING_BASE_MSB_OFFSET 0x4
#define CE_SRC_RING_ID_OFFSET 0x8
#define CE_SRC_RING_MISC_OFFSET 0x10
#define CE_SRC_CTRL_OFFSET 0x58
#define CE_SRC_R0_CE_CH_SRC_IS_OFFSET 0x5C
#define CE_SRC_RING_HP_OFFSET 0x400
#define CE_SRC_RING_TP_OFFSET 0x404
#define CE_DEST_RING_BASE_LSB_OFFSET 0x0
#define CE_DEST_RING_BASE_MSB_OFFSET 0x4
#define CE_DEST_RING_ID_OFFSET 0x8
#define CE_DEST_RING_MISC_OFFSET 0x10
#define CE_DEST_CTRL_OFFSET 0xB0
#define CE_CH_DST_IS_OFFSET 0xB4
#define CE_CH_DEST_CTRL2_OFFSET 0xB8
#define CE_DEST_RING_HP_OFFSET 0x400
#define CE_DEST_RING_TP_OFFSET 0x404
#define CE_STATUS_RING_BASE_LSB_OFFSET 0x58
#define CE_STATUS_RING_BASE_MSB_OFFSET 0x5C
#define CE_STATUS_RING_ID_OFFSET 0x60
#define CE_STATUS_RING_MISC_OFFSET 0x68
#define CE_STATUS_RING_HP_OFFSET 0x408
#define CE_STATUS_RING_TP_OFFSET 0x40C
#define CE_COMMON_GXI_ERR_INTS 0x14
#define CE_COMMON_GXI_ERR_STATS 0x18
#define CE_COMMON_GXI_WDOG_STATUS 0x2C
#define CE_COMMON_TARGET_IE_0 0x48
#define CE_COMMON_TARGET_IE_1 0x4C
#define CE_REG_INTERVAL 0x2000
#define SHADOW_REG_COUNT 36
#define PCIE_SHADOW_REG_VALUE_0 0x8FC
#define PCIE_SHADOW_REG_VALUE_34 0x984
#define PCIE_SHADOW_REG_VALUE_35 0x988
#define SHADOW_REG_INTER_COUNT 43
#define PCIE_SHADOW_REG_INTER_0 0x1E05000
#define QDSS_APB_DEC_CSR_BASE 0x1C01000
#define QDSS_APB_DEC_CSR_ETRIRQCTRL_OFFSET 0x6C
#define QDSS_APB_DEC_CSR_PRESERVEETF_OFFSET 0x70
#define QDSS_APB_DEC_CSR_PRESERVEETR0_OFFSET 0x74
#define QDSS_APB_DEC_CSR_PRESERVEETR1_OFFSET 0x78
#define MAX_UNWINDOWED_ADDRESS 0x80000
#define WINDOW_ENABLE_BIT 0x40000000
#define WINDOW_SHIFT 19
#define WINDOW_VALUE_MASK 0x3F
#define WINDOW_START MAX_UNWINDOWED_ADDRESS
#define WINDOW_RANGE_MASK 0x7FFFF
#define TIME_SYNC_ENABLE 0x80000000
#define TIME_SYNC_CLEAR 0x0
#define QCA6390_DEBUG_PBL_LOG_SRAM_START 0x01403D58
#define QCA6390_DEBUG_PBL_LOG_SRAM_MAX_SIZE 80
#define QCA6390_V2_SBL_DATA_START 0x016C8580
#define QCA6390_V2_SBL_DATA_END (0x016C8580 + 0x00011000)
#define QCA6390_DEBUG_SBL_LOG_SRAM_MAX_SIZE 44
#define QCA6490_DEBUG_PBL_LOG_SRAM_START 0x01403DA0
#define QCA6490_DEBUG_PBL_LOG_SRAM_MAX_SIZE 40
#define QCA6490_V1_SBL_DATA_START 0x0143B000
#define QCA6490_V1_SBL_DATA_END (0x0143B000 + 0x00011000)
#define QCA6490_V2_SBL_DATA_START 0x01435000
#define QCA6490_V2_SBL_DATA_END (0x01435000 + 0x00011000)
#define QCA6490_DEBUG_SBL_LOG_SRAM_MAX_SIZE 48
#define WCN7850_DEBUG_PBL_LOG_SRAM_START 0x01403D98
#define WCN7850_DEBUG_PBL_LOG_SRAM_MAX_SIZE 40
#define WCN7850_SBL_DATA_START 0x01790000
#define WCN7850_SBL_DATA_END (0x01790000 + 0x00011000)
#define WCN7850_DEBUG_SBL_LOG_SRAM_MAX_SIZE 48
#define WCN7850_PBL_BOOTSTRAP_STATUS 0x01A10008
#define TCSR_PBL_LOGGING_REG 0x01B000F8
#define PCIE_BHI_ERRDBG2_REG 0x01E0E238
#define PCIE_BHI_ERRDBG3_REG 0x01E0E23C
#define PBL_WLAN_BOOT_CFG 0x01E22B34
#define PBL_BOOTSTRAP_STATUS 0x01910008
#define QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG 0x01E04234
#define QCA6390_PCIE_SOC_WDOG_DISC_BAD_DATA_LOW_CFG_SOC_PCIE_REG_VAL 0xDEAD1234
#define QCA6390_PCIE_PCIE_WCSS_STATUS_FOR_DEBUG_LOW_PCIE_LOCAL_REG 0x01E03140
#define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_MASK_SOC_PCIE_REG 0x1E04054
#define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_STATUS_SOC_PCIE_REG 0x1E04058
#define QCA6390_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x1E05090
#define QCA6390_PCIE_PCIE_PARF_LTSSM 0x01E081B0
#define QCA6390_PCIE_PCIE_PARF_PM_STTS 0x01E08024
#define QCA6390_PCIE_PCIE_PARF_PM_STTS_1 0x01E08028
#define QCA6390_PCIE_PCIE_PARF_INT_STATUS 0x01E08220
#define QCA6390_PCIE_PCIE_INT_ALL_STATUS 0x01E08224
#define QCA6390_PCIE_PCIE_INT_ALL_MASK 0x01E0822C
#define QCA6390_PCIE_PCIE_PARF_BDF_TO_SID_CFG 0x01E0AC00
#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_4 0x01E08530
#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_NO_MHI_ACCESS_HANDLER_RD_3 0x01E0852c
#define QCA6390_PCIE_PCIE_PARF_MHI_CLOCK_RESET_CTRL 0x01E08174
#define QCA6390_PCIE_PCIE_PARF_MHI_BASE_ADDR_LOWER 0x01E08178
#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS 0x01E084D0
#define QCA6390_PCIE_PCIE_PARF_L1SS_SLEEP_MODE_HANDLER_CFG 0x01E084d4
#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2 0x01E0ec88
#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1SUB 0x01E0ec08
#define QCA6390_PCIE_PCIE_CORE_CONFIG 0x01E08640
#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L2 0x01E0EC04
#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_PM_LINKST_IN_L1 0x01E0EC0C
#define QCA6390_PCIE_PCIE_PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1 0x01E0EC84
#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_HIGH 0x01E030C8
#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSSAON_PCIE_SR_STATUS_LOW 0x01E030CC
#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_HIGH 0x01E0313C
#define QCA6390_PCIE_PCIE_LOCAL_REG_WCSS_STATUS_FOR_DEBUG_LOW 0x01E03140
#define QCA6390_PCIE_PCIE_BHI_EXECENV_REG 0x01E0E228
#define QCA6390_GCC_DEBUG_CLK_CTL 0x001E4025C
#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_PLL_MODE 0x00D00200
#define QCA6390_WCSS_WFSS_PMM_WFSS_PMM_R0_PMM_CTRL 0x00B60164
#define QCA6390_WCSS_PMM_TOP_PMU_CX_CSR 0x00B70080
#define QCA6390_WCSS_PMM_TOP_AON_INT_RAW_STAT 0x00B700E0
#define QCA6390_WCSS_PMM_TOP_AON_INT_EN 0x00B700D0
#define QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_STS 0x00B70020
#define QCA6390_WCSS_PMM_TOP_PMU_TESTBUS_CTL 0x00B7001C
#define QCA6390_WCSS_PMM_TOP_TESTBUS_STS 0x00B70028
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG 0x00DB0008
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_CFG_MSK 0x20
#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_CTL 0x00D02000
#define QCA6390_WCSS_Q6SS_PUBCSR_QDSP6SS_TEST_BUS_VALUE 0x00D02004
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_STS 0x00DB000C
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_CTL 0x00DB0030
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_0 0x00DB0400
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_SAW2_SPM_SLP_SEQ_ENTRY_9 0x00DB0424
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS0 0x00D90380
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS1 0x00D90384
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS2 0x00D90388
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS3 0x00D9038C
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS4 0x00D90390
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS5 0x00D90394
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_STATUS6 0x00D90398
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE0 0x00D90100
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE1 0x00D90104
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE2 0x00D90108
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE3 0x00D9010C
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE4 0x00D90110
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE5 0x00D90114
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_ENABLE6 0x00D90118
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING0 0x00D90500
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING1 0x00D90504
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING2 0x00D90508
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING3 0x00D9050C
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING4 0x00D90510
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING5 0x00D90514
#define QCA6390_WCSS_Q6SS_PRIVCSR_QDSP6SS_L2VIC_INT_PENDING6 0x00D90518
#define QCA6390_WCSS_CC_WCSS_UMAC_NOC_CBCR 0x00C3029C
#define QCA6390_WCSS_CC_WCSS_UMAC_AHB_CBCR 0x00C302BC
#define QCA6390_WCSS_CC_WCSS_UMAC_GDSCR 0x00C30298
#define QCA6390_WCSS_CC_WCSS_WLAN1_GDSCR 0x00C300C4
#define QCA6390_WCSS_CC_WCSS_WLAN2_GDSCR 0x00C30138
#define QCA6390_WCSS_PMM_TOP_PMM_INT_CLR 0x00B70168
#define QCA6390_WCSS_PMM_TOP_AON_INT_STICKY_EN 0x00B700D8
#define QCA6390_TLMM_GPIO_IN_OUT57 0x01839004
#define QCA6390_TLMM_GPIO_INTR_CFG57 0x01839008
#define QCA6390_TLMM_GPIO_INTR_STATUS57 0x0183900C
#define QCA6390_TLMM_GPIO_IN_OUT59 0x0183b004
#define QCA6390_TLMM_GPIO_INTR_CFG59 0x0183b008
#define QCA6390_TLMM_GPIO_INTR_STATUS59 0x0183b00C
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_STATUS_REG2 0x00B6017C
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_STATUS_REG2 0x00B60190
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN2_CFG_REG1 0x00B6018C
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_PMM_WLAN1_CFG_REG1 0x00B60178
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN2_APS_STATUS_REG1 0x00B600B0
#define QCA6390_WFSS_PMM_WFSS_PMM_R0_WLAN1_APS_STATUS_REG1 0x00B60044
#define WLAON_SOC_POWER_CTRL 0x01F80000
#define WLAON_SOC_PWR_WDG_BARK_THRSHD 0x1F80004
#define WLAON_SOC_PWR_WDG_BITE_THRSHD 0x1F80008
#define WLAON_SW_COLD_RESET 0x1F8000C
#define WLAON_RFA_MEM_SLP_NRET_N_OVERRIDE 0x1F8001C
#define WLAON_GDSC_DELAY_SETTING 0x1F80024
#define WLAON_GDSC_DELAY_SETTING2 0x1F80028
#define WLAON_WL_PWR_STATUS_REG 0x1F8002C
#define WLAON_WL_AON_DBG_CFG_REG 0x1F80030
#define WLAON_WL_AON_DBG_ENABLE_GRP0_REG 0x1F80034
#define WLAON_WL_AON_DBG_ENABLE_GRP1_REG 0x1F80038
#define WLAON_WL_AON_APM_CFG_CTRL0 0x1F80040
#define WLAON_WL_AON_APM_CFG_CTRL1 0x1F80044
#define WLAON_WL_AON_APM_CFG_CTRL2 0x1F80048
#define WLAON_WL_AON_APM_CFG_CTRL3 0x1F8004C
#define WLAON_WL_AON_APM_CFG_CTRL4 0x1F80050
#define WLAON_WL_AON_APM_CFG_CTRL5 0x1F80054
#define WLAON_WL_AON_APM_CFG_CTRL5_1 0x1F80058
#define WLAON_WL_AON_APM_CFG_CTRL6 0x1F8005C
#define WLAON_WL_AON_APM_CFG_CTRL6_1 0x1F80060
#define WLAON_WL_AON_APM_CFG_CTRL7 0x1F80064
#define WLAON_WL_AON_APM_CFG_CTRL8 0x1F80068
#define WLAON_WL_AON_APM_CFG_CTRL8_1 0x1F8006C
#define WLAON_WL_AON_APM_CFG_CTRL9 0x1F80070
#define WLAON_WL_AON_APM_CFG_CTRL9_1 0x1F80074
#define WLAON_WL_AON_APM_CFG_CTRL10 0x1F80078
#define WLAON_WL_AON_APM_CFG_CTRL11 0x1F8007C
#define WLAON_WL_AON_APM_CFG_CTRL12 0x1F80080
#define WLAON_WL_AON_APM_OVERRIDE_REG 0x1F800B0
#define WLAON_WL_AON_CXPC_REG 0x1F800B4
#define WLAON_WL_AON_APM_STATUS0 0x1F800C0
#define WLAON_WL_AON_APM_STATUS1 0x1F800C4
#define WLAON_WL_AON_APM_STATUS2 0x1F800C8
#define WLAON_WL_AON_APM_STATUS3 0x1F800CC
#define WLAON_WL_AON_APM_STATUS4 0x1F800D0
#define WLAON_WL_AON_APM_STATUS5 0x1F800D4
#define WLAON_WL_AON_APM_STATUS6 0x1F800D8
#define WLAON_GLOBAL_COUNTER_CTRL1 0x1F80100
#define WLAON_GLOBAL_COUNTER_CTRL6 0x1F80108
#define WLAON_GLOBAL_COUNTER_CTRL7 0x1F8010C
#define WLAON_GLOBAL_COUNTER_CTRL3 0x1F80118
#define WLAON_GLOBAL_COUNTER_CTRL4 0x1F8011C
#define WLAON_GLOBAL_COUNTER_CTRL5 0x1F80120
#define WLAON_GLOBAL_COUNTER_CTRL8 0x1F801F0
#define WLAON_GLOBAL_COUNTER_CTRL2 0x1F801F4
#define WLAON_GLOBAL_COUNTER_CTRL9 0x1F801F8
#define WLAON_RTC_CLK_CAL_CTRL1 0x1F80200
#define WLAON_RTC_CLK_CAL_CTRL2 0x1F80204
#define WLAON_RTC_CLK_CAL_CTRL3 0x1F80208
#define WLAON_RTC_CLK_CAL_CTRL4 0x1F8020C
#define WLAON_RTC_CLK_CAL_CTRL5 0x1F80210
#define WLAON_RTC_CLK_CAL_CTRL6 0x1F80214
#define WLAON_RTC_CLK_CAL_CTRL7 0x1F80218
#define WLAON_RTC_CLK_CAL_CTRL8 0x1F8021C
#define WLAON_RTC_CLK_CAL_CTRL9 0x1F80220
#define WLAON_WCSSAON_CONFIG_REG 0x1F80300
#define WLAON_WLAN_OEM_DEBUG_REG 0x1F80304
#define WLAON_WLAN_RAM_DUMP_REG 0x1F80308
#define WLAON_QDSS_WCSS_REG 0x1F8030C
#define WLAON_QDSS_WCSS_ACK 0x1F80310
#define WLAON_WL_CLK_CNTL_KDF_REG 0x1F80314
#define WLAON_WL_CLK_CNTL_PMU_HFRC_REG 0x1F80318
#define WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C
#define QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK 0x4
#define QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK 0x1
#define WLAON_DLY_CONFIG 0x1F80400
#define WLAON_WLAON_Q6_IRQ_REG 0x1F80404
#define WLAON_PCIE_INTF_SW_CFG_REG 0x1F80408
#define WLAON_PCIE_INTF_STICKY_SW_CFG_REG 0x1F8040C
#define WLAON_PCIE_INTF_PHY_SW_CFG_REG 0x1F80410
#define WLAON_PCIE_INTF_PHY_NOCSR_SW_CFG_REG 0x1F80414
#define WLAON_Q6_COOKIE_BIT 0x1F80500
#define WLAON_WARM_SW_ENTRY 0x1F80504
#define WLAON_RESET_DBG_SW_ENTRY 0x1F80508
#define WLAON_WL_PMUNOC_CFG_REG 0x1F8050C
#define WLAON_RESET_CAUSE_CFG_REG 0x1F80510
#define WLAON_SOC_WCSSAON_WAKEUP_IRQ_7_EN_REG 0x1F80514
#define WLAON_DEBUG 0x1F80600
#define WLAON_SOC_PARAMETERS 0x1F80604
#define WLAON_WLPM_SIGNAL 0x1F80608
#define WLAON_SOC_RESET_CAUSE_REG 0x1F8060C
#define WLAON_WAKEUP_PCIE_SOC_REG 0x1F80610
#define WLAON_PBL_STACK_CANARY 0x1F80614
#define WLAON_MEM_TOT_NUM_GRP_REG 0x1F80618
#define WLAON_MEM_TOT_BANKS_IN_GRP0_REG 0x1F8061C
#define WLAON_MEM_TOT_BANKS_IN_GRP1_REG 0x1F80620
#define WLAON_MEM_TOT_BANKS_IN_GRP2_REG 0x1F80624
#define WLAON_MEM_TOT_BANKS_IN_GRP3_REG 0x1F80628
#define WLAON_MEM_TOT_SIZE_IN_GRP0_REG 0x1F8062C
#define WLAON_MEM_TOT_SIZE_IN_GRP1_REG 0x1F80630
#define WLAON_MEM_TOT_SIZE_IN_GRP2_REG 0x1F80634
#define WLAON_MEM_TOT_SIZE_IN_GRP3_REG 0x1F80638
#define WLAON_MEM_SLP_NRET_OVERRIDE_GRP0_REG 0x1F8063C
#define WLAON_MEM_SLP_NRET_OVERRIDE_GRP1_REG 0x1F80640
#define WLAON_MEM_SLP_NRET_OVERRIDE_GRP2_REG 0x1F80644
#define WLAON_MEM_SLP_NRET_OVERRIDE_GRP3_REG 0x1F80648
#define WLAON_MEM_SLP_RET_OVERRIDE_GRP0_REG 0x1F8064C
#define WLAON_MEM_SLP_RET_OVERRIDE_GRP1_REG 0x1F80650
#define WLAON_MEM_SLP_RET_OVERRIDE_GRP2_REG 0x1F80654
#define WLAON_MEM_SLP_RET_OVERRIDE_GRP3_REG 0x1F80658
#define WLAON_MEM_CNT_SEL_REG 0x1F8065C
#define WLAON_MEM_NO_EXTBHS_REG 0x1F80660
#define WLAON_MEM_DEBUG_REG 0x1F80664
#define WLAON_MEM_DEBUG_BUS_REG 0x1F80668
#define WLAON_MEM_REDUN_CFG_REG 0x1F8066C
#define WLAON_WL_AON_SPARE2 0x1F80670
#define WLAON_VSEL_CFG_FOR_WL_RET_DISABLE_REG 0x1F80680
#define WLAON_BTFM_WLAN_IPC_STATUS_REG 0x1F80690
#define WLAON_MPM_COUNTER_CHICKEN_BITS 0x1F806A0
#define WLAON_WLPM_CHICKEN_BITS 0x1F806A4
#define WLAON_PCIE_PHY_PWR_REG 0x1F806A8
#define WLAON_WL_CLK_CNTL_PMU_LPO2M_REG 0x1F806AC
#define WLAON_WL_SS_ROOT_CLK_SWITCH_REG 0x1F806B0
#define WLAON_POWERCTRL_PMU_REG 0x1F806B4
#define WLAON_POWERCTRL_MEM_REG 0x1F806B8
#define WLAON_PCIE_PWR_CTRL_REG 0x01F806BC
#define WLAON_SOC_PWR_PROFILE_REG 0x1F806C0
#define WLAON_WCSSAON_PCIE_SR_STATUS_HI_REG 0x01F806C4
#define WLAON_WCSSAON_PCIE_SR_STATUS_LO_REG 0x1F806C8
#define WLAON_WCSS_TCSR_PMM_SR_STATUS_HI_REG 0x1F806CC
#define WLAON_WCSS_TCSR_PMM_SR_STATUS_LO_REG 0x1F806D0
#define WLAON_MEM_SVS_CFG_REG 0x1F806D4
#define WLAON_CMN_AON_MISC_REG 0x1F806D8
#define WLAON_INTR_STATUS 0x1F80700
#define WLAON_INTR_ENABLE 0x1F807040
#define WLAON_NOC_DBG_BUS_SEL_REG 0x1F80708
#define WLAON_NOC_DBG_BUS_REG 0x1F8070C
#define WLAON_WL_CTRL_MISC_REG 0x1F80710
#define WLAON_DBG_STATUS0 0x1F80720
#define WLAON_DBG_STATUS1 0x1F80724
#define WLAON_TIMERSYNC_OFFSET_L 0x1F80730
#define WLAON_TIMERSYNC_OFFSET_H 0x1F80734
#define WLAON_PMU_LDO_SETTLE_REG 0x1F80740
#define QCA6390_SYSPM_SYSPM_PWR_STATUS 0x1F82000
#define QCA6390_SYSPM_DBG_BTFM_AON_REG 0x1F82004
#define QCA6390_SYSPM_DBG_BUS_SEL_REG 0x1F82008
#define QCA6390_SYSPM_WCSSAON_SR_STATUS 0x1F8200C
/* PCIE SOC scratch registers, address same for QCA6390 & QCA6490*/
#define PCIE_SCRATCH_0_SOC_PCIE_REG 0x1E04040
#define PCIE_SCRATCH_1_SOC_PCIE_REG 0x1E04044
#define PCIE_SCRATCH_2_SOC_PCIE_REG 0x1E0405C
#endif

9
cnss_genl/Kconfig Normal file
View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config CNSS_GENL
tristate "CNSS Generic Netlink Socket Driver"
help
This module creates generic netlink family "CLD80211". This can be
used by cld driver and userspace utilities to communicate over
netlink sockets. This module creates different multicast groups to
facilitate the same.

3
cnss_genl/Makefile Normal file
View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CNSS_GENL) := cnss_nl.o

229
cnss_genl/cnss_nl.c Normal file
View File

@@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. */
#include <net/genetlink.h>
#include <net/cnss_nl.h>
#include <linux/module.h>
#include <linux/of.h>
#define CLD80211_GENL_NAME "cld80211"
#define CLD80211_MULTICAST_GROUP_SVC_MSGS "svc_msgs"
#define CLD80211_MULTICAST_GROUP_HOST_LOGS "host_logs"
#define CLD80211_MULTICAST_GROUP_FW_LOGS "fw_logs"
#define CLD80211_MULTICAST_GROUP_PER_PKT_STATS "per_pkt_stats"
#define CLD80211_MULTICAST_GROUP_DIAG_EVENTS "diag_events"
#define CLD80211_MULTICAST_GROUP_FATAL_EVENTS "fatal_events"
#define CLD80211_MULTICAST_GROUP_OEM_MSGS "oem_msgs"
static const struct genl_multicast_group nl_mcgrps[] = {
[CLD80211_MCGRP_SVC_MSGS] = { .name =
CLD80211_MULTICAST_GROUP_SVC_MSGS},
[CLD80211_MCGRP_HOST_LOGS] = { .name =
CLD80211_MULTICAST_GROUP_HOST_LOGS},
[CLD80211_MCGRP_FW_LOGS] = { .name =
CLD80211_MULTICAST_GROUP_FW_LOGS},
[CLD80211_MCGRP_PER_PKT_STATS] = { .name =
CLD80211_MULTICAST_GROUP_PER_PKT_STATS},
[CLD80211_MCGRP_DIAG_EVENTS] = { .name =
CLD80211_MULTICAST_GROUP_DIAG_EVENTS},
[CLD80211_MCGRP_FATAL_EVENTS] = { .name =
CLD80211_MULTICAST_GROUP_FATAL_EVENTS},
[CLD80211_MCGRP_OEM_MSGS] = { .name =
CLD80211_MULTICAST_GROUP_OEM_MSGS},
};
struct cld_ops {
cld80211_cb cb;
void *cb_ctx;
};
struct cld80211_nl_data {
struct cld_ops cld_ops[CLD80211_MAX_COMMANDS];
};
static struct cld80211_nl_data nl_data;
static inline struct cld80211_nl_data *get_local_ctx(void)
{
return &nl_data;
}
static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS];
/* policy for the attributes */
static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX + 1] = {
[CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED },
[CLD80211_ATTR_DATA] = { .type = NLA_BINARY,
.len = CLD80211_MAX_NL_DATA },
[CLD80211_ATTR_META_DATA] = { .type = NLA_BINARY,
.len = CLD80211_MAX_NL_DATA },
[CLD80211_ATTR_CMD] = { .type = NLA_U32 },
[CLD80211_ATTR_CMD_TAG_DATA] = { .type = NLA_NESTED },
};
static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
{
u8 cmd_id = ops->cmd;
struct cld80211_nl_data *nl = get_local_ctx();
if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) {
pr_err("CLD80211: Command Not supported: %u\n", cmd_id);
return -EOPNOTSUPP;
}
info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb;
info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx;
return 0;
}
/* The netlink family */
static struct genl_family cld80211_fam __ro_after_init = {
.name = CLD80211_GENL_NAME,
.hdrsize = 0, /* no private header */
.version = 1, /* no particular meaning now */
.maxattr = CLD80211_ATTR_MAX,
.policy = cld80211_policy,
.netnsok = true,
.pre_doit = cld80211_pre_doit,
.post_doit = NULL,
.module = THIS_MODULE,
.ops = nl_ops,
.n_ops = ARRAY_SIZE(nl_ops),
.mcgrps = nl_mcgrps,
.n_mcgrps = ARRAY_SIZE(nl_mcgrps),
};
int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx)
{
struct cld80211_nl_data *nl = get_local_ctx();
pr_debug("CLD80211: Registering command: %d\n", cmd_id);
if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
pr_debug("CLD80211: invalid command: %d\n", cmd_id);
return -EINVAL;
}
nl->cld_ops[cmd_id - 1].cb = func;
nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx;
return 0;
}
EXPORT_SYMBOL(register_cld_cmd_cb);
int deregister_cld_cmd_cb(u8 cmd_id)
{
struct cld80211_nl_data *nl = get_local_ctx();
pr_debug("CLD80211: De-registering command: %d\n", cmd_id);
if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
pr_debug("CLD80211: invalid command: %d\n", cmd_id);
return -EINVAL;
}
nl->cld_ops[cmd_id - 1].cb = NULL;
nl->cld_ops[cmd_id - 1].cb_ctx = NULL;
return 0;
}
EXPORT_SYMBOL(deregister_cld_cmd_cb);
struct genl_family *cld80211_get_genl_family(void)
{
return &cld80211_fam;
}
EXPORT_SYMBOL(cld80211_get_genl_family);
static int cld80211_doit(struct sk_buff *skb, struct genl_info *info)
{
cld80211_cb cld_cb;
void *cld_ctx;
cld_cb = info->user_ptr[0];
if (!cld_cb) {
pr_err("CLD80211: Not supported\n");
return -EOPNOTSUPP;
}
cld_ctx = info->user_ptr[1];
if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) {
cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
cld_ctx, info->snd_portid);
} else {
pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n");
return -EINVAL;
}
return 0;
}
static int __cld80211_init(void)
{
int err, i;
memset(&nl_ops[0], 0, sizeof(nl_ops));
pr_info("CLD80211: Initializing\n");
for (i = 0; i < CLD80211_MAX_COMMANDS; i++) {
nl_ops[i].cmd = i + 1;
nl_ops[i].doit = cld80211_doit;
nl_ops[i].flags = GENL_ADMIN_PERM;
}
err = genl_register_family(&cld80211_fam);
if (err) {
pr_err("CLD80211: Failed to register cld80211 family: %d\n",
err);
}
return err;
}
static void __cld80211_exit(void)
{
genl_unregister_family(&cld80211_fam);
}
/**
* cld80211_is_valid_dt_node_found - Check if valid device tree node present
*
* Valid device tree node means a node with "qcom,wlan" property present and
* "status" property not disabled.
*
* Return: true if valid device tree node found, false if not found
*/
static bool cld80211_is_valid_dt_node_found(void)
{
struct device_node *dn = NULL;
for_each_node_with_property(dn, "qcom,wlan") {
if (of_device_is_available(dn))
break;
}
if (dn)
return true;
return false;
}
static int __init cld80211_init(void)
{
if (!cld80211_is_valid_dt_node_found())
return -ENODEV;
return __cld80211_init();
}
static void __exit cld80211_exit(void)
{
__cld80211_exit();
}
module_init(cld80211_init);
module_exit(cld80211_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CNSS generic netlink module");

9
cnss_prealloc/Kconfig Normal file
View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config WCNSS_MEM_PRE_ALLOC
tristate "WCNSS pre-alloc memory support"
help
Pre-allocate memory for the WLAN driver module.
This feature enable cld wlan driver to use pre allocated memory
for it's internal usage and release it to back to pre allocated pool.
This memory is allocated at the cold boot time.

3
cnss_prealloc/Makefile Normal file
View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc.o

View File

@@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012,2014-2017,2019-2021 The Linux Foundation. All rights reserved. */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mempool.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/of.h>
#include <net/cnss_prealloc.h>
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CNSS prealloc driver");
/* cnss preallocation scheme is a memory pool that always tries to keep a
* list of free memory for use in emergencies. It is implemented on kernel
* features: memorypool and kmem cache.
*/
struct cnss_pool {
size_t size;
int min;
const char name[50];
mempool_t *mp;
struct kmem_cache *cache;
};
/**
* Memory pool
* -----------
*
* How to update this table:
*
* 1. Add a new row with following elements
* size : Size of one allocation unit in bytes.
* min : Minimum units to be reserved. Used only if a regular
* allocation fails.
* name : Name of the cache/pool. Will be displayed in /proc/slabinfo
* if not merged with another pool.
* mp : A pointer to memory pool. Updated during init.
* cache : A pointer to cache. Updated during init.
* 2. Always keep the table in increasing order
* 3. Please keep the reserve pool as minimum as possible as it's always
* preallocated.
* 4. Always profile with different use cases after updating this table.
* 5. A dynamic view of this pool can be viewed at /proc/slabinfo.
* 6. Each pool has a sys node at /sys/kernel/slab/<name>
*
*/
/* size, min pool reserve, name, memorypool handler, cache handler*/
static struct cnss_pool cnss_pools[] = {
{8 * 1024, 22, "cnss-pool-8k", NULL, NULL},
{16 * 1024, 16, "cnss-pool-16k", NULL, NULL},
{32 * 1024, 6, "cnss-pool-32k", NULL, NULL},
{64 * 1024, 8, "cnss-pool-64k", NULL, NULL},
{128 * 1024, 2, "cnss-pool-128k", NULL, NULL},
};
/**
* cnss_pool_alloc_threshold() - Allocation threshold
*
* Minimum memory size to be part of cnss pool.
*
* Return: Size
*
*/
static inline size_t cnss_pool_alloc_threshold(void)
{
return cnss_pools[0].size;
}
/**
* cnss_pool_int() - Initialize memory pools.
*
* Create cnss pools as configured by cnss_pools[]. It is the responsibility of
* the caller to invoke cnss_pool_deinit() routine to clean it up. This
* function needs to be called at early boot to preallocate minimum buffers in
* the pool.
*
* Return: 0 - success, otherwise error code.
*
*/
static int cnss_pool_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(cnss_pools); i++) {
/* Create the slab cache */
cnss_pools[i].cache =
kmem_cache_create_usercopy(cnss_pools[i].name,
cnss_pools[i].size, 0,
SLAB_ACCOUNT, 0,
cnss_pools[i].size, NULL);
if (!cnss_pools[i].cache) {
pr_err("cnss_prealloc: cache %s failed\n",
cnss_pools[i].name);
continue;
}
/* Create the pool and associate to slab cache */
cnss_pools[i].mp =
mempool_create(cnss_pools[i].min, mempool_alloc_slab,
mempool_free_slab, cnss_pools[i].cache);
if (!cnss_pools[i].mp) {
pr_err("cnss_prealloc: mempool %s failed\n",
cnss_pools[i].name);
kmem_cache_destroy(cnss_pools[i].cache);
cnss_pools[i].cache = NULL;
continue;
}
pr_info("cnss_prealloc: created mempool %s of min size %d * %zu\n",
cnss_pools[i].name, cnss_pools[i].min,
cnss_pools[i].size);
}
return 0;
}
/**
* cnss_pool_deinit() - Free memory pools.
*
* Free the memory pools and return resources back to the system. It warns
* if there is any pending element in memory pool or cache.
*
*/
static void cnss_pool_deinit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(cnss_pools); i++) {
pr_info("cnss_prealloc: destroy mempool %s\n",
cnss_pools[i].name);
mempool_destroy(cnss_pools[i].mp);
kmem_cache_destroy(cnss_pools[i].cache);
}
}
/**
* cnss_pool_get_index() - Get the index of memory pool
* @mem: Allocated memory
*
* Returns the index of the memory pool which fits the reqested memory. The
* complexity of this check is O(num of memory pools). Returns a negative
* value with error code in case of failure.
*
*/
static int cnss_pool_get_index(void *mem)
{
struct page *page;
struct kmem_cache *cache;
int i;
if (!virt_addr_valid(mem))
return -EINVAL;
/* mem -> page -> cache */
page = virt_to_head_page(mem);
if (!page)
return -ENOENT;
cache = page->slab_cache;
if (!cache)
return -ENOENT;
/* Check if memory belongs to a pool */
for (i = 0; i < ARRAY_SIZE(cnss_pools); i++) {
if (cnss_pools[i].cache == cache)
return i;
}
return -ENOENT;
}
/**
* wcnss_prealloc_get() - Get preallocated memory from a pool
* @size: Size to allocate
*
* Memory pool is chosen based on the size. If memory is not available in a
* given pool it goes to next higher sized pool until it succeeds.
*
* Return: A void pointer to allocated memory
*/
void *wcnss_prealloc_get(size_t size)
{
void *mem = NULL;
gfp_t gfp_mask = __GFP_ZERO;
int i;
if (in_interrupt() || irqs_disabled())
gfp_mask |= GFP_ATOMIC;
else
gfp_mask |= GFP_KERNEL;
if (size >= cnss_pool_alloc_threshold()) {
for (i = 0; i < ARRAY_SIZE(cnss_pools); i++) {
if (cnss_pools[i].size >= size) {
mem = mempool_alloc(cnss_pools[i].mp, gfp_mask);
if (mem)
break;
}
}
}
if (!mem && size >= cnss_pool_alloc_threshold()) {
pr_debug("cnss_prealloc: not available for size %zu, flag %x\n",
size, gfp_mask);
}
return mem;
}
EXPORT_SYMBOL(wcnss_prealloc_get);
/**
* wcnss_prealloc_put() - Relase allocated memory
* @mem: Allocated memory
*
* Free the memory got by wcnss_prealloc_get() to slab or pool reserve if memory
* pool doesn't have enough elements.
*
* Return: 1 - success
* 0 - fail
*/
int wcnss_prealloc_put(void *mem)
{
int i;
if (!mem)
return 0;
i = cnss_pool_get_index(mem);
if (i >= 0 && i < ARRAY_SIZE(cnss_pools)) {
mempool_free(mem, cnss_pools[i].mp);
return 1;
}
return 0;
}
EXPORT_SYMBOL(wcnss_prealloc_put);
/* Not implemented. Make use of Linux SLAB features. */
void wcnss_prealloc_check_memory_leak(void) {}
EXPORT_SYMBOL(wcnss_prealloc_check_memory_leak);
/* Not implemented. Make use of Linux SLAB features. */
int wcnss_pre_alloc_reset(void) { return -EOPNOTSUPP; }
EXPORT_SYMBOL(wcnss_pre_alloc_reset);
/**
* cnss_prealloc_is_valid_dt_node_found - Check if valid device tree node
* present
*
* Valid device tree node means a node with "qcom,wlan" property present
* and "status" property not disabled.
*
* Return: true if valid device tree node found, false if not found
*/
static bool cnss_prealloc_is_valid_dt_node_found(void)
{
struct device_node *dn = NULL;
for_each_node_with_property(dn, "qcom,wlan") {
if (of_device_is_available(dn))
break;
}
if (dn)
return true;
return false;
}
static int __init cnss_prealloc_init(void)
{
if (!cnss_prealloc_is_valid_dt_node_found())
return -ENODEV;
return cnss_pool_init();
}
static void __exit cnss_prealloc_exit(void)
{
cnss_pool_deinit();
}
module_init(cnss_prealloc_init);
module_exit(cnss_prealloc_exit);

20
cnss_utils/Kconfig Normal file
View File

@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
config CNSS_UTILS
tristate "CNSS utilities support"
help
Add CNSS utilities support for the WLAN driver module.
This feature enables wlan driver to use CNSS utilities APIs to set
and get wlan related information.
config CNSS_QMI_SVC
tristate "CNSS QMI SVC support"
help
Add CNSS QMI SVC support for the WLAN driver module.
This feature enable wlan driver to use CNSS QMI service APIs to set
and get wlan related information.
config CNSS_PLAT_IPC_QMI_SVC
tristate "CNSS Platform QMI IPC Support"
help
Add CNSS platform kernel and user space components IPC using QMI.

8
cnss_utils/Makefile Normal file
View File

@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o
obj-$(CONFIG_CNSS_QMI_SVC) += wlan_firmware_service.o
wlan_firmware_service-y := wlan_firmware_service_v01.o device_management_service_v01.o
obj-$(CONFIG_CNSS_PLAT_IPC_QMI_SVC) += cnss_plat_ipc_qmi_svc.o
cnss_plat_ipc_qmi_svc-y := cnss_plat_ipc_qmi.o cnss_plat_ipc_service_v01.o

View File

@@ -0,0 +1,975 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2021, The Linux Foundation. All rights reserved. */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/qrtr.h>
#include <linux/soc/qcom/qmi.h>
#if IS_ENABLED(CONFIG_IPC_LOGGING)
#include <linux/ipc_logging.h>
#endif
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/limits.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include "cnss_plat_ipc_qmi.h"
#include "cnss_plat_ipc_service_v01.h"
#define CNSS_MAX_FILE_SIZE (32 * 1024 * 1024)
#define CNSS_PLAT_IPC_MAX_USER 1
#define CNSS_PLAT_IPC_QMI_FILE_TXN_TIMEOUT 10000
#define QMI_INIT_RETRY_MAX_TIMES 240
#define QMI_INIT_RETRY_DELAY_MS 250
#define NUM_LOG_PAGES 10
/**
* struct cnss_plat_ipc_file_data: File transfer context data
* @name: File name
* @buf: Buffer provided for TX/RX file contents
* @id: File ID corresponding to file name
* @buf_size: Buffer size
* @file_fize: File Size
* @seg_index: Running index for buffer segments
* @seg_len: Total number of segments
* @end: End of transaction
* @complete: Completion variable for file transfer
*/
struct cnss_plat_ipc_file_data {
char *name;
char *buf;
u32 id;
u32 buf_size;
u32 file_size;
u32 seg_index;
u32 seg_len;
u32 end;
struct completion complete;
};
/**
* struct cnss_plat_ipc_qmi_client_ctx: Context for QMI IPC client
* @client_sq: QMI IPC client QRTR socket
* @client_connected: QMI IPC client connection status
* @connection_update_cb: Registered user callback for QMI connection status
* @cb_ctx: Context for registered user
* @num_user: Number of registered users
*/
struct cnss_plat_ipc_qmi_client_ctx {
struct sockaddr_qrtr client_sq;
bool client_connected;
cnss_plat_ipc_connection_update
connection_update_cb[CNSS_PLAT_IPC_MAX_USER];
void *cb_ctx[CNSS_PLAT_IPC_MAX_USER];
u32 num_user;
};
/**
* struct cnss_plat_ipc_qmi_svc_ctx: Platform context for QMI IPC service
* @svc_hdl: QMI server handle
* @file_idr: File ID generator
* @flle_idr_lock: File ID generator usage lock
* @qmi_client_ctx: ontext for QMI IPC client
*/
struct cnss_plat_ipc_qmi_svc_ctx {
struct qmi_handle *svc_hdl;
struct idr file_idr;
struct mutex file_idr_lock; /* File ID generator usage lock */
struct cnss_plat_ipc_qmi_client_ctx
qmi_client_ctx[CNSS_PLAT_IPC_MAX_QMI_CLIENTS + 1];
};
static struct cnss_plat_ipc_qmi_svc_ctx plat_ipc_qmi_svc;
static struct cnss_plat_ipc_daemon_config daemon_cfg;
#if IS_ENABLED(CONFIG_IPC_LOGGING)
static void *cnss_plat_ipc_log_context;
static void cnss_plat_ipc_logging_init(void)
{
cnss_plat_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
"cnss_plat", 0);
if (!cnss_plat_ipc_log_context)
pr_err("cnss_plat: Unable to create log context\n");
}
static void cnss_plat_ipc_logging_deinit(void)
{
if (cnss_plat_ipc_log_context) {
ipc_log_context_destroy(cnss_plat_ipc_log_context);
cnss_plat_ipc_log_context = NULL;
}
}
void cnss_plat_ipc_debug_log_print(void *log_ctx, char *process, const char *fn,
const char *log_level, char *fmt, ...)
{
struct va_format vaf;
va_list va_args;
va_start(va_args, fmt);
vaf.fmt = fmt;
vaf.va = &va_args;
if (log_level)
printk("%scnss_plat: %pV", log_level, &vaf);
ipc_log_string(log_ctx, "[%s] %s: %pV", process, fn, &vaf);
va_end(va_args);
}
#define cnss_plat_ipc_log_print(_x...) \
cnss_plat_ipc_debug_log_print(cnss_plat_ipc_log_context, _x)
#else
static void cnss_plat_ipc_logging_init(void) {};
static void cnss_plat_ipc_logging_deinit(void) {};
void cnss_plat_ipc_debug_log_print(void *log_ctx, char *process, const char *fn,
const char *log_level, char *fmt, ...)
{
struct va_format vaf;
va_list va_args;
va_start(va_args, fmt);
vaf.fmt = fmt;
vaf.va = &va_args;
if (log_level)
printk("%scnss_plat: %pV", log_level, &vaf);
va_end(va_args);
}
#define cnss_plat_ipc_log_print(_x...) \
cnss_plat_ipc_debug_log_print((void *)NULL, _x)
#endif
#define proc_name (in_irq() ? "irq" : \
(in_softirq() ? "soft_irq" : current->comm))
#define cnss_plat_ipc_err(_fmt, ...) \
cnss_plat_ipc_log_print(proc_name, __func__, \
KERN_ERR, _fmt, ##__VA_ARGS__)
#define cnss_plat_ipc_info(_fmt, ...) \
cnss_plat_ipc_log_print(proc_name, __func__, \
KERN_INFO, _fmt, ##__VA_ARGS__)
#define cnss_plat_ipc_dbg(_fmt, ...) \
cnss_plat_ipc_log_print(proc_name, __func__, \
KERN_DEBUG, _fmt, ##__VA_ARGS__)
/**
* cnss_plat_ipc_init_file_data() - Initialize file transfer context data
* @name: File name
* @buf: Buffer pointer for file contents
* @buf_size: Buffer size for download / upload
* @file_size: File size for upload
*
* Return: File data pointer
*/
static
struct cnss_plat_ipc_file_data *cnss_plat_ipc_init_file_data(char *name,
char *buf,
u32 buf_size,
u32 file_size)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_file_data *fd;
fd = kmalloc(sizeof(*fd), GFP_KERNEL);
if (!fd)
goto end;
fd->name = name;
fd->buf = buf;
fd->buf_size = buf_size;
fd->file_size = file_size;
fd->seg_index = 0;
fd->end = 0;
if (file_size)
fd->seg_len =
(file_size / CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01) +
!!(file_size % CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01);
else
fd->seg_len = 0;
init_completion(&fd->complete);
mutex_lock(&svc->file_idr_lock);
fd->id = idr_alloc_cyclic(&svc->file_idr, fd, 0, U32_MAX, GFP_KERNEL);
if (fd->id < 0) {
kfree(fd);
fd = NULL;
}
mutex_unlock(&svc->file_idr_lock);
end:
return fd;
}
/**
* cnss_plat_ipc_deinit_file_data() - Release file transfer context data
* @fd: File data pointer
*
* Return: 0 on success, negative error values otherwise
*/
static int cnss_plat_ipc_deinit_file_data(struct cnss_plat_ipc_file_data *fd)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
int ret = 0;
if (unlikely(!fd))
return -EINVAL;
mutex_lock(&svc->file_idr_lock);
idr_remove(&svc->file_idr, fd->id);
mutex_unlock(&svc->file_idr_lock);
if (!fd->end)
ret = -EINVAL;
kfree(fd);
return ret;
}
/**
* cnss_plat_ipc_qmi_update_user() - Inform registered users about QMI
* client status
* @client_id: User space QMI IPC client ID. Also works as
* array index for QMI client context
*
* Return: None
*/
static void
cnss_plat_ipc_qmi_update_user(enum cnss_plat_ipc_qmi_client_id_v01 client_id)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client =
&svc->qmi_client_ctx[client_id];
int i;
for (i = 0; i < qmi_client->num_user; i++) {
if (qmi_client->connection_update_cb[i])
qmi_client->connection_update_cb[i]
(qmi_client->cb_ctx[i],
qmi_client->client_connected);
}
}
/**
* cnss_plat_ipc_qmi_file_upload() - Upload data as platform accessible file
* @client_id: User space QMI IPC client ID. Also works as
* array index for QMI client context
* @file_mame: File name to store in platform data location
* @file_buf: Pointer to buffer with file contents
* @file_size: Provides the size of buffer / file size
*
* Return: 0 on success, negative error values otherwise
*/
int cnss_plat_ipc_qmi_file_upload(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, u8 *file_buf,
u32 file_size)
{
struct cnss_plat_ipc_qmi_file_upload_ind_msg_v01 ind;
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client;
int ret;
struct cnss_plat_ipc_file_data *fd;
if (client_id > CNSS_PLAT_IPC_MAX_QMI_CLIENTS) {
cnss_plat_ipc_err("Invalid Client ID: %d\n", client_id);
return -EINVAL;
}
qmi_client = &svc->qmi_client_ctx[client_id];
if (!qmi_client->client_connected || !file_name || !file_buf)
return -EINVAL;
cnss_plat_ipc_info("File name: %s Size: %d\n", file_name, file_size);
if (file_size == 0 || file_size > CNSS_MAX_FILE_SIZE)
return -EINVAL;
fd = cnss_plat_ipc_init_file_data(file_name, file_buf, file_size,
file_size);
if (!fd) {
cnss_plat_ipc_err("Unable to initialize file transfer data\n");
return -EINVAL;
}
scnprintf(ind.file_name, CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01, "%s",
fd->name);
ind.file_size = fd->file_size;
ind.file_id = fd->id;
ret = qmi_send_indication
(svc->svc_hdl, &qmi_client->client_sq,
CNSS_PLAT_IPC_QMI_FILE_UPLOAD_IND_V01,
CNSS_PLAT_IPC_QMI_FILE_UPLOAD_IND_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_file_upload_ind_msg_v01_ei, &ind);
if (ret < 0) {
cnss_plat_ipc_err("QMI failed: %d\n", ret);
goto end;
}
ret = wait_for_completion_timeout(&fd->complete,
msecs_to_jiffies
(CNSS_PLAT_IPC_QMI_FILE_TXN_TIMEOUT));
if (!ret)
cnss_plat_ipc_err("Timeout Uploading file: %s\n", fd->name);
end:
ret = cnss_plat_ipc_deinit_file_data(fd);
cnss_plat_ipc_dbg("Status: %d\n", ret);
return ret;
}
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_upload);
/**
* cnss_plat_ipc_qmi_file_upload_req_handler() - QMI Upload data request handler
* @handle: Pointer to QMI handle
* @sq: QMI socket
* @txn: QMI transaction pointer
* @decoded_msg: Pointer to decoded QMI message
*
* Handles the QMI upload sequence from userspace. It uses the file descriptor
* ID to upload buffer contents to QMI messages as segments.
*
* Return: None
*/
static void
cnss_plat_ipc_qmi_file_upload_req_handler(struct qmi_handle *handle,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded_msg)
{
struct cnss_plat_ipc_qmi_file_upload_req_msg_v01 *req_msg;
struct cnss_plat_ipc_qmi_file_upload_resp_msg_v01 *resp;
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
int ret = 0;
struct cnss_plat_ipc_file_data *fd;
req_msg = (struct cnss_plat_ipc_qmi_file_upload_req_msg_v01 *)
decoded_msg;
if (!req_msg)
return;
cnss_plat_ipc_dbg("File ID: %d Seg Index: %d\n", req_msg->file_id,
req_msg->seg_index);
mutex_lock(&svc->file_idr_lock);
fd = idr_find(&svc->file_idr, req_msg->file_id);
mutex_unlock(&svc->file_idr_lock);
if (!fd) {
cnss_plat_ipc_err("Invalid File ID %d\n", req_msg->file_id);
return;
}
if (req_msg->seg_index != fd->seg_index) {
cnss_plat_ipc_err("File %s transfer segment failure\n", fd->name);
complete(&fd->complete);
}
resp = kzalloc(sizeof(*resp), GFP_KERNEL);
if (!resp)
return;
resp->file_id = fd->id;
resp->seg_index = fd->seg_index++;
resp->seg_buf_len =
(fd->buf_size > CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01 ?
CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01 : fd->buf_size);
resp->end = (fd->seg_index == fd->seg_len);
memcpy(resp->seg_buf, fd->buf, resp->seg_buf_len);
cnss_plat_ipc_dbg("ID: %d Seg ID: %d Len: %d End: %d\n", resp->file_id,
resp->seg_index, resp->seg_buf_len, resp->end);
ret = qmi_send_response
(svc->svc_hdl, sq, txn,
CNSS_PLAT_IPC_QMI_FILE_UPLOAD_RESP_V01,
CNSS_PLAT_IPC_QMI_FILE_UPLOAD_RESP_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_file_upload_resp_msg_v01_ei,
resp);
if (ret < 0) {
cnss_plat_ipc_err("QMI failed: %d\n", ret);
goto end;
}
fd->buf_size -= resp->seg_buf_len;
fd->buf += resp->seg_buf_len;
if (resp->end) {
fd->end = true;
complete(&fd->complete);
}
end:
kfree(resp);
}
/**
* cnss_plat_ipc_qmi_file_download() - Download platform accessible file
* @client_id: User space QMI IPC client ID. Also works as
* array index for QMI client context
* @file_mame: File name to get from platform data location
* @buf: Pointer of the buffer to store file contents
* @size: Provides the size of buffer. It is updated to reflect the file size
* at the end of file download.
*/
int cnss_plat_ipc_qmi_file_download(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, char *buf,
u32 *size)
{
struct cnss_plat_ipc_qmi_file_download_ind_msg_v01 ind;
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client;
int ret;
struct cnss_plat_ipc_file_data *fd;
if (client_id > CNSS_PLAT_IPC_MAX_QMI_CLIENTS) {
cnss_plat_ipc_err("Invalid Client ID: %d\n", client_id);
return -EINVAL;
}
qmi_client = &svc->qmi_client_ctx[client_id];
if (!qmi_client->client_connected || !file_name || !buf)
return -EINVAL;
fd = cnss_plat_ipc_init_file_data(file_name, buf, *size, 0);
if (!fd) {
cnss_plat_ipc_err("Unable to initialize file transfer data\n");
return -EINVAL;
}
scnprintf(ind.file_name, CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01, "%s",
file_name);
ind.file_id = fd->id;
ret = qmi_send_indication
(svc->svc_hdl, &qmi_client->client_sq,
CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_IND_V01,
CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_file_download_ind_msg_v01_ei, &ind);
if (ret < 0) {
cnss_plat_ipc_err("QMI failed: %d\n", ret);
goto end;
}
ret = wait_for_completion_timeout(&fd->complete,
msecs_to_jiffies
(CNSS_PLAT_IPC_QMI_FILE_TXN_TIMEOUT));
if (!ret)
cnss_plat_ipc_err("Timeout downloading file:%s\n", fd->name);
end:
*size = fd->file_size;
ret = cnss_plat_ipc_deinit_file_data(fd);
cnss_plat_ipc_dbg("Status: %d Size: %d\n", ret, *size);
return ret;
}
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_download);
/**
* cnss_plat_ipc_qmi_file_download_req_handler() - QMI download request handler
* @handle: Pointer to QMI handle
* @sq: QMI socket
* @txn: QMI transaction pointer
* @decoded_msg: Pointer to decoded QMI message
*
* Handles the QMI download request sequence to userspace. It uses the file
* descriptor ID to download QMI message buffer segment to file descriptor
* buffer.
*
* Return: None
*/
static void
cnss_plat_ipc_qmi_file_download_req_handler(struct qmi_handle *handle,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded_msg)
{
struct cnss_plat_ipc_qmi_file_download_req_msg_v01 *req_msg;
struct cnss_plat_ipc_qmi_file_download_resp_msg_v01 resp = {0};
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
int ret = 0;
struct cnss_plat_ipc_file_data *fd;
req_msg = (struct cnss_plat_ipc_qmi_file_download_req_msg_v01 *)
decoded_msg;
if (!req_msg)
return;
cnss_plat_ipc_dbg("File ID: %d Size: %d Seg Len: %d Index: %d End: %d\n",
req_msg->file_id, req_msg->file_size,
req_msg->seg_buf_len, req_msg->seg_index,
req_msg->end);
mutex_lock(&svc->file_idr_lock);
fd = idr_find(&svc->file_idr, req_msg->file_id);
mutex_unlock(&svc->file_idr_lock);
if (!fd) {
cnss_plat_ipc_err("Invalid File ID: %d\n", req_msg->file_id);
return;
}
if (req_msg->file_size > fd->buf_size) {
cnss_plat_ipc_err("File %s size %d larger than buffer size %d\n",
fd->name, req_msg->file_size, fd->buf_size);
goto file_error;
}
if (req_msg->seg_buf_len > CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01 ||
((req_msg->seg_buf_len + fd->file_size) > fd->buf_size)) {
cnss_plat_ipc_err("Segment buf ID: %d buffer size %d not allowed\n",
req_msg->seg_index, req_msg->seg_buf_len);
goto file_error;
}
if (req_msg->seg_index != fd->seg_index) {
cnss_plat_ipc_err("File %s transfer segment failure\n",
fd->name);
goto file_error;
}
memcpy(fd->buf, req_msg->seg_buf, req_msg->seg_buf_len);
fd->seg_index++;
fd->buf += req_msg->seg_buf_len;
fd->file_size += req_msg->seg_buf_len;
resp.file_id = fd->id;
resp.seg_index = fd->seg_index;
ret = qmi_send_response
(svc->svc_hdl, sq, txn,
CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_RESP_V01,
CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_file_download_resp_msg_v01_ei,
&resp);
if (ret < 0)
cnss_plat_ipc_err("QMI failed: %d\n", ret);
if (req_msg->end) {
fd->end = true;
complete(&fd->complete);
}
return;
file_error:
complete(&fd->complete);
}
/**
* cnss_plat_ipc_qmi_init_setup_req_handler() - Init_Setup QMI message handler
* @handle: Pointer to QMI handle
* @sq: QMI socket
* @txn: QMI transaction pointer
* @decoded_msg: Pointer to decoded QMI message
*
* Handles the QMI Init setup handshake message from userspace.
* buffer.
*
* Return: None
*/
static void
cnss_plat_ipc_qmi_init_setup_req_handler(struct qmi_handle *handle,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded_msg)
{
struct cnss_plat_ipc_qmi_init_setup_req_msg_v01 *req_msg;
struct cnss_plat_ipc_qmi_init_setup_resp_msg_v01 resp = {0};
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_daemon_config *cfg = &daemon_cfg;
int ret = 0;
req_msg =
(struct cnss_plat_ipc_qmi_init_setup_req_msg_v01 *)decoded_msg;
cnss_plat_ipc_dbg("MAC: %d HW_TRC: %d CAL: %d\n",
req_msg->dms_mac_addr_supported,
req_msg->qdss_hw_trace_override,
req_msg->cal_file_available_bitmask);
cfg->dms_mac_addr_supported = req_msg->dms_mac_addr_supported;
cfg->qdss_hw_trace_override = req_msg->qdss_hw_trace_override;
cfg->cal_file_available_bitmask = req_msg->cal_file_available_bitmask;
ret = qmi_send_response
(svc->svc_hdl, sq, txn,
CNSS_PLAT_IPC_QMI_INIT_SETUP_RESP_V01,
CNSS_PLAT_IPC_QMI_INIT_SETUP_RESP_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_init_setup_resp_msg_v01_ei, &resp);
if (ret < 0)
cnss_plat_ipc_err("%s: QMI failed: %d\n", __func__, ret);
}
/**
* cnss_plat_ipc_qmi_reg_client_req_handler() - Register QMI client
* @handle: Pointer to QMI handle
* @sq: QMI socket
* @txn: QMI transaction pointer
* @decoded_msg: Pointer to decoded QMI message
*
* Handles the userspace QMI client registration.
*
* Return: None
*/
static void
cnss_plat_ipc_qmi_reg_client_req_handler(struct qmi_handle *handle,
struct sockaddr_qrtr *sq,
struct qmi_txn *txn,
const void *decoded_msg)
{
struct cnss_plat_ipc_qmi_reg_client_req_msg_v01 *req_msg;
struct cnss_plat_ipc_qmi_reg_client_resp_msg_v01 resp = {0};
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client = svc->qmi_client_ctx;
int ret = 0;
req_msg =
(struct cnss_plat_ipc_qmi_reg_client_req_msg_v01 *)decoded_msg;
if (req_msg->client_id_valid) {
if (req_msg->client_id <= CNSS_PLAT_IPC_MAX_QMI_CLIENTS &&
!qmi_client[req_msg->client_id].client_connected) {
cnss_plat_ipc_info
("%s: QMI Client Connected. QMI Socket Node: %d Port: %d ID: %d\n",
__func__, sq->sq_node, sq->sq_port,
req_msg->client_id);
qmi_client[req_msg->client_id].client_sq = *sq;
qmi_client[req_msg->client_id].client_connected = true;
cnss_plat_ipc_qmi_update_user
((enum cnss_plat_ipc_qmi_client_id_v01)
req_msg->client_id);
} else {
cnss_plat_ipc_err("QMI client already connected or Invalid client id\n");
return;
}
}
ret = qmi_send_response
(svc->svc_hdl, sq, txn,
CNSS_PLAT_IPC_QMI_REG_CLIENT_RESP_V01,
CNSS_PLAT_IPC_QMI_REG_CLIENT_RESP_MSG_V01_MAX_MSG_LEN,
cnss_plat_ipc_qmi_reg_client_resp_msg_v01_ei, &resp);
if (ret < 0)
cnss_plat_ipc_err("QMI failed: %d\n", ret);
}
/**
* cnss_plat_ipc_qmi_disconnect_cb() - Handler for QMI node disconnect specific
* to node and port
* @handle: Pointer to QMI handle
* @node: QMI node that is disconnected
* @port: QMI port that is disconnected
*
* Return: None
*/
static void cnss_plat_ipc_qmi_disconnect_cb(struct qmi_handle *handle,
unsigned int node,
unsigned int port)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client =
svc->qmi_client_ctx;
struct cnss_plat_ipc_file_data *fd;
u32 file_id;
int i;
if (svc->svc_hdl != handle) {
cnss_plat_ipc_err("Invalid QMI Handle\n");
return;
}
for (i = 0; i <= CNSS_PLAT_IPC_MAX_QMI_CLIENTS; i++) {
if (qmi_client[i].client_connected &&
qmi_client[i].client_sq.sq_node == node &&
qmi_client[i].client_sq.sq_port == port) {
cnss_plat_ipc_err
("%s: QMI client disconnect. QMI Socket Node:%d Port:%d ID: %d\n",
__func__, node, port, i);
qmi_client[i].client_sq.sq_node = 0;
qmi_client[i].client_sq.sq_port = 0;
qmi_client[i].client_sq.sq_family = 0;
qmi_client[i].client_connected = false;
/* Daemon killed. Fail any download / upload in progress. This
* will also free stale fd
*/
mutex_lock(&svc->file_idr_lock);
idr_for_each_entry(&svc->file_idr, fd, file_id)
complete(&fd->complete);
mutex_unlock(&svc->file_idr_lock);
cnss_plat_ipc_qmi_update_user(i);
}
}
}
/**
* cnss_plat_ipc_qmi_bye_cb() - Handler for QMI node disconnect for all port of
* the given node.
* @handle: Pointer to QMI handle
* @node: QMI node that is disconnected
*
* Return: None
*/
static void cnss_plat_ipc_qmi_bye_cb(struct qmi_handle *handle,
unsigned int node)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client =
svc->qmi_client_ctx;
int i;
for (i = 0; i <= CNSS_PLAT_IPC_MAX_QMI_CLIENTS; i++) {
cnss_plat_ipc_qmi_disconnect_cb
(handle, node,
qmi_client[i].client_sq.sq_port);
}
}
static struct qmi_ops cnss_plat_ipc_qmi_ops = {
/* inform a client that all clients from a node are gone */
.bye = cnss_plat_ipc_qmi_bye_cb,
.del_client = cnss_plat_ipc_qmi_disconnect_cb,
};
static struct qmi_msg_handler cnss_plat_ipc_qmi_req_handlers[] = {
{
.type = QMI_REQUEST,
.msg_id = CNSS_PLAT_IPC_QMI_REG_CLIENT_REQ_V01,
.ei = cnss_plat_ipc_qmi_reg_client_req_msg_v01_ei,
.decoded_size =
CNSS_PLAT_IPC_QMI_REG_CLIENT_REQ_MSG_V01_MAX_MSG_LEN,
.fn = cnss_plat_ipc_qmi_reg_client_req_handler,
},
{
.type = QMI_REQUEST,
.msg_id = CNSS_PLAT_IPC_QMI_INIT_SETUP_REQ_V01,
.ei = cnss_plat_ipc_qmi_init_setup_req_msg_v01_ei,
.decoded_size =
CNSS_PLAT_IPC_QMI_INIT_SETUP_REQ_MSG_V01_MAX_MSG_LEN,
.fn = cnss_plat_ipc_qmi_init_setup_req_handler,
},
{
.type = QMI_REQUEST,
.msg_id = CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_REQ_V01,
.ei = cnss_plat_ipc_qmi_file_download_req_msg_v01_ei,
.decoded_size =
CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN,
.fn = cnss_plat_ipc_qmi_file_download_req_handler,
},
{
.type = QMI_REQUEST,
.msg_id = CNSS_PLAT_IPC_QMI_FILE_UPLOAD_REQ_V01,
.ei = cnss_plat_ipc_qmi_file_upload_req_msg_v01_ei,
.decoded_size =
CNSS_PLAT_IPC_QMI_FILE_UPLOAD_REQ_MSG_V01_MAX_MSG_LEN,
.fn = cnss_plat_ipc_qmi_file_upload_req_handler,
},
{}
};
/**
* cnss_plat_ipc_qmi_daemon_config() - Get daemon config for CNSS platform
*
* Return: Pointer to daemon client config
*/
struct cnss_plat_ipc_daemon_config *cnss_plat_ipc_qmi_daemon_config(void)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client =
&svc->qmi_client_ctx[CNSS_PLAT_IPC_DAEMON_QMI_CLIENT_V01];
if (!qmi_client->client_connected)
return NULL;
return &daemon_cfg;
}
EXPORT_SYMBOL(cnss_plat_ipc_qmi_daemon_config);
/**
* cnss_plat_ipc_register() - Register for QMI IPC client status update
* @client_id: User space QMI IPC client ID. Also works as
* array index for QMI client context
* @connect_update_cb: Function pointer for callback
* @cb_ctx: Callback context
*
* Return: 0 on success, negative error value otherwise
*/
int cnss_plat_ipc_register(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
cnss_plat_ipc_connection_update
connection_update_cb, void *cb_ctx)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client;
int num_user;
if (client_id > CNSS_PLAT_IPC_MAX_QMI_CLIENTS) {
cnss_plat_ipc_err("Invalid Client ID: %d\n", client_id);
return -EINVAL;
}
qmi_client = &svc->qmi_client_ctx[client_id];
num_user = qmi_client->num_user;
if (num_user >= CNSS_PLAT_IPC_MAX_USER) {
cnss_plat_ipc_err("Max Service users reached\n");
return -EINVAL;
}
qmi_client->connection_update_cb[num_user] = connection_update_cb;
qmi_client->cb_ctx[num_user] = cb_ctx;
qmi_client->num_user++;
return 0;
}
EXPORT_SYMBOL(cnss_plat_ipc_register);
/**
* cnss_plat_ipc_unregister() - Unregister QMI IPC client status callback
* @client_id: User space QMI IPC client ID. Also works as
* array index for QMI client context
* @cb_cbt: Callback context provided during registration
*
* Return: None
*/
void cnss_plat_ipc_unregister(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
void *cb_ctx)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
struct cnss_plat_ipc_qmi_client_ctx *qmi_client;
int i;
if (client_id > CNSS_PLAT_IPC_MAX_QMI_CLIENTS) {
cnss_plat_ipc_err("Invalid Client ID: %d\n", client_id);
return;
}
qmi_client = &svc->qmi_client_ctx[client_id];
for (i = 0; i < qmi_client->num_user; i++) {
if (qmi_client->cb_ctx[i] == cb_ctx) {
qmi_client->cb_ctx[i] = NULL;
qmi_client->connection_update_cb[i] = NULL;
qmi_client->num_user--;
break;
}
}
}
EXPORT_SYMBOL(cnss_plat_ipc_unregister);
/**
* cnss_plat_ipc_init_fn() - CNSS Platform qmi service init function
*
* Initialize a QMI client handle and register new QMI service for CNSS Platform
*
* Return: None
*/
static void cnss_plat_ipc_init_fn(struct work_struct *work)
{
int ret = 0, retry = 0;
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
svc->svc_hdl = kzalloc(sizeof(*svc->svc_hdl), GFP_KERNEL);
if (!svc->svc_hdl)
return;
retry:
ret = qmi_handle_init(svc->svc_hdl,
CNSS_PLAT_IPC_QMI_MAX_MSG_SIZE_V01,
&cnss_plat_ipc_qmi_ops,
cnss_plat_ipc_qmi_req_handlers);
if (ret < 0) {
/* If QMI fails to init, retry for total
* QMI_INIT_RETRY_DELAY_MS * QMI_INIT_RETRY_MAX_TIMES ms.
*/
if (retry++ < QMI_INIT_RETRY_MAX_TIMES) {
msleep(QMI_INIT_RETRY_DELAY_MS);
goto retry;
}
cnss_plat_ipc_err("Failed to init QMI handle after %d ms * %d, err = %d\n",
ret, QMI_INIT_RETRY_DELAY_MS,
QMI_INIT_RETRY_MAX_TIMES);
goto free_svc_hdl;
}
ret = qmi_add_server(svc->svc_hdl,
CNSS_PLATFORM_SERVICE_ID_V01,
CNSS_PLATFORM_SERVICE_VERS_V01, 0);
if (ret < 0) {
cnss_plat_ipc_err("Server add fail: %d\n", ret);
goto release_svc_hdl;
}
cnss_plat_ipc_info("CNSS Platform IPC QMI Service is started\n");
idr_init(&svc->file_idr);
mutex_init(&svc->file_idr_lock);
return;
release_svc_hdl:
qmi_handle_release(svc->svc_hdl);
free_svc_hdl:
kfree(svc->svc_hdl);
}
/**
* cnss_plat_ipc_is_valid_dt_node_found - Check if valid device tree node
* present
*
* Valid device tree node means a node with "qcom,wlan" property present
* and "status" property not disabled.
*
* Return: true if valid device tree node found, false if not found
*/
static bool cnss_plat_ipc_is_valid_dt_node_found(void)
{
struct device_node *dn = NULL;
for_each_node_with_property(dn, "qcom,wlan") {
if (of_device_is_available(dn))
break;
}
if (dn)
return true;
return false;
}
static DECLARE_WORK(cnss_plat_ipc_init_work, cnss_plat_ipc_init_fn);
static int __init cnss_plat_ipc_qmi_svc_init(void)
{
if (!cnss_plat_ipc_is_valid_dt_node_found())
return -ENODEV;
/* Schedule a work to do real init to avoid blocking here */
cnss_plat_ipc_logging_init();
schedule_work(&cnss_plat_ipc_init_work);
return 0;
}
/**
* cnss_plat_ipc_qmi_svc_exit() - CNSS Platform qmi service exit
*
* Release all resources during exit
*
* Return: None
*/
static void __exit cnss_plat_ipc_qmi_svc_exit(void)
{
struct cnss_plat_ipc_qmi_svc_ctx *svc = &plat_ipc_qmi_svc;
cancel_work_sync(&cnss_plat_ipc_init_work);
if (svc->svc_hdl) {
qmi_handle_release(svc->svc_hdl);
kfree(svc->svc_hdl);
idr_destroy(&svc->file_idr);
}
cnss_plat_ipc_logging_deinit();
}
module_init(cnss_plat_ipc_qmi_svc_init);
module_exit(cnss_plat_ipc_qmi_svc_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CNSS Platform IPC QMI Service");

View File

@@ -0,0 +1,86 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
#ifndef _CNSS_PLAT_IPC_QMI_H
#define _CNSS_PLAT_IPC_QMI_H
#include "cnss_plat_ipc_service_v01.h"
/* As the value of CNSS_PLAT_IPC_MAX_QMI_CLIENTS will keep changing
* addition of new QMI client, it cannot be kept in IDL as change in
* existing value can cause backward compatibily issue. Keep it here
* and update its value with new QMI client ID added in enum in IDL.
*/
#define CNSS_PLAT_IPC_MAX_QMI_CLIENTS CNSS_PLAT_IPC_DAEMON_QMI_CLIENT_V01
/**
* cnss_plat_ipc_daemon_config: Config options provided by cnss-daemon
* @dms_mac_addr_supported: DMS MAC address provisioning support
* @qdss_hw_trace_override: QDSS config for HW trace enable
* @cal_file_available_bitmask: Calibration file available
*/
struct cnss_plat_ipc_daemon_config {
u8 dms_mac_addr_supported;
u8 qdss_hw_trace_override;
u32 cal_file_available_bitmask;
};
typedef void (*cnss_plat_ipc_connection_update)(void *cb_ctx,
bool connection_status);
/**
* Persistent caldb file store which is a runtime FW param based feature will
* fail if CONFIG_CNSS_PLAT_IPC_QMI_SVC is not enabled.
**/
#if IS_ENABLED(CONFIG_CNSS_PLAT_IPC_QMI_SVC)
int cnss_plat_ipc_register(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
cnss_plat_ipc_connection_update
connection_update_cb, void *cb_ctx);
void cnss_plat_ipc_unregister(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
void *cb_ctx);
int cnss_plat_ipc_qmi_file_download(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, char *buf,
u32 *size);
int cnss_plat_ipc_qmi_file_upload(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, u8 *file_buf,
u32 file_size);
struct cnss_plat_ipc_daemon_config *cnss_plat_ipc_qmi_daemon_config(void);
#else
static inline
int cnss_plat_ipc_register(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
cnss_plat_ipc_connection_update
connection_update_cb, void *cb_ctx)
{
return 0;
}
static inline
void cnss_plat_ipc_unregister(enum cnss_plat_ipc_qmi_client_id_v01 client_id,
void *cb_ctx)
{
}
static inline
int cnss_plat_ipc_qmi_file_download(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, char *buf,
u32 *size)
{
return -EOPNOTSUPP;
}
static inline
int cnss_plat_ipc_qmi_file_upload(enum cnss_plat_ipc_qmi_client_id_v01
client_id, char *file_name, u8 *file_buf,
u32 file_size)
{
return -EOPNOTSUPP;
}
static inline
struct cnss_plat_ipc_daemon_config *cnss_plat_ipc_qmi_daemon_config(void)
{
return NULL;
}
#endif
#endif

View File

@@ -0,0 +1,402 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2021, The Linux Foundation. All rights reserved. */
#include "cnss_plat_ipc_service_v01.h"
#include <linux/module.h>
struct qmi_elem_info cnss_plat_ipc_qmi_init_setup_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
cnss_plat_ipc_qmi_init_setup_req_msg_v01,
dms_mac_addr_supported),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_init_setup_req_msg_v01,
qdss_hw_trace_override),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_init_setup_req_msg_v01,
cal_file_available_bitmask),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_init_setup_req_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_init_setup_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_init_setup_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
.elem_size = sizeof(u64),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_init_setup_resp_msg_v01,
drv_status),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_init_setup_resp_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_download_ind_msg_v01_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01 + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_ind_msg_v01,
file_name),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_ind_msg_v01,
file_id),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_download_ind_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_download_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
file_id),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
file_size),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
end),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x04,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
seg_index),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x05,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
seg_buf_len),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01,
.elem_size = sizeof(u8),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x05,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_req_msg_v01,
seg_buf),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_download_req_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_download_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_resp_msg_v01,
file_id),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x04,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_download_resp_msg_v01,
seg_index),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_download_resp_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_ind_msg_v01_ei[] = {
{
.data_type = QMI_STRING,
.elem_len = CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01 + 1,
.elem_size = sizeof(char),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_ind_msg_v01,
file_name),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_ind_msg_v01,
file_id),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_ind_msg_v01,
file_size),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_upload_ind_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_req_msg_v01,
file_id),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_req_msg_v01,
seg_index),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_upload_req_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x03,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
file_id),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x04,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
end),
},
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
.elem_size = sizeof(u32),
.array_type = NO_ARRAY,
.tlv_type = 0x05,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
seg_index),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u16),
.array_type = NO_ARRAY,
.tlv_type = 0x06,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
seg_buf_len),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01,
.elem_size = sizeof(u8),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x06,
.offset = offsetof(struct
cnss_plat_ipc_qmi_file_upload_resp_msg_v01,
seg_buf),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_file_upload_resp_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_reg_client_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
cnss_plat_ipc_qmi_reg_client_req_msg_v01,
client_id_valid),
},
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum cnss_plat_ipc_qmi_client_id_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
cnss_plat_ipc_qmi_reg_client_req_msg_v01,
client_id),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_reg_client_req_msg_v01_ei);
struct qmi_elem_info cnss_plat_ipc_qmi_reg_client_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
cnss_plat_ipc_qmi_reg_client_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(cnss_plat_ipc_qmi_reg_client_resp_msg_v01_ei);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("WLAN FW QMI service");

View File

@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
#ifndef CNSS_PLAT_IPC_SERVICE_V01_H
#define CNSS_PLAT_IPC_SERVICE_V01_H
#include <linux/soc/qcom/qmi.h>
#define CNSS_PLATFORM_SERVICE_ID_V01 0x42E
#define CNSS_PLATFORM_SERVICE_VERS_V01 0x01
#define CNSS_PLAT_IPC_QMI_REG_CLIENT_RESP_V01 0x0006
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_REQ_V01 0x0003
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_IND_V01 0x0004
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_IND_V01 0x0002
#define CNSS_PLAT_IPC_QMI_REG_CLIENT_REQ_V01 0x0006
#define CNSS_PLAT_IPC_QMI_INIT_SETUP_REQ_V01 0x0001
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_REQ_V01 0x0005
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_RESP_V01 0x0003
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_RESP_V01 0x0005
#define CNSS_PLAT_IPC_QMI_INIT_SETUP_RESP_V01 0x0001
#define CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01 32
#define CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01 61440
#define CNSS_PLAT_IPC_QMI_MAX_MSG_SIZE_V01 65535
#define CNSS_PLAT_IPC_QMI_DRIVER_CBC_DONE_V01 ((u64)0x01ULL)
#define CNSS_PLAT_IPC_QMI_DRIVER_WLAN_ACTIVE_V01 ((u64)0x02ULL)
enum cnss_plat_ipc_qmi_client_id_v01 {
CNSS_PLAT_IPC_QMI_CLIENT_ID_MIN_VAL_V01 = INT_MIN,
CNSS_PLAT_IPC_BT_QMI_CLIENT_V01 = 0,
CNSS_PLAT_IPC_DAEMON_QMI_CLIENT_V01 = 1,
CNSS_PLAT_IPC_QMI_CLIENT_ID_MAX_VAL_V01 = INT_MAX,
};
struct cnss_plat_ipc_qmi_init_setup_req_msg_v01 {
u8 dms_mac_addr_supported;
u8 qdss_hw_trace_override;
u32 cal_file_available_bitmask;
};
#define CNSS_PLAT_IPC_QMI_INIT_SETUP_REQ_MSG_V01_MAX_MSG_LEN 15
extern struct qmi_elem_info cnss_plat_ipc_qmi_init_setup_req_msg_v01_ei[];
struct cnss_plat_ipc_qmi_init_setup_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u64 drv_status;
};
#define CNSS_PLAT_IPC_QMI_INIT_SETUP_RESP_MSG_V01_MAX_MSG_LEN 18
extern struct qmi_elem_info cnss_plat_ipc_qmi_init_setup_resp_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_download_ind_msg_v01 {
char file_name[CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01 + 1];
u32 file_id;
};
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 42
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_download_ind_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_download_req_msg_v01 {
u32 file_id;
u32 file_size;
u8 end;
u32 seg_index;
u32 seg_buf_len;
u8 seg_buf[CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01];
};
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 61470
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_download_req_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u32 file_id;
u32 seg_index;
};
#define CNSS_PLAT_IPC_QMI_FILE_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 21
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_download_resp_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_upload_ind_msg_v01 {
char file_name[CNSS_PLAT_IPC_QMI_MAX_FILE_NAME_LEN_V01 + 1];
u32 file_id;
u32 file_size;
};
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_IND_MSG_V01_MAX_MSG_LEN 49
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_ind_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_upload_req_msg_v01 {
u32 file_id;
u32 seg_index;
};
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_REQ_MSG_V01_MAX_MSG_LEN 14
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_req_msg_v01_ei[];
struct cnss_plat_ipc_qmi_file_upload_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u32 file_id;
u8 end;
u32 seg_index;
u32 seg_buf_len;
u8 seg_buf[CNSS_PLAT_IPC_QMI_MAX_DATA_SIZE_V01];
};
#define CNSS_PLAT_IPC_QMI_FILE_UPLOAD_RESP_MSG_V01_MAX_MSG_LEN 61470
extern struct qmi_elem_info cnss_plat_ipc_qmi_file_upload_resp_msg_v01_ei[];
struct cnss_plat_ipc_qmi_reg_client_req_msg_v01 {
u8 client_id_valid;
enum cnss_plat_ipc_qmi_client_id_v01 client_id;
};
#define CNSS_PLAT_IPC_QMI_REG_CLIENT_REQ_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info cnss_plat_ipc_qmi_reg_client_req_msg_v01_ei[];
struct cnss_plat_ipc_qmi_reg_client_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define CNSS_PLAT_IPC_QMI_REG_CLIENT_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info cnss_plat_ipc_qmi_reg_client_resp_msg_v01_ei[];
#endif

500
cnss_utils/cnss_utils.c Normal file
View File

@@ -0,0 +1,500 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017, 2019, 2021 The Linux Foundation. All rights reserved. */
#define pr_fmt(fmt) "cnss_utils: " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <net/cnss_utils.h>
#define CNSS_MAX_CH_NUM 157
struct cnss_unsafe_channel_list {
u16 unsafe_ch_count;
u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
};
struct cnss_dfs_nol_info {
void *dfs_nol_info;
u16 dfs_nol_info_len;
};
#define MAX_NO_OF_MAC_ADDR 4
#define MAC_PREFIX_LEN 2
struct cnss_wlan_mac_addr {
u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
u32 no_of_mac_addr_set;
};
enum mac_type {
CNSS_MAC_PROVISIONED,
CNSS_MAC_DERIVED,
};
static struct cnss_utils_priv {
struct cnss_unsafe_channel_list unsafe_channel_list;
struct cnss_dfs_nol_info dfs_nol_info;
/* generic mutex for unsafe channel */
struct mutex unsafe_channel_list_lock;
/* generic spin-lock for dfs_nol info */
spinlock_t dfs_nol_info_lock;
int driver_load_cnt;
struct cnss_wlan_mac_addr wlan_mac_addr;
struct cnss_wlan_mac_addr wlan_der_mac_addr;
enum cnss_utils_cc_src cc_source;
struct dentry *root_dentry;
} *cnss_utils_priv;
int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
u16 *unsafe_ch_list, u16 ch_count)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
mutex_lock(&priv->unsafe_channel_list_lock);
if (!unsafe_ch_list || ch_count > CNSS_MAX_CH_NUM) {
mutex_unlock(&priv->unsafe_channel_list_lock);
return -EINVAL;
}
priv->unsafe_channel_list.unsafe_ch_count = ch_count;
if (ch_count == 0)
goto end;
memcpy(priv->unsafe_channel_list.unsafe_ch_list,
unsafe_ch_list, ch_count * sizeof(u16));
end:
mutex_unlock(&priv->unsafe_channel_list_lock);
return 0;
}
EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
u16 *unsafe_ch_list,
u16 *ch_count, u16 buf_len)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
mutex_lock(&priv->unsafe_channel_list_lock);
if (!unsafe_ch_list || !ch_count) {
mutex_unlock(&priv->unsafe_channel_list_lock);
return -EINVAL;
}
if (buf_len <
(priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
mutex_unlock(&priv->unsafe_channel_list_lock);
return -ENOMEM;
}
*ch_count = priv->unsafe_channel_list.unsafe_ch_count;
memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
mutex_unlock(&priv->unsafe_channel_list_lock);
return 0;
}
EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
int cnss_utils_wlan_set_dfs_nol(struct device *dev,
const void *info, u16 info_len)
{
void *temp;
void *old_nol_info;
struct cnss_dfs_nol_info *dfs_info;
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
if (!info || !info_len)
return -EINVAL;
temp = kmemdup(info, info_len, GFP_ATOMIC);
if (!temp)
return -ENOMEM;
spin_lock_bh(&priv->dfs_nol_info_lock);
dfs_info = &priv->dfs_nol_info;
old_nol_info = dfs_info->dfs_nol_info;
dfs_info->dfs_nol_info = temp;
dfs_info->dfs_nol_info_len = info_len;
spin_unlock_bh(&priv->dfs_nol_info_lock);
kfree(old_nol_info);
return 0;
}
EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
int cnss_utils_wlan_get_dfs_nol(struct device *dev,
void *info, u16 info_len)
{
int len;
struct cnss_dfs_nol_info *dfs_info;
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
if (!info || !info_len)
return -EINVAL;
spin_lock_bh(&priv->dfs_nol_info_lock);
dfs_info = &priv->dfs_nol_info;
if (!dfs_info->dfs_nol_info ||
dfs_info->dfs_nol_info_len == 0) {
spin_unlock_bh(&priv->dfs_nol_info_lock);
return -ENOENT;
}
len = min(info_len, dfs_info->dfs_nol_info_len);
memcpy(info, dfs_info->dfs_nol_info, len);
spin_unlock_bh(&priv->dfs_nol_info_lock);
return len;
}
EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
void cnss_utils_increment_driver_load_cnt(struct device *dev)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return;
++(priv->driver_load_cnt);
}
EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
int cnss_utils_get_driver_load_cnt(struct device *dev)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
return priv->driver_load_cnt;
}
EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
enum mac_type type)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
u32 no_of_mac_addr;
struct cnss_wlan_mac_addr *addr = NULL;
int iter;
u8 *temp = NULL;
if (!priv)
return -EINVAL;
if (len == 0 || (len % ETH_ALEN) != 0) {
pr_err("Invalid length %d\n", len);
return -EINVAL;
}
no_of_mac_addr = len / ETH_ALEN;
if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
pr_err("Exceed maximum supported MAC address %u %u\n",
MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
return -EINVAL;
}
if (type == CNSS_MAC_PROVISIONED)
addr = &priv->wlan_mac_addr;
else
addr = &priv->wlan_der_mac_addr;
if (addr->no_of_mac_addr_set) {
pr_err("WLAN MAC address is already set, num %d type %d\n",
addr->no_of_mac_addr_set, type);
return 0;
}
addr->no_of_mac_addr_set = no_of_mac_addr;
temp = &addr->mac_addr[0][0];
for (iter = 0; iter < no_of_mac_addr;
++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
ether_addr_copy(temp, mac_list);
pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
temp[0], temp[1], temp[2],
temp[3], temp[4], temp[5]);
}
return 0;
}
int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
{
return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
}
EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
int cnss_utils_set_wlan_derived_mac_address(const u8 *mac_list,
const uint32_t len)
{
return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
}
EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
static u8 *get_wlan_mac_address(struct device *dev,
u32 *num, enum mac_type type)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
struct cnss_wlan_mac_addr *addr = NULL;
if (!priv)
goto out;
if (type == CNSS_MAC_PROVISIONED)
addr = &priv->wlan_mac_addr;
else
addr = &priv->wlan_der_mac_addr;
if (!addr->no_of_mac_addr_set) {
pr_err("WLAN MAC address is not set, type %d\n", type);
goto out;
}
*num = addr->no_of_mac_addr_set;
return &addr->mac_addr[0][0];
out:
*num = 0;
return NULL;
}
u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
{
return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
}
EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev,
uint32_t *num)
{
return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
}
EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
void cnss_utils_set_cc_source(struct device *dev,
enum cnss_utils_cc_src cc_source)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return;
priv->cc_source = cc_source;
}
EXPORT_SYMBOL(cnss_utils_set_cc_source);
enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
{
struct cnss_utils_priv *priv = cnss_utils_priv;
if (!priv)
return -EINVAL;
return priv->cc_source;
}
EXPORT_SYMBOL(cnss_utils_get_cc_source);
static ssize_t cnss_utils_mac_write(struct file *fp,
const char __user *user_buf,
size_t count, loff_t *off)
{
struct cnss_utils_priv *priv =
((struct seq_file *)fp->private_data)->private;
char buf[128];
char *input, *mac_type, *mac_address;
u8 *dest_mac;
u8 val;
const char *delim = "\n";
size_t len = 0;
char temp[3] = "";
len = min_t(size_t, count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EINVAL;
buf[len] = '\0';
input = buf;
mac_type = strsep(&input, delim);
if (!mac_type)
return -EINVAL;
if (!input)
return -EINVAL;
mac_address = strsep(&input, delim);
if (!mac_address)
return -EINVAL;
if (strcmp("0x", mac_address)) {
pr_err("Invalid MAC prefix\n");
return -EINVAL;
}
len = strlen(mac_address);
mac_address += MAC_PREFIX_LEN;
len -= MAC_PREFIX_LEN;
if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR ||
len % (ETH_ALEN * 2) != 0) {
pr_err("Invalid MAC address length %zu\n", len);
return -EINVAL;
}
if (!strcmp("provisioned", mac_type)) {
dest_mac = &priv->wlan_mac_addr.mac_addr[0][0];
priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2);
} else if (!strcmp("derived", mac_type)) {
dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0];
priv->wlan_der_mac_addr.no_of_mac_addr_set =
len / (ETH_ALEN * 2);
} else {
pr_err("Invalid MAC address type %s\n", mac_type);
return -EINVAL;
}
while (len--) {
temp[0] = *mac_address++;
temp[1] = *mac_address++;
if (kstrtou8(temp, 16, &val))
return -EINVAL;
*dest_mac++ = val;
}
return count;
}
static int cnss_utils_mac_show(struct seq_file *s, void *data)
{
u8 mac[6];
int i;
struct cnss_utils_priv *priv = s->private;
struct cnss_wlan_mac_addr *addr = NULL;
addr = &priv->wlan_mac_addr;
if (addr->no_of_mac_addr_set) {
seq_puts(s, "\nProvisioned MAC addresseses\n");
for (i = 0; i < addr->no_of_mac_addr_set; i++) {
ether_addr_copy(mac, addr->mac_addr[i]);
seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]);
}
}
addr = &priv->wlan_der_mac_addr;
if (addr->no_of_mac_addr_set) {
seq_puts(s, "\nDerived MAC addresseses\n");
for (i = 0; i < addr->no_of_mac_addr_set; i++) {
ether_addr_copy(mac, addr->mac_addr[i]);
seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]);
}
}
return 0;
}
static int cnss_utils_mac_open(struct inode *inode, struct file *file)
{
return single_open(file, cnss_utils_mac_show, inode->i_private);
}
static const struct file_operations cnss_utils_mac_fops = {
.read = seq_read,
.write = cnss_utils_mac_write,
.release = single_release,
.open = cnss_utils_mac_open,
.owner = THIS_MODULE,
.llseek = seq_lseek,
};
static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv)
{
int ret = 0;
struct dentry *root_dentry;
root_dentry = debugfs_create_dir("cnss_utils", NULL);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
pr_err("Unable to create debugfs %d\n", ret);
goto out;
}
priv->root_dentry = root_dentry;
debugfs_create_file("mac_address", 0600, root_dentry, priv,
&cnss_utils_mac_fops);
out:
return ret;
}
/**
* cnss_utils_is_valid_dt_node_found - Check if valid device tree node present
*
* Valid device tree node means a node with "qcom,wlan" property present and
* "status" property not disabled.
*
* Return: true if valid device tree node found, false if not found
*/
static bool cnss_utils_is_valid_dt_node_found(void)
{
struct device_node *dn = NULL;
for_each_node_with_property(dn, "qcom,wlan") {
if (of_device_is_available(dn))
break;
}
if (dn)
return true;
return false;
}
static int __init cnss_utils_init(void)
{
struct cnss_utils_priv *priv = NULL;
if (!cnss_utils_is_valid_dt_node_found())
return -ENODEV;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->cc_source = CNSS_UTILS_SOURCE_CORE;
mutex_init(&priv->unsafe_channel_list_lock);
spin_lock_init(&priv->dfs_nol_info_lock);
cnss_utils_debugfs_create(priv);
cnss_utils_priv = priv;
return 0;
}
static void __exit cnss_utils_exit(void)
{
kfree(cnss_utils_priv);
cnss_utils_priv = NULL;
}
module_init(cnss_utils_init);
module_exit(cnss_utils_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CNSS Utilities Driver");

View File

@@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
#include <linux/soc/qcom/qmi.h>
#include "device_management_service_v01.h"
struct qmi_elem_info dms_get_mac_address_req_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
.elem_size = sizeof(enum dms_device_mac_enum_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x01,
.offset = offsetof(struct
dms_get_mac_address_req_msg_v01,
device),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(dms_get_mac_address_req_msg_v01_ei);
struct qmi_elem_info dms_get_mac_address_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
.elem_size = sizeof(struct qmi_response_type_v01),
.array_type = NO_ARRAY,
.tlv_type = 0x02,
.offset = offsetof(struct
dms_get_mac_address_resp_msg_v01,
resp),
.ei_array = qmi_response_type_v01_ei,
},
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
dms_get_mac_address_resp_msg_v01,
mac_address_valid),
},
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
.elem_size = sizeof(u8),
.array_type = NO_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
dms_get_mac_address_resp_msg_v01,
mac_address_len),
},
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = QMI_DMS_MAC_ADDR_MAX_V01,
.elem_size = sizeof(u8),
.array_type = VAR_LEN_ARRAY,
.tlv_type = 0x10,
.offset = offsetof(struct
dms_get_mac_address_resp_msg_v01,
mac_address),
},
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
},
};
EXPORT_SYMBOL(dms_get_mac_address_resp_msg_v01_ei);

View File

@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
#ifndef DEVICE_MANAGEMENT_SERVICE_V01_H
#define DEVICE_MANAGEMENT_SERVICE_V01_H
#define DMS_SERVICE_ID_V01 0x02
#define DMS_SERVICE_VERS_V01 0x01
#define QMI_DMS_GET_MAC_ADDRESS_RESP_V01 0x005C
#define QMI_DMS_GET_MAC_ADDRESS_REQ_V01 0x005C
#define QMI_DMS_MAC_ADDR_MAX_V01 8
enum dms_device_mac_enum_v01 {
DMS_DEVICE_MAC_ENUM_MIN_VAL_V01 = INT_MIN,
DMS_DEVICE_MAC_WLAN_V01 = 0,
DMS_DEVICE_MAC_BT_V01 = 1,
DMS_DEVICE_MAC_ENUM_MAX_VAL_V01 = INT_MAX,
};
struct dms_get_mac_address_req_msg_v01 {
enum dms_device_mac_enum_v01 device;
};
#define DMS_GET_MAC_ADDRESS_REQ_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info dms_get_mac_address_req_msg_v01_ei[];
struct dms_get_mac_address_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 mac_address_valid;
u32 mac_address_len;
u8 mac_address[QMI_DMS_MAC_ADDR_MAX_V01];
};
#define DMS_GET_MAC_ADDRESS_RESP_MSG_V01_MAX_MSG_LEN 19
extern struct qmi_elem_info dms_get_mac_address_resp_msg_v01_ei[];
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

278
inc/cnss2.h Normal file
View File

@@ -0,0 +1,278 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS2_H
#define _NET_CNSS2_H
#include <linux/pci.h>
#define CNSS_MAX_FILE_NAME 20
#define CNSS_MAX_TIMESTAMP_LEN 32
#define CNSS_MAX_DEV_MEM_NUM 4
/*
* Temporary change for compilation, will be removed
* after WLAN host driver switched to use new APIs
*/
#define CNSS_API_WITH_DEV
enum cnss_bus_width_type {
CNSS_BUS_WIDTH_NONE,
CNSS_BUS_WIDTH_IDLE,
CNSS_BUS_WIDTH_LOW,
CNSS_BUS_WIDTH_MEDIUM,
CNSS_BUS_WIDTH_HIGH,
CNSS_BUS_WIDTH_VERY_HIGH,
CNSS_BUS_WIDTH_LOW_LATENCY
};
enum cnss_platform_cap_flag {
CNSS_HAS_EXTERNAL_SWREG = 0x01,
CNSS_HAS_UART_ACCESS = 0x02,
CNSS_HAS_DRV_SUPPORT = 0x04,
};
struct cnss_platform_cap {
u32 cap_flag;
};
struct cnss_fw_files {
char image_file[CNSS_MAX_FILE_NAME];
char board_data[CNSS_MAX_FILE_NAME];
char otp_data[CNSS_MAX_FILE_NAME];
char utf_file[CNSS_MAX_FILE_NAME];
char utf_board_data[CNSS_MAX_FILE_NAME];
char epping_file[CNSS_MAX_FILE_NAME];
char evicted_data[CNSS_MAX_FILE_NAME];
};
struct cnss_device_version {
u32 family_number;
u32 device_number;
u32 major_version;
u32 minor_version;
};
struct cnss_dev_mem_info {
u64 start;
u64 size;
};
struct cnss_soc_info {
void __iomem *va;
phys_addr_t pa;
uint32_t chip_id;
uint32_t chip_family;
uint32_t board_id;
uint32_t soc_id;
uint32_t fw_version;
char fw_build_timestamp[CNSS_MAX_TIMESTAMP_LEN + 1];
struct cnss_device_version device_version;
struct cnss_dev_mem_info dev_mem_info[CNSS_MAX_DEV_MEM_NUM];
};
struct cnss_wlan_runtime_ops {
int (*runtime_suspend)(struct pci_dev *pdev);
int (*runtime_resume)(struct pci_dev *pdev);
};
enum cnss_driver_status {
CNSS_UNINITIALIZED,
CNSS_INITIALIZED,
CNSS_LOAD_UNLOAD,
CNSS_RECOVERY,
CNSS_FW_DOWN,
CNSS_HANG_EVENT,
CNSS_BUS_EVENT,
};
enum cnss_bus_event_type {
BUS_EVENT_PCI_LINK_DOWN = 0,
BUS_EVENT_INVALID = 0xFFFF,
};
struct cnss_hang_event {
void *hang_event_data;
u16 hang_event_data_len;
};
struct cnss_bus_event {
enum cnss_bus_event_type etype;
void *event_data;
};
struct cnss_uevent_data {
enum cnss_driver_status status;
void *data;
};
struct cnss_wlan_driver {
char *name;
int (*probe)(struct pci_dev *pdev, const struct pci_device_id *id);
void (*remove)(struct pci_dev *pdev);
int (*idle_restart)(struct pci_dev *pdev,
const struct pci_device_id *id);
int (*idle_shutdown)(struct pci_dev *pdev);
int (*reinit)(struct pci_dev *pdev, const struct pci_device_id *id);
void (*shutdown)(struct pci_dev *pdev);
void (*crash_shutdown)(struct pci_dev *pdev);
int (*suspend)(struct pci_dev *pdev, pm_message_t state);
int (*resume)(struct pci_dev *pdev);
int (*suspend_noirq)(struct pci_dev *pdev);
int (*resume_noirq)(struct pci_dev *pdev);
void (*modem_status)(struct pci_dev *pdev, int state);
void (*update_status)(struct pci_dev *pdev, uint32_t status);
int (*update_event)(struct pci_dev *pdev,
struct cnss_uevent_data *uevent);
struct cnss_wlan_runtime_ops *runtime_ops;
const struct pci_device_id *id_table;
};
struct cnss_ce_tgt_pipe_cfg {
u32 pipe_num;
u32 pipe_dir;
u32 nentries;
u32 nbytes_max;
u32 flags;
u32 reserved;
};
struct cnss_ce_svc_pipe_cfg {
u32 service_id;
u32 pipe_dir;
u32 pipe_num;
};
struct cnss_shadow_reg_cfg {
u16 ce_id;
u16 reg_offset;
};
struct cnss_shadow_reg_v2_cfg {
u32 addr;
};
struct cnss_rri_over_ddr_cfg {
u32 base_addr_low;
u32 base_addr_high;
};
struct cnss_wlan_enable_cfg {
u32 num_ce_tgt_cfg;
struct cnss_ce_tgt_pipe_cfg *ce_tgt_cfg;
u32 num_ce_svc_pipe_cfg;
struct cnss_ce_svc_pipe_cfg *ce_svc_cfg;
u32 num_shadow_reg_cfg;
struct cnss_shadow_reg_cfg *shadow_reg_cfg;
u32 num_shadow_reg_v2_cfg;
struct cnss_shadow_reg_v2_cfg *shadow_reg_v2_cfg;
bool rri_over_ddr_cfg_valid;
struct cnss_rri_over_ddr_cfg rri_over_ddr_cfg;
};
enum cnss_driver_mode {
CNSS_MISSION,
CNSS_FTM,
CNSS_EPPING,
CNSS_WALTEST,
CNSS_OFF,
CNSS_CCPM,
CNSS_QVIT,
CNSS_CALIBRATION,
};
enum cnss_recovery_reason {
CNSS_REASON_DEFAULT,
CNSS_REASON_LINK_DOWN,
CNSS_REASON_RDDM,
CNSS_REASON_TIMEOUT,
};
enum cnss_remote_mem_type {
CNSS_REMOTE_MEM_TYPE_FW,
CNSS_REMOTE_MEM_TYPE_QDSS,
CNSS_REMOTE_MEM_TYPE_MAX,
};
struct cnss_mem_segment {
size_t size;
void *va;
phys_addr_t pa;
};
extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver);
extern void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver);
extern void cnss_device_crashed(struct device *dev);
extern int cnss_pci_prevent_l1(struct device *dev);
extern void cnss_pci_allow_l1(struct device *dev);
extern int cnss_pci_link_down(struct device *dev);
extern int cnss_pci_is_device_down(struct device *dev);
extern void cnss_schedule_recovery(struct device *dev,
enum cnss_recovery_reason reason);
extern int cnss_self_recovery(struct device *dev,
enum cnss_recovery_reason reason);
extern int cnss_force_fw_assert(struct device *dev);
extern int cnss_force_collect_rddm(struct device *dev);
extern int cnss_qmi_send_get(struct device *dev);
extern int cnss_qmi_send_put(struct device *dev);
extern int cnss_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 void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size);
extern int cnss_get_fw_files_for_target(struct device *dev,
struct cnss_fw_files *pfw_files,
u32 target_type, u32 target_version);
extern int cnss_get_platform_cap(struct device *dev,
struct cnss_platform_cap *cap);
extern struct iommu_domain *cnss_smmu_get_domain(struct device *dev);
extern int cnss_smmu_map(struct device *dev,
phys_addr_t paddr, uint32_t *iova_addr, size_t size);
extern int cnss_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size);
extern int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info);
extern int cnss_request_bus_bandwidth(struct device *dev, int bandwidth);
extern int cnss_power_up(struct device *dev);
extern int cnss_power_down(struct device *dev);
extern int cnss_idle_restart(struct device *dev);
extern int cnss_idle_shutdown(struct device *dev);
extern void cnss_request_pm_qos(struct device *dev, u32 qos_val);
extern void cnss_remove_pm_qos(struct device *dev);
extern void cnss_lock_pm_sem(struct device *dev);
extern void cnss_release_pm_sem(struct device *dev);
extern void cnss_pci_lock_reg_window(struct device *dev, unsigned long *flags);
extern void cnss_pci_unlock_reg_window(struct device *dev,
unsigned long *flags);
extern int cnss_wlan_pm_control(struct device *dev, bool vote);
extern int cnss_auto_suspend(struct device *dev);
extern int cnss_auto_resume(struct device *dev);
extern int cnss_pci_is_drv_connected(struct device *dev);
extern int cnss_pci_force_wake_request_sync(struct device *dev, int timeout);
extern int cnss_pci_force_wake_request(struct device *dev);
extern int cnss_pci_is_device_awake(struct device *dev);
extern int cnss_pci_force_wake_release(struct device *dev);
extern int cnss_get_user_msi_assignment(struct device *dev, char *user_name,
int *num_vectors,
uint32_t *user_base_data,
uint32_t *base_vector);
extern int cnss_get_msi_irq(struct device *dev, unsigned int vector);
extern void cnss_get_msi_address(struct device *dev, uint32_t *msi_addr_low,
uint32_t *msi_addr_high);
extern int cnss_wlan_enable(struct device *dev,
struct cnss_wlan_enable_cfg *config,
enum cnss_driver_mode mode,
const char *host_version);
extern int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode);
extern unsigned int cnss_get_boot_timeout(struct device *dev);
extern int cnss_athdiag_read(struct device *dev, uint32_t offset,
uint32_t mem_type, uint32_t data_len,
uint8_t *output);
extern int cnss_athdiag_write(struct device *dev, uint32_t offset,
uint32_t mem_type, uint32_t data_len,
uint8_t *input);
extern int cnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode);
extern int cnss_set_pcie_gen_speed(struct device *dev, u8 pcie_gen_speed);
extern int cnss_get_mem_seg_count(enum cnss_remote_mem_type type, u32 *seg);
extern int cnss_get_mem_segment_info(enum cnss_remote_mem_type type,
struct cnss_mem_segment segment[],
u32 segment_count);
#endif /* _NET_CNSS2_H */

103
inc/cnss_nl.h Normal file
View File

@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS_GENETLINK_H_
#define _NET_CNSS_GENETLINK_H_
#include <linux/types.h>
#define CLD80211_MAX_COMMANDS 40
#define CLD80211_MAX_NL_DATA 4096
/**
* enum cld80211_attr - Driver/Application embeds the data in nlmsg with the
* help of below attributes
*
* @CLD80211_ATTR_VENDOR_DATA: Embed all other attributes in this nested
* attribute.
* @CLD80211_ATTR_DATA: Embed complete data in this attribute
* @CLD80211_ATTR_META_DATA: Embed meta data for above data. This will help
* wlan driver to peek into request message packet without opening up definition
* of complete request message.
* @CLD80211_ATTR_CMD: cld80211 vendor subcommand in this attribute
* @CLD80211_ATTR_CMD_TAG_DATA: cld80211 vendor subcommand data is present in
* this attribute. It is a nested attribute with sub attributes of specified
* vendor sub command.
*
* Any new message in future can be added as another attribute
*/
enum cld80211_attr {
CLD80211_ATTR_VENDOR_DATA = 1,
CLD80211_ATTR_DATA,
CLD80211_ATTR_META_DATA,
CLD80211_ATTR_CMD,
CLD80211_ATTR_CMD_TAG_DATA,
/* add new attributes above here */
__CLD80211_ATTR_AFTER_LAST,
CLD80211_ATTR_MAX = __CLD80211_ATTR_AFTER_LAST - 1
};
/**
* enum cld80211_multicast_groups - List of multicast groups supported
*
* @CLD80211_MCGRP_SVC_MSGS: WLAN service message will be sent to this group.
* Ex: Status ind messages
* @CLD80211_MCGRP_HOST_LOGS: All logging related messages from driver will be
* sent to this multicast group
* @CLD80211_MCGRP_FW_LOGS: Firmware logging messages will be sent to this group
* @CLD80211_MCGRP_PER_PKT_STATS: Messages related packet stats debugging infra
* will be sent to this group
* @CLD80211_MCGRP_DIAG_EVENTS: Driver/Firmware status logging diag events will
* be sent to this group
* @CLD80211_MCGRP_FATAL_EVENTS: Any fatal message generated in driver/firmware
* will be sent to this group
* @CLD80211_MCGRP_OEM_MSGS: All OEM message will be sent to this group
* Ex: LOWI messages
*/
enum cld80211_multicast_groups {
CLD80211_MCGRP_SVC_MSGS,
CLD80211_MCGRP_HOST_LOGS,
CLD80211_MCGRP_FW_LOGS,
CLD80211_MCGRP_PER_PKT_STATS,
CLD80211_MCGRP_DIAG_EVENTS,
CLD80211_MCGRP_FATAL_EVENTS,
CLD80211_MCGRP_OEM_MSGS,
};
/**
* typedef cld80211_cb - Callback to be called when an nlmsg is received with
* the registered cmd_id command from userspace
* @data: Payload of the message to be sent to driver
* @data_len: Length of the payload
* @cb_ctx: callback context to be returned to driver when the callback
* is called
* @pid: process id of the sender
*/
typedef void (*cld80211_cb)(const void *data, int data_len,
void *cb_ctx, int pid);
/**
* register_cld_cmd_cb() - Allows cld driver to register for commands with
* callback
* @cmd_id: Command to be registered. Valid range [1, CLD80211_MAX_COMMANDS]
* @cb: Callback to be called when an nlmsg is received with cmd_id command
* from userspace
* @cb_ctx: context provided by driver; Send this as cb_ctx of func()
* to driver
*/
int register_cld_cmd_cb(u8 cmd_id, cld80211_cb cb, void *cb_ctx);
/**
* deregister_cld_cmd_cb() - Allows cld driver to de-register the command it
* has already registered
* @cmd_id: Command to be deregistered.
*/
int deregister_cld_cmd_cb(u8 cmd_id);
/**
* cld80211_get_genl_family() - Returns current netlink family context
*/
struct genl_family *cld80211_get_genl_family(void);
#endif /* _NET_CNSS_GENETLINK_H_ */

16
inc/cnss_prealloc.h Normal file
View File

@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2016,2019 The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS_PREALLOC_H_
#define _NET_CNSS_PREALLOC_H_
#include <linux/types.h>
#define WCNSS_PRE_ALLOC_GET_THRESHOLD (4*1024)
extern void *wcnss_prealloc_get(size_t size);
extern int wcnss_prealloc_put(void *ptr);
extern int wcnss_pre_alloc_reset(void);
void wcnss_prealloc_check_memory_leak(void);
#endif /* _NET_CNSS__PREALLOC_H_ */

38
inc/cnss_utils.h Normal file
View File

@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. */
#ifndef _CNSS_UTILS_H_
#define _CNSS_UTILS_H_
#include <linux/types.h>
struct device;
enum cnss_utils_cc_src {
CNSS_UTILS_SOURCE_CORE,
CNSS_UTILS_SOURCE_11D,
CNSS_UTILS_SOURCE_USER
};
extern int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
u16 *unsafe_ch_list,
u16 ch_count);
extern int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
u16 *unsafe_ch_list,
u16 *ch_count, u16 buf_len);
extern int cnss_utils_wlan_set_dfs_nol(struct device *dev,
const void *info, u16 info_len);
extern int cnss_utils_wlan_get_dfs_nol(struct device *dev,
void *info, u16 info_len);
extern int cnss_utils_get_driver_load_cnt(struct device *dev);
extern void cnss_utils_increment_driver_load_cnt(struct device *dev);
extern int cnss_utils_set_wlan_mac_address(const u8 *in, uint32_t len);
extern u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num);
extern int cnss_utils_set_wlan_derived_mac_address(const u8 *in, uint32_t len);
extern u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev,
uint32_t *num);
extern void cnss_utils_set_cc_source(struct device *dev,
enum cnss_utils_cc_src cc_source);
extern enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev);
#endif