From d56ecf073e3b60aec42fa189ea0793a7c53311b2 Mon Sep 17 00:00:00 2001 From: Sabeeh Khan Date: Thu, 30 Jan 2025 15:46:40 -0600 Subject: [PATCH] ti-linux-6.6.y: add patch to move cc33xx driver to 1.0.0.8 ti-linux-6.6.y already has support for cc33xx but they are outdated. This patch upgrades the driver to 1.0.0.8. Signed-off-by: Sabeeh Khan --- README.md | 1 + .../drivers-cc33xx-update-to-1.0.0.8.patch | 7180 +++++++++++++++++ 2 files changed, 7181 insertions(+) create mode 100644 ti-linux-6.6.y/drivers-cc33xx-update-to-1.0.0.8.patch diff --git a/README.md b/README.md index aa2500b..dc49523 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,5 @@ version: | 4.14.y | [1.0.0.6](https://www.ti.com/tool/download/CC33XX-LINUX-MPU/1.0.0.6) | | 5.4.y | [1.0.0.6](https://www.ti.com/tool/download/CC33XX-LINUX-MPU/1.0.0.6) | | 5.10.y | [1.0.0.6](https://www.ti.com/tool/download/CC33XX-LINUX-MPU/1.0.0.6) | +| ti-linux-6.6.y | [1.0.0.8](https://www.ti.com/tool/download/CC33XX-LINUX-MPU/1.0.0.8) | diff --git a/ti-linux-6.6.y/drivers-cc33xx-update-to-1.0.0.8.patch b/ti-linux-6.6.y/drivers-cc33xx-update-to-1.0.0.8.patch new file mode 100644 index 0000000..6931ed5 --- /dev/null +++ b/ti-linux-6.6.y/drivers-cc33xx-update-to-1.0.0.8.patch @@ -0,0 +1,7180 @@ +From bde3e32a0132b43a187325335cecacf5b31304d6 Mon Sep 17 00:00:00 2001 +From: Sabeeh Khan +Date: Sun, 27 Oct 2024 00:01:44 -0500 +Subject: [PATCH] drivers: cc33xx: update to 1.0.0.8 + +update ti-linux-6.6.y with latest driver from CC33xx SDK 1.0.0.8 +--- + drivers/bluetooth/Kconfig | 35 + + drivers/bluetooth/Makefile | 6 + + drivers/bluetooth/btti_debugfs.c | 156 ++ + drivers/bluetooth/btti_drv.h | 85 + + drivers/bluetooth/btti_main.c | 482 +++++ + drivers/bluetooth/btti_sdio.c | 1023 ++++++++++ + drivers/bluetooth/btti_sdio.h | 80 + + drivers/bluetooth/btti_uart.c | 742 +++++++ + drivers/net/wireless/ti/cc33xx/Makefile | 2 +- + drivers/net/wireless/ti/cc33xx/acx.c | 455 ++++- + drivers/net/wireless/ti/cc33xx/acx.h | 152 +- + drivers/net/wireless/ti/cc33xx/boot.c | 8 + + drivers/net/wireless/ti/cc33xx/cc33xx_i.h | 4 +- + drivers/net/wireless/ti/cc33xx/cmd.c | 100 +- + drivers/net/wireless/ti/cc33xx/cmd.h | 10 +- + drivers/net/wireless/ti/cc33xx/conf.h | 647 +----- + drivers/net/wireless/ti/cc33xx/debugfs.c | 2233 +++++++++++++++++++++ + drivers/net/wireless/ti/cc33xx/debugfs.h | 102 + + drivers/net/wireless/ti/cc33xx/io.c | 5 - + drivers/net/wireless/ti/cc33xx/main.c | 107 +- + drivers/net/wireless/ti/cc33xx/ps.c | 2 - + drivers/net/wireless/ti/cc33xx/rx.c | 3 + + drivers/net/wireless/ti/cc33xx/rx.h | 6 +- + 23 files changed, 5666 insertions(+), 779 deletions(-) + create mode 100644 drivers/bluetooth/btti_debugfs.c + create mode 100644 drivers/bluetooth/btti_drv.h + create mode 100644 drivers/bluetooth/btti_main.c + create mode 100644 drivers/bluetooth/btti_sdio.c + create mode 100644 drivers/bluetooth/btti_sdio.h + create mode 100644 drivers/bluetooth/btti_uart.c + create mode 100644 drivers/net/wireless/ti/cc33xx/debugfs.c + create mode 100644 drivers/net/wireless/ti/cc33xx/debugfs.h + +diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig +index bc211c324206..45dcaf5b0d7a 100644 +--- a/drivers/bluetooth/Kconfig ++++ b/drivers/bluetooth/Kconfig +@@ -370,6 +370,41 @@ config BT_HCIVHCI + + Say Y here to compile support for virtual HCI devices into the + kernel or say M to compile it as module (hci_vhci). ++ ++config BT_TI ++ tristate "TI Bluetooth driver support" ++ help ++ The core driver to support TI Bluetooth devices. ++ ++ Say Y here to compile TI Bluetooth driver ++ into the kernel or say M to compile it as module. ++ ++config BT_TI_SDIO ++ tristate "TI BT-over-SDIO driver" ++ depends on BT_TI && MMC ++ select FW_LOADER ++ select WANT_DEV_COREDUMP ++ help ++ The driver for TI Bluetooth chipsets with SDIO interface. ++ ++ This driver is required if you want to use TI Bluetooth ++ devices with SDIO interface. ++ chipsets are supported. ++ ++ Say Y here to compile support for TI BT-over-SDIO driver ++ into the kernel or say M to compile it as module. ++ ++config BT_TI_UART ++ tristate "TI BT-over-UART driver" ++ depends on SERIAL_DEV_BUS ++ help ++ Bluetooth HCI UART driver for CC33XX TI devices. ++ This driver is required if you want to use CC33XX device with ++ UART interface. ++ Currently does not require BT_TI core. ++ ++ Say Y here to compile support for CC33XX UART devices into the ++ kernel or say M to compile it as module (btuart). + + config BT_MRVL + tristate "Marvell Bluetooth driver support" +diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile +index 7a5967e9ac48..8e3520d124bf 100644 +--- a/drivers/bluetooth/Makefile ++++ b/drivers/bluetooth/Makefile +@@ -18,6 +18,9 @@ obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o + + obj-$(CONFIG_BT_INTEL) += btintel.o + obj-$(CONFIG_BT_ATH3K) += ath3k.o ++obj-$(CONFIG_BT_TI) += btti.o ++obj-$(CONFIG_BT_TI_SDIO) += btti_sdio.o ++obj-$(CONFIG_BT_TI_UART) += btti_uart.o + obj-$(CONFIG_BT_MRVL) += btmrvl.o + obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o + obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o +@@ -35,6 +38,9 @@ obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o + + obj-$(CONFIG_BT_HCIRSI) += btrsi.o + ++btti-y := btti_main.o ++btti-$(CONFIG_DEBUG_FS) += btti_debugfs.o ++ + btmrvl-y := btmrvl_main.o + btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o + +diff --git a/drivers/bluetooth/btti_debugfs.c b/drivers/bluetooth/btti_debugfs.c +new file mode 100644 +index 000000000000..4ccc324e5ded +--- /dev/null ++++ b/drivers/bluetooth/btti_debugfs.c +@@ -0,0 +1,156 @@ ++/* ++ * This file is part of TI BLE over SDIO ++ * ++ * Copyright (C) 2022 Texas Instruments ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "btti_drv.h" ++ ++struct btti_debugfs_dir { ++ struct dentry *config_dir; ++ struct dentry *status_dir; ++}; ++ ++int btti_debugfs_if_prepare_command(u8 cmd_type,\ ++ struct btti_private *private_data); ++ ++ ++#define DEBUGFS_FORMAT_BUFFER_SIZE 256 ++ ++int btti_format_buffer(char __user *userbuf, size_t count, ++ loff_t *ppos, char *fmt, ...) ++{ ++ va_list args; ++ char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; ++ int res; ++ ++ va_start(args, fmt); ++ res = vscnprintf(buf, sizeof(buf), fmt, args); ++ va_end(args); ++ ++ return simple_read_from_buffer(userbuf, count, ppos, buf, res); ++} ++ ++//ble_enable ++static ssize_t ble_enable_read(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct btti_private *private_data = file->private_data; ++ char buf[16]; ++ int ret; ++ BT_INFO("[bt sdio] ble enable read"); ++ ++ ret = snprintf(buf, sizeof(buf) - 1, "%d\n", \ ++ private_data->hci_adapter->ble_enable); ++ ++ return btti_format_buffer(userbuf, count, ppos, "%d\n", \ ++ private_data->hci_adapter->ble_enable); ++} ++ ++static ssize_t ble_enable_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct btti_private *private_data = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ BT_INFO("[bt sdio] ble enable write: %ld", value); ++ ++ if(!private_data || !private_data->hci_adapter){ ++ BT_ERR("[bt sdio] driver is not ready"); ++ } ++ ++ if (private_data->hci_adapter->ble_enable) { ++ BT_WARN("[bt sdio] ble_enable is already %d",\ ++ private_data->hci_adapter->ble_enable); ++ return -EINVAL; ++ } ++ ++ if (value != 1) { ++ BT_WARN("illegal value in ble_enable "\ ++ "(only value allowed is is 1)"); ++ BT_WARN("ble_enable cant be disabled after being enabled."); ++ return -EINVAL; ++ } ++ ++ ret = btti_debugfs_if_prepare_command((u8)CMD_TYPE_BLE_ENABLE,\ ++ private_data); ++ ++ return count; ++} ++ ++ ++ ++//ble_enable ++ ++static const struct file_operations ble_enable_ops = { ++ .read = ble_enable_read, ++ .write = ble_enable_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++ ++void btti_debugfs_init(struct hci_dev *hdev) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ struct btti_debugfs_dir *dbg; ++ struct dentry *root_cfg_dir; ++ ++ if (!hdev->debugfs) ++ return; ++ ++ dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); ++ private_data->debugfs_dir_vals = dbg; ++ ++ if (!dbg) { ++ BT_ERR("Can not allocate memory for btti_debugfs_dir."); ++ return; ++ } ++ ++ dbg->config_dir = debugfs_create_dir("config", hdev->debugfs); ++ root_cfg_dir = dbg->config_dir; ++ ++ debugfs_create_file("ble_enable", 0744, root_cfg_dir, private_data,\ ++ &ble_enable_ops); ++ ++ //dbg->status_dir = debugfs_create_dir("cc3xx_status", hdev->debugfs); ++} ++ ++void btti_debugfs_remove(struct hci_dev *hdev) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ struct btti_debugfs_dir *dbgfs = private_data->debugfs_dir_vals; ++ ++ if (!dbgfs) ++ return; ++ ++ if(dbgfs->config_dir) ++ { ++ debugfs_remove_recursive(dbgfs->config_dir); ++ } ++ if(dbgfs->status_dir) ++ { ++ debugfs_remove_recursive(dbgfs->status_dir); ++ } ++ ++ kfree(dbgfs); ++} +diff --git a/drivers/bluetooth/btti_drv.h b/drivers/bluetooth/btti_drv.h +new file mode 100644 +index 000000000000..78fdf7f7f7b9 +--- /dev/null ++++ b/drivers/bluetooth/btti_drv.h +@@ -0,0 +1,85 @@ ++/* ++ * This file is part of TI BLE over SDIO ++ * ++ * Copyright (C) 2022 Texas Instruments ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define BT_SDIO_HEADER_LEN 4 ++#define BT_SDIO_UPLD_SIZE (251+BT_SDIO_HEADER_LEN) ++ ++#define CMD_TYPE_BLE_ENABLE 1 ++ ++ ++ ++struct btti_worker_thread { ++ struct task_struct *task; ++ wait_queue_head_t wait_queue; ++ void *private_data; ++}; ++ ++struct btti_device { ++ void *sdiodev; //see btti_sdio_dev ++ struct hci_dev *hcidev; ++ u8 dev_type; ++}; ++ ++struct btti_hci_adapter { ++ u32 num_of_interrupt; ++ struct sk_buff_head tx_queue; ++ u8 ble_enable; ++ u8 enable_autosuspend; ++ bool is_suspended; ++ bool is_suspending; ++}; ++ ++struct btti_private { ++ struct btti_device btti_dev; ++ struct btti_hci_adapter *hci_adapter; ++ struct btti_worker_thread work_thread; ++ int (*card_tx_packet_funcp)\ ++ (struct btti_private *private_data,struct sk_buff *skb); ++ int (*card_power_up_firmware_funcp)(struct btti_private *private_data); ++ int (*card_power_dn_firmware_funcp)(struct btti_private *private_data); ++ int (*card_process_rx_funcp)(struct btti_private *private_data); ++ spinlock_t irq_cnt_lock; /* spinlock used by driver */ ++#ifdef CONFIG_DEBUG_FS ++ void *debugfs_dir_vals; ++#endif ++ bool sdio_dev_removed; ++}; ++ ++/* Prototype of global function */ ++int btti_hci_register_hdev(struct btti_private *private_data); ++struct btti_private *btti_hci_add_sdio_dev(void *sdiodev); ++int btti_hci_remove_sdio_dev(struct btti_private *private_data); ++void btti_hci_irq_handler(struct btti_private *private_data); ++ ++#ifdef CONFIG_DEBUG_FS ++void btti_debugfs_init(struct hci_dev *hdev); ++void btti_debugfs_remove(struct hci_dev *hdev); ++#endif +diff --git a/drivers/bluetooth/btti_main.c b/drivers/bluetooth/btti_main.c +new file mode 100644 +index 000000000000..6857d8b90df0 +--- /dev/null ++++ b/drivers/bluetooth/btti_main.c +@@ -0,0 +1,482 @@ ++/* ++ * This file is part of TI BLE over SDIO ++ * ++ * Copyright (C) 2022 Texas Instruments ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "btti_drv.h" ++#include "btti_sdio.h" ++ ++#define VERSION "ti_1.0" ++ ++#define BTTI_SDIO_AUTOSUSPEND_DELAY 8000 ++ ++static int btti_hci_is_ble_enabled(struct btti_private *private_data); ++ ++ ++/* ++ * This function is called by the interrupt handler. ++ * It wakes up the worker thread.to make the RX reception. ++ */ ++void btti_hci_irq_handler(struct btti_private *private_data) ++{ ++ ulong flags; ++ ++ spin_lock_irqsave(&private_data->irq_cnt_lock, flags); ++ private_data->hci_adapter->num_of_interrupt++; ++ spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags); ++ ++ // wakeup to btti_service_work_thread ++ wake_up_interruptible(&private_data->work_thread.wait_queue); ++} ++EXPORT_SYMBOL_GPL(btti_hci_irq_handler); ++ ++ ++int btti_debugfs_if_prepare_command(u8 cmd_type,\ ++ struct btti_private *private_data) ++{ ++ int ret = 0; ++ switch(cmd_type){ ++ case CMD_TYPE_BLE_ENABLE: ++ BT_INFO("[bt sdio hci] "\ ++ "btti_debugfs_if_prepare_command "\ ++ " CMD_TYPE_BLE_ENABLE "); ++ ret = btti_hci_is_ble_enabled(private_data); ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static int btti_hci_tx_pkt(struct btti_private *private_data,\ ++ struct sk_buff *skb) ++{ ++ int ret = 0; ++ ++ if (!skb || !skb->data) ++ return -EINVAL; ++ ++ if (!skb->len || ((skb->len + BT_SDIO_HEADER_LEN) > BT_SDIO_UPLD_SIZE)) ++ { ++ BT_ERR("[bt sdio hci] TX Error: Bad skb length %d : %d", ++ skb->len, BT_SDIO_UPLD_SIZE); ++ return -EINVAL; ++ } ++ ++ skb_push(skb, BT_SDIO_HEADER_LEN); ++ ++ /* header type: byte[3] ++ * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor ++ * header length: byte[2][1][0] ++ */ ++ ++ skb->data[0] = (skb->len & 0x0000ff); ++ skb->data[1] = (skb->len & 0x00ff00) >> 8; ++ skb->data[2] = (skb->len & 0xff0000) >> 16; ++ skb->data[3] = hci_skb_pkt_type(skb); ++ ++ ++ BT_DBG("[bt sdio hci] TX buff 0x: %*ph", skb->len, skb->data); ++ ++ ++ if (private_data->card_tx_packet_funcp) ++ ret = private_data->card_tx_packet_funcp(private_data,\ ++ skb);//btti_sdio_tx_packet ++ ++ return ret; ++} ++ ++static void btti_hci_init_hci_adapter(struct btti_private *private_data) ++{ ++ ++ skb_queue_head_init(&private_data->hci_adapter->tx_queue); ++ private_data->hci_adapter->enable_autosuspend = true; ++} ++ ++static void btti_hci_free_hci_adapter(struct btti_private *private_data) ++{ ++ skb_queue_purge(&private_data->hci_adapter->tx_queue); ++ kfree(private_data->hci_adapter); ++ ++ private_data->hci_adapter = NULL; ++} ++ ++ ++ ++ ++//it is on purpose without lock , ++//worse case card_ble_verify_if_ble_enable_funcp will be called more than once ++static int btti_hci_is_ble_enabled(struct btti_private *private_data) ++{ ++ //if ble_enabled -- return 0 ++ return( private_data->hci_adapter->ble_enable); ++} ++ ++ ++static int btti_hci_if_open(struct hci_dev *hdev) ++{ ++ BT_DBG("[bt sdio hci] btti_hci_if_open"); ++ return 0; ++} ++ ++static int btti_hci_if_close(struct hci_dev *hdev) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ BT_DBG("[bt sdio hci] btti_hci_if_close"); ++ ++ pm_runtime_disable(&sdiodev->func->dev); ++ skb_queue_purge(&private_data->hci_adapter->tx_queue); ++ ++ return 0; ++} ++ ++static int btti_hci_if_flush(struct hci_dev *hdev) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ BT_DBG("[bt sdio hci] btti_hci_if_flush"); ++ ++ skb_queue_purge(&private_data->hci_adapter->tx_queue); ++ ++ return 0; ++} ++static int btti_hci_if_tx_frame(struct hci_dev *hdev, struct sk_buff *skb) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ ++ BT_INFO("[bt sdio hci] TX from HCI received ,type=%d,"\ ++ " opcode: 0x%x len=%d ble_enable=%d", ++ hci_skb_pkt_type(skb), hci_skb_opcode(skb),\ ++ skb->len,private_data->hci_adapter->ble_enable); ++ ++ if (private_data->hci_adapter->is_suspending\ ++ || private_data->hci_adapter->is_suspended) { ++ BT_ERR("[bt sdio hci] %s:"\ ++ " Device is suspending or suspended", __func__); ++ goto fail; ++ } ++ ++ //check of ble is enabled ++ if(!btti_hci_is_ble_enabled(private_data)){ ++ BT_INFO("[bt sdio hci] ble is not enabled"); ++ goto fail; ++ } ++ ++ ++ switch (hci_skb_pkt_type(skb)) { ++ case HCI_COMMAND_PKT: ++ if(hdev){ ++ hdev->stat.cmd_tx++; ++ } ++ break; ++ ++ case HCI_ACLDATA_PKT: ++ if(hdev){ ++ hdev->stat.acl_tx++; ++ } ++ break; ++ ++ case HCI_SCODATA_PKT: ++ BT_WARN("[bt sdio hci] HCI_SCODATA_PKT not supported"); ++ if(hdev){ ++ hdev->stat.sco_tx++; ++ } ++ break; ++ ++ default: ++ BT_ERR("[bt sdio hci] unknown packet type :%d",\ ++ hci_skb_pkt_type(skb)); ++ return -EILSEQ; ++ ++ } ++ ++ ++ skb_queue_tail(&private_data->hci_adapter->tx_queue, skb); ++ ++ if (!private_data->hci_adapter->is_suspended){ ++ wake_up_interruptible(&private_data->work_thread.wait_queue); ++ }else{ ++ skb_dequeue_tail(&private_data->hci_adapter->tx_queue); ++ BT_INFO("[bt sdio hci] Device is suspending"); ++ goto fail; ++ } ++ ++ return 0; ++fail: ++ //in case of error HCI frees the skb ++ return -EIO; ++} ++ ++ ++ ++static int btti_hci_if_setup(struct hci_dev *hdev) ++{ ++ struct btti_private *private_data = hci_get_drvdata(hdev); ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ ++ BT_INFO("[bt sdio hci] btti_hci_if_setup"); ++ pm_runtime_set_autosuspend_delay(&sdiodev->func->dev, ++ BTTI_SDIO_AUTOSUSPEND_DELAY); ++ pm_runtime_use_autosuspend(&sdiodev->func->dev); ++ ++ pm_runtime_set_active(&sdiodev->func->dev); ++ ++ /* Default forbid runtime auto suspend ++ */ ++ pm_runtime_forbid(&sdiodev->func->dev); ++ pm_runtime_enable(&sdiodev->func->dev); ++ ++ ++ //auto suspend is allowed ++ if (private_data->hci_adapter\ ++ && private_data->hci_adapter->enable_autosuspend){ ++ BT_DBG("[bt sdio hci] btti_hci_if_setup , enable auto suspend"); ++ pm_runtime_allow(&sdiodev->func->dev); ++ } ++ return 0; ++} ++ ++/* ++ * This function handles the event generated by firmware, rx data ++ * received from firmware, and tx data sent from kernel. ++ * work_thread.wait_queue ++ */ ++static int btti_service_work_thread(void *data) ++{ ++ struct btti_worker_thread *thread = data; ++ struct btti_private *private_data = thread->private_data; ++ struct btti_hci_adapter *hci_adapter = private_data->hci_adapter; ++ wait_queue_entry_t wait; ++ struct sk_buff *skb; ++ ulong flags; ++ ++ BT_INFO("[bt sdio hci] work thread is started"); ++ ++ init_waitqueue_entry(&wait, current); ++ ++ for (;;) { ++ add_wait_queue(&thread->wait_queue, &wait); ++ ++ if (kthread_should_stop() || private_data->sdio_dev_removed) { ++ BT_DBG("[bt sdio hci]work_thread: "\ ++ " thread is need to stopped"); ++ break; ++ } ++ ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ //if no RX and queue is empty or ble is not enabled ++ if ((!private_data->hci_adapter->ble_enable) || ++ ((!hci_adapter->num_of_interrupt) &&\ ++ skb_queue_empty\ ++ (&hci_adapter->tx_queue))) { ++ BT_INFO("[bt sdio hci] work thread is sleeping..."); ++ schedule(); ++ } ++ ++ set_current_state(TASK_RUNNING); ++ ++ remove_wait_queue(&thread->wait_queue, &wait); ++ ++ BT_DBG("[bt sdio hci] work thread woke up"); ++ ++ if (kthread_should_stop() || private_data->sdio_dev_removed) { ++ BT_INFO("[bt sdio hci] work_thread:"\ ++ " break from main thread"); ++ break; ++ } ++ ++ //handle the RX, process the interrupt function ++ spin_lock_irqsave(&private_data->irq_cnt_lock, flags); ++ if (hci_adapter->num_of_interrupt) { ++ hci_adapter->num_of_interrupt = 0; ++ spin_unlock_irqrestore(&private_data->irq_cnt_lock,\ ++ flags); ++ if(private_data->card_process_rx_funcp){ ++ private_data->card_process_rx_funcp\ ++ (private_data);//call to btti_sdio_process_rx ++ } ++ } else { ++ spin_unlock_irqrestore(&private_data->irq_cnt_lock,\ ++ flags); ++ } ++ ++ if (private_data->hci_adapter->is_suspended) ++ { ++ BT_INFO("[bt sdio hci] work thread not available,"\ ++ " is_suspended:%d", ++ private_data->hci_adapter->is_suspended); ++ continue; ++ } ++ //handle the TX ++ skb = skb_dequeue(&hci_adapter->tx_queue); ++ if (skb) { ++ if (btti_hci_tx_pkt(private_data, skb)){ ++ //handle tx packet ++ BT_ERR("[bt sdio hci] TX , error send packet"); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int btti_hci_register_hdev(struct btti_private *private_data) ++ ++{ ++ struct hci_dev *hdev = NULL; ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ int ret; ++ BT_INFO("[bt sdio hci] btti_hci_register_hdev"); ++ ++ hdev = hci_alloc_dev(); ++ if (!hdev) { ++ BT_ERR("[bt sdio hci] Can not allocate HCI device"); ++ goto err_hdev; ++ } ++ ++ private_data->btti_dev.hcidev = hdev; ++ hci_set_drvdata(hdev, private_data); ++ ++ hdev->bus = HCI_SDIO; ++ hdev->open = btti_hci_if_open; ++ hdev->close = btti_hci_if_close; ++ hdev->flush = btti_hci_if_flush; ++ hdev->send = btti_hci_if_tx_frame; ++ hdev->setup = btti_hci_if_setup; ++ SET_HCIDEV_DEV(hdev, &sdiodev->func->dev); ++ ++ set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); ++ ++ hdev->dev_type = HCI_PRIMARY; ++ ++ ret = hci_register_dev(hdev); ++ if (ret < 0) { ++ BT_ERR("[bt sdio hci] Can not register HCI device"); ++ goto err_hci_register_dev; ++ } ++ ++#ifdef CONFIG_DEBUG_FS ++ btti_debugfs_init(hdev); ++#endif ++ ++ return 0; ++ ++err_hci_register_dev: ++ hci_free_dev(hdev); ++ ++err_hdev: ++ /* Stop the thread servicing the interrupts */ ++ kthread_stop(private_data->work_thread.task); ++ ++ btti_hci_free_hci_adapter(private_data); ++ kfree(private_data); ++ ++ return -ENOMEM; ++} ++EXPORT_SYMBOL_GPL(btti_hci_register_hdev); ++ ++struct btti_private *btti_hci_add_sdio_dev(void *sdiodev) ++{ ++ struct btti_private *private_data; ++ BT_INFO("[bt sdio hci] btti_hci_add_sdio_dev"); ++ ++ ++ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL); ++ if (!private_data) { ++ BT_ERR("[bt sdio hci] Can not allocate private_data"); ++ goto err_priv; ++ } ++ ++ private_data->hci_adapter = \ ++ kzalloc(sizeof(*private_data->hci_adapter), GFP_KERNEL); ++ if (!private_data->hci_adapter) { ++ BT_ERR("[bt sdio hci] Allocate buffer"\ ++ " for btti_hci_adapter failed!"); ++ goto err_hci_adapter; ++ } ++ ++ btti_hci_init_hci_adapter(private_data); ++ ++ BT_INFO("[bt sdio hci] Starting work thread..."); ++ private_data->work_thread.private_data = private_data; ++ spin_lock_init(&private_data->irq_cnt_lock); ++ ++ init_waitqueue_head(&private_data->work_thread.wait_queue); ++ private_data->work_thread.task = kthread_run(btti_service_work_thread, ++ &private_data->work_thread,\ ++ "btti_main_service"); ++ if (IS_ERR(private_data->work_thread.task)) ++ goto err_thread; ++ ++ private_data->btti_dev.sdiodev = sdiodev; ++ return private_data; ++ ++err_thread: ++ btti_hci_free_hci_adapter(private_data); ++ ++err_hci_adapter: ++ kfree(private_data); ++ ++err_priv: ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(btti_hci_add_sdio_dev); ++ ++int btti_hci_remove_sdio_dev(struct btti_private *private_data) ++{ ++ struct hci_dev *hdev; ++ ++ BT_INFO("[bt sdio hci] remove sdio dev"); ++ ++ hdev = private_data->btti_dev.hcidev; ++ ++ if(private_data->work_thread.task) ++ kthread_stop(private_data->work_thread.task); ++ ++ ++ if(hdev) ++ { ++ BT_INFO("[bt sdio hci] unregister hci"); ++#ifdef CONFIG_DEBUG_FS ++ btti_debugfs_remove(hdev); ++#endif ++ hci_unregister_dev(hdev); ++ hci_free_dev(hdev); ++ } ++ ++ private_data->btti_dev.hcidev = NULL; ++ ++ btti_hci_free_hci_adapter(private_data); ++ ++ kfree(private_data); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(btti_hci_remove_sdio_dev); ++ ++MODULE_AUTHOR("Texas Instruments."); ++MODULE_DESCRIPTION("Texas Instruments Bluetooth driver ver " VERSION); ++MODULE_VERSION(VERSION); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/bluetooth/btti_sdio.c b/drivers/bluetooth/btti_sdio.c +new file mode 100644 +index 000000000000..4f8fda6209b9 +--- /dev/null ++++ b/drivers/bluetooth/btti_sdio.c +@@ -0,0 +1,1023 @@ ++/* ++ * This file is part of TI BLE over SDIO ++ * ++ * Copyright (C) 2022 Texas Instruments ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ */ ++ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "btti_drv.h" ++#include "btti_sdio.h" ++ ++#define VERSION "ti_1.0" ++ ++#define SDIO_DEVICE_ID_TI_CC33XX 0x4077 ++ ++ ++static void btti_sdio_if_remove(struct sdio_func *func); ++static int __maybe_unused btti_sdio_unregister_dev(struct sdio_func *func); ++static int btti_sdio_rx_packet(struct btti_private *private_dataate_data); ++static int btti_sdio_tx_packet(struct btti_private *private_data, ++ struct sk_buff *skb); ++static void btti_sdio_irq_handler(struct sdio_func *func); ++static int btti_sdio_power_up_fw(struct btti_private *private_data); ++static int btti_handle_rx_vendor_event(struct btti_private *private_data, ++ struct sk_buff *skb ); ++static void btti_acknldg_packet(struct btti_sdio_dev *sdiodev, u8 Ack); ++ ++ ++ ++static const struct of_device_id btti_sdio_of_match_table[] = { ++ { .compatible = "ti,cc33xxbt" }, ++ { } ++}; ++ ++static int btti_sdio_if_probe_of(struct device *dev) ++{ ++ ++ if (!dev || !dev->of_node || ++ !of_match_node(btti_sdio_of_match_table, dev->of_node)) { ++ BT_ERR("[bt sdio] sdio device tree data "\ ++ " not available\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static const struct btti_sdio_dev_reg_map btti_reg_map_cc33xx = { ++ /* fun0 ,ELP Wakeup Reg address, func0 not used*/ ++ .sdio_wup_ble = 0x40, ++ /*fun1, set 0x1, to enable interrupts, 0 to disable */ ++ .sdio_enable_int = 0x14, ++ /* fun1,read and write to the card are at 0x0 address */ ++ .sdio_rt_data = 0x0, ++ /* fun1,write 1 to clear rx interrupt after reception*/ ++ .sdio_cl_int = 0x13, ++ /*fun1, bt mode status, bit 1 means ack mode, ++ * currently the code just read it */ ++ .bt_mode_status = 0x20, ++ /* fun1 read packet control, write 1 is set ack for RX, ++ * write 0 for nack */ ++ .sdio_pc_rrt = 0x10, ++}; ++ ++static const struct btti_sdio_device btti_sdio_cc33xx_bt = { ++ .reg_map = &btti_reg_map_cc33xx, ++}; ++ ++static const struct sdio_device_id btti_sdio_ids[] = { ++ { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_CC33XX) ,.driver_data\ ++ = (unsigned long)&btti_sdio_cc33xx_bt }, ++ { } /* Terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE(sdio, btti_sdio_ids); ++ ++static int btti_sdio_enable_func_interrupt(struct btti_sdio_dev *sdiodev, ++ u8 mask) ++{ ++ int ret; ++ ++ sdio_writeb(sdiodev->func, mask, sdiodev->reg_map->sdio_enable_int,\ ++ &ret); ++ if (ret) { ++ BT_ERR("[bt sdio] Unable to enable the host interrupt!"); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int btti_sdio_disable_func_interrupt(struct btti_sdio_dev *sdiodev, ++ u8 mask) ++{ ++ u8 sdio_enable_int_mask; ++ int ret; ++ ++ sdio_enable_int_mask = sdio_readb(sdiodev->func,\ ++ sdiodev->reg_map->sdio_enable_int, &ret); ++ if (ret) ++ return -EIO; ++ ++ sdio_enable_int_mask &= ~mask; ++ ++ sdio_writeb(sdiodev->func, sdio_enable_int_mask,\ ++ sdiodev->reg_map->sdio_enable_int, &ret); ++ if (ret < 0) { ++ BT_ERR("[bt sdio] Unable to disable the host interrupt!"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++ ++//Read Rx, called from btti_service_work_thread ++static int btti_sdio_process_rx(struct btti_private *private_data) ++{ ++ ulong flags; ++ u8 intrpt_occur; ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ ++ spin_lock_irqsave(&private_data->irq_cnt_lock, flags); ++ intrpt_occur = sdiodev->Intrpt_triggered; ++ sdiodev->Intrpt_triggered = 0; ++ spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags); ++ ++ sdio_claim_host(sdiodev->func); ++ if (intrpt_occur) ++ { ++ btti_sdio_rx_packet(private_data);//read Rx, ++ } ++ else{ ++ btti_acknldg_packet(sdiodev,1);//nack ++ } ++ ++ sdio_release_host(sdiodev->func); ++ ++ return 0; ++} ++ ++static int btti_sdio_clr_irq(struct btti_sdio_dev *sdiodev) ++{ ++ int ret; ++ ++ BT_DBG("[bt sdio] clear int_status"); ++ ++ sdio_writeb(sdiodev->func, 1 ,sdiodev->reg_map->sdio_cl_int, &ret); ++ if (ret) { ++ BT_ERR("[bt sdio] clear int status failed: %d", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++static int btti_sdio_register_dev(struct btti_sdio_dev *sdiodev) ++{ ++ struct sdio_func *func; ++ u8 reg; ++ int ret; ++ ++ if (!sdiodev || !sdiodev->func) { ++ BT_ERR("[bt sdio] Error: sdiodev or function is NULL!"); ++ ret = -EINVAL; ++ goto failed; ++ } ++ ++ func = sdiodev->func; ++ ++ sdio_claim_host(func); ++ ++ func->enable_timeout = 0; ++ ++ ret = sdio_enable_func(func); ++ if (ret) { ++ //this is ok, since the ble is not up yet so SDIO_CCCR_IOEx ++ //is not set on this sage ++ BT_DBG("[bt sdio] sdio_enable_func: %d failed: ret=%d",\ ++ func->num, ret ); ++ } ++ else{ ++ BT_INFO("[bt sdio] sdio_enable_func: %d success", func->num ); ++ } ++ ++ //set block size ++ ret = sdio_set_block_size(sdiodev->func, SDIO_BLOCK_SIZE); ++ if (ret) { ++ BT_ERR("[bt sdio] cannot set SDIO block size"); ++ ret = -EIO; ++ goto unreg_device; ++ } ++ ++ //clear interrupt, write to clear ++ ret = btti_sdio_clr_irq(sdiodev); ++ ++ //read status --not in use ++ reg = sdio_readb(func, sdiodev->reg_map->bt_mode_status, &ret); ++ if (ret < 0) { ++ BT_ERR("[bt sdio] Failed to read bt_mode_status "); ++ ret = -EIO; ++ goto unreg_device; ++ } ++ sdiodev->bt_mode_status = ret; ++ ++ BT_DBG("[bt sdio] SDIO FUNC%d IO port: 0x%x BT MODE: 0x%x",\ ++ func->num, sdiodev->reg_map->sdio_rt_data,\ ++ sdiodev->bt_mode_status); ++ ++ sdio_set_drvdata(func, sdiodev); ++ ++ sdio_release_host(func); ++ ++ return 0; ++ ++unreg_device: ++ sdio_release_host(func); ++ btti_sdio_unregister_dev(func); ++ return ret; ++failed: ++ return ret; ++ ++} ++ ++static int btti_sdio_unregister_dev(struct sdio_func *func) ++{ ++ BT_INFO("[bt sdio] btti_sdio_unregister_dev "); ++ ++ if (func) { ++ sdio_claim_host(func); ++ BT_DBG("[bt sdio] sdio_release_irq"); ++ sdio_release_irq(func); ++ sdio_disable_func(func); ++ sdio_set_drvdata(func, NULL); ++ sdio_release_host(func); ++ } ++ ++ return 0; ++} ++ ++static int btti_sdio_enable_int(struct btti_sdio_dev *sdiodev) ++{ ++ int ret; ++ ++ if (!sdiodev || !sdiodev->func) ++ return -EINVAL; ++ ++ sdio_claim_host(sdiodev->func); ++ ++ ret = btti_sdio_enable_func_interrupt(sdiodev, INTRPT_ENABLE_MASk); ++ ++ sdio_release_host(sdiodev->func); ++ ++ return ret; ++} ++ ++static int btti_sdio_disable_int(struct btti_sdio_dev *sdiodev) ++{ ++ int ret; ++ ++ if (!sdiodev || !sdiodev->func) ++ return -EINVAL; ++ ++ sdio_claim_host(sdiodev->func); ++ ++ ret = btti_sdio_disable_func_interrupt(sdiodev, INTRPT_ENABLE_MASk); ++ ++ sdio_release_host(sdiodev->func); ++ ++ return ret; ++} ++ ++ ++ ++static void btti_sdio_dump_regs(struct btti_private *private_data) ++{ ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ int ret = 0; ++ unsigned int reg, reg_start, reg_end; ++ char buf[256], *ptr; ++ u8 loop, func, data; ++ int MAX_LOOP = 2; ++ ++ btti_sdio_power_up_fw(private_data); ++ sdio_claim_host(sdiodev->func); ++ ++ for (loop = 0; loop < MAX_LOOP; loop++) { ++ memset(buf, 0, sizeof(buf)); ++ ptr = buf; ++ ++ if (loop == 0) { ++ /* Read the registers of SDIO function0 */ ++ func = loop; ++ reg_start = 0; ++ reg_end = 9; ++ } else { ++ func = 2; ++ reg_start = 0; ++ reg_end = 0x09; ++ } ++ ++ ptr += sprintf(ptr, "[bt sdio] SDIO Func%d (%#x-%#x): ", ++ func, reg_start, reg_end); ++ for (reg = reg_start; reg <= reg_end; reg++) { ++ if (func == 0) ++ data = sdio_f0_readb(sdiodev->func, reg, &ret); ++ else ++ data = sdio_readb(sdiodev->func, reg, &ret); ++ ++ if (!ret) { ++ ptr += sprintf(ptr, "%02x ", data); ++ } else { ++ ptr += sprintf(ptr, "ERR"); ++ break; ++ } ++ } ++ ++ BT_INFO("%s", buf); ++ } ++ ++ sdio_release_host(sdiodev->func); ++} ++ ++/* This function dump sdio register and memory data */ ++static void btti_sdio_coredump(struct device *dev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ struct btti_sdio_dev *sdiodev; ++ struct btti_private *private_data; ++ ++ sdiodev = sdio_get_drvdata(func); ++ if(sdiodev) ++ { ++ private_data = sdiodev->private_data; ++ if(!private_data){ ++ BT_ERR("[bt sdio] private_data is not allocated"); ++ return; ++ } ++ }else { ++ BT_ERR("[bt sdio] no sdiodev"); ++ return; ++ } ++ ++ /* dump sdio register first */ ++ btti_sdio_dump_regs(private_data); ++} ++static int btti_sdio_power_up_fw(struct btti_private *private_data) ++{ ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ int ret = 0; ++ ++ BT_DBG("[bt sdio] power up FW"); ++ ++ if (!sdiodev || !sdiodev->func) { ++ BT_ERR("[bt sdio] sdiodev or function is NULL!"); ++ return -EINVAL; ++ } ++ ++ sdio_claim_host(sdiodev->func); ++ ++ // BLE firmware remains awake , force Lx to remain awake; ++ sdio_f0_writeb(sdiodev->func, HOST_POWER_UP_MASK,\ ++ sdiodev->reg_map->sdio_wup_ble, &ret); ++ if (ret) { ++ BT_ERR("[bt sdio] Unable to power up ble firmware!"); ++ ret = -EIO; ++ } ++ ++ sdio_release_host(sdiodev->func); ++ ++ BT_INFO("[bt sdio] wake up firmware"); ++ ++ return ret; ++} ++ ++static int btti_sdio_power_dn_fw(struct btti_private *private_data) ++{ ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ int ret = 0; ++ ++ BT_DBG("[bt sdio] power up FW"); ++ ++ if (!sdiodev || !sdiodev->func) { ++ BT_ERR("[bt sdio] sdiodev or function is NULL!"); ++ return -EINVAL; ++ } ++ ++ sdio_claim_host(sdiodev->func); ++ ++ // Lx Core can go to sleep (from host perspective) ++ sdio_f0_writeb(sdiodev->func, ~HOST_POWER_UP_MASK,\ ++ sdiodev->reg_map->sdio_wup_ble, &ret); ++ if (ret) { ++ BT_ERR("[bt sdio] Unable to power down ble firmware!"); ++ ret = -EIO; ++ } ++ ++ sdio_release_host(sdiodev->func); ++ ++ BT_INFO("[bt sdio] wake up firmware"); ++ ++ return ret; ++} ++static void btti_sdio_irq_handler(struct sdio_func *func) ++{ ++ struct btti_private *private_data; ++ struct btti_sdio_dev *sdiodev; ++ ulong flags; ++ int ret; ++ ++ BT_INFO("[bt sdio] RX btti_sdio_irq_handler received"); ++ ++ sdiodev = sdio_get_drvdata(func); ++ if (!sdiodev || !sdiodev->private_data) { ++ BT_ERR("[bt sdio] RX btti_hci_irq_handler(%d) "\ ++ "%p sdiodev or private_data is NULL,"\ ++ " sdiodev=%p", ++ func->num, func, sdiodev); ++ return; ++ } ++ ++ private_data = sdiodev->private_data; ++ ++ if (private_data->sdio_dev_removed) { ++ BT_ERR("[bt sdio] RX sdio_dev_removed"); ++ return; ++ } ++ ++ sdio_claim_host(func); ++ ++ //clear interrupt ++ ret = btti_sdio_clr_irq(sdiodev); ++ ++ sdio_release_host(func); ++ ++ if (ret){ ++ BT_ERR("[bt sdio] RX btti_sdio_irq_handler:failed to clear"); ++ return; ++ } ++ ++ spin_lock_irqsave(&private_data->irq_cnt_lock, flags); ++ sdiodev->Intrpt_triggered = 1;// signal that interrupt happened ++ spin_unlock_irqrestore(&private_data->irq_cnt_lock, flags); ++ ++ ++ btti_hci_irq_handler(private_data); ++} ++ ++ ++//rx ++static int btti_sdio_rx_packet(struct btti_private *private_data) ++{ ++ u32 packet_len = 0; ++ int ret; ++ u32 data_read_size; ++ struct sk_buff *skb = NULL; ++ u32 packet_type; ++ ++ struct hci_dev *hdev = private_data->btti_dev.hcidev; ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ u8 *sdio_header = sdiodev->sdio_header; ++ ++ if (!sdiodev || !sdiodev->func) { ++ BT_ERR("[bt sdio] RX sdiodev or function is NULL!"); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ //Read the length of data to be transferred ++ ret = sdio_readsb(sdiodev->func, sdio_header, \ ++ sdiodev->reg_map->sdio_rt_data, SDIO_HEADER_LEN); ++ if (ret < 0) { ++ BT_ERR("[bt sdio] RX read rx_len failed"); ++ ret = -EIO; ++ goto exit; ++ } ++ ++ packet_len = \ ++ sdio_header[0] | (sdio_header[1] << 8) | (sdio_header[2] << 16); ++ packet_type = sdio_header[3]; ++ ++ BT_INFO("[bt sdio] RX packet_len:%d packet_type:%d "\ ++ "packet header hex: %*ph", packet_len,packet_type,\ ++ SDIO_HEADER_LEN, sdio_header); ++ ++ if ((packet_len <= SDIO_HEADER_LEN)\ ++ || (packet_len > HCI_MAX_FRAME_SIZE)) { ++ BT_ERR("[bt sdio] RX packet length: %d not HCI"\ ++ " valid length", packet_len); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ++ if (packet_len > (TIDRV_SIZE_OF_CMD_BUFFER+SDIO_HEADER_LEN)) { ++ BT_WARN("[bt sdio] RX packet length: %d not supported",\ ++ packet_len); ++ } ++ ++ data_read_size = roundup((packet_len - SDIO_HEADER_LEN),\ ++ BTSDIO_RX_ALIGN); ++ ++ ++ skb = bt_skb_alloc(data_read_size, GFP_KERNEL); ++ if (!skb) { ++ /* Out of memory. Prepare a read retry and just ++ * return with the expectation that the next time ++ * we're called we'll have more memory. ++ */ ++ BT_ERR("[bt sdio] No free skb"); ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ skb_put(skb, packet_len-SDIO_HEADER_LEN); ++ ++ ret = sdio_readsb(sdiodev->func, skb->data,\ ++ sdiodev->reg_map->sdio_rt_data, data_read_size); ++ if (ret < 0) { ++ BT_ERR("[bt sdio] RX sdio_readsb failed: %d, size read:%d",\ ++ ret, data_read_size); ++ ret = -EIO; ++ goto exit; ++ } ++ BT_INFO("[bt sdio] RX packet , packet data(without header) hex: %*ph",\ ++ data_read_size, skb->data); ++ ++ switch (packet_type) { ++ case HCI_ACLDATA_PKT: ++ case HCI_SCODATA_PKT: ++ case HCI_EVENT_PKT: ++ if(hdev != NULL){ ++ btti_acknldg_packet(sdiodev, 0);//ack ++ hdev->stat.byte_rx += data_read_size; ++ hci_skb_pkt_type(skb) = packet_type; ++ ++ ret= hci_recv_frame(hdev, skb); ++ if (ret < 0){ ++ BT_ERR("[bt sdio] RX hci_recv_frame" ++ " failed :%d ", ret); ++ } ++ ret=0; ++ }else ++ { ++ ret = -EPERM; ++ } ++ //no need to free the skb in here, it is freed at hci_recv_frame ++ break; ++ ++ case HCI_VENDOR_PKT: ++ BT_INFO("[bt sdio] vendor packet received"); ++ if (!(ret=btti_handle_rx_vendor_event(private_data, skb))) ++ { ++ //if hdev was just created read it ++ hdev = private_data->btti_dev.hcidev; ++ if(hdev != NULL) ++ { ++ btti_acknldg_packet(sdiodev, 0);//ack ++ BT_INFO("[bt sdio] Hdev was created"); ++ hdev->stat.byte_rx += data_read_size; ++ hci_skb_pkt_type(skb) = HCI_EVENT_PKT; ++ ret= hci_recv_frame(hdev, skb); ++ if (ret < 0){ ++ BT_ERR("[bt sdio] vendor RX " ++ "hci_recv_frame " ++ "failed :%d ", ret); ++ } ++ //no need to free the skb in here, ++ //it is freed at hci_recv_frame ++ ret=0; ++ }else{ ++ ret=-EPERM; ++ } ++ } ++ break; ++ ++ ++ default: ++ BT_ERR("[bt sdio] RX Unknown packet packet_type:%d",\ ++ packet_type); ++ ret = -ENOENT; ++ break; ++ } ++ ++exit: ++ //send nack ++ if (ret) { ++ if(hdev){ ++ hdev->stat.err_rx++; ++ } ++ kfree_skb(skb); ++ skb = NULL; ++ btti_acknldg_packet(sdiodev, 1);//nack ++ } ++ return ret; ++} ++ ++static void btti_acknldg_packet(struct btti_sdio_dev *sdiodev, u8 Ack) ++{ ++ int retACK = 0; ++ ++ //ack the packet, HCI will release the skb ++ BT_DBG("[bt sdio] RX sdio_writesb: send ACK "); ++ sdio_writeb(sdiodev->func, Ack,\ ++ sdiodev->reg_map->sdio_pc_rrt, &retACK); ++ if (retACK && (Ack==0)) { ++ BT_ERR("[bt sdio] RX sdio_writesb:"\ ++ " send ACK failed: %d", retACK); ++ } ++ if (retACK && (Ack==1)) { ++ BT_ERR("[bt sdio] RX sdio_writesb:"\ ++ " send NACK failed: %d", retACK); ++ } ++} ++ ++ ++ ++static int btti_handle_rx_vendor_event(struct btti_private *private_data, ++ struct sk_buff *skb ) ++{ ++ int ret = 0; ++ struct btti_vendor_event* vendor_event; ++ vendor_event = (struct btti_vendor_event *) skb->data; ++ switch (vendor_event->event_opcode) { ++ case BTTI_BLE_FIRMWARE_UP: ++ BT_INFO("[bt sdio] vendor packet- ble is up"); ++ private_data->hci_adapter->ble_enable = 1; ++ if (btti_hci_register_hdev(private_data)) { ++ BT_ERR("[bt sdio] Register hdev failed!"); ++ ret = -ENODEV; ++ } ++ BT_INFO("[bt sdio] registered to HCI"); ++ ++ break; ++ default: ++ BT_ERR("[bt sdio] unsupported rx vendor event code:"\ ++ " %d", vendor_event->event_code); ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++ ++} ++//tx ++//packet_len, the length includes the header. ++static int btti_sdio_tx_packet(struct btti_private *private_data, ++ struct sk_buff *skb) ++{ ++ struct btti_sdio_dev *sdiodev = private_data->btti_dev.sdiodev; ++ int ret = 0; ++ int i = 0; ++ void *tmpbuf = NULL; ++ u32 data_send_size = 0; ++ u32 packet_len; ++ char* payload; ++ bool alignment_required; ++ ++ ++ BT_DBG("[bt sdio] TX btti_sdio_tx_packet"); ++ ++ if (!sdiodev || !sdiodev->func) { ++ BT_ERR("[bt sdio] sdiodev or function is NULL!"); ++ return -EINVAL; ++ } ++ ++ packet_len = skb->len; ++ payload = skb->data; ++ data_send_size = roundup(packet_len, BTSDIO_TX_ALIGN); ++ alignment_required = (data_send_size != packet_len); ++ ++ if(alignment_required) ++ { ++ tmpbuf = kzalloc(data_send_size, GFP_KERNEL); ++ if (!tmpbuf){ ++ BT_ERR("[bt sdio] TX allocation failed"); ++ return -ENOMEM; ++ } ++ memcpy(tmpbuf, payload, data_send_size); ++ } ++ else ++ { ++ tmpbuf = payload; ++ } ++ ++ pm_runtime_get_sync(&sdiodev->func->dev); ++ ++ sdio_claim_host(sdiodev->func); ++ do { ++ /* Transfer data to device */ ++ ret = sdio_writesb(sdiodev->func,\ ++ sdiodev->reg_map->sdio_rt_data, tmpbuf,\ ++ data_send_size); ++ if (ret < 0) { ++ i++; ++ BT_ERR("[bt sdio] TX i=%d writesb failed: %d", i, ret); ++ BT_ERR("[bt sdio] TX data_send_size: %d hex: %*ph",\ ++ data_send_size, data_send_size,tmpbuf); ++ ret = -EIO; ++ if (i > MAX_SDIO_TX_RETRY) ++ goto exit; ++ } ++ BT_INFO("[bt sdio] TX to SDIO sdiodev done : %*ph ",\ ++ data_send_size, tmpbuf); ++ ++ } while (ret); ++ ++exit: ++ sdio_release_host(sdiodev->func); ++ ++ if(alignment_required){ ++ kfree(tmpbuf); ++ } ++ ++ pm_runtime_mark_last_busy(&sdiodev->func->dev); ++ pm_runtime_put_autosuspend(&sdiodev->func->dev); ++ ++ if(ret){ ++ private_data->btti_dev.hcidev->stat.err_tx++; ++ } ++ else{ ++ private_data->btti_dev.hcidev->stat.byte_tx\ ++ += data_send_size; ++ } ++ kfree_skb(skb); ++ return ret; ++} ++ ++ ++static int btti_sdio_if_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ int ret = 0; ++ struct btti_private *private_data = NULL; ++ struct btti_sdio_dev *sdiodev = NULL; ++ ++ BT_INFO("[bt sdio] PROBE vendor=0x%x, device=0x%x,"\ ++ " class=%d, fn=%d 0x%lx", ++ id->vendor, id->device, id->class, func->num,\ ++ ( unsigned long)func); ++ ++ /* We are only able to handle the wlan function */ ++ if (func->num != 0x01) ++ { ++ BT_DBG("[bt sdio] PROBE incorrect function number!"); ++ ret = -ENODEV; ++ return ret; ++ } ++ ++ /* Device tree node parsing */ ++ if(btti_sdio_if_probe_of(&func->dev)){ ++ ret = -ENODEV; ++ return ret; ++ } ++ ++ sdiodev = devm_kzalloc(&func->dev, sizeof(*sdiodev), GFP_KERNEL); ++ if (!sdiodev){ ++ ret = -ENODEV; ++ return ret; ++ } ++ ++ sdiodev->func = func; ++ sdiodev->private_data = NULL; ++ ++ if (id->driver_data) { ++ struct btti_sdio_device *data = (void *) id->driver_data; ++ sdiodev->reg_map = data->reg_map; ++ } ++ ++ if (btti_sdio_register_dev(sdiodev) < 0) { ++ BT_ERR("[bt sdio] PROBE Failed to register BT device!"); ++ ret = -ENODEV; ++ goto remove_device; ++ } ++ //enable interrupts ++ btti_sdio_enable_int(sdiodev); ++ ++ private_data = btti_hci_add_sdio_dev(sdiodev); ++ if (!private_data) { ++ BT_ERR("[bt sdio] PROBE Initializing sdiodev failed!"); ++ ret = -ENODEV; ++ goto remove_device; ++ } ++ ++ sdiodev->private_data = private_data; ++ ++ /* Initialize the interface specific function pointers */ ++ private_data->card_tx_packet_funcp = btti_sdio_tx_packet; ++ private_data->card_power_up_firmware_funcp = btti_sdio_power_up_fw; ++ private_data->card_power_dn_firmware_funcp = btti_sdio_power_dn_fw; ++ private_data->card_process_rx_funcp = btti_sdio_process_rx; ++ ++ ++ /* pm_runtime_enable would be done after the firmware is being ++ * downloaded because the core layer probably already enables ++ * runtime PM for this func such as the case host->caps & ++ * MMC_CAP_POWER_OFF_CARD. ++ */ ++ if (pm_runtime_enabled(&sdiodev->func->dev)) ++ pm_runtime_disable(&sdiodev->func->dev); ++ ++ /* As explaination in drivers/mmc/core/sdio_bus.c tells us: ++ * Unbound SDIO functions are always suspended. ++ * During probe, the function is set active and the usage count ++ * is incremented. If the driver supports runtime PM, ++ * it should call pm_runtime_put_noidle() in its probe routine and ++ * pm_runtime_get_noresume() in its remove routine. ++ * ++ * So, put a pm_runtime_put_noidle here ! ++ */ ++ pm_runtime_put_noidle(&sdiodev->func->dev); ++ ++ BT_DBG("[bt sdio] PROBE sdio_claim_irq"); ++ sdio_claim_host(func); ++ ret = sdio_claim_irq(func, btti_sdio_irq_handler); ++ sdio_release_host(func); ++ ++ if (ret) { ++ BT_ERR("[bt sdio] PROBE sdio_claim_irq failed: ret=%d", ret); ++ ret = -EIO; ++ goto remove_device; ++ } ++ ++ ++ BT_INFO("[bt sdio] TI cc33xx BLE-over-SDIO driver is up and running!"); ++ ++ return 0; ++ ++/*disable_sdio_dev_int: ++ btti_sdio_disable_int(sdiodev); ++ btti_sdio_unregister_dev(func);*/ ++remove_device: ++ btti_sdio_if_remove(func); ++ return ret; ++} ++ ++static void btti_sdio_if_remove(struct sdio_func *func) ++{ ++ struct btti_sdio_dev *sdiodev; ++ BT_INFO("[bt sdio if] sdio remove"); ++ ++ if (func != NULL) { ++ sdiodev = sdio_get_drvdata(func); ++ if (sdiodev != NULL) { ++ BT_INFO("[bt sdio] disable interrupt"); ++ btti_sdio_disable_int(sdiodev); ++ if(sdiodev->private_data != NULL){ ++ sdiodev->private_data->sdio_dev_removed = true; ++ btti_hci_remove_sdio_dev(sdiodev->private_data); ++ } ++ else { ++ BT_ERR("[bt sdio] private_data was "\ ++ "not allocated"); ++ } ++ } ++ btti_sdio_unregister_dev(func); ++ } ++ /* Be consistent the state in btti_sdio_if_probe */ ++ pm_runtime_get_noresume(&func->dev); ++ ++} ++ ++static int btti_sdio_if_suspend(struct device *dev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ struct btti_sdio_dev *sdiodev; ++ struct btti_private *private_data; ++ mmc_pm_flag_t pm_flags; ++ struct hci_dev *hcidev; ++ ++ BT_INFO("[bt sdio] suspend"); ++ if (func) { ++ pm_flags = sdio_get_host_pm_caps(func); ++ BT_INFO("[bt sdio] %s: suspend: PM flags = 0x%x",\ ++ sdio_func_id(func), ++ pm_flags); ++ if (!(pm_flags & MMC_PM_KEEP_POWER)) { ++ BT_ERR("[bt sdio] %s: cannot remain"\ ++ " alive while suspended", ++ sdio_func_id(func)); ++ return -ENOSYS; ++ } ++ //sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); ++ ++ sdiodev = sdio_get_drvdata(func); ++ if (!sdiodev || !sdiodev->private_data) { ++ BT_ERR("[bt sdio] sdiodev or private_data"\ ++ " structure is not valid"); ++ return 0; ++ } ++ } else { ++ BT_ERR("[bt sdio] sdio_func is not specified"); ++ return 0; ++ } ++ ++ private_data = sdiodev->private_data; ++ private_data->hci_adapter->is_suspending = true; ++ hcidev = private_data->btti_dev.hcidev; ++ BT_DBG("[bt sdio] %s: SDIO suspend", hcidev->name); ++ hci_suspend_dev(hcidev); ++ private_data->hci_adapter->is_suspending = false; ++ private_data->hci_adapter->is_suspended = true; ++ ++ //if(private_data->card_power_dn_firmware_funcp){ ++ // private_data->card_power_dn_firmware_funcp(private_data); ++ //} ++ ++ BT_DBG("[bt sdio] suspend end"); ++ return 0; ++} ++ ++static int btti_sdio_if_resume(struct device *dev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ struct btti_sdio_dev *sdiodev; ++ struct btti_private *private_data; ++ mmc_pm_flag_t pm_flags; ++ struct hci_dev *hcidev; ++ ++ BT_INFO("[bt sdio] resume"); ++ ++ if (func) { ++ pm_flags = sdio_get_host_pm_caps(func); ++ BT_INFO("[bt sdio] %s: resume: PM flags = 0x%x",\ ++ sdio_func_id(func), ++ pm_flags); ++ sdiodev = sdio_get_drvdata(func); ++ if (!sdiodev || !sdiodev->private_data) { ++ BT_ERR("[bt sdio] sdiodev or private_data"\ ++ " structure is not valid"); ++ return 0; ++ } ++ } else { ++ BT_ERR("[bt sdio] sdio_func is not specified"); ++ return 0; ++ } ++ private_data = sdiodev->private_data; ++ if(!private_data) ++ { ++ BT_ERR("[bt sdio] private_data is not allocated"); ++ return 0; ++ } ++ ++ if (!private_data->hci_adapter->is_suspended) { ++ BT_WARN("[bt sdio] device already resumed"); ++ return 0; ++ } ++ ++ //if(private_data->card_power_up_firmware_funcp){ ++ // private_data->card_power_up_firmware_funcp(private_data); ++ //} ++ hcidev = private_data->btti_dev.hcidev; ++ private_data->hci_adapter->is_suspended = false; ++ BT_INFO("[bt sdio] %s: SDIO resume", hcidev->name); ++ hci_resume_dev(hcidev); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops btti_sdio_pm_ops = { ++ .suspend = btti_sdio_if_suspend, ++ .resume = btti_sdio_if_resume, ++}; ++ ++static struct sdio_driver bt_ti_sdio = { ++ .name = "btti_cc33xx_bt_sdio", ++ .id_table = btti_sdio_ids, ++ .probe = btti_sdio_if_probe, ++ .remove = btti_sdio_if_remove, ++ .drv = { ++ .owner = THIS_MODULE, ++ .coredump = btti_sdio_coredump, ++ .pm = &btti_sdio_pm_ops, ++ } ++}; ++ ++static int __init btti_sdio_init_module(void) ++{ ++ BT_INFO("[bt sdio] BLE SDIO init module"); ++ if (sdio_register_driver(&bt_ti_sdio) != 0) { ++ BT_ERR("[bt sdio] SDIO Driver Registration Failed"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static void __exit btti_sdio_exit_module(void) ++{ ++ BT_INFO("[bt sdio] BLE SDIO exit module"); ++ sdio_unregister_driver(&bt_ti_sdio); ++} ++ ++module_init(btti_sdio_init_module); ++module_exit(btti_sdio_exit_module); ++ ++MODULE_AUTHOR("Texas Instruments."); ++MODULE_DESCRIPTION("Texas Instruments BT-over-SDIO driver ver " VERSION); ++MODULE_VERSION(VERSION); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/bluetooth/btti_sdio.h b/drivers/bluetooth/btti_sdio.h +new file mode 100644 +index 000000000000..1fa7441d3573 +--- /dev/null ++++ b/drivers/bluetooth/btti_sdio.h +@@ -0,0 +1,80 @@ ++/* ++ * This file is part of TI BLE over SDIO ++ * ++ * Copyright (C) 2022 Texas Instruments ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ */ ++ ++ ++#define SDIO_HEADER_LEN 4 ++ ++/* SD block size can not bigger than 64 due to buf size limit in firmware */ ++/* define SD block size for data Tx/Rx */ ++#define SDIO_BLOCK_SIZE 128 ++ ++#define TIDRV_BT_RX_PACKET_BUFFER_SIZE (HCI_MAX_FRAME_SIZE) ++#define TIDRV_SIZE_OF_CMD_BUFFER (251) ++ ++#define MAX_RX_PACKET_SIZE (((min_t (int, TIDRV_BT_RX_PACKET_BUFFER_SIZE, \ ++ TIDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ ++ + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \ ++ * SDIO_BLOCK_SIZE) ++ ++ ++/* Max retry number of CMD53 write */ ++#define MAX_SDIO_TX_RETRY 2 ++ ++/* register bitmasks */ ++#define HOST_POWER_UP_MASK 1 // 1 to power up BLE SDIO firmware wake up bit */ ++ ++#define INTRPT_ENABLE_MASk 1 //interrupt set ++ ++struct btti_sdio_dev_reg_map { ++ u8 sdio_wup_ble; ++ u8 sdio_enable_int; ++ u8 sdio_rt_data; ++ u8 sdio_cl_int; ++ u8 bt_mode_status; ++ u8 sdio_pc_rrt;//ack interrupt ++}; ++ ++struct btti_sdio_dev { ++ struct sdio_func *func; ++ const struct btti_sdio_dev_reg_map *reg_map; ++ u8 bt_mode_status;/* the status read from 0x20 address */ ++ struct btti_private *private_data; ++ u8 sdio_header[SDIO_HEADER_LEN]; ++ bool Intrpt_triggered; ++}; ++ ++struct btti_sdio_device { ++ const struct btti_sdio_dev_reg_map *reg_map; ++}; ++ ++struct btti_vendor_event { ++ u8 event_code;//0xFF ++ u8 total_length;//0x2 ++ u16 event_opcode;// EOGF = 1 ; ESG = 0 ; CMD ++ u8 param_00[2];//Data ++} __packed; ++ ++//BTTI_BLE_FIRMWARE_UP: EOGF = 1 ; ESG = 0 ; CMD= 42 => opcode = 0x042A ++#define BTTI_BLE_FIRMWARE_UP 0x2A04 //on the packet it is 0x042A ++ ++// CC33xx HW FIFOs must be accessed with 32 bit alignment ++#define BTSDIO_RX_ALIGN 4 ++#define BTSDIO_TX_ALIGN 4 ++ ++ ++/* Macros for Data Alignment : size */ ++#define ALIGN_SZ(p, a) \ ++ (((p) + ((a) - 1)) & ~((a) - 1)) +diff --git a/drivers/bluetooth/btti_uart.c b/drivers/bluetooth/btti_uart.c +new file mode 100644 +index 000000000000..62e52ac5fad9 +--- /dev/null ++++ b/drivers/bluetooth/btti_uart.c +@@ -0,0 +1,742 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * TI Bluetooth HCI UART driver ++ * Copyright (C) 2023 Texas Instruments ++ * ++ * Acknowledgements: ++ * This file is based on btuart.c, which was written by Marcel Holtmann. ++ */ ++ ++#define DEBUG ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "h4_recv.h" ++ ++#define VERSION "0.84" ++ ++struct btti_uart_vnd { ++ const struct h4_recv_pkt *recv_pkts; ++ int recv_pkts_cnt; ++ unsigned int manufacturer; ++}; ++ ++enum state{ ++ STATE_PROBING = 0, ++ STATE_HW_OFF, ++ STATE_HW_ON, ++ STATE_HW_READY, ++ STATE_REMOVED ++}; ++ ++static const char *sm_state_to_string(enum state state) ++{ ++ switch (state){ ++ case STATE_PROBING: ++ return "STATE_PROBING"; ++ case STATE_HW_OFF: ++ return "STATE_HW_OFF"; ++ case STATE_HW_ON: ++ return "STATE_HW_ON"; ++ case STATE_HW_READY: ++ return "STATE_HW_READY"; ++ case STATE_REMOVED: ++ return "STATE_REMOVED"; ++ } ++ ++ return "Illegal state value"; ++}; ++ ++enum sm_event{ ++ EVENT_PROBE_DONE, ++ EVENT_REMOVE, ++ EVENT_REGULATOR_ENABLE, ++ EVENT_REGULATOR_DISABLE, ++ EVENT_HCI_WAKEUP_FRAME_RECEIVED, ++}; ++ ++static const char *event_to_string(enum sm_event event) ++{ ++ switch (event){ ++ case EVENT_PROBE_DONE: ++ return "EVENT_PROBE_DONE"; ++ case EVENT_REMOVE: ++ return "EVENT_REMOVE"; ++ case EVENT_REGULATOR_ENABLE: ++ return "EVENT_REGULATOR_ENABLE"; ++ case EVENT_REGULATOR_DISABLE: ++ return "EVENT_REGULATOR_DISABLE"; ++ case EVENT_HCI_WAKEUP_FRAME_RECEIVED: ++ return "EVENT_HCI_WAKEUP_FRAME_RECEIVED"; ++ } ++ ++ return "Illegal event value"; ++}; ++ ++struct btti_uart_dev { ++ struct hci_dev *hdev; ++ struct serdev_device *serdev; ++ struct regulator *reg; ++ struct notifier_block nb; ++ ++ struct work_struct tx_work; ++ struct sk_buff_head txq; ++ ++ struct work_struct btti_uart_sm_work; ++ struct llist_head sm_event_list; ++ enum state sm_state; ++ ++ struct sk_buff *rx_skb; ++ ++ const struct btti_uart_vnd *vnd; ++ ++ struct gpio_desc *host_wakeup; ++ ++ struct pinctrl *pinctrl; ++ struct pinctrl_state *pins_runtime; ++ struct pinctrl_state *pins_sleep; ++}; ++ ++struct event_node{ ++ struct llist_node node; ++ enum sm_event event; ++}; ++ ++static int serial_open(struct btti_uart_dev *bdev) ++{ ++ struct serdev_device *serdev = bdev->serdev; ++ bool disable_flow_control = false; ++ u32 max_speed = 3000000; ++ int ret=0; ++ ++ ret = serdev_device_open(serdev); ++ if (ret){ ++ dev_err(&serdev->dev, "Cannot open serial port (%d)", ret); ++ return ret; ++ } ++ ++ of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed); ++ disable_flow_control = of_property_read_bool(serdev->dev.of_node, "disable-flow-control"); ++ ++ serdev_device_set_baudrate(serdev, max_speed); ++ if (disable_flow_control) ++ serdev_device_set_flow_control(serdev, false); ++ ++ if (bdev->host_wakeup) ++ pm_runtime_enable(&serdev->dev); ++ ++ return ret; ++} ++ ++static void serial_close(struct btti_uart_dev *bdev) ++{ ++ struct serdev_device *serdev = bdev->serdev; ++ ++ if (bdev->host_wakeup) ++ pm_runtime_disable(&serdev->dev); ++ ++ serdev_device_close(serdev); ++} ++ ++static int btti_uart_open(struct hci_dev *hdev) ++{ ++ return 0; ++} ++ ++static int btti_uart_close(struct hci_dev *hdev) ++{ ++ return 0; ++} ++ ++static int btti_uart_flush(struct hci_dev *hdev) ++{ ++ struct btti_uart_dev *bdev = hci_get_drvdata(hdev); ++ ++ /* Flush any pending characters */ ++ serdev_device_write_flush(bdev->serdev); ++ skb_queue_purge(&bdev->txq); ++ ++ cancel_work_sync(&bdev->tx_work); ++ ++ kfree_skb(bdev->rx_skb); ++ bdev->rx_skb = NULL; ++ ++ return 0; ++} ++ ++static int btti_uart_setup(struct hci_dev *hdev) ++{ ++ return 0; ++} ++ ++static int btti_uart_tx_wakeup(struct btti_uart_dev *bdev) ++{ ++ schedule_work(&bdev->tx_work); ++ return 0; ++} ++ ++static int btti_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) ++{ ++ struct btti_uart_dev *bdev = hci_get_drvdata(hdev); ++ ++ /* Prepend skb with frame type */ ++ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); ++ skb_queue_tail(&bdev->txq, skb); ++ ++ btti_uart_tx_wakeup(bdev); ++ return 0; ++} ++ ++static int btti_uart_register_hci_device(struct btti_uart_dev *bdev) ++{ ++ struct serdev_device *serdev = bdev->serdev; ++ struct hci_dev *hdev; ++ int ret; ++ ++ hdev = hci_alloc_dev(); ++ if (!hdev) { ++ dev_err(&serdev->dev, "Can't allocate HCI device"); ++ return -ENOMEM; ++ } ++ ++ hci_set_drvdata(hdev, bdev); ++ hdev->bus = HCI_UART; ++ hdev->open = btti_uart_open; ++ hdev->close = btti_uart_close; ++ hdev->flush = btti_uart_flush; ++ hdev->setup = btti_uart_setup; ++ hdev->send = btti_uart_send_frame; ++ SET_HCIDEV_DEV(hdev, &serdev->dev); ++ ++ ret = hci_register_dev(hdev); ++ if (ret){ ++ dev_err(&serdev->dev, "Can't register HCI device (%d)", ret); ++ hci_free_dev(hdev); ++ hdev = NULL; ++ } ++ ++ bdev->hdev = hdev; ++ return ret; ++} ++ ++static void btti_uart_unregister_device(struct btti_uart_dev *bdev) ++{ ++ struct hci_dev *hdev = bdev->hdev; ++ ++ hci_unregister_dev(hdev); ++ hci_free_dev(hdev); ++ ++ bdev->hdev = NULL; ++} ++ ++static void btti_uart_tx_work(struct work_struct *work) ++{ ++ struct btti_uart_dev *bdev = container_of(work, struct btti_uart_dev, ++ tx_work); ++ struct serdev_device *serdev = bdev->serdev; ++ struct hci_dev *hdev = bdev->hdev; ++ ++ while (1) { ++ struct sk_buff *skb = skb_dequeue(&bdev->txq); ++ int len; ++ ++ if (!skb) ++ break; ++ ++ len = serdev_device_write_buf(serdev, skb->data, ++ skb->len); ++ hdev->stat.byte_tx += len; ++ ++ skb_pull(skb, len); ++ if (skb->len > 0) { ++ skb_queue_head(&bdev->txq, skb); ++ break; ++ } ++ ++ switch (hci_skb_pkt_type(skb)) { ++ case HCI_COMMAND_PKT: ++ hdev->stat.cmd_tx++; ++ break; ++ case HCI_ACLDATA_PKT: ++ hdev->stat.acl_tx++; ++ break; ++ case HCI_SCODATA_PKT: ++ hdev->stat.sco_tx++; ++ break; ++ } ++ ++ kfree_skb(skb); ++ } ++} ++ ++static void unexpected_event(struct serdev_device *serdev, ++ enum state state, enum sm_event event) ++{ ++ dev_err(&serdev->dev, "Unexpected event %s at state %s", ++ event_to_string(event), sm_state_to_string(state)); ++ WARN_ON(1); ++} ++ ++static void btti_uart_sm_post_event(struct btti_uart_dev *bdev, enum sm_event event) ++{ ++ struct event_node *event_node; ++ ++ event_node = kzalloc(sizeof(*event_node), GFP_KERNEL); ++ if (unlikely(!event_node)){ ++ dev_err(&bdev->serdev->dev, "Event allocation failure"); ++ return; ++ } ++ ++ event_node->event = event; ++ ++ llist_add(&event_node->node, &bdev->sm_event_list); ++ schedule_work(&bdev->btti_uart_sm_work); ++} ++ ++static void sm_process(struct btti_uart_dev *bdev, enum sm_event event) ++{ ++ struct serdev_device *serdev = bdev->serdev; ++ enum state next_state = bdev->sm_state; ++ ++ switch (bdev->sm_state){ ++ case STATE_PROBING: ++ switch (event){ ++ case EVENT_PROBE_DONE: ++ if (regulator_is_enabled(bdev->reg)) ++ btti_uart_sm_post_event(bdev, EVENT_REGULATOR_ENABLE); ++ ++ next_state = STATE_HW_OFF; ++ break; ++ ++ default: ++ unexpected_event(serdev, bdev->sm_state, event); ++ } ++ break; ++ ++ case STATE_HW_OFF: ++ switch(event){ ++ case EVENT_REGULATOR_ENABLE: ++ if (0 == serial_open(bdev)) ++ next_state = STATE_HW_ON; ++ else ++ dev_err(&serdev->dev, "Could not open serial port"); ++ break; ++ ++ case EVENT_REMOVE: ++ next_state = STATE_REMOVED; ++ break; ++ ++ case EVENT_REGULATOR_DISABLE: ++ break; ++ ++ default: ++ unexpected_event(serdev, bdev->sm_state, event); ++ } ++ break; ++ ++ case STATE_HW_ON: ++ switch(event){ ++ case EVENT_REMOVE: ++ case EVENT_REGULATOR_DISABLE: ++ serial_close(bdev); ++ next_state = (event == EVENT_REMOVE) ? ++ STATE_REMOVED: STATE_HW_OFF; ++ break; ++ ++ case EVENT_HCI_WAKEUP_FRAME_RECEIVED: ++ if (0 == btti_uart_register_hci_device(bdev)) ++ next_state = STATE_HW_READY; ++ break; ++ ++ case EVENT_REGULATOR_ENABLE: ++ break; ++ ++ default: ++ unexpected_event(serdev, bdev->sm_state, event); ++ } ++ break; ++ ++ case STATE_HW_READY: ++ switch(event){ ++ case EVENT_REMOVE: ++ case EVENT_REGULATOR_DISABLE: ++ btti_uart_unregister_device(bdev); ++ serial_close(bdev); ++ next_state = (event == EVENT_REMOVE) ? ++ STATE_REMOVED: STATE_HW_OFF; ++ break; ++ ++ case EVENT_REGULATOR_ENABLE: ++ break; ++ ++ default: ++ unexpected_event(serdev, bdev->sm_state, event); ++ } ++ break; ++ ++ case STATE_REMOVED: ++ switch(event){ ++ case EVENT_REGULATOR_DISABLE: ++ case EVENT_REGULATOR_ENABLE: ++ case EVENT_HCI_WAKEUP_FRAME_RECEIVED: ++ break; ++ ++ default: ++ unexpected_event(serdev, bdev->sm_state, event); ++ } ++ break; ++ } ++ ++ dev_dbg(&serdev->dev, ++ "SM: Got %s, moving from %s to %s", ++ event_to_string(event), sm_state_to_string(bdev->sm_state), ++ sm_state_to_string(next_state)); ++ ++ bdev->sm_state = next_state; ++} ++ ++inline static struct llist_node* get_event_list(struct btti_uart_dev *bdev) ++{ ++ struct llist_node* node; ++ ++ node = llist_del_all(&bdev->sm_event_list); ++ if (!node) ++ return NULL; ++ ++ return llist_reverse_order(node); ++} ++ ++static void btti_uart_sm_work(struct work_struct *work) ++{ ++ struct btti_uart_dev *bdev; ++ struct event_node *event_node, *tmp; ++ struct llist_node *event_list; ++ ++ bdev = container_of(work, struct btti_uart_dev, btti_uart_sm_work); ++ event_list = get_event_list(bdev); ++ ++ llist_for_each_entry_safe(event_node, tmp, event_list, node){ ++ sm_process(bdev, event_node->event); ++ kfree(event_node); ++ } ++} ++ ++static int btti_uart_wakeup_event_match(struct serdev_device *serdev, ++ const u8 *data, size_t count) ++{ ++ struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev); ++ const u8 hw_wakeup_evt[] = {HCI_VENDOR_PKT, 0xff, 0x02, 0x04, 0x2a, 0x00, 0x00}; ++ ++ if (count < sizeof hw_wakeup_evt) ++ return 0; // Reject data until all bytes have been received ++ ++ if (count > sizeof hw_wakeup_evt) ++ goto error; ++ ++ if (0 != memcmp(&hw_wakeup_evt, data, sizeof hw_wakeup_evt)) ++ goto error; ++ ++ btti_uart_sm_post_event(bdev, EVENT_HCI_WAKEUP_FRAME_RECEIVED); ++ return count; ++ ++error: ++ dev_err(&serdev->dev, "Unexpected wakeup pattern"); ++ print_hex_dump(KERN_DEBUG, "Pattern:", ++ DUMP_PREFIX_NONE, 16, 1, data, count, true); ++ return count; ++} ++ ++static int btti_uart_receive_buf(struct serdev_device *serdev, const u8 *data, ++ size_t count) ++{ ++ struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev); ++ const struct btti_uart_vnd *vnd = bdev->vnd; ++ ++ if (unlikely(!bdev->hdev)){ ++ // Accept only wakeup event until driver is registered with HCI ++ return btti_uart_wakeup_event_match(serdev, data, count); ++ } ++ ++ bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, data, count, ++ vnd->recv_pkts, vnd->recv_pkts_cnt); ++ if (IS_ERR(bdev->rx_skb)) { ++ int err = PTR_ERR(bdev->rx_skb); ++ dev_err(&serdev->dev, "Frame reassembly failed (%d)", err); ++ bdev->rx_skb = NULL; ++ print_hex_dump( KERN_DEBUG, "Frame:", DUMP_PREFIX_NONE, ++ 16, 1, data, count, true); ++ return 0; ++ } ++ ++ bdev->hdev->stat.byte_rx += count; ++ ++ return count; ++} ++ ++static void btti_uart_write_wakeup(struct serdev_device *serdev) ++{ ++ struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev); ++ ++ btti_uart_tx_wakeup(bdev); ++} ++ ++static const struct serdev_device_ops btti_uart_client_ops = { ++ .receive_buf = btti_uart_receive_buf, ++ .write_wakeup = btti_uart_write_wakeup, ++}; ++ ++static const struct h4_recv_pkt default_recv_pkts[] = { ++ { H4_RECV_ACL, .recv = hci_recv_frame }, ++ { H4_RECV_SCO, .recv = hci_recv_frame }, ++ { H4_RECV_EVENT, .recv = hci_recv_frame }, ++}; ++ ++static const struct btti_uart_vnd default_vnd = { ++ .recv_pkts = default_recv_pkts, ++ .recv_pkts_cnt = ARRAY_SIZE(default_recv_pkts), ++}; ++ ++static const struct btti_uart_vnd ti_vnd = { ++ .recv_pkts = default_recv_pkts, ++ .recv_pkts_cnt = ARRAY_SIZE(default_recv_pkts), ++ .manufacturer = 13, ++}; ++ ++static int btti_uart_regulator_event(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct btti_uart_dev *bdev = container_of(nb, struct btti_uart_dev, nb); ++ ++ if (event & REGULATOR_EVENT_DISABLE) { ++ btti_uart_sm_post_event(bdev, EVENT_REGULATOR_DISABLE); ++ } ++ ++ if (event & REGULATOR_EVENT_ENABLE) { ++ btti_uart_sm_post_event(bdev, EVENT_REGULATOR_ENABLE); ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static irqreturn_t host_wake_irq(int irq, void *data) ++{ ++ struct btti_uart_dev *bdev = data; ++ struct serdev_device *serdev = bdev->serdev; ++ ++ dev_info(&serdev->dev, "CC33xx wake IRQ"); ++ ++ pm_wakeup_event(&serdev->dev, 0); ++ pm_system_wakeup(); ++ ++ return IRQ_HANDLED; ++} ++ ++static void btti_uart_host_wake_init(struct serdev_device *serdev) ++{ ++ struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev); ++ int ret; ++ ++ if (!bdev->pinctrl) ++ goto out_err; ++ ++ bdev->pins_sleep = pinctrl_lookup_state(bdev->pinctrl, "sleep"); ++ if (IS_ERR(bdev->pins_sleep)) ++ bdev->pins_sleep = NULL; ++ ++ bdev->host_wakeup = devm_gpiod_get_optional(&serdev->dev, "host-wakeup", GPIOD_IN); ++ if (IS_ERR(bdev->host_wakeup)) ++ goto out_err; ++ ++ if (device_init_wakeup(&serdev->dev, true) != 0) ++ goto out_err; ++ ++ ret = devm_request_irq(&serdev->dev, gpiod_to_irq(bdev->host_wakeup), ++ host_wake_irq, IRQF_TRIGGER_RISING, "btti_host_wake", bdev); ++ if (ret) ++ goto out_err; ++ ++ ret = enable_irq_wake(gpiod_to_irq(bdev->host_wakeup)); ++ if (ret < 0) ++ goto out_err_disable_wake; ++ ++ disable_irq(gpiod_to_irq(bdev->host_wakeup)); ++ ++ dev_info(&serdev->dev, "Host wakeup enabled"); ++ return; ++ ++ ++out_err_disable_wake: ++ device_init_wakeup(&serdev->dev, false); ++out_err: ++ bdev->host_wakeup = NULL; ++ bdev->pins_sleep = NULL; ++ dev_info(&serdev->dev, "Host wakeup NOT enabled"); ++ return; ++} ++ ++static int btti_uart_probe(struct serdev_device *serdev) ++{ ++ struct btti_uart_dev *bdev; ++ int ret; ++ ++ bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL); ++ if (!bdev) ++ return -ENOMEM; ++ ++ /* Request the vendor specific data and callbacks */ ++ bdev->vnd = device_get_match_data(&serdev->dev); ++ if (!bdev->vnd) ++ bdev->vnd = &default_vnd; ++ ++ bdev->serdev = serdev; ++ serdev_device_set_drvdata(serdev, bdev); ++ ++ /* Using the optional get regulator API as normal get returns a dummy ++ if the regulator is not found. */ ++ bdev->reg = devm_regulator_get_optional(&serdev->dev, "cc33xx"); ++ if (PTR_ERR(bdev->reg) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ if (IS_ERR(bdev->reg)) { ++ dev_err(&serdev->dev, "can't get regulator"); ++ return PTR_ERR(bdev->reg); ++ } ++ ++ bdev->pinctrl = devm_pinctrl_get(&serdev->dev); ++ if (!IS_ERR(bdev->pinctrl)){ ++ bdev->pins_runtime = pinctrl_lookup_state(bdev->pinctrl, "default"); ++ if (IS_ERR(bdev->pins_runtime)){ ++ dev_err(&serdev->dev, "can't lookup default pin state"); ++ return PTR_ERR(bdev->pins_runtime); ++ } ++ } else { ++ bdev->pinctrl = NULL; ++ bdev->pins_runtime = NULL; ++ } ++ ++ btti_uart_host_wake_init(serdev); ++ ++ if (bdev->pins_runtime) ++ pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime); ++ ++ bdev->nb.notifier_call = btti_uart_regulator_event; ++ ++ ret = regulator_register_notifier(bdev->reg, &bdev->nb); ++ if (ret != 0) { ++ dev_err(&serdev->dev, ++ "Failed to register regulator notifier (%d)", ret); ++ return ret; ++ } ++ ++ serdev_device_set_client_ops(serdev, &btti_uart_client_ops); ++ ++ INIT_WORK(&bdev->tx_work, btti_uart_tx_work); ++ skb_queue_head_init(&bdev->txq); ++ ++ INIT_WORK(&bdev->btti_uart_sm_work, btti_uart_sm_work); ++ init_llist_head(&bdev->sm_event_list); ++ ++ btti_uart_sm_post_event(bdev, EVENT_PROBE_DONE); ++ ++ return 0; ++} ++ ++static void btti_uart_remove(struct serdev_device *serdev) ++{ ++ struct btti_uart_dev *bdev = serdev_device_get_drvdata(serdev); ++ ++ if (bdev->host_wakeup){ ++ device_init_wakeup(&serdev->dev, false); ++ disable_irq_wake(gpiod_to_irq(bdev->host_wakeup)); ++ } ++ ++ regulator_unregister_notifier(bdev->reg, &bdev->nb); ++ btti_uart_sm_post_event(bdev, EVENT_REMOVE); ++ flush_work(&bdev->btti_uart_sm_work); ++} ++ ++static int btti_suspend_device(struct device *dev) ++{ ++ struct btti_uart_dev *bdev = dev_get_drvdata(dev); ++ struct serdev_device *serdev = bdev->serdev; ++ int ret; ++ ++ if (bdev->pins_sleep){ ++ ret = pinctrl_select_state(bdev->pinctrl, bdev->pins_sleep); ++ if (ret < 0) ++ goto out_err; ++ } ++ ++ enable_irq(gpiod_to_irq(bdev->host_wakeup)); ++ ++ ret = serdev_device_set_rts(serdev, false); ++ if (ret < 0) ++ goto out_err; ++ ++ return 0; ++ ++out_err: ++ if (bdev->pins_runtime) ++ pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime); ++ ++ serdev_device_set_rts(serdev, true); ++ ++ return ret; ++} ++ ++static int btti_resume_device(struct device *dev) ++{ ++ struct btti_uart_dev *bdev = dev_get_drvdata(dev); ++ struct serdev_device *serdev = bdev->serdev; ++ int ret; ++ ++ disable_irq_nosync(gpiod_to_irq(bdev->host_wakeup)); ++ ++ if (bdev->pins_runtime){ ++ ret = pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return serdev_device_set_rts(serdev, true); ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id btti_uart_of_match_table[] = { ++ { .compatible = "ti,cc33xx-bt", .data = &ti_vnd }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, btti_uart_of_match_table); ++#endif ++ ++static const struct dev_pm_ops btti_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(btti_suspend_device, btti_resume_device) ++}; ++ ++static struct serdev_device_driver btti_uart_driver = { ++ .probe = btti_uart_probe, ++ .remove = btti_uart_remove, ++ .driver = { ++ .name = "btti", ++ .of_match_table = of_match_ptr(btti_uart_of_match_table), ++ .pm = &btti_pm_ops ++ }, ++}; ++ ++module_serdev_device_driver(btti_uart_driver); ++ ++MODULE_DESCRIPTION("TI Bluetooth UART driver ver " VERSION); ++MODULE_VERSION(VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/wireless/ti/cc33xx/Makefile b/drivers/net/wireless/ti/cc33xx/Makefile +index 6156f778edee..9bed399fb1f9 100644 +--- a/drivers/net/wireless/ti/cc33xx/Makefile ++++ b/drivers/net/wireless/ti/cc33xx/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + + cc33xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ +- boot.o init.o scan.o ++ boot.o init.o scan.o debugfs.o + + cc33xx_sdio-objs = sdio.o + +diff --git a/drivers/net/wireless/ti/cc33xx/acx.c b/drivers/net/wireless/ti/cc33xx/acx.c +index 34a6f9cee245..84a59853d0ad 100644 +--- a/drivers/net/wireless/ti/cc33xx/acx.c ++++ b/drivers/net/wireless/ti/cc33xx/acx.c +@@ -5,30 +5,6 @@ + + #include "acx.h" + +-int cc33xx_acx_clear_statistics(struct cc33xx *cc) +-{ +- struct acx_header *acx; +- int ret = 0; +- +- cc33xx_debug(DEBUG_ACX, "acx clear statistics"); +- +- acx = kzalloc(sizeof(*acx), GFP_KERNEL); +- if (!acx) { +- ret = -ENOMEM; +- goto out; +- } +- +- ret = cc33xx_cmd_configure(cc, ACX_CLEAR_STATISTICS, acx, sizeof(*acx)); +- if (ret < 0) { +- cc33xx_warning("failed to clear firmware statistics: %d", ret); +- goto out; +- } +- +-out: +- kfree(acx); +- return ret; +-} +- + int cc33xx_acx_wake_up_conditions(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval) + { +@@ -431,23 +407,6 @@ int cc33xx_acx_cts_protect(struct cc33xx *cc, struct cc33xx_vif *wlvif, + return ret; + } + +-int cc33xx_acx_statistics(struct cc33xx *cc, void *stats) +-{ +- int ret; +- +- cc33xx_debug(DEBUG_ACX, "acx statistics"); +- +- ret = cc33xx_cmd_interrogate(cc, ACX_STATISTICS, stats, +- sizeof(struct acx_header), +- sizeof(struct cc33xx_acx_statistics)); +- if (ret < 0) { +- cc33xx_warning("acx statistics failed: %d", ret); +- return -ENOMEM; +- } +- +- return 0; +-} +- + int cc33xx_update_ap_rates(struct cc33xx *cc, u8 role_id, + u32 basic_rates_set, u32 supported_rates) + { +@@ -666,58 +625,6 @@ int cc33xx_acx_set_ba_receiver_session(struct cc33xx *cc, u8 tid_index, u16 ssn, + return ret; + } + +-int cc33xx_acx_tsf_info(struct cc33xx *cc, +- struct cc33xx_vif *wlvif, u64 *mactime) +-{ +- struct cc33xx_acx_fw_tsf_information *tsf_info; +- int ret = 0; +- +- tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); +- if (!tsf_info) { +- ret = -ENOMEM; +- goto out; +- } +- +- tsf_info->role_id = wlvif->role_id; +- +- *mactime = le32_to_cpu(tsf_info->current_tsf_low) | +- ((u64)le32_to_cpu(tsf_info->current_tsf_high) << 32); +- +-out: +- kfree(tsf_info); +- return ret; +-} +- +-int cc33xx_acx_config_ps(struct cc33xx *cc, struct cc33xx_vif *wlvif) +-{ +- struct cc33xx_acx_config_ps *config_ps; +- int ret; +- +- cc33xx_debug(DEBUG_ACX, "acx config ps"); +- +- config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL); +- if (!config_ps) { +- ret = -ENOMEM; +- goto out; +- } +- +- config_ps->exit_retries = cc->conf.host_conf.conn.psm_exit_retries; +- config_ps->enter_retries = cc->conf.host_conf.conn.psm_entry_retries; +- config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate); +- +- ret = cc33xx_cmd_configure(cc, ACX_CONFIG_PS, config_ps, +- sizeof(*config_ps)); +- +- if (ret < 0) { +- cc33xx_warning("acx config ps failed: %d", ret); +- goto out; +- } +- +-out: +- kfree(config_ps); +- return ret; +-} +- + int cc33xx_acx_average_rssi(struct cc33xx *cc, + struct cc33xx_vif *wlvif, s8 *avg_rssi) + { +@@ -983,6 +890,59 @@ int cc33xx_acx_set_peer_cap(struct cc33xx *cc, + return ret; + } + ++int cc33xx_acx_set_antenna_select(struct cc33xx *cc, u8 selection) ++{ ++ struct acx_antenna_select *acx; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx setting antenna to %d", selection); ++ ++ acx = kzalloc(sizeof(*acx), GFP_KERNEL); ++ if (!acx) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ acx->selection = selection; ++ ++ ret = cc33xx_cmd_configure(cc, SET_ANTENNA_SELECT_CFG, ++ acx, sizeof(*acx)); ++ if (ret < 0) { ++ cc33xx_warning("acx setting antenna failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(acx); ++ return ret; ++} ++ ++int cc33xx_acx_set_tsf(struct cc33xx *cc, u64 tsf_val) ++{ ++ struct debug_set_tsf *set_tsf_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx set tsf. new tsf value: %llx", tsf_val); ++ ++ set_tsf_cmd = kzalloc(sizeof(*set_tsf_cmd), GFP_KERNEL); ++ if (!set_tsf_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ set_tsf_cmd->tsf_val = cpu_to_le64(tsf_val); ++ ++ ret = cc33xx_cmd_debug(cc, SET_TSF, set_tsf_cmd, sizeof(*set_tsf_cmd)); ++ if (ret < 0) { ++ cc33xx_error("acx set tsf failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(set_tsf_cmd); ++ return ret; ++} ++ + int cc33xx_acx_trigger_fw_assert(struct cc33xx *cc) + { + struct debug_header *buf; +@@ -1006,3 +966,312 @@ int cc33xx_acx_trigger_fw_assert(struct cc33xx *cc) + kfree(buf); + return ret; + } ++ ++int cc33xx_acx_burst_mode_cfg(struct cc33xx *cc, u8 burst_disable) ++{ ++ struct debug_burst_mode_cfg *burst_mode_cfg; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx burst mode cfg. burst_disable = %d", ++ burst_disable); ++ ++ burst_mode_cfg = kzalloc(sizeof(*burst_mode_cfg), GFP_KERNEL); ++ if (!burst_mode_cfg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ burst_mode_cfg->burst_disable = burst_disable; ++ ++ ret = cc33xx_cmd_debug(cc, BURST_MODE_CFG, burst_mode_cfg, ++ sizeof(*burst_mode_cfg)); ++ if (ret < 0) { ++ cc33xx_warning("acx burst mode cfg failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(burst_mode_cfg); ++ return ret; ++} ++ ++int cc33xx_acx_get_antenna_diversity_status(struct cc33xx *cc) ++{ ++ struct acx_diversity_status *get_diversity_status_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx get antenna diversity status"); ++ ++ get_diversity_status_cmd = kzalloc(sizeof(*get_diversity_status_cmd), GFP_KERNEL); ++ if (!get_diversity_status_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_interrogate(cc, GET_ANT_DIV_STATUS, get_diversity_status_cmd, ++ sizeof(struct acx_header), sizeof(*get_diversity_status_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx get antenna diversity status failed: %d", ret); ++ goto out; ++ } ++ ++ ret = get_diversity_status_cmd->enable; ++ ++out: ++ kfree(get_diversity_status_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_set_antenna_diversity_status(struct cc33xx *cc, u8 enable) ++{ ++ struct acx_diversity_status *set_diversity_status_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx set antenna diversity status. enable = %d", enable); ++ ++ set_diversity_status_cmd = kzalloc(sizeof(*set_diversity_status_cmd), GFP_KERNEL); ++ if (!set_diversity_status_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ set_diversity_status_cmd->enable = enable; ++ ++ ret = cc33xx_cmd_configure(cc, ANT_DIV_ENABLE, set_diversity_status_cmd, ++ sizeof(*set_diversity_status_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx set antenna diversity status failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(set_diversity_status_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_antenna_diversity_get_rssi_threshold(struct cc33xx *cc, s8 *threshold) ++{ ++ struct acx_diversity_rssi_threshold *get_rssi_threshold_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx antenna diversity get rssi threshold"); ++ ++ get_rssi_threshold_cmd = kzalloc(sizeof(*get_rssi_threshold_cmd), GFP_KERNEL); ++ if (!get_rssi_threshold_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_interrogate(cc, GET_ANT_DIV_RSSI_THRESHOLD, get_rssi_threshold_cmd, ++ sizeof(struct acx_header), sizeof(*get_rssi_threshold_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx antenna diversity get rssi threshold failed: %d", ret); ++ goto out; ++ } ++ ++ *threshold = get_rssi_threshold_cmd->rssi_threshold; ++ ret = 0; ++ ++out: ++ kfree(get_rssi_threshold_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_antenna_diversity_set_rssi_threshold(struct cc33xx *cc, s8 rssi_threshold) ++{ ++ struct acx_diversity_rssi_threshold *set_rssi_threshold_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx antenna diversity set rssi threshold to %d", ++ rssi_threshold); ++ ++ set_rssi_threshold_cmd = kzalloc(sizeof(*set_rssi_threshold_cmd), GFP_KERNEL); ++ if (!set_rssi_threshold_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ set_rssi_threshold_cmd->rssi_threshold = rssi_threshold; ++ ++ ret = cc33xx_cmd_configure(cc, ANT_DIV_SET_RSSI_THRESHOLD, set_rssi_threshold_cmd, ++ sizeof(*set_rssi_threshold_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx antenna diversity set rssi threshold failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(set_rssi_threshold_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_antenna_diversity_get_default_antenna(struct cc33xx *cc) ++{ ++ struct acx_diversity_default_antenna *get_default_antenna_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx antenna diversity get default antenna"); ++ ++ get_default_antenna_cmd = kzalloc(sizeof(*get_default_antenna_cmd), GFP_KERNEL); ++ if (!get_default_antenna_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_interrogate(cc, GET_ANT_DIV_DEFAULT_ANTENNA, get_default_antenna_cmd, ++ sizeof(struct acx_header), sizeof(*get_default_antenna_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx antenna diversity get default antenna failed: %d", ret); ++ goto out; ++ } ++ ++ ret = get_default_antenna_cmd->default_antenna; ++ ++out: ++ kfree(get_default_antenna_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_antenna_diversity_select_default_antenna(struct cc33xx *cc, u8 default_antenna) ++{ ++ struct acx_diversity_default_antenna *select_default_antenna_cmd; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx antenna diversity select default antenna. default_antenna = %d", default_antenna); ++ ++ select_default_antenna_cmd = kzalloc(sizeof(*select_default_antenna_cmd), GFP_KERNEL); ++ if (!select_default_antenna_cmd) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ select_default_antenna_cmd->default_antenna = default_antenna; ++ ++ ret = cc33xx_cmd_configure(cc, ANT_DIV_SELECT_DEFAULT_ANTENNA, select_default_antenna_cmd, sizeof(*select_default_antenna_cmd)); ++ if (ret < 0) { ++ cc33xx_warning("acx antenna diversity select default antenna failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(select_default_antenna_cmd); ++ return ret; ++} ++ ++int cc33xx_acx_twt_setup(struct cc33xx *cc, u32 min_wake_duration_usec, ++ u32 min_wake_interval_mantissa, u32 min_wake_interval_exponent, ++ u32 max_wake_interval_mantissa, u32 max_wake_interval_exponent, ++ u8 valid_params) ++{ ++ struct acx_twt_setup *acx; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx config twt setup. valid_params: %d, " ++ "min_wake_duration_usec: %d, min_wake_interval_mantissa: %d, " ++ "min_wake_interval_exponent: %d, max_wake_interval_mantissa: %d, " ++ "max_wake_interval_exponent: %d", ++ valid_params, min_wake_duration_usec, ++ min_wake_interval_mantissa, min_wake_interval_exponent, ++ max_wake_interval_mantissa, max_wake_interval_exponent); ++ ++ acx = kzalloc(sizeof(*acx), GFP_KERNEL); ++ if (!acx) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ acx->min_wake_duration_usec = cpu_to_le32(min_wake_duration_usec); ++ acx->min_wake_interval_mantissa = cpu_to_le32(min_wake_interval_mantissa); ++ acx->min_wake_interval_exponent = cpu_to_le32(min_wake_interval_exponent); ++ acx->max_wake_interval_mantissa = cpu_to_le32(max_wake_interval_mantissa); ++ acx->max_wake_interval_exponent = cpu_to_le32(max_wake_interval_exponent); ++ acx->valid_params = valid_params; ++ ++ ret = cc33xx_cmd_configure(cc, TWT_SETUP, acx, sizeof(*acx)); ++ if (ret < 0) { ++ cc33xx_warning("acx config twt setup failed: %d", ret); ++ goto out; ++ } ++ ++ cc->min_wake_duration_usec = min_wake_duration_usec; ++ cc->min_wake_interval_mantissa = min_wake_interval_mantissa; ++ cc->min_wake_interval_exponent = min_wake_interval_exponent; ++ cc->max_wake_interval_mantissa = max_wake_interval_mantissa; ++ cc->max_wake_interval_exponent = max_wake_interval_exponent; ++out: ++ kfree(acx); ++ return ret; ++} ++ ++int cc33xx_acx_twt_terminate(struct cc33xx *cc) ++{ ++ struct acx_twt_terminate *acx; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx config twt terminate"); ++ ++ acx = kzalloc(sizeof(*acx), GFP_KERNEL); ++ if (!acx) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_configure(cc, TWT_TERMINATE, acx, sizeof(*acx)); ++ if (ret < 0) { ++ cc33xx_warning("acx config twt terminate failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(acx); ++ return ret; ++} ++ ++int cc33xx_acx_twt_suspend(struct cc33xx *cc) ++{ ++ struct acx_twt_terminate *acx; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx config twt suspend"); ++ ++ acx = kzalloc(sizeof(*acx), GFP_KERNEL); ++ if (!acx) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_configure(cc, TWT_SUSPEND, acx, sizeof(*acx)); ++ if (ret < 0) { ++ cc33xx_warning("acx config twt suspend failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ kfree(acx); ++ return ret; ++} ++ ++int cc33xx_acx_twt_resume(struct cc33xx *cc) ++{ ++ struct acx_twt_terminate *acx; ++ int ret; ++ ++ cc33xx_debug(DEBUG_ACX, "acx config twt resume"); ++ ++ acx = kzalloc(sizeof(*acx), GFP_KERNEL); ++ if (!acx) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = cc33xx_cmd_configure(cc, TWT_RESUME, acx, sizeof(*acx)); ++ if (ret < 0) { ++ cc33xx_warning("acx config twt resume failed: %d", ret); ++ goto out; ++ } ++ ++out: ++ ++ kfree(acx); ++ return ret; ++} +diff --git a/drivers/net/wireless/ti/cc33xx/acx.h b/drivers/net/wireless/ti/cc33xx/acx.h +index f250758c3da5..b130356ed0bc 100644 +--- a/drivers/net/wireless/ti/cc33xx/acx.h ++++ b/drivers/net/wireless/ti/cc33xx/acx.h +@@ -398,15 +398,6 @@ struct cc33xx_acx_fw_tsf_information { + u8 padding2[3]; + } __packed; + +-struct cc33xx_acx_config_ps { +- struct acx_header header; +- +- u8 exit_retries; +- u8 enter_retries; +- u8 padding[2]; +- __le32 null_data_rate; +-} __packed; +- + #define ACX_RATE_MGMT_ALL_PARAMS 0xff + + struct acx_default_rx_filter { +@@ -521,18 +512,15 @@ enum interrogate_opt { + GET_PREAMBLE_AND_TX_RATE_INTR = 4, + GET_MAC_ADDRESS = 5, + READ_COEX_STATISTICS = 6, ++ GET_ANT_DIV_STATUS = 7, ++ GET_ANT_DIV_RSSI_THRESHOLD = 8, ++ GET_ANT_DIV_DEFAULT_ANTENNA = 9, + LAST_IE_VALUE, + MAX_DOT11_IE = LAST_IE_VALUE, + + MAX_IE = 0xFFFF /*force enumeration to 16bits*/ + }; + +-enum { +- ACX_STATISTICS = LAST_CFG_VALUE, +- ACX_CONFIG_PS, +- ACX_CLEAR_STATISTICS = 0x0054, +-}; +- + struct cc33xx_acx_error_stats { + __le32 error_frame_non_ctrl; + __le32 error_frame_ctrl; +@@ -775,6 +763,46 @@ struct cc33xx_acx_peer_cap { + u8 padding; + } __packed; + ++struct acx_antenna_select { ++ struct acx_header header; ++ ++ u8 selection; ++ u8 padding[3]; ++} __packed; ++ ++struct debug_set_tsf { ++ struct debug_header header; ++ ++ __le64 tsf_val; ++} __packed; ++ ++struct debug_burst_mode_cfg { ++ struct debug_header header; ++ ++ u8 burst_disable; ++ u8 padding[3]; ++} __packed; ++struct acx_twt_setup { ++ struct acx_header header; ++ __le32 min_wake_duration_usec; ++ __le32 min_wake_interval_mantissa; ++ __le32 min_wake_interval_exponent; ++ __le32 max_wake_interval_mantissa; ++ __le32 max_wake_interval_exponent; ++ u8 valid_params; ++ u8 padding [3]; ++} __packed; ++ ++#define MIN_WAKE_DURATION_VALID BIT(0) ++#define MIN_WAKE_INTERVAL_MANTISSA_VALID BIT(1) ++#define MIN_WAKE_INTERVAL_EXPONENT_VALID BIT(2) ++#define MAX_WAKE_INTERVAL_MANTISSA_VALID BIT(3) ++#define MAX_WAKE_INTERVAL_EXPONENT_VALID BIT(4) ++ ++struct acx_twt_terminate { ++ struct acx_header header; ++} __packed; ++ + struct acx_preamble_and_tx_rate { + struct acx_header header; + u16 tx_rate; +@@ -782,6 +810,76 @@ struct acx_preamble_and_tx_rate { + u8 role_id; + } __packed; + ++static const u16 cc33xx_idx_to_rate_100Kbps[] = ++{ ++ 10, 20, 55, 110, 60, 90, 120, 180, 240, 360, 480, 540 ++}; ++ ++struct cc33xx_coex_statistics { ++ __le32 wifi_request_assertion_log; ++ __le32 wifi_request_de_assertion_log; ++ __le32 wifi_grant_assertion_log; ++ __le32 wifi_grant_deassertion_log; ++ __le32 wifi_prio_reject_log; ++ __le32 wifi_grant_during_dual_ant_assertion_log; ++ __le32 wifi_grant_during_dual_ant_deassertion_log; ++ __le32 ble_request_assertion_log; ++ __le32 ble_request_deassertion_log; ++ __le32 ble_grant_assertion_log; ++ __le32 ble_grant_deassertion_log; ++ __le32 ble_tx_high_prio_reject_log; ++ __le32 ble_tx_low_prio_reject_log; ++ __le32 ble_rx_high_prio_reject_log; ++ __le32 ble_rx_low_prio_reject_log; ++ __le32 soc_request_assertion_log; ++ __le32 soc_request_deassertion_log; ++ __le32 soc_grant_assertion_log; ++ __le32 soc_grant_deassertion_log; ++ __le32 soc_high_prio_reject_log; ++ __le32 soc_low_prio_reject_log; ++} __packed; ++ ++struct cc33xx_coex_stat_and_entities { ++ __u8 coex_entities_bitmap; ++ __u8 padding[3]; ++ struct cc33xx_coex_statistics coex_statistics; ++} __packed; ++ ++ ++struct cc33xx_acx_coex_statistics { ++ struct acx_header header; ++ ++ struct cc33xx_coex_stat_and_entities coex_stat; ++} __packed; ++ ++struct cc33xx_acx_coex_statistics_cfg { ++ struct acx_header header; ++ ++ __u8 coex_statictics; ++ __u8 padding[3]; ++} __packed; ++ ++struct acx_diversity_status { ++ struct acx_header header; ++ ++ u8 enable; ++ u8 padding[3]; ++} __packed; ++ ++struct acx_diversity_rssi_threshold { ++ struct acx_header header; ++ ++ s8 rssi_threshold; ++ u8 padding[3]; ++} __packed; ++ ++struct acx_diversity_default_antenna { ++ struct acx_header header; ++ ++ u8 default_antenna; ++ u8 padding[3]; ++} __packed; ++ + int cc33xx_acx_wake_up_conditions(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval); + int cc33xx_acx_sleep_auth(struct cc33xx *cc, u8 sleep_auth); +@@ -799,7 +897,6 @@ int cc33xx_acx_set_preamble(struct cc33xx *cc, struct cc33xx_vif *wlvif, + enum acx_preamble_type preamble); + int cc33xx_acx_cts_protect(struct cc33xx *cc, struct cc33xx_vif *wlvif, + enum acx_ctsprotect_type ctsprotect); +-int cc33xx_acx_statistics(struct cc33xx *cc, void *stats); + int cc33xx_tx_param_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 ac, + u8 cw_min, u16 cw_max, u8 aifsn, u16 txop, bool acm, + u8 ps_scheme, u8 is_mu_edca, u8 mu_edca_aifs, +@@ -813,9 +910,6 @@ int cc33xx_acx_set_ht_information(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u16 he_oper_nss_set); + int cc33xx_acx_set_ba_receiver_session(struct cc33xx *cc, u8 tid_index, u16 ssn, + bool enable, u8 peer_hlid, u8 win_size); +-int cc33xx_acx_tsf_info(struct cc33xx *cc, +- struct cc33xx_vif *wlvif, u64 *mactime); +-int cc33xx_acx_config_ps(struct cc33xx *cc, struct cc33xx_vif *wlvif); + int cc33xx_acx_get_tx_rate(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct station_info *sinfo); + int cc33xx_acx_average_rssi(struct cc33xx *cc, +@@ -824,12 +918,30 @@ int cc33xx_acx_default_rx_filter_enable(struct cc33xx *cc, bool enable, + enum rx_filter_action action); + int cc33xx_acx_set_rx_filter(struct cc33xx *cc, u8 index, bool enable, + struct cc33xx_rx_filter *filter); +-int cc33xx_acx_clear_statistics(struct cc33xx *cc); + int cc33xx_acx_set_peer_cap(struct cc33xx *cc, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_sta_he_cap *he_cap, + struct cc33xx_vif *wlvif, bool allow_ht_operation, + u32 rate_set, u8 hlid); ++int cc33xx_acx_set_antenna_select(struct cc33xx *cc, u8 selection); ++int cc33xx_acx_set_tsf(struct cc33xx *cc, u64 tsf_val); + int cc33xx_acx_trigger_fw_assert(struct cc33xx *cc); ++int cc33xx_acx_burst_mode_cfg(struct cc33xx *cc, u8 burst_disable); ++int cc33xx_acx_get_antenna_diversity_status(struct cc33xx *cc); ++int cc33xx_acx_set_antenna_diversity_status(struct cc33xx *cc, u8 enable); ++int cc33xx_acx_antenna_diversity_get_rssi_threshold(struct cc33xx *cc, s8 *threshold); ++int cc33xx_acx_antenna_diversity_set_rssi_threshold(struct cc33xx *cc, s8 rssi_threshold); ++int cc33xx_acx_antenna_diversity_get_default_antenna(struct cc33xx *cc); ++int cc33xx_acx_antenna_diversity_select_default_antenna(struct cc33xx *cc, u8 default_antenna); ++int cc33xx_acx_twt_setup(struct cc33xx *wl, ++ u32 min_wake_duration_usec, ++ u32 min_wake_interval_mantissa, ++ u32 min_wake_interval_exponent, ++ u32 max_wake_interval_mantissa, ++ u32 max_wake_interval_exponent, ++ u8 valid_params); ++int cc33xx_acx_twt_terminate(struct cc33xx *wl); ++int cc33xx_acx_twt_resume(struct cc33xx *wl); ++int cc33xx_acx_twt_suspend(struct cc33xx *wl); + + #endif /* __CC33XX_ACX_H__ */ +diff --git a/drivers/net/wireless/ti/cc33xx/boot.c b/drivers/net/wireless/ti/cc33xx/boot.c +index f734659f989c..f53b4ea9e7d0 100644 +--- a/drivers/net/wireless/ti/cc33xx/boot.c ++++ b/drivers/net/wireless/ti/cc33xx/boot.c +@@ -130,6 +130,14 @@ static int wait_for_boot_irq(struct cc33xx *cc, u32 boot_irq_mask, + + fw_download = cc->fw_download; + ++ /** ++ * Hosts can miss boot-done signal after powerup or internal reset. ++ * Work around this by explicitly triggering IRQ handler which will ++ * check current device status after a safe delay. ++ */ ++ msleep(10); ++ cc33xx_irq(cc); ++ + ret = wait_for_completion_interruptible_timeout(&fw_download->wait_on_irq, + msecs_to_jiffies(timeout)); + +diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx_i.h b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h +index 9437f78b364f..f5e36e79b6db 100644 +--- a/drivers/net/wireless/ti/cc33xx/cc33xx_i.h ++++ b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h +@@ -58,6 +58,7 @@ enum cc33xx_state { + CC33XX_STATE_OFF, + CC33XX_STATE_RESTARTING, + CC33XX_STATE_ON, ++ CC33XX_STATE_FAILED, + }; + + struct cc33xx; +@@ -136,7 +137,6 @@ enum cc33xx_vif_flags { + WLVIF_FLAG_STA_AUTHORIZED, + WLVIF_FLAG_IBSS_JOINED, + WLVIF_FLAG_AP_STARTED, +- WLVIF_FLAG_IN_PS, + WLVIF_FLAG_STA_STATE_SENT, + WLVIF_FLAG_PSPOLL_FAILURE, + WLVIF_FLAG_CS_PROGRESS, +@@ -398,6 +398,8 @@ struct cc33xx_vif { + u8 persistent[]; + }; + ++void cc33xx_irq(void *cookie); ++ + static inline struct cc33xx_vif *cc33xx_vif_to_data(struct ieee80211_vif *vif) + { + WARN_ON(!vif); +diff --git a/drivers/net/wireless/ti/cc33xx/cmd.c b/drivers/net/wireless/ti/cc33xx/cmd.c +index 9654b3fe1810..7b41861bfaca 100644 +--- a/drivers/net/wireless/ti/cc33xx/cmd.c ++++ b/drivers/net/wireless/ti/cc33xx/cmd.c +@@ -8,7 +8,7 @@ + #include "io.h" + #include "tx.h" + +-#define CC33XX_REBOOT_TIMEOUT_MSEC 100 ++#define CC33XX_REBOOT_TIMEOUT_MSEC 250 + + static void init_cmd_header(struct cc33xx_cmd_header *header, + size_t cmd_len, u16 id) +@@ -327,6 +327,87 @@ int cc33xx_set_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 link) + return 0; + } + ++static u8 cc33xx_check_is_dfs_channel(struct cc33xx *cc, ++ enum nl80211_band rate_band, u8 channel) ++{ ++ struct ieee80211_supported_band *band; ++ u8 is_dfs = 0; ++ ++ if (rate_band != NL80211_BAND_5GHZ) { ++ return is_dfs; ++ } ++ ++ band = cc->hw->wiphy->bands[NL80211_BAND_5GHZ]; ++ ++ for (int i = 0; i < band->n_channels; i++) { ++ if (band->channels[i].hw_value == channel) { ++ is_dfs = !!(band->channels[i].flags & IEEE80211_CHAN_RADAR); ++ break; ++ } ++ } ++ ++ return is_dfs; ++} ++ ++static u8 cc33xx_get_sub_channel_type(u8 band, u8 channel, u8 band_width) ++{ ++ if (band == NL80211_BAND_5GHZ) { ++ switch (channel) { ++ case 36: ++ case 52: ++ case 100: ++ case 116: ++ case 132: ++ case 149: ++ case 165: ++ return 0; ++ case 40: ++ case 56: ++ case 104: ++ case 120: ++ case 136: ++ case 153: ++ case 169: ++ return 1; ++ case 44: ++ case 60: ++ case 108: ++ case 124: ++ case 140: ++ case 157: ++ case 173: ++ return 2; ++ case 48: ++ case 64: ++ case 112: ++ case 128: ++ case 144: ++ case 161: ++ case 177: ++ return 3; ++ default: ++ return 0xff; ++ } ++ } ++ ++ if (band == NL80211_BAND_2GHZ) { ++ switch (channel) { ++ case 9: ++ case 5: ++ return 0; ++ case 1: ++ return 0; ++ case 13: ++ return 1; ++ default: ++ return 0; ++ } ++ } ++ ++ return 0xff; ++} ++ ++ + void cc33xx_clear_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 *hlid) + { + unsigned long flags; +@@ -615,6 +696,7 @@ int cc33xx_cmd_role_start_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif) + cmd->role_id = wlvif->role_id; + cmd->role_type = CC33XX_ROLE_STA; + cmd->channel = wlvif->channel; ++ cmd->is_dfs_channel = cc33xx_check_is_dfs_channel(cc, wlvif->band, cmd->channel); + if (wlvif->band == NL80211_BAND_5GHZ) { + cmd->band = CC33XX_BAND_5GHZ; + cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set +@@ -635,9 +717,8 @@ int cc33xx_cmd_role_start_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif) + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; + +- cmd->sta.local_rates = cpu_to_le32(supported_rates); +- +- cmd->channel_type = cc33xx_get_native_channel_type(wlvif->channel_type); ++ cmd->sta.local_rates = cpu_to_le32(supported_rates); ++ cmd->channel_type = cc33xx_get_sub_channel_type(cmd->band, cmd->channel, 0); + + /* We don't have the correct remote rates in this stage. The + * rates will be reconfigured later, after association, if the +@@ -747,6 +828,7 @@ int cc33xx_cmd_role_start_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif) + cmd->ap.wmm = wlvif->wmm_enabled; + cmd->channel = wlvif->channel; + cmd->channel_type = cc33xx_get_native_channel_type(wlvif->channel_type); ++ cmd->is_dfs_channel = cc33xx_check_is_dfs_channel(cc, wlvif->band, cmd->channel); + + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES; + if (wlvif->p2p) +@@ -1443,12 +1525,12 @@ static int cc33xx_get_reg_conf_ch_idx(enum nl80211_band band, u16 ch) + case 52 ... 64: + /* channels 52,56..64 are mapped to 29..32 */ + return 29 + (ch - 52) / 4; +- case 100 ... 140: +- /* channels 100,104..140 are mapped to 33..43 */ ++ case 100 ... 144: ++ /* channels 100,104..144 are mapped to 33..44 */ + return 33 + (ch - 100) / 4; +- case 149 ... 165: +- /* channels 149,153..165 are mapped to 44..48 */ +- return 44 + (ch - 149) / 4; ++ case 149 ... 169: ++ /* channels 149,153..169 are mapped to 45..50 */ ++ return 45 + (ch - 149) / 4; + default: + break; + } +diff --git a/drivers/net/wireless/ti/cc33xx/cmd.h b/drivers/net/wireless/ti/cc33xx/cmd.h +index f6e4877cf7b4..e819a92a2122 100644 +--- a/drivers/net/wireless/ti/cc33xx/cmd.h ++++ b/drivers/net/wireless/ti/cc33xx/cmd.h +@@ -137,12 +137,12 @@ enum cc33xx_cmd { + CMD_DOWNLOAD_INI_PARAMS = 37, + CMD_SET_BD_ADDR = 38, + CMD_BLE_COMMANDS = 39, ++ CMD_SET_PS_MODE = 40, + + CMD_LAST_SUPPORTED_COMMAND, + + /* The following commands are legacy and are not yet supported */ + +- CMD_SET_PS_MODE, + CMD_DFS_CHANNEL_CONFIG, + CMD_CONFIG_FWLOGGER, + CMD_START_FWLOGGER, +@@ -258,7 +258,7 @@ struct cc33xx_cmd_role_start { + u8 role_type; + u8 band; + u8 channel; +- ++ u8 is_dfs_channel; + u8 channel_type; + + union { +@@ -316,7 +316,7 @@ struct cc33xx_cmd_role_start { + u8 padding_1[42]; + } __packed ap; + }; +- u8 padding; ++ u8 padding[4]; + } __packed; + + struct cc33xx_cmd_complete_role_start { +@@ -640,8 +640,8 @@ struct cmd_channel_switch { + + u8 channel_type; + u8 band; +- +- u8 padding[2]; ++ u8 is_dfs_channel; ++ u8 padding; + } __packed; + + struct cmd_set_bd_addr { +diff --git a/drivers/net/wireless/ti/cc33xx/conf.h b/drivers/net/wireless/ti/cc33xx/conf.h +index d3881aadeade..cf8d2e09ca71 100644 +--- a/drivers/net/wireless/ti/cc33xx/conf.h ++++ b/drivers/net/wireless/ti/cc33xx/conf.h +@@ -13,10 +13,12 @@ struct cc33xx_conf_header { + } __packed; + + #define CC33XX_CONF_MAGIC 0x10e100ca +-#define CC33XX_CONF_VERSION 0x01070069 ++#define CC33XX_CONF_VERSION 0x010700e1 + #define CC33XX_CONF_MASK 0x0000ffff + #define CC33X_CONF_SIZE (sizeof(struct cc33xx_conf_file)) + ++#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff ++ + enum { + CONF_HW_BIT_RATE_1MBPS = BIT(1), + CONF_HW_BIT_RATE_2MBPS = BIT(2), +@@ -82,11 +84,25 @@ enum { + CONF_PREAMBLE_TYPE_INVALID = 0xFF + }; + +-#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff ++enum { ++ CONF_PS_SCHEME_LEGACY = 0, ++ CONF_PS_SCHEME_UPSD_TRIGGER = 1, ++ CONF_PS_SCHEME_LEGACY_PSPOLL = 2, ++ CONF_PS_SCHEME_SAPSD = 3, ++}; + +-enum conf_rx_queue_type { +- CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */ +- CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */ ++enum { ++ CONF_TX_AC_BE = 0, ++ CONF_TX_AC_BK = 1, ++ CONF_TX_AC_VI = 2, ++ CONF_TX_AC_VO = 3, ++ CONF_TX_AC_CTS2SELF = 4, ++ CONF_TX_AC_ANY_TID = 0xff ++}; ++ ++enum { ++ CONF_BCN_FILT_MODE_DISABLED = 0, ++ CONF_BCN_FILT_MODE_ENABLED = 1 + }; + + struct cc33xx_clk_cfg { +@@ -97,204 +113,8 @@ struct cc33xx_clk_cfg { + u8 swallow; + }; + +-struct conf_rx_settings { +- /* The maximum amount of time, in TU, before the +- * firmware discards the MSDU. +- * +- * Range: 0 - 0xFFFFFFFF +- */ +- u32 rx_msdu_life_time; +- +- /* Packet detection threshold in the PHY. +- * +- * FIXME: details unknown. +- */ +- u32 packet_detection_threshold; +- +- /* The longest time the STA will wait to receive traffic from the AP +- * after a PS-poll has been transmitted. +- * +- * Range: 0 - 200000 +- */ +- u16 ps_poll_timeout; +- /* The longest time the STA will wait to receive traffic from the AP +- * after a frame has been sent from an UPSD enabled queue. +- * +- * Range: 0 - 200000 +- */ +- u16 upsd_timeout; +- +- /* The number of octets in an MPDU, below which an RTS/CTS +- * handshake is not performed. +- * +- * Range: 0 - 4096 +- */ +- u16 rts_threshold; +- +- /* The RX Clear Channel Assessment threshold in the PHY +- * (the energy threshold). +- * +- * Range: ENABLE_ENERGY_D == 0x140A +- * DISABLE_ENERGY_D == 0xFFEF +- */ +- u16 rx_cca_threshold; +- +- /* Occupied Rx mem-blocks number which requires interrupting the host +- * (0 = no buffering, 0xffff = disabled). +- * +- * Range: uint16_t +- */ +- u16 irq_blk_threshold; +- +- /* Rx packets number which requires interrupting the host +- * (0 = no buffering). +- * +- * Range: uint16_t +- */ +- u16 irq_pkt_threshold; +- +- /* Max time in msec the FW may delay RX-Complete interrupt. +- * +- * Range: 1 - 100 +- */ +- u16 irq_timeout; +- +- /* The RX queue type. +- * +- * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY, +- */ +- u8 queue_type; +-} __packed; +- +-#define CONF_TX_MAX_RATE_CLASSES 10 +- +-#define CONF_TX_RATE_MASK_UNSPECIFIED 0 +-#define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | \ +- CONF_HW_BIT_RATE_2MBPS) +-#define CONF_TX_RATE_RETRY_LIMIT 10 +- +-/* basic rates for p2p operations (probe req/resp, etc.) */ +-#define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS +- +-/* Rates supported for data packets when operating as STA/AP. Note the absence +- * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop +- * one. The rate dropped is not mandatory under any operating mode. +- */ +-#define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ +- CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ +- CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ +- CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ +- CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS | \ +- CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ +- CONF_HW_BIT_RATE_54MBPS) +- +-#define CONF_TX_CCK_RATES (CONF_HW_BIT_RATE_1MBPS | \ +- CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ +- CONF_HW_BIT_RATE_11MBPS) +- +-#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ +- CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ +- CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ +- CONF_HW_BIT_RATE_54MBPS) +- +-#define CONF_TX_MCS_RATES (CONF_HW_BIT_RATE_MCS_0 | \ +- CONF_HW_BIT_RATE_MCS_1 | CONF_HW_BIT_RATE_MCS_2 | \ +- CONF_HW_BIT_RATE_MCS_3 | CONF_HW_BIT_RATE_MCS_4 | \ +- CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 | \ +- CONF_HW_BIT_RATE_MCS_7) +- +-/* Default rates for management traffic when operating in AP mode. This +- * should be configured according to the basic rate set of the AP +- */ +-#define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ +- CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) +- +-/* default rates for working as IBSS (11b and OFDM) */ +-#define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ +- CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ +- CONF_HW_BIT_RATE_11MBPS | CONF_TX_OFDM_RATES) +- +-struct conf_tx_rate_class { +- /* The rates enabled for this rate class. +- * +- * Range: CONF_HW_BIT_RATE_* bit mask +- */ +- u32 enabled_rates; +- +- /* The dot11 short retry limit used for TX retries. +- * +- * Range: uint8_t +- */ +- u8 short_retry_limit; +- +- /* The dot11 long retry limit used for TX retries. +- * +- * Range: uint8_t +- */ +- u8 long_retry_limit; +- +- /* Flags controlling the attributes of TX transmission. +- * +- * Range: bit 0: Truncate - when set, FW attempts to send a frame stop +- * when the total valid per-rate attempts have +- * been exhausted; otherwise transmissions +- * will continue at the lowest available rate +- * until the appropriate one of the +- * short_retry_limit, long_retry_limit, +- * dot11_max_transmit_msdu_life_time, or +- * max_tx_life_time, is exhausted. +- * 1: Preamble Override - indicates if the preamble type +- * should be used in TX. +- * 2: Preamble Type - the type of the preamble to be used by +- * the policy (0 - long preamble, 1 - short preamble. +- */ +- u8 aflags; +-} __packed; +- + #define CONF_TX_MAX_AC_COUNT 4 + +-/* Slot number setting to start transmission at PIFS interval */ +-#define CONF_TX_AIFS_PIFS 1 +-/* Slot number setting to start transmission at DIFS interval normal +- * DCF access +- */ +-#define CONF_TX_AIFS_DIFS 2 +- +-enum conf_tx_ac { +- CONF_TX_AC_BE = 0, /* best effort / legacy */ +- CONF_TX_AC_BK = 1, /* background */ +- CONF_TX_AC_VI = 2, /* video */ +- CONF_TX_AC_VO = 3, /* voice */ +- CONF_TX_AC_CTS2SELF = 4, /* fictitious AC, follows AC_VO */ +- CONF_TX_AC_ANY_TID = 0xff +-}; +- +-struct conf_sig_weights { +- /* RSSI from beacons average weight. +- * +- * Range: uint8_t +- */ +- u8 rssi_bcn_avg_weight; +- +- /* RSSI from data average weight. +- * +- * Range: uint8_t +- */ +- u8 rssi_pkt_avg_weight; +- +- /* SNR from beacons average weight. +- * +- * Range: uint8_t +- */ +- u8 snr_bcn_avg_weight; +- +- /* SNR from data average weight. +- * +- * Range: uint8_t +- */ +- u8 snr_pkt_avg_weight; +-} __packed; +- + struct conf_tx_ac_category { + /* The AC class identifier. + * +@@ -355,51 +175,44 @@ struct conf_tx_ac_category { + u8 mu_edca_timer; + } __packed; + +-#define CONF_TX_MAX_TID_COUNT 8 ++struct conf_tx_tid { ++ u8 channel_type; ++ u8 ps_scheme; ++} __packed; + +-/* Allow TX BA on all TIDs but 6,7. These are currently reserved in the FW */ +-#define CONF_TX_BA_ENABLED_TID_BITMAP 0x3F ++#define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS) + +-enum { +- CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/ +- CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/ +- CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/ +-}; ++#define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ ++ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ ++ CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ ++ CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ ++ CONF_HW_BIT_RATE_18MBPS | CONF_HW_BIT_RATE_24MBPS | \ ++ CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ ++ CONF_HW_BIT_RATE_54MBPS) + +-enum { +- CONF_PS_SCHEME_LEGACY = 0, +- CONF_PS_SCHEME_UPSD_TRIGGER = 1, +- CONF_PS_SCHEME_LEGACY_PSPOLL = 2, +- CONF_PS_SCHEME_SAPSD = 3, +-}; ++#define CONF_TX_MCS_RATES (CONF_HW_BIT_RATE_MCS_0 | \ ++ CONF_HW_BIT_RATE_MCS_1 | CONF_HW_BIT_RATE_MCS_2 | \ ++ CONF_HW_BIT_RATE_MCS_3 | CONF_HW_BIT_RATE_MCS_4 | \ ++ CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 | \ ++ CONF_HW_BIT_RATE_MCS_7) + +-enum { +- CONF_ACK_POLICY_LEGACY = 0, +- CONF_ACK_POLICY_NO_ACK = 1, +- CONF_ACK_POLICY_BLOCK = 2, +-}; ++#define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ ++ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ ++ CONF_HW_BIT_RATE_11MBPS | CONF_TX_OFDM_RATES) + +-struct conf_tx_tid { +- u8 queue_id; +- u8 channel_type; +- u8 tsid; +- u8 ps_scheme; +- u8 ack_policy; +- u32 apsd_conf[2]; +-} __packed; ++#define CONF_TX_CCK_RATES (CONF_HW_BIT_RATE_1MBPS | \ ++ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ ++ CONF_HW_BIT_RATE_11MBPS) + +-struct conf_tx_settings { +- /* The TX ED value for TELEC Enable/Disable. +- * +- * Range: 0, 1 +- */ +- u8 tx_energy_detection; ++#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ ++ CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ ++ CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ ++ CONF_HW_BIT_RATE_54MBPS) + +- /* Configuration for rate classes for TX (currently only one +- * rate class supported). Used in non-AP mode. +- */ +- struct conf_tx_rate_class sta_rc_conf; ++#define CONF_BCN_IE_OUI_LEN 3 ++#define CONF_BCN_IE_VER_LEN 2 + ++struct conf_tx_settings { + /* Configuration for access categories for TX rate control. + */ + u8 ac_conf_count; +@@ -416,11 +229,6 @@ struct conf_tx_settings { + */ + u8 max_tx_retries; + +- /* AP-mode - after this number of seconds a connected station is +- * considered inactive. +- */ +- u16 ap_aging_period; +- + /* Configuration for TID parameters. + */ + u8 tid_conf_count; +@@ -434,25 +242,12 @@ struct conf_tx_settings { + struct conf_tx_tid tid_conf6; + struct conf_tx_tid tid_conf7; + +- /* The TX fragmentation threshold. +- * +- * Range: uint16_t +- */ +- u16 frag_threshold; +- + /* Max time in msec the FW may delay frame TX-Complete interrupt. + * + * Range: uint16_t + */ + u16 tx_compl_timeout; + +- /* Completed TX packet count which requires to issue the TX-Complete +- * interrupt. +- * +- * Range: uint16_t +- */ +- u16 tx_compl_threshold; +- + /* The rate used for control messages and scanning on the 2.4GHz band + * + * Range: CONF_HW_BIT_RATE_* bit mask +@@ -465,43 +260,12 @@ struct conf_tx_settings { + */ + u32 basic_rate_5; + +- /* TX retry limits for templates +- */ +- u8 tmpl_short_retry_limit; +- u8 tmpl_long_retry_limit; +- + /* Time in ms for Tx watchdog timer to expire */ + u32 tx_watchdog_timeout; +- +- /* when a slow link has this much packets pending, it becomes a low +- * priority link, scheduling-wise +- */ +- u8 slow_link_thold; +- +- /* when a fast link has this much packets pending, it becomes a low +- * priority link, scheduling-wise +- */ +- u8 fast_link_thold; + } __packed; + +-enum { +- CONF_WAKE_UP_EVENT_BEACON = 0x00, /* Wake on every Beacon */ +- CONF_WAKE_UP_EVENT_DTIM = 0x01, /* Wake on every DTIM */ +- CONF_WAKE_UP_EVENT_N_DTIM = 0x02, /* Wake every Nth DTIM */ +- CONF_WAKE_UP_EVENT_LIMIT = CONF_WAKE_UP_EVENT_N_DTIM, +- /* Not supported: */ +- CONF_WAKE_UP_EVENT_N_BEACONS = 0x03, /* Wake every Nth beacon */ +- CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F +-}; +- + #define CONF_MAX_BCN_FILT_IE_COUNT 32 + +-#define CONF_BCN_RULE_PASS_ON_CHANGE BIT(0) +-#define CONF_BCN_RULE_PASS_ON_APPEARANCE BIT(1) +- +-#define CONF_BCN_IE_OUI_LEN 3 +-#define CONF_BCN_IE_VER_LEN 2 +- + struct conf_bcn_filt_rule { + /* IE number to which to associate a rule. + * +@@ -528,16 +292,6 @@ struct conf_bcn_filt_rule { + u8 version[2]; + } __packed; + +-enum conf_bcn_filt_mode { +- CONF_BCN_FILT_MODE_DISABLED = 0, +- CONF_BCN_FILT_MODE_ENABLED = 1 +-}; +- +-enum conf_bet_mode { +- CONF_BET_MODE_DISABLE = 0, +- CONF_BET_MODE_ENABLE = 1, +-}; +- + struct conf_conn_settings { + /* Enable or disable the beacon filtering. + * +@@ -598,88 +352,11 @@ struct conf_conn_settings { + */ + u32 bss_lose_timeout; + +- /* Beacon receive timeout. +- * +- * Range: uint32_t +- */ +- u32 beacon_rx_timeout; +- +- /* Broadcast receive timeout. +- * +- * Range: uint32_t +- */ +- u32 broadcast_timeout; +- +- /* Enable/disable reception of broadcast packets in power save mode +- * +- * Range: 1 - enable, 0 - disable +- */ +- u8 rx_broadcast_in_ps; +- +- /* Consecutive PS Poll failures before sending event to driver +- * +- * Range: uint8_t +- */ +- u8 ps_poll_threshold; +- +- /* Configuration of signal average weights. +- */ +- struct conf_sig_weights sig_weights; +- +- /* Specifies if beacon early termination procedure is enabled or +- * disabled. +- * +- * Range: CONF_BET_MODE_* +- */ +- u8 bet_enable; +- +- /* Specifies the maximum number of consecutive beacons that may be +- * early terminated. After this number is reached at least one full +- * beacon must be correctly received in FW before beacon ET +- * resumes. +- * +- * Range 0 - 255 +- */ +- u8 bet_max_consecutive; +- +- /* Specifies the maximum number of times to try PSM entry if it fails +- * (if sending the appropriate null-func message fails.) +- * +- * Range 0 - 255 +- */ +- u8 psm_entry_retries; +- +- /* Specifies the maximum number of times to try PSM exit if it fails +- * (if sending the appropriate null-func message fails.) +- * +- * Range 0 - 255 +- */ +- u8 psm_exit_retries; +- +- /* Specifies the maximum number of times to try transmit the PSM entry +- * null-func frame for each PSM entry attempt +- * +- * Range 0 - 255 +- */ +- u8 psm_entry_nullfunc_retries; +- + /* Specifies the dynamic PS timeout in ms that will be used + * by the FW when in AUTO_PS mode + */ + u16 dynamic_ps_timeout; + +- /* Specifies whether dynamic PS should be disabled and PSM forced. +- * This is required for certain WiFi certification tests. +- */ +- u8 forced_ps; +- +- /* Specifies the interval of the connection keep-alive null-func +- * frame in ms. +- * +- * Range: 1000 - 3600000 +- */ +- u32 keep_alive_interval; +- + /* Maximum listen interval supported by the driver in units of beacons. + * + * Range: uint16_t +@@ -696,65 +373,6 @@ struct conf_conn_settings { + u8 suspend_rx_ba_activity; + } __packed; + +-struct conf_itrim_settings { +- /* enable dco itrim */ +- u8 enable; +- +- /* moderation timeout in microsecs from the last TX */ +- u32 timeout; +-} __packed; +- +-enum conf_fast_wakeup { +- CONF_FAST_WAKEUP_ENABLE, +- CONF_FAST_WAKEUP_DISABLE, +-}; +- +-struct conf_pm_config_settings { +- /* Host clock settling time +- * +- * Range: 0 - 30000 us +- */ +- u32 host_clk_settling_time; +- +- /* Host fast wakeup support +- * +- * Range: enum conf_fast_wakeup +- */ +- u8 host_fast_wakeup_support; +-} __packed; +- +-struct conf_roam_trigger_settings { +- /* The minimum interval between two trigger events. +- * +- * Range: 0 - 60000 ms +- */ +- u16 trigger_pacing; +- +- /* The weight for rssi/beacon average calculation +- * +- * Range: 0 - 255 +- */ +- u8 avg_weight_rssi_beacon; +- +- /* The weight for rssi/data frame average calculation +- * +- * Range: 0 - 255 +- */ +- u8 avg_weight_rssi_data; +- +- /* The weight for snr/beacon average calculation +- * +- * Range: 0 - 255 +- */ +- u8 avg_weight_snr_beacon; +- +- /* The weight for snr/data frame average calculation +- * +- * Range: 0 - 255 +- */ +- u8 avg_weight_snr_data; +-} __packed; +- + struct conf_scan_settings { + /* The minimum time to wait on each channel for active scans + * This value will be used whenever there's a connected interface. +@@ -858,78 +476,11 @@ struct conf_sched_scan_settings { + + struct conf_ht_setting { + u8 rx_ba_win_size; +- u8 tx_ba_win_size; +- u16 inactivity_timeout; +- +- /* bitmap of enabled TIDs for TX BA sessions */ +- u8 tx_ba_tid_bitmap; + + /* DEFAULT / WIDE / SISO20 */ + u8 mode; + } __packed; + +-struct conf_memory_settings { +- /* Number of stations supported in IBSS mode */ +- u8 num_stations; +- +- /* Number of ssid profiles used in IBSS mode */ +- u8 ssid_profiles; +- +- /* Number of memory buffers allocated to rx pool */ +- u8 rx_block_num; +- +- /* Minimum number of blocks allocated to tx pool */ +- u8 tx_min_block_num; +- +- /* Disable/Enable dynamic memory */ +- u8 dynamic_memory; +- +- /* Minimum required free tx memory blocks in order to assure optimum +- * performance +- * +- * Range: 0-120 +- */ +- u8 min_req_tx_blocks; +- +- /* Minimum required free rx memory blocks in order to assure optimum +- * performance +- * +- * Range: 0-120 +- */ +- u8 min_req_rx_blocks; +- +- /* Minimum number of mem blocks (free+used) guaranteed for TX +- * +- * Range: 0-120 +- */ +- u8 tx_min; +-} __packed; +- +-struct conf_rx_streaming_settings { +- /* RX Streaming duration (in msec) from last tx/rx +- * +- * Range: uint32_t +- */ +- u32 duration; +- +- /* Bitmap of tids to be polled during RX streaming. +- * (Note: it doesn't look like it really matters) +- * +- * Range: 0x1-0xff +- */ +- u8 queues; +- +- /* RX Streaming interval. +- * (Note:this value is also used as the rx streaming timeout) +- * Range: 0 (disabled), 10 - 100 +- */ +- u8 interval; +- +- /* enable rx streaming also when there is no coex activity +- */ +- u8 always; +-} __packed; +- + struct conf_fwlog { + /* Continuous or on-demand */ + u8 mode; +@@ -953,53 +504,6 @@ struct conf_fwlog { + u8 threshold; + } __packed; + +-#define ACX_RATE_MGMT_NUM_OF_RATES 13 +-struct conf_rate_policy_settings { +- u16 rate_retry_score; +- u16 per_add; +- u16 per_th1; +- u16 per_th2; +- u16 max_per; +- u8 inverse_curiosity_factor; +- u8 tx_fail_low_th; +- u8 tx_fail_high_th; +- u8 per_alpha_shift; +- u8 per_add_shift; +- u8 per_beta1_shift; +- u8 per_beta2_shift; +- u8 rate_check_up; +- u8 rate_check_down; +- u8 rate_retry_policy[13]; +-} __packed; +- +-struct conf_hangover_settings { +- u32 recover_time; +- u8 hangover_period; +- u8 dynamic_mode; +- u8 early_termination_mode; +- u8 max_period; +- u8 min_period; +- u8 increase_delta; +- u8 decrease_delta; +- u8 quiet_time; +- u8 increase_time; +- u8 window_size; +-} __packed; +- +-enum { +- CLOCK_CONFIG_16_2_M = 1, +- CLOCK_CONFIG_16_368_M, +- CLOCK_CONFIG_16_8_M, +- CLOCK_CONFIG_19_2_M, +- CLOCK_CONFIG_26_M, +- CLOCK_CONFIG_32_736_M, +- CLOCK_CONFIG_33_6_M, +- CLOCK_CONFIG_38_468_M, +- CLOCK_CONFIG_52_M, +- +- NUM_CLOCK_CONFIGS, +-}; +- + enum cc33xx_ht_mode { + /* Default - use MIMO, fallback to SISO20 */ + HT_MODE_DEFAULT = 0, +@@ -1011,37 +515,6 @@ enum cc33xx_ht_mode { + HT_MODE_SISO20 = 2, + }; + +-struct conf_ap_sleep_settings { +- /* Duty Cycle (20-80% of staying Awake) for IDLE AP +- * (0: disable) +- */ +- u8 idle_duty_cycle; +- /* Duty Cycle (20-80% of staying Awake) for Connected AP +- * (0: disable) +- */ +- u8 connected_duty_cycle; +- /* Maximum stations that are allowed to be connected to AP +- * (255: no limit) +- */ +- u8 max_stations_thresh; +- /* Timeout till enabling the Sleep Mechanism after data stops +- * [unit: 100 msec] +- */ +- u8 idle_conn_thresh; +-} __packed; +- +-#define CHANNELS_COUNT 39 /* 14 2.4GHz channels, 25 5GHz channels*/ +-#define PER_CHANNEL_REG_RULE_BYTES 13 +-#define REG_RULES_COUNT (CHANNELS_COUNT * PER_CHANNEL_REG_RULE_BYTES) /* 507 */ +- +-/* TX Power limitation for a channel, used for reg domain */ +-struct conf_channel_power_limit { +- u32 reg_lim_0; +- u32 reg_lim_1; +- u32 reg_lim_2; +- u8 reg_lim_3; +-} __packed; +- + struct conf_coex_configuration { + /* Work without Coex HW + * +@@ -1178,7 +651,7 @@ struct cc33xx_core_conf { + u8 wake_up_event; + u8 suspend_listen_interval; + u8 suspend_wake_up_event; +- u8 per_channel_power_limit[507]; ++ u8 per_channel_power_limit[520]; + u32 internal_slowclk_wakeup_earlier; + u32 internal_slowclk_open_window_longer; + u32 external_slowclk_wakeup_earlier; +@@ -1190,14 +663,18 @@ struct cc33xx_core_conf { + u8 mixed_mode_support; + u8 sram_ldo_voltage_trimming; + u32 xtal_settling_time_usec; ++ u8 max_rx_ampdu_len; + struct conf_ant_diversity ant_diversity; + struct conf_iomux_configuration iomux_configuration; + } __packed; + + struct cc33xx_mac_conf { ++ u8 ps_mode; + u8 ps_scheme; + u8 he_enable; + u8 ap_max_num_stations; ++ u8 fw_defrag; ++ u16 rx_memblks_override; + } __packed; + + struct cc33xx_phy_conf { +@@ -1214,24 +691,18 @@ struct cc33xx_phy_conf { + u8 num_of_antennas; + u8 reg_domain; + u16 calib_period; ++ u8 tx_psat_compensation_2_4GHz; ++ u8 tx_psat_compensation_5GHz; ++ u8 reserved_2; + } __packed; + + struct cc33xx_host_conf { +- struct conf_rx_settings rx; + struct conf_tx_settings tx; + struct conf_conn_settings conn; +- struct conf_itrim_settings itrim; +- struct conf_pm_config_settings pm_config; +- struct conf_roam_trigger_settings roam_trigger; + struct conf_scan_settings scan; + struct conf_sched_scan_settings sched_scan; + struct conf_ht_setting ht; +- struct conf_memory_settings mem; +- struct conf_rx_streaming_settings rx_streaming; + struct conf_fwlog fwlog; +- struct conf_rate_policy_settings rate; +- struct conf_hangover_settings hangover; +- struct conf_ap_sleep_settings ap_sleep; + + } __packed; + +diff --git a/drivers/net/wireless/ti/cc33xx/debugfs.c b/drivers/net/wireless/ti/cc33xx/debugfs.c +new file mode 100644 +index 000000000000..34ccae0cbf60 +--- /dev/null ++++ b/drivers/net/wireless/ti/cc33xx/debugfs.c +@@ -0,0 +1,2233 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * This file is part of cc33xx ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * ++ * Contact: Luciano Coelho ++ */ ++ ++#include "debugfs.h" ++#include "acx.h" ++#include "ps.h" ++#include "io.h" ++#include "tx.h" ++#include "../net/mac80211/ieee80211_i.h" ++ ++ ++#define CC33XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ ++ DEBUGFS_FWSTATS_FILE(a, b, c, cc33xx_acx_statistics) ++#define CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \ ++ DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, cc33xx_acx_statistics) ++ ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_non_ctrl, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_ctrl, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, error_frame_during_protection, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, null_frame_tx_start, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, null_frame_cts_start, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, bar_retry, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, num_frame_cts_nul_flid, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, tx_abort_failure, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, tx_resume_failure, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, rx_cmplt_db_overflow_cnt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_rx_exch, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_tx_exch, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_tx, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, elp_while_nvic_pending, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, rx_excessive_frame_len, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, burst_mismatch, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(error, tbc_exch_mismatch, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_stop, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(tx, tx_retry_per_rate, ++ NUM_OF_RATES_INDEXES); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cfe1, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, tx_cfe2, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_rts_timeout, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread, ++ PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_time_count, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_time_avg, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_cycle_avg, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, sleep_percent, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_active_conf, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_user_conf, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pwr, ap_sleep_counter, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(rx_rate, rx_frames_per_rates, 50); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_rate, ++ AGGR_STATS_TX_AGG); ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_len, ++ AGGR_STATS_TX_AGG); ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size, ++ AGGR_STATS_RX_SIZE_LEN); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_rx_xfer_swi, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full, ++ PIPE_STATS_HW_FIFO); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(diversity, num_of_packets_per_ant, ++ DIVERSITY_STATS_NUM_OF_ANT); ++CC33XX_DEBUGFS_FWSTATS_FILE(diversity, total_num_of_toggles, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, irq_thr_low, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, irq_thr_high, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, tx_stop, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, tx_resume, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, false_irq, "%u"); ++CC33XX_DEBUGFS_FWSTATS_FILE(thermal, adc_source_unexpected, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE_ARRAY(calib, fail_count, ++ CC33XX_NUM_OF_CALIBRATIONS_ERRORS); ++CC33XX_DEBUGFS_FWSTATS_FILE(calib, calib_count, "%u"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(roaming, rssi_level, "%d"); ++ ++CC33XX_DEBUGFS_FWSTATS_FILE(dfs, num_of_radar_detections, "%d"); ++ ++struct cc33xx_cmd_dfs_radar_debug { ++ struct cc33xx_cmd_header header; ++ ++ u8 channel; ++ u8 padding[3]; ++} __packed; ++ ++/* ms */ ++#define CC33XX_DEBUGFS_STATS_LIFETIME 1000 ++#define MAX_VERSIONS_LEN 59 ++#define MAX_VERSIONS_EXTENDED_LEN 86 ++ ++int cc33xx_cmd_radar_detection_debug(struct cc33xx *wl, u8 channel) ++{ ++ struct cc33xx_cmd_dfs_radar_debug *cmd; ++ int ret = 0; ++ ++ cc33xx_debug(DEBUG_CMD, "cmd radar detection debug (chan %d)", ++ channel); ++ ++ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); ++ if (!cmd) ++ return -ENOMEM; ++ ++ cmd->channel = channel; ++ ++ ret = cc33xx_cmd_send(wl, CMD_DFS_RADAR_DETECTION_DEBUG, ++ cmd, sizeof(*cmd), 0); ++ if (ret < 0) { ++ cc33xx_error("failed to send radar detection debug command"); ++ goto out_free; ++ } ++ ++out_free: ++ kfree(cmd); ++ return ret; ++} ++ ++static ssize_t conf_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_conf_header header; ++ char *buf, *pos; ++ size_t len; ++ int ret; ++ ++ len = CC33X_CONF_SIZE; ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ header.magic = cpu_to_le32(CC33XX_CONF_MAGIC); ++ header.version = cpu_to_le32(CC33XX_CONF_VERSION); ++ header.checksum = 0; ++ ++ mutex_lock(&wl->mutex); ++ ++ pos = buf; ++ memcpy(pos, &header, sizeof(header)); ++ pos += sizeof(header); ++ memcpy(pos, &wl->conf, sizeof(wl->conf)); ++ ++ mutex_unlock(&wl->mutex); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); ++ ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations conf_ops = { ++ .read = conf_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t clear_fw_stats_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ return count; ++} ++ ++static const struct file_operations clear_fw_stats_ops = { ++ .write = clear_fw_stats_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t radar_detection_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ u8 channel; ++ ++ ret = kstrtou8_from_user(user_buf, count, 10, &channel); ++ if (ret < 0) { ++ cc33xx_warning("illegal channel"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) ++ goto out; ++ ++ ret = cc33xx_cmd_radar_detection_debug(wl, channel); ++ if (ret < 0) ++ count = ret; ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations radar_detection_ops = { ++ .write = radar_detection_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t dynamic_fw_traces_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ if (ret < 0) ++ return ret; ++ ++ wl->dynamic_fw_traces = value; ++ return count; ++} ++ ++static ssize_t dynamic_fw_traces_read(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(userbuf, count, ppos, ++ "%d\n", wl->dynamic_fw_traces); ++} ++ ++static const struct file_operations dynamic_fw_traces_ops = { ++ .read = dynamic_fw_traces_read, ++ .write = dynamic_fw_traces_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS ++static ssize_t radar_debug_mode_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal radar_debug_mode value!"); ++ return -EINVAL; ++ } ++ ++ /* valid values: 0/1 */ ++ if (!(value == 0 || value == 1)) { ++ cc33xx_warning("value is not in valid!"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->radar_debug_mode = value; ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) ++ goto out; ++ ++ cc33xx_for_each_wlvif_ap(wl, wlvif) { ++ wlcore_cmd_generic_cfg(wl, wlvif, ++ WLCORE_CFG_FEATURE_RADAR_DEBUG, ++ wl->radar_debug_mode, 0); ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static ssize_t radar_debug_mode_read(struct file *file, ++ char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ return cc33xx_format_buffer(userbuf, count, ppos, ++ "%d\n", wl->radar_debug_mode); ++} ++ ++static const struct file_operations radar_debug_mode_ops = { ++ .write = radar_debug_mode_write, ++ .read = radar_debug_mode_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static void cc33xx_debugfs_add_files_helper(struct dentry *moddir) ++{ ++ DEBUGFS_ADD(radar_debug_mode, moddir); ++} ++#else ++static void cc33xx_debugfs_add_files_helper(struct dentry *moddir) {} ++#endif /* CFG80211_CERTIFICATION_ONUS */ ++ ++ ++/* debugfs macros idea from mac80211 */ ++int cc33xx_format_buffer(char __user *userbuf, size_t count, ++ loff_t *ppos, char *fmt, ...) ++{ ++ va_list args; ++ char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; ++ int res; ++ ++ va_start(args, fmt); ++ res = vscnprintf(buf, sizeof(buf), fmt, args); ++ va_end(args); ++ ++ return simple_read_from_buffer(userbuf, count, ppos, buf, res); ++} ++ ++void cc33xx_debugfs_update_stats(struct cc33xx *wl) ++{ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) ++ goto out; ++ ++ ++ if (!wl->plt && time_after(jiffies, wl->stats.fw_stats_update + ++ msecs_to_jiffies(CC33XX_DEBUGFS_STATS_LIFETIME))) { ++ wl->stats.fw_stats_update = jiffies; ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++} ++ ++DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count); ++DEBUGFS_READONLY_FILE(excessive_retries, "%u", wl->stats.excessive_retries); ++ ++static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ u32 queue_len; ++ char buf[20]; ++ int res; ++ ++ queue_len = cc33xx_tx_total_queue_count(wl); ++ ++ res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); ++ return simple_read_from_buffer(userbuf, count, ppos, buf, res); ++} ++ ++static const struct file_operations tx_queue_len_ops = { ++ .read = tx_queue_len_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++#define CC33XX_CONF_DEBUGFS(param, conf_sub_struct, \ ++ min_val, max_val, write_handler_locked, \ ++ write_handler_arg) \ ++ static ssize_t param##_read(struct file *file, \ ++ char __user *user_buf, \ ++ size_t count, loff_t *ppos) \ ++ { \ ++ struct cc33xx *wl = file->private_data; \ ++ return cc33xx_format_buffer(user_buf, count, \ ++ ppos, "%d\n", \ ++ wl->conf.host_conf.conf_sub_struct.param); \ ++ } \ ++ \ ++ static ssize_t param##_write(struct file *file, \ ++ const char __user *user_buf, \ ++ size_t count, loff_t *ppos) \ ++ { \ ++ struct cc33xx *wl = file->private_data; \ ++ unsigned long value; \ ++ int ret; \ ++ \ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); \ ++ if (ret < 0) { \ ++ cc33xx_warning("illegal value for " #param); \ ++ return -EINVAL; \ ++ } \ ++ \ ++ if (value < min_val || value > max_val) { \ ++ cc33xx_warning(#param " is not in valid range"); \ ++ return -ERANGE; \ ++ } \ ++ \ ++ mutex_lock(&wl->mutex); \ ++ wl->conf.host_conf.conf_sub_struct.param = value; \ ++ \ ++ write_handler_locked(wl, value, write_handler_arg); \ ++ \ ++ mutex_unlock(&wl->mutex); \ ++ return count; \ ++ } \ ++ \ ++ static const struct file_operations param##_ops = { \ ++ .read = param##_read, \ ++ .write = param##_write, \ ++ .open = simple_open, \ ++ .llseek = default_llseek, \ ++ }; ++ ++static ssize_t gpio_power_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ bool state = test_bit(CC33XX_FLAG_GPIO_POWER, &wl->flags); ++ ++ int res; ++ char buf[10]; ++ ++ res = scnprintf(buf, sizeof(buf), "%d\n", state); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, res); ++} ++ ++static ssize_t gpio_power_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in gpio_power"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (value) ++ cc33xx_power_on(wl); ++ else ++ cc33xx_power_off(wl); ++ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations gpio_power_ops = { ++ .read = gpio_power_read, ++ .write = gpio_power_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t start_recovery_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ mutex_lock(&wl->mutex); ++ cc33xx_queue_recovery_work(wl); ++ mutex_unlock(&wl->mutex); ++ ++ return count; ++} ++ ++static const struct file_operations start_recovery_ops = { ++ .write = start_recovery_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t dynamic_ps_timeout_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->conf.host_conf.conn.dynamic_ps_timeout); ++} ++ ++static ssize_t dynamic_ps_timeout_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in dynamic_ps"); ++ return -EINVAL; ++ } ++ ++ if (value < 1 || value > 65535) { ++ cc33xx_warning("dynamic_ps_timeout is not in valid range"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.host_conf.conn.dynamic_ps_timeout = value; ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) ++ goto out; ++ ++ /* In case we're already in PSM, trigger it again to set new timeout ++ * immediately without waiting for re-association ++ */ ++ ++ cc33xx_for_each_wlvif_sta(wl, wlvif) { ++ cc33xx_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations dynamic_ps_timeout_ops = { ++ .read = dynamic_ps_timeout_read, ++ .write = dynamic_ps_timeout_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ps_mode_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->conf.mac.ps_mode); ++} ++ ++static ssize_t ps_mode_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in ps_mode"); ++ return -EINVAL; ++ } ++ ++ if (value < 0 || value > 2) { ++ cc33xx_warning("ps_mode should be either 0 or 1 or 2"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (wl->conf.mac.ps_mode == value) ++ goto out; ++ ++ wl->conf.mac.ps_mode = value; ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) ++ goto out; ++ ++ /* In case we're already in PSM, trigger it again to switch mode ++ * immediately without waiting for re-association ++ */ ++ ++ cc33xx_for_each_wlvif_sta(wl, wlvif) { ++ cc33xx_ps_set_mode(wl, wlvif, value); ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations ps_mode_ops = { ++ .read = ps_mode_read, ++ .write = ps_mode_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t split_scan_timeout_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->conf.host_conf.scan.split_scan_timeout / 1000); ++} ++ ++static ssize_t split_scan_timeout_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in split_scan_timeout"); ++ return -EINVAL; ++ } ++ ++ if (value == 0) ++ cc33xx_info("split scan will be disabled"); ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.host_conf.scan.split_scan_timeout = value * 1000; ++ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations split_scan_timeout_ops = { ++ .read = split_scan_timeout_read, ++ .write = split_scan_timeout_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++#define DRIVER_STATE_BUF_LEN 1024 ++ ++#define DRIVER_STATE_PRINT(x, fmt) \ ++ (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ ++ #x " = " fmt "\n", wl->x)) ++ ++#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...) \ ++ (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ ++ #x " = " fmt "\n", args)) ++ ++#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") ++#define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") ++#define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") ++#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") ++#define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") ++ ++static ssize_t driver_state_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int res = 0; ++ ssize_t ret; ++ char *buf; ++ struct cc33xx_vif *wlvif; ++ ++ ++ buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mutex_lock(&wl->mutex); ++ ++ cc33xx_for_each_wlvif_sta(wl, wlvif) { ++ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ++ continue; ++ ++ DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, ++ wlvif->p2p ? "P2P-CL" : "STA"); ++ } ++ ++ cc33xx_for_each_wlvif_ap(wl, wlvif) ++ DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, ++ wlvif->p2p ? "P2P-GO" : "AP"); ++ ++ DRIVER_STATE_PRINT_INT(tx_blocks_available); ++ DRIVER_STATE_PRINT_INT(tx_allocated_blocks); ++ DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); ++ DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]); ++ DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]); ++ DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]); ++ DRIVER_STATE_PRINT_INT(tx_frames_cnt); ++ DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]); ++ DRIVER_STATE_PRINT_INT(tx_queue_count[0]); ++ DRIVER_STATE_PRINT_INT(tx_queue_count[1]); ++ DRIVER_STATE_PRINT_INT(tx_queue_count[2]); ++ DRIVER_STATE_PRINT_INT(tx_queue_count[3]); ++ DRIVER_STATE_PRINT_LHEX(flags); ++ DRIVER_STATE_PRINT_INT(rx_counter); ++ DRIVER_STATE_PRINT_INT(state); ++ DRIVER_STATE_PRINT_INT(band); ++ DRIVER_STATE_PRINT_INT(power_level); ++ DRIVER_STATE_PRINT_INT(enable_11a); ++ DRIVER_STATE_PRINT_LHEX(ap_fw_ps_map); ++ DRIVER_STATE_PRINT_LHEX(ap_ps_map); ++ DRIVER_STATE_PRINT_HEX(quirks); ++ /* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */ ++ ++ mutex_unlock(&wl->mutex); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, res); ++ kfree(buf); ++ return ret; ++} ++ ++#undef DRIVER_STATE_PRINT_INT ++#undef DRIVER_STATE_PRINT_LONG ++#undef DRIVER_STATE_PRINT_HEX ++#undef DRIVER_STATE_PRINT_LHEX ++#undef DRIVER_STATE_PRINT_STR ++#undef DRIVER_STATE_PRINT ++#undef DRIVER_STATE_BUF_LEN ++ ++static const struct file_operations driver_state_ops = { ++ .read = driver_state_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t vifs_state_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif; ++ int ret, res = 0; ++ const int buf_size = 4096; ++ char *buf; ++ char tmp_buf[64]; ++ ++ buf = kzalloc(buf_size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mutex_lock(&wl->mutex); ++ ++#define VIF_STATE_PRINT(x, fmt) \ ++ (res += scnprintf(buf + res, buf_size - res, \ ++ #x " = " fmt "\n", wlvif->x)) ++ ++#define VIF_STATE_PRINT_LONG(x) VIF_STATE_PRINT(x, "%ld") ++#define VIF_STATE_PRINT_INT(x) VIF_STATE_PRINT(x, "%d") ++#define VIF_STATE_PRINT_STR(x) VIF_STATE_PRINT(x, "%s") ++#define VIF_STATE_PRINT_LHEX(x) VIF_STATE_PRINT(x, "0x%lx") ++#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx") ++#define VIF_STATE_PRINT_HEX(x) VIF_STATE_PRINT(x, "0x%x") ++ ++#define VIF_STATE_PRINT_NSTR(x, len) \ ++ do { \ ++ memset(tmp_buf, 0, sizeof(tmp_buf)); \ ++ memcpy(tmp_buf, wlvif->x, \ ++ min_t(u8, len, sizeof(tmp_buf) - 1)); \ ++ res += scnprintf(buf + res, buf_size - res, \ ++ #x " = %s\n", tmp_buf); \ ++ } while (0) ++ ++ cc33xx_for_each_wlvif(wl, wlvif) { ++ VIF_STATE_PRINT_INT(role_id); ++ VIF_STATE_PRINT_INT(bss_type); ++ VIF_STATE_PRINT_LHEX(flags); ++ VIF_STATE_PRINT_INT(p2p); ++ VIF_STATE_PRINT_INT(dev_role_id); ++ VIF_STATE_PRINT_INT(dev_hlid); ++ ++ if (wlvif->bss_type == BSS_TYPE_STA_BSS || ++ wlvif->bss_type == BSS_TYPE_IBSS) { ++ VIF_STATE_PRINT_INT(sta.hlid); ++ VIF_STATE_PRINT_INT(sta.basic_rate_idx); ++ VIF_STATE_PRINT_INT(sta.ap_rate_idx); ++ VIF_STATE_PRINT_INT(sta.p2p_rate_idx); ++ VIF_STATE_PRINT_INT(sta.qos); ++ } else { ++ VIF_STATE_PRINT_INT(ap.global_hlid); ++ VIF_STATE_PRINT_INT(ap.bcast_hlid); ++ VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]); ++ VIF_STATE_PRINT_INT(ap.mgmt_rate_idx); ++ VIF_STATE_PRINT_INT(ap.bcast_rate_idx); ++ VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]); ++ VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]); ++ VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]); ++ VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]); ++ } ++ VIF_STATE_PRINT_INT(last_tx_hlid); ++ VIF_STATE_PRINT_INT(tx_queue_count[0]); ++ VIF_STATE_PRINT_INT(tx_queue_count[1]); ++ VIF_STATE_PRINT_INT(tx_queue_count[2]); ++ VIF_STATE_PRINT_INT(tx_queue_count[3]); ++ VIF_STATE_PRINT_LHEX(links_map[0]); ++ VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len); ++ VIF_STATE_PRINT_INT(band); ++ VIF_STATE_PRINT_INT(channel); ++ VIF_STATE_PRINT_HEX(bitrate_masks[0]); ++ VIF_STATE_PRINT_HEX(bitrate_masks[1]); ++ VIF_STATE_PRINT_HEX(basic_rate_set); ++ VIF_STATE_PRINT_HEX(basic_rate); ++ VIF_STATE_PRINT_HEX(rate_set); ++ VIF_STATE_PRINT_INT(beacon_int); ++ VIF_STATE_PRINT_INT(default_key); ++ VIF_STATE_PRINT_INT(aid); ++ VIF_STATE_PRINT_INT(psm_entry_retry); ++ VIF_STATE_PRINT_INT(power_level); ++ VIF_STATE_PRINT_INT(rssi_thold); ++ VIF_STATE_PRINT_INT(last_rssi_event); ++ VIF_STATE_PRINT_INT(ba_support); ++ VIF_STATE_PRINT_INT(ba_allowed); ++ VIF_STATE_PRINT_LLHEX(total_freed_pkts); ++ } ++ ++#undef VIF_STATE_PRINT_INT ++#undef VIF_STATE_PRINT_LONG ++#undef VIF_STATE_PRINT_HEX ++#undef VIF_STATE_PRINT_LHEX ++#undef VIF_STATE_PRINT_LLHEX ++#undef VIF_STATE_PRINT_STR ++#undef VIF_STATE_PRINT_NSTR ++#undef VIF_STATE_PRINT ++ ++ mutex_unlock(&wl->mutex); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, res); ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations vifs_state_ops = { ++ .read = vifs_state_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++enum { ++ CONF_WAKE_UP_EVENT_BEACON = 0x00, /* Wake on every Beacon */ ++ CONF_WAKE_UP_EVENT_DTIM = 0x01, /* Wake on every DTIM */ ++ CONF_WAKE_UP_EVENT_N_DTIM = 0x02, /* Wake every Nth DTIM */ ++ CONF_WAKE_UP_EVENT_LIMIT = CONF_WAKE_UP_EVENT_N_DTIM, ++ /* Not supported: */ ++ CONF_WAKE_UP_EVENT_N_BEACONS = 0x03, /* Wake every Nth beacon */ ++ CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F ++}; ++ ++static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ u8 value; ++ ++ if (wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_DTIM || ++ wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) ++ value = wl->conf.core.listen_interval; ++ else ++ value = 0; ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value); ++} ++ ++static ssize_t dtim_interval_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif = NULL; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ struct ieee80211_vif *vif = NULL; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value for dtim_interval"); ++ return -EINVAL; ++ } ++ ++ if (value < 1 || value > 10) { ++ cc33xx_warning("dtim value is not in valid range"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.core.listen_interval = value; ++ ++ if (value == 1) ++ wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_DTIM; ++ else ++ wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; ++ ++ cc33xx_for_each_wlvif_sta(wl, wlvif) { ++ if (!cc33xx_is_p2p_mgmt(wlvif)) ++ { ++ vif = cc33xx_wlvif_to_vif(wlvif); ++ sdata = vif_to_sdata(vif); ++ cc33xx_debug(DEBUG_CMD, "Setting LSI on interface %s", ++ sdata->name); ++ ret = cc33xx_acx_wake_up_conditions(wl, wlvif, ++ wl->conf.core.wake_up_event, ++ wl->conf.core.listen_interval); ++ if (ret < 0) { ++ vif = cc33xx_wlvif_to_vif(wlvif); ++ sdata = vif_to_sdata(vif); ++ cc33xx_warning("Failed to set LSI on " ++ "interface %s", sdata->name); ++ return ret; ++ } ++ } ++ } ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations dtim_interval_ops = { ++ .read = dtim_interval_read, ++ .write = dtim_interval_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t suspend_dtim_interval_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ u8 value; ++ ++ if (wl->conf.core.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM || ++ wl->conf.core.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) ++ value = wl->conf.core.suspend_listen_interval; ++ else ++ value = 0; ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value); ++} ++ ++static ssize_t suspend_dtim_interval_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value for suspend_dtim_interval"); ++ return -EINVAL; ++ } ++ ++ if (value < 1 || value > 10) { ++ cc33xx_warning("suspend_dtim value is not in valid range"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.core.suspend_listen_interval = value; ++ /* for some reason there are different event types for 1 and >1 */ ++ if (value == 1) ++ wl->conf.core.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM; ++ else ++ wl->conf.core.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; ++ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations suspend_dtim_interval_ops = { ++ .read = suspend_dtim_interval_read, ++ .write = suspend_dtim_interval_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t beacon_interval_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ u8 value; ++ ++ if (wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_BEACON || ++ wl->conf.core.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS) ++ value = wl->conf.core.listen_interval; ++ else ++ value = 0; ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", value); ++} ++ ++static ssize_t beacon_interval_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 10, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value for beacon_interval"); ++ return -EINVAL; ++ } ++ ++ if (value < 1 || value > 255) { ++ cc33xx_warning("beacon interval value is not in valid range"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.core.listen_interval = value; ++ /* for some reason there are different event types for 1 and >1 */ ++ if (value == 1) ++ wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_BEACON; ++ else ++ wl->conf.core.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS; ++ ++ /* ++ * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only ++ * take effect on the next time we enter psm. ++ */ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations beacon_interval_ops = { ++ .read = beacon_interval_read, ++ .write = beacon_interval_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t beacon_filtering_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_vif *wlvif; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value for beacon_filtering!"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ cc33xx_for_each_wlvif(wl, wlvif) { ++ ret = cc33xx_acx_beacon_filter_opt(wl, wlvif, !!value); ++ } ++ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations beacon_filtering_ops = { ++ .write = beacon_filtering_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t fw_stats_raw_read(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ cc33xx_debugfs_update_stats(wl); ++ ++ return simple_read_from_buffer(userbuf, count, ppos, ++ wl->stats.fw_stats, ++ sizeof(struct cc33xx_acx_statistics)); ++} ++ ++static const struct file_operations fw_stats_raw_ops = { ++ .read = fw_stats_raw_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t sleep_auth_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->sleep_auth); ++} ++ ++static ssize_t sleep_auth_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in sleep_auth"); ++ return -EINVAL; ++ } ++ ++ if (value > CC33XX_PSM_MAX) { ++ cc33xx_warning("sleep_auth must be between 0 and %d", ++ CC33XX_PSM_MAX); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.host_conf.conn.sta_sleep_auth = value; ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ /* this will show up on "read" in case we are off */ ++ wl->sleep_auth = value; ++ goto out; ++ } ++ ++ cc33xx_acx_sleep_auth(wl, value); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations sleep_auth_ops = { ++ .read = sleep_auth_read, ++ .write = sleep_auth_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++//ble_enable ++static ssize_t ble_enable_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->ble_enable); ++} ++ ++static ssize_t ble_enable_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ ++ if (value == wl->ble_enable) { ++ cc33xx_warning("ble_enable is already %d",wl->ble_enable); ++ return -EINVAL; ++ } ++ ++ if (value != 1) { ++ cc33xx_warning("illegal value in ble_enable (only value allowed is is 1)"); ++ cc33xx_warning("ble_enable cant be disabled after being enabled."); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ /* this will show up on "read" in case we are off */ ++ wl->ble_enable = value; ++ goto out; ++ } ++ ++ cc33xx_ble_enable(wl, value); ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++//ble_enable ++ ++static const struct file_operations ble_enable_ops = { ++ .read = ble_enable_read, ++ .write = ble_enable_write, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t set_tsf_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%llx\n", 0LL); ++} ++ ++static ssize_t set_tsf_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long long value; ++ int ret; ++ ++ ret = kstrtoull_from_user(user_buf, count, 0, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in set_tsf"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ cc33xx_acx_set_tsf(wl, value); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations set_tsf_ops = { ++ .open = simple_open, ++ .read = set_tsf_read, ++ .write = set_tsf_write, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t twt_action_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d %d %d %d %d\n", ++ wl->min_wake_duration_usec, ++ wl->min_wake_interval_mantissa, wl->min_wake_interval_exponent, ++ wl->max_wake_interval_mantissa, wl->max_wake_interval_exponent); ++} ++ ++static ssize_t twt_action_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ int min_wake_duration_usec = 0; ++ int min_wake_interval_mantissa = 0; ++ int min_wake_interval_exponent = 0; ++ int max_wake_interval_mantissa = 0; ++ int max_wake_interval_exponent = 0; ++ int twt_action_type; ++ u8 valid_params; ++ int ret; ++ int arg_count; ++ char* buffer; ++ ++ buffer = kzalloc(count, GFP_KERNEL); ++ if (!buffer) { ++ ret = -ENOMEM; ++ cc33xx_warning("error in twt_action: %d", ret); ++ return ret; ++ } ++ ++ ret = strncpy_from_user(buffer, user_buf, count); ++ if(-EFAULT == ret){ ++ cc33xx_warning("error in twt_action: %d", ret); ++ kfree(buffer); ++ return ret; ++ } ++ ++ arg_count = sscanf(buffer, "%d %d %d %d %d %d", &twt_action_type, &min_wake_duration_usec, ++ &min_wake_interval_mantissa, &min_wake_interval_exponent, ++ &max_wake_interval_mantissa, &max_wake_interval_exponent); ++ ++ kfree(buffer); ++ ++#define TWT_ACTION_SETUP (1) ++#define TWT_ACTION_SUSPEND (2) ++#define TWT_ACTION_RESUME (3) ++#define TWT_ACTION_TERMINATE (4) ++ ++ valid_params = 0; ++ if( (twt_action_type == TWT_ACTION_SETUP) && (arg_count > 1) ) ++ { ++ if( min_wake_duration_usec < 256) ++ { ++ cc33xx_warning("error in twt_action: duration cannot be under 256 "); ++ return count; ++ } ++ ++ if(min_wake_interval_mantissa <= 0) ++ { ++ cc33xx_warning("error in twt_action: interval mantissa must be over 0 "); ++ return count; ++ } ++ if((min_wake_interval_exponent < 0) || (max_wake_interval_mantissa < 0 ) || (max_wake_interval_exponent < 0 )) ++ { ++ cc33xx_warning("error in twt_action: negative value not allowed "); ++ return count; ++ } ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ // valid input is twt_action [min_wake_duration_usec min_wake_interval_mantissa ++ // min_wake_interval_exponent [max_wake_interval_mantissa max_wake_interval_exponent]] ++ if(twt_action_type != TWT_ACTION_SETUP && arg_count != 1) ++ { ++ cc33xx_warning("illegal arguments in twt_action"); ++ cc33xx_warning("twt_action_type: %d",twt_action_type); ++ goto out; ++ } ++ switch (twt_action_type) ++ { ++ case TWT_ACTION_SETUP:{ ++ if(arg_count == 1) ++ { ++ ++ } ++ else if(arg_count == 4) ++ { ++ valid_params |= (MIN_WAKE_DURATION_VALID | MIN_WAKE_INTERVAL_MANTISSA_VALID | ++ MIN_WAKE_INTERVAL_EXPONENT_VALID); ++ } ++ else if(arg_count == 6) ++ { ++ valid_params |= (MIN_WAKE_DURATION_VALID | MIN_WAKE_INTERVAL_MANTISSA_VALID | ++ MIN_WAKE_INTERVAL_EXPONENT_VALID | MAX_WAKE_INTERVAL_MANTISSA_VALID | ++ MAX_WAKE_INTERVAL_EXPONENT_VALID); ++ } ++ else ++ { ++ cc33xx_warning("illegal number of params for twt action setup"); ++ goto out; ++ } ++ ++ ret = cc33xx_acx_twt_setup(wl, min_wake_duration_usec, ++ min_wake_interval_mantissa, min_wake_interval_exponent, ++ max_wake_interval_mantissa, max_wake_interval_exponent, ++ valid_params); ++ ++ break; ++ } ++ case TWT_ACTION_SUSPEND: { ++ ++ ret = cc33xx_acx_twt_suspend(wl); ++ break; ++ } ++ case TWT_ACTION_RESUME: { ++ ++ ret = cc33xx_acx_twt_resume(wl); ++ ++ break; ++ } ++ case TWT_ACTION_TERMINATE: { ++ ++ ret = cc33xx_acx_twt_terminate(wl); ++ ++ break; ++ } ++ ++ default : { ++ cc33xx_warning("illegal twt command"); ++ goto out; ++ } ++ } ++ ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations twt_action_ops = { ++ .open = simple_open, ++ .read = twt_action_read, ++ .write = twt_action_write, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t dev_mem_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ return 0; ++} ++ ++static ssize_t dev_mem_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ return 0; ++} ++ ++static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig) ++{ ++ /* only requests of dword-aligned size and offset are supported */ ++ if (offset % 4) ++ return -EINVAL; ++ ++ return no_seek_end_llseek(file, offset, orig); ++} ++ ++static const struct file_operations dev_mem_ops = { ++ .open = simple_open, ++ .read = dev_mem_read, ++ .write = dev_mem_write, ++ .llseek = dev_mem_seek, ++}; ++ ++static ssize_t fw_logger_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->conf.host_conf.fwlog.output); ++} ++ ++static ssize_t fw_logger_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned long value; ++ int ret; ++ ++ ret = kstrtoul_from_user(user_buf, count, 0, &value); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in fw_logger"); ++ return -EINVAL; ++ } ++ ++ if ((value > 2) || (value == 0)) { ++ cc33xx_warning("fw_logger value must be 1-UART 2-SDIO"); ++ return -ERANGE; ++ } ++ ++ if (wl->conf.host_conf.fwlog.output == 0) { ++ cc33xx_warning("invalid operation - fw logger disabled by default, " ++ "please change mode via wlconf"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ wl->conf.host_conf.fwlog.output = value; ++ ++ cc33xx_cmd_config_fwlog(wl); ++ ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations fw_logger_ops = { ++ .open = simple_open, ++ .read = fw_logger_read, ++ .write = fw_logger_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t antenna_select_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ++ wl->antenna_selection); ++} ++ ++static ssize_t antenna_select_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ u8 selection; ++ ++ ret = kstrtou8_from_user(user_buf, count, 0, &selection); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in antenna_select"); ++ return -EINVAL; ++ } ++ ++ if (selection > 1) { ++ cc33xx_warning("selection should be either 0 or 1"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ ret = cc33xx_acx_set_antenna_select(wl, selection); ++ if (ret == 0) { ++ wl->antenna_selection = selection; ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations antenna_select_ops = { ++ .open = simple_open, ++ .read = antenna_select_read, ++ .write = antenna_select_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t get_versions_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_acx_fw_versions *fw_ver = wl->fw_ver; ++ ++ char all_versions_str [MAX_VERSIONS_LEN]; ++ ++ sprintf(all_versions_str, ++ "Firmware Version: %u.%u.%u", ++ fw_ver->major_version, fw_ver->minor_version, fw_ver->api_version); ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%s\n", ++ all_versions_str); ++} ++ ++static const struct file_operations get_versions_ops = { ++ .read = get_versions_read, ++ .open = simple_open, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t trigger_fw_assert_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ cc33xx_acx_trigger_fw_assert(wl); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations trigger_fw_assert_ops = { ++ .open = simple_open, ++ .write = trigger_fw_assert_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t burst_mode_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, ++ "%d\n", wl->burst_disable); ++} ++ ++static ssize_t burst_mode_write(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ u8 burst_disable; ++ ++ ret = kstrtou8_from_user(user_buf, count, 0, &burst_disable); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in burst_mode"); ++ return -EINVAL; ++ } ++ ++ if (burst_disable > 1) { ++ cc33xx_warning("burst_disable should be either 0 or 1"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ ret = cc33xx_acx_burst_mode_cfg(wl, burst_disable); ++ if (ret == 0) { ++ wl->burst_disable = burst_disable; ++ } ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations burst_mode_ops = { ++ .open = simple_open, ++ .read = burst_mode_read, ++ .write = burst_mode_write, ++ .llseek = default_llseek, ++}; ++ ++/* coex entities bitmap */ ++#define COEX_WIFI_ENABLE (0x1) ++#define COEX_BLE_ENABLE (0x2) ++#define COEX_SOC_ENABLE (0x4) ++ ++#define MAX_COEX_STATISTICS_LEN (850) ++ ++static ssize_t coex_statistics_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ struct cc33xx_acx_coex_statistics *coex_stats_cmd; ++ char coex_statistics_str[MAX_COEX_STATISTICS_LEN]; ++ struct cc33xx_coex_stat_and_entities *coex_stat_ent; ++ struct cc33xx_coex_statistics *coex_stats; ++ int res, restot = 0, ret = 0; ++ ++ cc33xx_debug(DEBUG_CMD, "coex_statistics_read"); ++ ++ coex_stats_cmd = kzalloc(sizeof(struct cc33xx_acx_coex_statistics), ++ GFP_KERNEL); ++ if (!coex_stats_cmd) ++ return -ENOMEM; ++ ++ coex_stat_ent = &coex_stats_cmd->coex_stat; ++ coex_stats = &coex_stat_ent->coex_statistics; ++ ++ ret = cc33xx_cmd_interrogate(wl, READ_COEX_STATISTICS, coex_stats_cmd, ++ sizeof(struct cc33xx_acx_coex_statistics), ++ sizeof(struct cc33xx_acx_coex_statistics)); ++ ++ if (ret < 0) { ++ cc33xx_error("failed to send interrogate command"); ++ goto out_free; ++ } ++ ++ if (coex_stats_cmd->header.cmd.status == CMD_STATUS_INVALID_PARAM) { ++ cc33xx_error("Coex statistics are disabled"); ++ goto out_free; ++ } ++ ++ res = 0; ++ ++ if (coex_stat_ent->coex_entities_bitmap & COEX_WIFI_ENABLE) { ++ res = snprintf(coex_statistics_str, MAX_COEX_STATISTICS_LEN, ++ "wifi:\nrequest assertion/deassertion: %d/%d\r\n" ++ "grant assertion/deassertion: %d/%d\r\n" ++ "prio reject: %d\r\n" ++ "grant during dual ant assertion/deassertion: " ++ "%d/%d\r\n\n", ++ cpu_to_le32(coex_stats->wifi_request_assertion_log), ++ cpu_to_le32(coex_stats->wifi_request_de_assertion_log), ++ cpu_to_le32(coex_stats->wifi_grant_assertion_log), ++ cpu_to_le32(coex_stats->wifi_grant_deassertion_log), ++ cpu_to_le32(coex_stats->wifi_prio_reject_log), ++ cpu_to_le32(coex_stats->wifi_grant_during_dual_ant_assertion_log), ++ cpu_to_le32(coex_stats->wifi_grant_during_dual_ant_deassertion_log)); ++ restot = res; ++ } ++ ++ if (coex_stat_ent->coex_entities_bitmap & COEX_BLE_ENABLE) { ++ res = snprintf(coex_statistics_str + restot, ++ MAX_COEX_STATISTICS_LEN - restot, "ble:\n" ++ "request assertion/deassertion: %d/%d\r\n" ++ "grant assertion/deassertion: %d/%d\r\n" ++ "tx high/low prio reject: %d/%d\r\n" ++ "rx high/low prio reject: %d/%d\r\n\n", ++ cpu_to_le32(coex_stats->ble_request_assertion_log), ++ cpu_to_le32(coex_stats->ble_request_deassertion_log), ++ cpu_to_le32(coex_stats->ble_grant_assertion_log), ++ cpu_to_le32(coex_stats->ble_grant_deassertion_log), ++ cpu_to_le32(coex_stats->ble_tx_high_prio_reject_log), ++ cpu_to_le32(coex_stats->ble_tx_low_prio_reject_log), ++ cpu_to_le32(coex_stats->ble_rx_high_prio_reject_log), ++ cpu_to_le32(coex_stats->ble_rx_low_prio_reject_log)); ++ restot += res; ++ } ++ ++ if (coex_stat_ent->coex_entities_bitmap & COEX_SOC_ENABLE) { ++ res = snprintf(coex_statistics_str + restot, ++ MAX_COEX_STATISTICS_LEN - restot, ++ "External SoC:\n" ++ "request assertion/deassertion: %d/%d\r\n" ++ "grant assertion/deassertion: %d/%d\r\n" ++ "high/low prio reject: %d/%d\r\n\n", ++ cpu_to_le32(coex_stats->soc_request_assertion_log), ++ cpu_to_le32(coex_stats->soc_request_deassertion_log), ++ cpu_to_le32(coex_stats->soc_grant_assertion_log), ++ cpu_to_le32(coex_stats->soc_grant_deassertion_log), ++ cpu_to_le32(coex_stats->soc_high_prio_reject_log), ++ cpu_to_le32(coex_stats->soc_low_prio_reject_log)); ++ restot += res; ++ } ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, ++ coex_statistics_str, restot); ++ ++out_free: ++ kfree(coex_stats_cmd); ++ ++ return ret; ++} ++ ++static ssize_t coex_statistics_write(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ unsigned int value; ++ struct cc33xx_acx_coex_statistics_cfg *pCoexStatictics; ++ int ret; ++ ++ ret = kstrtouint_from_user(user_buf, count, 0, &value); ++ if (value > 2) { ++ cc33xx_warning("Parameter value must be 0-Disable Coex counters, " ++ "1-Enable Coex counters, 2-Reset Coex counters"); ++ return -ERANGE; ++ } ++ ++ cc33xx_debug(DEBUG_CMD, "coex statistics (%d)", value); ++ ++ pCoexStatictics = kzalloc(sizeof(struct cc33xx_acx_coex_statistics_cfg), ++ GFP_KERNEL); ++ if (!pCoexStatictics) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ pCoexStatictics->coex_statictics = value; ++ ++ mutex_lock(&wl->mutex); ++ ++ ret = cc33xx_cmd_configure(wl, START_COEX_STATISTICS_CFG, ++ pCoexStatictics, ++ sizeof(struct cc33xx_acx_coex_statistics_cfg)); ++ if (ret < 0) { ++ cc33xx_error("failed to initiate coex statictics"); ++ goto out_free; ++ } ++ ++out_free: ++ kfree(pCoexStatictics); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ ++ return count; ++} ++ ++static const struct file_operations coex_statistics_ops = { ++ .open = simple_open, ++ .read = coex_statistics_read, ++ .write = coex_statistics_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t antenna_diversity_enable_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ ++ ret = cc33xx_acx_get_antenna_diversity_status(wl); ++ if (ret < 0) { ++ cc33xx_warning("diversity status read failed"); ++ return ret; ++ } ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ret); ++} ++ ++static ssize_t antenna_diversity_enable_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ u8 diversity_enable; ++ ++ ret = kstrtou8_from_user(user_buf, count, 0, &diversity_enable); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in antenna_diversity_enable"); ++ return -EINVAL; ++ } ++ ++ if ((wl->conf.phy.num_of_antennas == 1) && (diversity_enable == 1)) { ++ cc33xx_warning("diversity cannot be enabled when only one antenna on board"); ++ return -EINVAL; ++ } ++ ++ if (diversity_enable > 1) { ++ cc33xx_warning("diversity_enable should be either 0 or 1"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ cc33xx_acx_set_antenna_diversity_status(wl, diversity_enable); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations antenna_diversity_enable_ops = { ++ .open = simple_open, ++ .read = antenna_diversity_enable_read, ++ .write = antenna_diversity_enable_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t antenna_diversity_set_rssi_threshold_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ s8 threshold; ++ ++ ret = cc33xx_acx_antenna_diversity_get_rssi_threshold(wl, &threshold); ++ if (ret < 0) { ++ cc33xx_warning("diversity rssi threshold read failed"); ++ return ret; ++ } ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", threshold); ++} ++ ++static ssize_t antenna_diversity_set_rssi_threshold_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ s8 rssi_threshold; ++ ++ ret = kstrtos8_from_user(user_buf, count, 0, &rssi_threshold); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in antenna_diversity_set_rssi_threshold"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ cc33xx_acx_antenna_diversity_set_rssi_threshold(wl, rssi_threshold); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations antenna_diversity_set_rssi_threshold_ops = { ++ .open = simple_open, ++ .read = antenna_diversity_set_rssi_threshold_read, ++ .write = antenna_diversity_set_rssi_threshold_write, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t antenna_diversity_select_default_antenna_read(struct file *file, char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ ++ ret = cc33xx_acx_antenna_diversity_get_default_antenna(wl); ++ if (ret < 0) { ++ cc33xx_warning("diversity default antenna read failed"); ++ return ret; ++ } ++ ++ return cc33xx_format_buffer(user_buf, count, ppos, "%d\n", ret); ++} ++ ++static ssize_t antenna_diversity_select_default_antenna_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct cc33xx *wl = file->private_data; ++ int ret; ++ u8 default_antenna; ++ ++ ret = kstrtou8_from_user(user_buf, count, 0, &default_antenna); ++ if (ret < 0) { ++ cc33xx_warning("illegal value in antenna_diversity_select_default_antenna"); ++ return -EINVAL; ++ } ++ ++ if (wl->conf.phy.num_of_antennas == 1) { ++ cc33xx_warning("cannot change default antenna in board with only one antenna"); ++ return -EINVAL; ++ } ++ ++ if (default_antenna > 1) { ++ cc33xx_warning("default_antenna should be either 0 or 1"); ++ return -ERANGE; ++ } ++ ++ mutex_lock(&wl->mutex); ++ ++ if (unlikely(wl->state != CC33XX_STATE_ON)) { ++ goto out; ++ } ++ ++ cc33xx_acx_antenna_diversity_select_default_antenna(wl, default_antenna); ++ ++out: ++ mutex_unlock(&wl->mutex); ++ return count; ++} ++ ++static const struct file_operations antenna_diversity_select_default_antenna_ops = { ++ .open = simple_open, ++ .read = antenna_diversity_select_default_antenna_read, ++ .write = antenna_diversity_select_default_antenna_write, ++ .llseek = default_llseek, ++}; ++ ++int cc33xx_debugfs_add_files(struct cc33xx *wl, ++ struct dentry *rootdir) ++{ ++ struct dentry *stats, *moddir; ++ ++ moddir = rootdir; ++ stats = debugfs_create_dir("fw_stats", rootdir); ++ ++ DEBUGFS_ADD(tx_queue_len, rootdir); ++ DEBUGFS_ADD(retry_count, rootdir); ++ DEBUGFS_ADD(excessive_retries, rootdir); ++ DEBUGFS_ADD(gpio_power, rootdir); ++ DEBUGFS_ADD(start_recovery, rootdir); ++ DEBUGFS_ADD(driver_state, rootdir); ++ DEBUGFS_ADD(vifs_state, rootdir); ++ DEBUGFS_ADD(dtim_interval, rootdir); ++ DEBUGFS_ADD(suspend_dtim_interval, rootdir); ++ DEBUGFS_ADD(beacon_interval, rootdir); ++ DEBUGFS_ADD(beacon_filtering, rootdir); ++ DEBUGFS_ADD(dynamic_ps_timeout, rootdir); ++ DEBUGFS_ADD(ps_mode, rootdir); ++ DEBUGFS_ADD(split_scan_timeout, rootdir); ++ DEBUGFS_ADD(fw_stats_raw, rootdir); ++ DEBUGFS_ADD(sleep_auth, rootdir); ++ DEBUGFS_ADD(ble_enable, rootdir); ++ DEBUGFS_ADD(set_tsf, rootdir); ++ DEBUGFS_ADD(twt_action, rootdir); ++ DEBUGFS_ADD(fw_logger, rootdir); ++ DEBUGFS_ADD(antenna_select, rootdir); ++ DEBUGFS_ADD(get_versions, rootdir); ++ DEBUGFS_ADD(trigger_fw_assert, rootdir); ++ DEBUGFS_ADD(burst_mode, rootdir); ++ DEBUGFS_ADD(coex_statistics, rootdir); ++ DEBUGFS_ADD(clear_fw_stats, stats); ++ DEBUGFS_ADD(antenna_diversity_enable, rootdir); ++ DEBUGFS_ADD(antenna_diversity_set_rssi_threshold, rootdir); ++ DEBUGFS_ADD(antenna_diversity_select_default_antenna, rootdir); ++ ++ DEBUGFS_ADD_PREFIX(dev, mem, rootdir); ++ ++ DEBUGFS_FWSTATS_ADD(error, error_frame_non_ctrl); ++ DEBUGFS_FWSTATS_ADD(error, error_frame_ctrl); ++ DEBUGFS_FWSTATS_ADD(error, error_frame_during_protection); ++ DEBUGFS_FWSTATS_ADD(error, null_frame_tx_start); ++ DEBUGFS_FWSTATS_ADD(error, null_frame_cts_start); ++ DEBUGFS_FWSTATS_ADD(error, bar_retry); ++ DEBUGFS_FWSTATS_ADD(error, num_frame_cts_nul_flid); ++ DEBUGFS_FWSTATS_ADD(error, tx_abort_failure); ++ DEBUGFS_FWSTATS_ADD(error, tx_resume_failure); ++ DEBUGFS_FWSTATS_ADD(error, rx_cmplt_db_overflow_cnt); ++ DEBUGFS_FWSTATS_ADD(error, elp_while_rx_exch); ++ DEBUGFS_FWSTATS_ADD(error, elp_while_tx_exch); ++ DEBUGFS_FWSTATS_ADD(error, elp_while_tx); ++ DEBUGFS_FWSTATS_ADD(error, elp_while_nvic_pending); ++ DEBUGFS_FWSTATS_ADD(error, rx_excessive_frame_len); ++ DEBUGFS_FWSTATS_ADD(error, burst_mismatch); ++ DEBUGFS_FWSTATS_ADD(error, tbc_exch_mismatch); ++ DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs); ++ DEBUGFS_FWSTATS_ADD(tx, tx_cmplt); ++ DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared); ++ DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared); ++ DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed); ++ DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed); ++ DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed); ++ DEBUGFS_FWSTATS_ADD(tx, tx_starts); ++ DEBUGFS_FWSTATS_ADD(tx, tx_stop); ++ DEBUGFS_FWSTATS_ADD(tx, tx_start_templates); ++ DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates); ++ DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen); ++ DEBUGFS_FWSTATS_ADD(tx, tx_start_data); ++ DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame); ++ DEBUGFS_FWSTATS_ADD(tx, tx_exch); ++ DEBUGFS_FWSTATS_ADD(tx, tx_retry_template); ++ DEBUGFS_FWSTATS_ADD(tx, tx_retry_data); ++ DEBUGFS_FWSTATS_ADD(tx, tx_retry_per_rate); ++ DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending); ++ DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry); ++ DEBUGFS_FWSTATS_ADD(tx, tx_done_template); ++ DEBUGFS_FWSTATS_ADD(tx, tx_done_data); ++ DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template); ++ DEBUGFS_FWSTATS_ADD(tx, tx_cfe1); ++ DEBUGFS_FWSTATS_ADD(tx, tx_cfe2); ++ DEBUGFS_FWSTATS_ADD(tx, frag_called); ++ DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed); ++ DEBUGFS_FWSTATS_ADD(tx, frag_init_called); ++ DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called); ++ DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called); ++ DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found); ++ DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation); ++ DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num); ++ DEBUGFS_FWSTATS_ADD(tx, frag_failed); ++ DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit); ++ DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss); ++ DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term); ++ DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes); ++ DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow); ++ DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame); ++ DEBUGFS_FWSTATS_ADD(rx, rx_done); ++ DEBUGFS_FWSTATS_ADD(rx, rx_defrag); ++ DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end); ++ DEBUGFS_FWSTATS_ADD(rx, rx_cmplt); ++ DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt); ++ DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task); ++ DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr); ++ DEBUGFS_FWSTATS_ADD(rx, rx_timeout); ++ DEBUGFS_FWSTATS_ADD(rx, rx_rts_timeout); ++ DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_called); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_init_called); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed); ++ DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found); ++ DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt); ++ DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays); ++ DEBUGFS_FWSTATS_ADD(rx, rx_xfr); ++ DEBUGFS_FWSTATS_ADD(isr, irqs); ++ DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt); ++ DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt); ++ DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync); ++ DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread); ++ DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt); ++ DEBUGFS_FWSTATS_ADD(pwr, sleep_time_count); ++ DEBUGFS_FWSTATS_ADD(pwr, sleep_time_avg); ++ DEBUGFS_FWSTATS_ADD(pwr, sleep_cycle_avg); ++ DEBUGFS_FWSTATS_ADD(pwr, sleep_percent); ++ DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_active_conf); ++ DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_user_conf); ++ DEBUGFS_FWSTATS_ADD(pwr, ap_sleep_counter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, data_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter); ++ DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests); ++ DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep); ++ DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates); ++ DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_rate); ++ DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_len); ++ DEBUGFS_FWSTATS_ADD(aggr_size, rx_size); ++ DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int); ++ DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int); ++ DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int); ++ DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int); ++ DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi); ++ DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi); ++ DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi); ++ DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi); ++ DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_rx_xfer_swi); ++ DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in); ++ DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full); ++ DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out); ++ DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full); ++ DEBUGFS_FWSTATS_ADD(diversity, num_of_packets_per_ant); ++ DEBUGFS_FWSTATS_ADD(diversity, total_num_of_toggles); ++ DEBUGFS_FWSTATS_ADD(thermal, irq_thr_low); ++ DEBUGFS_FWSTATS_ADD(thermal, irq_thr_high); ++ DEBUGFS_FWSTATS_ADD(thermal, tx_stop); ++ DEBUGFS_FWSTATS_ADD(thermal, tx_resume); ++ DEBUGFS_FWSTATS_ADD(thermal, false_irq); ++ DEBUGFS_FWSTATS_ADD(thermal, adc_source_unexpected); ++ DEBUGFS_FWSTATS_ADD(calib, fail_count); ++ DEBUGFS_FWSTATS_ADD(calib, calib_count); ++ DEBUGFS_FWSTATS_ADD(roaming, rssi_level); ++ DEBUGFS_FWSTATS_ADD(dfs, num_of_radar_detections); ++ ++ DEBUGFS_ADD(conf, moddir); ++ DEBUGFS_ADD(radar_detection, moddir); ++ cc33xx_debugfs_add_files_helper(moddir); ++ DEBUGFS_ADD(dynamic_fw_traces, moddir); ++ ++ return 0; ++} ++ ++void cc33xx_debugfs_reset(struct cc33xx *wl) ++{ ++ if (!wl->stats.fw_stats) ++ return; ++ ++ memset(wl->stats.fw_stats, 0, sizeof(struct cc33xx_acx_statistics)); ++ wl->stats.retry_count = 0; ++ wl->stats.excessive_retries = 0; ++} ++ ++int cc33xx_debugfs_init(struct cc33xx *wl) ++{ ++ int ret; ++ struct dentry *rootdir; ++ ++ rootdir = debugfs_create_dir(KBUILD_MODNAME, wl->hw->wiphy->debugfsdir); ++ ++ wl->stats.fw_stats = kzalloc(sizeof(struct cc33xx_acx_statistics), ++ GFP_KERNEL); ++ if (!wl->stats.fw_stats) { ++ ret = -ENOMEM; ++ goto out_remove; ++ } ++ ++ wl->stats.fw_stats_update = jiffies; ++ ++ ret = cc33xx_debugfs_add_files(wl, rootdir); ++ if (ret < 0) ++ goto out_exit; ++ ++ goto out; ++ ++out_exit: ++ cc33xx_debugfs_exit(wl); ++ ++out_remove: ++ debugfs_remove_recursive(rootdir); ++ ++out: ++ return ret; ++} ++ ++void cc33xx_debugfs_exit(struct cc33xx *wl) ++{ ++ kfree(wl->stats.fw_stats); ++ wl->stats.fw_stats = NULL; ++} +diff --git a/drivers/net/wireless/ti/cc33xx/debugfs.h b/drivers/net/wireless/ti/cc33xx/debugfs.h +new file mode 100644 +index 000000000000..2e76f50ff432 +--- /dev/null ++++ b/drivers/net/wireless/ti/cc33xx/debugfs.h +@@ -0,0 +1,102 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * This file is part of cc33xx ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * ++ * Contact: Luciano Coelho ++ */ ++ ++#ifndef __DEBUGFS_H__ ++#define __DEBUGFS_H__ ++ ++#include "cc33xx.h" ++ ++__printf(4, 5) int cc33xx_format_buffer(char __user *userbuf, size_t count, ++ loff_t *ppos, char *fmt, ...); ++ ++int cc33xx_debugfs_init(struct cc33xx *wl); ++void cc33xx_debugfs_exit(struct cc33xx *wl); ++void cc33xx_debugfs_reset(struct cc33xx *wl); ++void cc33xx_debugfs_update_stats(struct cc33xx *wl); ++ ++#define DEBUGFS_FORMAT_BUFFER_SIZE 256 ++ ++#define DEBUGFS_READONLY_FILE(name, fmt, value...) \ ++static ssize_t name## _read(struct file *file, char __user *userbuf, \ ++ size_t count, loff_t *ppos) \ ++{ \ ++ struct cc33xx *wl = file->private_data; \ ++ return cc33xx_format_buffer(userbuf, count, ppos, \ ++ fmt "\n", ##value); \ ++} \ ++ \ ++static const struct file_operations name## _ops = { \ ++ .read = name## _read, \ ++ .open = simple_open, \ ++ .llseek = generic_file_llseek, \ ++}; ++ ++#define DEBUGFS_ADD(name, parent) \ ++ do { \ ++ debugfs_create_file(#name, 0400, parent, \ ++ wl, &name## _ops); \ ++ } while (0) ++ ++ ++#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ ++ do { \ ++ debugfs_create_file(#name, 0400, parent, \ ++ wl, &prefix## _## name## _ops); \ ++ } while (0) ++ ++#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \ ++static ssize_t sub## _ ##name## _read(struct file *file, \ ++ char __user *userbuf, \ ++ size_t count, loff_t *ppos) \ ++{ \ ++ struct cc33xx *wl = file->private_data; \ ++ struct struct_type *stats = wl->stats.fw_stats; \ ++ \ ++ cc33xx_debugfs_update_stats(wl); \ ++ \ ++ return cc33xx_format_buffer(userbuf, count, ppos, fmt "\n", \ ++ stats->sub.name); \ ++} \ ++ \ ++static const struct file_operations sub## _ ##name## _ops = { \ ++ .read = sub## _ ##name## _read, \ ++ .open = simple_open, \ ++ .llseek = generic_file_llseek, \ ++}; ++ ++#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type) \ ++static ssize_t sub## _ ##name## _read(struct file *file, \ ++ char __user *userbuf, \ ++ size_t count, loff_t *ppos) \ ++{ \ ++ struct cc33xx *wl = file->private_data; \ ++ struct struct_type *stats = wl->stats.fw_stats; \ ++ char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = ""; \ ++ int res, i; \ ++ \ ++ cc33xx_debugfs_update_stats(wl); \ ++ \ ++ for (i = 0; i < len; i++) \ ++ res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n", \ ++ buf, i, stats->sub.name[i]); \ ++ \ ++ return cc33xx_format_buffer(userbuf, count, ppos, "%s", buf); \ ++} \ ++ \ ++static const struct file_operations sub## _ ##name## _ops = { \ ++ .read = sub## _ ##name## _read, \ ++ .open = simple_open, \ ++ .llseek = generic_file_llseek, \ ++}; ++ ++#define DEBUGFS_FWSTATS_ADD(sub, name) \ ++ DEBUGFS_ADD(sub## _ ##name, stats) ++ ++ ++#endif /* CC33XX_DEBUGFS_H */ +diff --git a/drivers/net/wireless/ti/cc33xx/io.c b/drivers/net/wireless/ti/cc33xx/io.c +index c8759b63ac2d..e0f5dd2cb78a 100644 +--- a/drivers/net/wireless/ti/cc33xx/io.c ++++ b/drivers/net/wireless/ti/cc33xx/io.c +@@ -26,14 +26,9 @@ void cc33xx_disable_interrupts_nosync(struct cc33xx *cc) + cc->if_ops->disable_irq(cc->dev); + } + +-void cc33xx_irq(void *cookie); + void cc33xx_enable_interrupts(struct cc33xx *cc) + { + cc->if_ops->enable_irq(cc->dev); +- +- cc33xx_debug(DEBUG_CC33xx, "IBI_WA: Read core status"); +- cc33xx_irq(cc); +- cc33xx_debug(DEBUG_CC33xx, "IBI_WA: Core status processed"); + } + + void cc33xx_io_reset(struct cc33xx *cc) +diff --git a/drivers/net/wireless/ti/cc33xx/main.c b/drivers/net/wireless/ti/cc33xx/main.c +index 4c271221a14b..b10263d7c150 100644 +--- a/drivers/net/wireless/ti/cc33xx/main.c ++++ b/drivers/net/wireless/ti/cc33xx/main.c +@@ -20,6 +20,7 @@ + #include "testmode.h" + #include "scan.h" + #include "event.h" ++#include "debugfs.h" + + #define CC33XX_FW_RX_PACKET_RAM (9 * 1024) + static int no_recovery = -1; +@@ -226,20 +227,20 @@ static struct ieee80211_rate cc33xx_rates[] = { + }; + + /* can't be const, mac80211 writes to this */ +-static struct ieee80211_channel cc33xx_channels[] = { ++static struct ieee80211_channel cc33xx_channels_2ghz[] = { ++ { .hw_value = 6, .center_freq = 2437, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 1, .center_freq = 2412, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 2, .center_freq = 2417, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 11, .center_freq = 2462, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 3, .center_freq = 2422, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 4, .center_freq = 2427, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 5, .center_freq = 2432, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 6, .center_freq = 2437, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 7, .center_freq = 2442, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 8, .center_freq = 2447, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 9, .center_freq = 2452, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 13, .center_freq = 2472, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 2, .center_freq = 2417, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 4, .center_freq = 2427, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 8, .center_freq = 2447, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 10, .center_freq = 2457, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 11, .center_freq = 2462, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 12, .center_freq = 2467, .max_power = CC33XX_MAX_TXPWR }, +- { .hw_value = 13, .center_freq = 2472, .max_power = CC33XX_MAX_TXPWR }, + }; + + static struct ieee80211_sband_iftype_data iftype_data_2ghz[] = {{ +@@ -302,7 +303,8 @@ static struct ieee80211_sband_iftype_data iftype_data_2ghz[] = {{ + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB | +- IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, ++ (IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US << ++ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS), + }, + /* Set default Tx/Rx HE MCS NSS Support field. + * Indicate support for up to 2 spatial streams and all +@@ -325,8 +327,8 @@ static struct ieee80211_sband_iftype_data iftype_data_2ghz[] = {{ + + /* can't be const, mac80211 writes to this */ + static struct ieee80211_supported_band cc33xx_band_2ghz = { +- .channels = cc33xx_channels, +- .n_channels = ARRAY_SIZE(cc33xx_channels), ++ .channels = cc33xx_channels_2ghz, ++ .n_channels = ARRAY_SIZE(cc33xx_channels_2ghz), + .bitrates = cc33xx_rates, + .n_bitrates = ARRAY_SIZE(cc33xx_rates), + .iftype_data = iftype_data_2ghz, +@@ -382,6 +384,7 @@ static struct ieee80211_channel cc33xx_channels_5ghz[] = { + { .hw_value = 132, .center_freq = 5660, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 136, .center_freq = 5680, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 140, .center_freq = 5700, .max_power = CC33XX_MAX_TXPWR }, ++ { .hw_value = 144, .center_freq = 5720, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 149, .center_freq = 5745, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 153, .center_freq = 5765, .max_power = CC33XX_MAX_TXPWR }, + { .hw_value = 157, .center_freq = 5785, .max_power = CC33XX_MAX_TXPWR }, +@@ -449,7 +452,8 @@ static struct ieee80211_sband_iftype_data iftype_data_5ghz[] = {{ + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB | +- IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, ++ (IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US << ++ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_POS), + }, + /* Set default Tx/Rx HE MCS NSS Support field. + * Indicate support for up to 2 spatial streams and all +@@ -1003,10 +1007,15 @@ static void cc33xx_get_vif_count(struct ieee80211_hw *hw, + void cc33xx_queue_recovery_work(struct cc33xx *cc) + { + /* Avoid a recursive recovery */ +- if (cc->state == CC33XX_STATE_ON) { ++ if (cc->state == CC33XX_STATE_ON && cc->mac80211_registered) { + cc->state = CC33XX_STATE_RESTARTING; + set_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &cc->flags); ++ cc33xx_disable_interrupts_nosync(cc); + ieee80211_queue_work(cc->hw, &cc->recovery_work); ++ } else if (cc->state == CC33XX_STATE_OFF || cc->state == CC33XX_STATE_ON) { ++ cc33xx_error("Fatal error during driver init, cannot recover"); ++ cc->state = CC33XX_STATE_FAILED; ++ cc33xx_disable_interrupts_nosync(cc); + } + } + +@@ -1844,11 +1853,6 @@ static void cc33xx_turn_off(struct cc33xx *cc) + */ + cc->state = CC33XX_STATE_OFF; + +- /* Use the nosync variant to disable interrupts, so the mutex could be +- * held while doing so without deadlocking. +- */ +- cc33xx_disable_interrupts_nosync(cc); +- + mutex_unlock(&cc->mutex); + + if (!test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &cc->flags)) +@@ -1899,6 +1903,8 @@ static void cc33xx_turn_off(struct cc33xx *cc) + for (i = 0; i < NUM_TX_QUEUES; i++) + cc->tx_allocated_pkts[i] = 0; + ++ cc33xx_debugfs_reset(cc); ++ + kfree(cc->target_mem_map); + cc->target_mem_map = NULL; + +@@ -2335,11 +2341,6 @@ static int cc33xx_op_add_interface(struct ieee80211_hw *hw, + &wlvif->dev_role_id); + if (ret < 0) + goto out; +- +- /* needed mainly for configuring rate policies */ +- ret = cc33xx_acx_config_ps(cc, wlvif); +- if (ret < 0) +- goto out; + } + + list_add(&wlvif->list, &cc->wlvif_list); +@@ -3052,6 +3053,7 @@ static int cc33xx_set_key(struct cc33xx *cc, enum set_key_cmd cmd, + case WLAN_CIPHER_SUITE_TKIP: + key_type = KEY_TKIP; + key_conf->hw_key_idx = key_conf->keyidx; ++ key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + break; + case WLAN_CIPHER_SUITE_CCMP: + key_type = KEY_AES; +@@ -3819,35 +3821,20 @@ static void cc33xx_bss_info_changed_sta(struct cc33xx *cc, + } + + if (changed & BSS_CHANGED_PS) { +- if (vif->cfg.ps && +- test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && +- !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { +- int ps_mode; +- char *ps_mode_str; +- +- if (cc->conf.host_conf.conn.forced_ps) { +- ps_mode = STATION_POWER_SAVE_MODE; +- ps_mode_str = "forced"; +- } else { +- ps_mode = STATION_AUTO_PS_MODE; +- ps_mode_str = "auto"; +- } +- +- cc33xx_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); ++ ret = 0; + +- ret = cc33xx_ps_set_mode(cc, wlvif, ps_mode); +- if (ret < 0) +- cc33xx_warning("enter %s ps failed %d", +- ps_mode_str, ret); +- } else if (!vif->cfg.ps && test_bit(WLVIF_FLAG_IN_PS, +- &wlvif->flags)) { +- cc33xx_debug(DEBUG_PSM, "auto ps disabled"); +- +- ret = cc33xx_ps_set_mode(cc, wlvif, +- STATION_ACTIVE_MODE); +- if (ret < 0) +- cc33xx_warning("exit auto ps failed %d", ret); ++ if (cc->conf.mac.ps_mode == STATION_AUTO_PS_MODE) { ++ if ((vif->cfg.ps) && test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { ++ ret = cc33xx_ps_set_mode(cc, wlvif, STATION_AUTO_PS_MODE); ++ } else if (!vif->cfg.ps) { ++ ret = cc33xx_ps_set_mode(cc, wlvif, STATION_ACTIVE_MODE); ++ } ++ } else { ++ ret = cc33xx_ps_set_mode(cc, wlvif, cc->conf.mac.ps_mode); + } ++ ++ if (ret < 0) ++ cc33xx_warning("exit auto ps failed %d", ret); + } + + /* Handle new association with HT. Do this after join. */ +@@ -4197,7 +4184,6 @@ static int cc33xx_op_conf_tx(struct ieee80211_hw *hw, + static u64 cc33xx_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) + { + struct cc33xx *cc = hw->priv; +- struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif); + u64 mactime = ULLONG_MAX; + + cc33xx_debug(DEBUG_MAC80211, "mac80211 get tsf"); +@@ -4207,8 +4193,6 @@ static u64 cc33xx_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) + if (unlikely(cc->state != CC33XX_STATE_ON)) + goto out; + +- cc33xx_acx_tsf_info(cc, wlvif, &mactime); +- + out: + mutex_unlock(&cc->mutex); + +@@ -5195,6 +5179,8 @@ static int cc33xx_register_hw(struct cc33xx *cc) + + cc->mac80211_registered = true; + ++ cc33xx_debugfs_init(cc); ++ + out: + return ret; + } +@@ -5289,7 +5275,7 @@ static int cc33xx_init_ieee80211(struct cc33xx *cc) + /* clear channel flags from the previous usage + * and restore max_power & max_antenna_gain values. + */ +- for (i = 0; i < ARRAY_SIZE(cc33xx_channels); i++) { ++ for (i = 0; i < ARRAY_SIZE(cc33xx_channels_2ghz); i++) { + cc33xx_band_2ghz.channels[i].flags = 0; + cc33xx_band_2ghz.channels[i].max_power = CC33XX_MAX_TXPWR; + cc33xx_band_2ghz.channels[i].max_antenna_gain = 0; +@@ -5493,6 +5479,7 @@ static struct ieee80211_hw *cc33xx_alloc_hw(u32 aggr_buf_size) + destroy_workqueue(cc->freezable_netstack_wq); + + err_hw_alloc: ++ cc33xx_debugfs_exit(cc); + return NULL; + } + +@@ -5509,6 +5496,8 @@ static int cc33xx_free_hw(struct cc33xx *cc) + dev_kfree_skb(cc->dummy_packet); + free_pages((unsigned long)cc->aggr_buf, get_order(cc->aggr_buf_size)); + ++ cc33xx_debugfs_exit(cc); ++ + kfree(cc->nvs_mac_addr); + cc->nvs_mac_addr = NULL; + +@@ -5715,6 +5704,17 @@ static int cc33xx_setup(struct cc33xx *cc) + if (ret < 0) + return ret; + ++ if (cc->conf.core.max_rx_ampdu_len == 0) { ++ cc33xx_siso40_ht_cap_2ghz.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; ++ cc33xx_siso40_ht_cap_5ghz.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; ++ cc33xx_siso20_ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; ++ } else if (cc->conf.core.max_rx_ampdu_len == 1) { ++ cc33xx_siso40_ht_cap_2ghz.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; ++ cc33xx_siso40_ht_cap_5ghz.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; ++ cc33xx_siso20_ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; ++ } ++ ++ + if (cc->conf.host_conf.ht.mode == HT_MODE_DEFAULT) { + cc33xx_set_ht_cap(cc, NL80211_BAND_2GHZ, + &cc33xx_siso40_ht_cap_2ghz); +@@ -5815,6 +5815,7 @@ static int cc33xx_remove(struct platform_device *pdev) + + device_init_wakeup(cc->dev, false); + cc33xx_unregister_hw(cc); ++ cc33xx_disable_interrupts_nosync(cc); + cc33xx_turn_off(cc); + + out: +diff --git a/drivers/net/wireless/ti/cc33xx/ps.c b/drivers/net/wireless/ti/cc33xx/ps.c +index e708ef8d5f46..1386a3f9f344 100644 +--- a/drivers/net/wireless/ti/cc33xx/ps.c ++++ b/drivers/net/wireless/ti/cc33xx/ps.c +@@ -23,7 +23,6 @@ int cc33xx_ps_set_mode(struct cc33xx *cc, struct cc33xx_vif *wlvif, + if (ret < 0) + return ret; + +- set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); + break; + + case STATION_ACTIVE_MODE: +@@ -33,7 +32,6 @@ int cc33xx_ps_set_mode(struct cc33xx *cc, struct cc33xx_vif *wlvif, + if (ret < 0) + return ret; + +- clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); + break; + + default: +diff --git a/drivers/net/wireless/ti/cc33xx/rx.c b/drivers/net/wireless/ti/cc33xx/rx.c +index e6467e1e223e..3493238de8e2 100644 +--- a/drivers/net/wireless/ti/cc33xx/rx.c ++++ b/drivers/net/wireless/ti/cc33xx/rx.c +@@ -67,6 +67,9 @@ static void cc33xx_rx_status(struct cc33xx *cc, + cc33xx_warning("Michael MIC error. Desc: 0x%x", + desc_err_code); + } ++ ++ if (desc->status & CC33XX_RX_DESC_NEW_KEY) ++ status->flag |= RX_FLAG_PN_VALIDATED; + } + + if (beacon || probe_rsp) +diff --git a/drivers/net/wireless/ti/cc33xx/rx.h b/drivers/net/wireless/ti/cc33xx/rx.h +index 46ff6867749f..523a35288d15 100644 +--- a/drivers/net/wireless/ti/cc33xx/rx.h ++++ b/drivers/net/wireless/ti/cc33xx/rx.h +@@ -25,13 +25,15 @@ + * + * Bits 0-2 - error code + * Bits 3-5 - process_id tag (AP mode FW) +- * Bits 6-7 - reserved ++ * Bits 6 - new key ++ * Bits 7 - reserved + */ + enum { + CC33XX_RX_DESC_SUCCESS = 0x00, + CC33XX_RX_DESC_DECRYPT_FAIL = 0x01, + CC33XX_RX_DESC_MIC_FAIL = 0x02, +- CC33XX_RX_DESC_STATUS_MASK = 0x07 ++ CC33XX_RX_DESC_STATUS_MASK = 0x07, ++ CC33XX_RX_DESC_NEW_KEY = 0x40 + }; + + /* Account for the padding inserted by the FW in case of RX_ALIGNMENT +-- +2.34.1 +