From patchwork Tue Oct 29 17:23:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839581 Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D446197A92; Tue, 29 Oct 2024 17:24:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.142 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222676; cv=none; b=U/4ItQDQuIh6s/Jv26mdKT+ySNQambXngh2BFzysbp+yYJtkDO9v+1x9CxVT10uHZjvAK/o3KaHx5lJPrjM+FGb3i4H1WdBcpXtdC4Yu+4Xvbb/58MDq4cevHmDRlKKMjy/9X9S3YBGCk87R7e6rz+8syHvoR6q6H0OE0lpYPxk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222676; c=relaxed/simple; bh=WfCZcj6DMgLWunhYB8FG/0Q0t4VDpsZjBIAAiuE978Y=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jN3uqaEigjpDS+ES6yscLVcCzaTNqsy3U8tH8uWQPKt5qGKfVDgK+2P5mGK/l1jmmrxQ4paOsa5TuDbaREnOoameUuGSAO7x8uIc4xeE4/zaMjux2Od39OpNgLSL2/+UINB2iCd012T1l3cVzMK8XcGP+Sa5NWIJOejtoDoQdhE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=ABoMywCe; arc=none smtp.client-ip=198.47.19.142 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="ABoMywCe" Received: from lelv0266.itg.ti.com ([10.180.67.225]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOOCG014495; Tue, 29 Oct 2024 12:24:24 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222664; bh=aHsRm+RMCYHI+WOtDLAexUtemMUM/jxCl8MxdYRwBBk=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=ABoMywCeosc1twhdWLUvTQCCSb7SFNuLmgATqML8kfFpg9RiVsa80y5KMBAcv0DE3 qqynjnK079s533+kr3GzLUwrDKLduVnwf4ZpkpMNqav6WF+XE9zI1AIGQUlBXqyXIV vNULDUuv/nU5V06sHXHZG0tD25vV+5jnWVV2FkLo= Received: from DFLE105.ent.ti.com (dfle105.ent.ti.com [10.64.6.26]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOO3i068197; Tue, 29 Oct 2024 12:24:24 -0500 Received: from DFLE103.ent.ti.com (10.64.6.24) by DFLE105.ent.ti.com (10.64.6.26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:24 -0500 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DFLE103.ent.ti.com (10.64.6.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:24 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THONBj065624; Tue, 29 Oct 2024 12:24:23 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 02/17] wifi: cc33xx: Add cc33xx.h, cc33xx_i.h Date: Tue, 29 Oct 2024 19:23:39 +0200 Message-ID: <20241029172354.4027886-3-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea These are header files with definitions common to the entire driver. Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/cc33xx.h | 483 ++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/cc33xx_i.h | 459 ++++++++++++++++++++ 2 files changed, 942 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx.h create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx_i.h diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx.h b/drivers/net/wireless/ti/cc33xx/cc33xx.h new file mode 100644 index 000000000000..e756dc6a7d41 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/cc33xx.h @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __CC33XX_H__ +#define __CC33XX_H__ + +#include "cc33xx_i.h" +#include "rx.h" + +/* The maximum number of Tx descriptors in all chip families */ +#define CC33XX_MAX_TX_DESCRIPTORS 32 + +#define CC33XX_CMD_MAX_SIZE (896) +#define CC33XX_INI_PARAM_COMMAND_SIZE (16UL) +#define CC33XX_INI_CMD_MAX_SIZE (CC33X_CONF_SIZE + CC33XX_INI_PARAM_COMMAND_SIZE + sizeof(int)) + +#define CC33XX_CMD_BUFFER_SIZE ((CC33XX_INI_CMD_MAX_SIZE > CC33XX_CMD_MAX_SIZE)\ + ? CC33XX_INI_CMD_MAX_SIZE : CC33XX_CMD_MAX_SIZE) + +#define CC33XX_NUM_MAC_ADDRESSES 3 + +#define CC33XX_AGGR_BUFFER_SIZE (8 * PAGE_SIZE) + +#define CC33XX_NUM_TX_DESCRIPTORS 32 +#define CC33XX_NUM_RX_DESCRIPTORS 32 + +#define CC33XX_RX_BA_MAX_SESSIONS 13 + +#define CC33XX_MAX_AP_STATIONS 16 + +struct cc33xx_tx_hw_descr; +struct cc33xx_rx_descriptor; +struct partial_rx_frame; +struct core_fw_status; +struct core_status; + +enum wl_rx_buf_align; + +struct cc33xx_stats { + void *fw_stats; + unsigned long fw_stats_update; + unsigned int retry_count; + unsigned int excessive_retries; +}; + +struct cc33xx_ant_diversity { + u8 diversity_enable; + s8 rssi_threshold; + u8 default_antenna; + u8 padding; +}; + +struct cc33xx { + bool initialized; + struct ieee80211_hw *hw; + bool mac80211_registered; + + struct device *dev; + struct platform_device *pdev; + + const struct cc33xx_if_operations *if_ops; + + int wakeirq; + + /* Protects all mac80211 operations and parts of Rx, Tx and IRQ handling. + * All functions postfixed with "_locked" (i.e, cc33xx_irq_locked) + * assume this lock is held by caller. + */ + struct mutex mutex; + + /* Used independently from above mutex to protect short critical sections. + * Though many of these sections may theoretically execute in parallel, a + * single lock will be used for simplicity. + */ + spinlock_t cc_lock; + + enum cc33xx_state state; + bool plt; + enum plt_mode plt_mode; + u8 plt_role_id; + u8 fem_manuf; + u8 last_vif_count; + + struct core_status *core_status; + u8 last_fw_rls_idx; + u8 command_result[CC33XX_CMD_MAX_SIZE]; + u16 result_length; + struct partial_rx_frame partial_rx; + + unsigned long flags; + + void *nvs_mac_addr; + size_t nvs_mac_addr_len; + struct cc33xx_fw_download *fw_download; + + struct mac_address addresses[CC33XX_NUM_MAC_ADDRESSES]; + + unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)]; + unsigned long roles_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)]; + unsigned long roc_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)]; + unsigned long rate_policies_map[BITS_TO_LONGS(CC33XX_MAX_RATE_POLICIES)]; + + u8 session_ids[CC33XX_MAX_LINKS]; + + struct list_head wlvif_list; + + u8 sta_count; + u8 ap_count; + + struct cc33xx_acx_mem_map *target_mem_map; + + /* Accounting for allocated / available TX blocks on HW */ + + u32 tx_blocks_available; + u32 tx_allocated_blocks; + + /* Accounting for allocated / available Tx packets in HW */ + + u32 tx_allocated_pkts[NUM_TX_QUEUES]; + + /* Time-offset between host and chipset clocks */ + + /* Frames scheduled for transmission, not handled yet */ + int tx_queue_count[NUM_TX_QUEUES]; + unsigned long queue_stop_reasons[NUM_TX_QUEUES * CC33XX_NUM_MAC_ADDRESSES]; + + /* Frames received, not handled yet by mac80211 */ + struct sk_buff_head deferred_rx_queue; + + /* Frames sent, not returned yet to mac80211 */ + struct sk_buff_head deferred_tx_queue; + + struct work_struct tx_work; + struct workqueue_struct *freezable_wq; + + /*freezable wq for netstack_work*/ + struct workqueue_struct *freezable_netstack_wq; + + /* Pending TX frames */ + unsigned long tx_frames_map[BITS_TO_LONGS(CC33XX_MAX_TX_DESCRIPTORS)]; + struct sk_buff *tx_frames[CC33XX_MAX_TX_DESCRIPTORS]; + int tx_frames_cnt; + + /* FW Rx counter */ + u32 rx_counter; + + /* Intermediate buffer, used for packet aggregation */ + u8 *aggr_buf; + u32 aggr_buf_size; + size_t max_transaction_len; + + /* Reusable dummy packet template */ + struct sk_buff *dummy_packet; + + /* Network stack work */ + struct work_struct netstack_work; + /* FW log buffer */ + u8 *fwlog; + + /* Number of valid bytes in the FW log buffer */ + ssize_t fwlog_size; + + /* Hardware recovery work */ + struct work_struct recovery_work; + + struct work_struct irq_deferred_work; + + /* Reg domain last configuration */ + DECLARE_BITMAP(reg_ch_conf_last, 64); + /* Reg domain pending configuration */ + DECLARE_BITMAP(reg_ch_conf_pending, 64); + + /* Lock-less list for deferred event handling */ + struct llist_head event_list; + /* The mbox event mask */ + u32 event_mask; + /* events to unmask only when ap interface is up */ + u32 ap_event_mask; + + /* Are we currently scanning */ + struct cc33xx_vif *scan_wlvif; + struct cc33xx_scan scan; + struct delayed_work scan_complete_work; + + struct ieee80211_vif *roc_vif; + struct delayed_work roc_complete_work; + + struct cc33xx_vif *sched_vif; + + u8 mac80211_scan_stopped; + + /* The current band */ + enum nl80211_band band; + + /* in dBm */ + int power_level; + + struct cc33xx_stats stats; + + __le32 *buffer_32; + + /* Current chipset configuration */ + struct cc33xx_conf_file conf; + + bool enable_11a; + + /* bands supported by this instance of cc33xx */ + struct ieee80211_supported_band bands[CC33XX_NUM_BANDS]; + + /* wowlan trigger was configured during suspend. + * (currently, only "ANY" and "PATTERN" trigger is supported) + */ + + bool keep_device_power; + + /* AP-mode - links indexed by HLID. The global and broadcast links + * are always active. + */ + struct cc33xx_link links[CC33XX_MAX_LINKS]; + + /* number of currently active links */ + int active_link_count; + + /* AP-mode - a bitmap of links currently in PS mode according to FW */ + unsigned long ap_fw_ps_map; + + /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ + unsigned long ap_ps_map; + + /* Quirks of specific hardware revisions */ + unsigned int quirks; + + /* number of currently active RX BA sessions */ + int ba_rx_session_count; + + /* AP-mode - number of currently connected stations */ + int active_sta_count; + + /* last wlvif we transmitted from */ + struct cc33xx_vif *last_wlvif; + + /* work to fire when Tx is stuck */ + struct delayed_work tx_watchdog_work; + + /* HW HT (11n) capabilities */ + struct ieee80211_sta_ht_cap ht_cap[CC33XX_NUM_BANDS]; + + /* the current dfs region */ + enum nl80211_dfs_regions dfs_region; + bool radar_debug_mode; + + /* RX Data filter rule state - enabled/disabled */ + /* used in CONFIG PM AND W8 Code */ + unsigned long rx_filter_enabled[BITS_TO_LONGS(CC33XX_MAX_RX_FILTERS)]; + + /* mutex for protecting the tx_flush function */ + struct mutex flush_mutex; + + /* sleep auth value currently configured to FW */ + int sleep_auth; + + /*ble_enable value - 1=enabled, 0=disabled. */ + int ble_enable; + + /* parameters for joining a TWT agreement */ + int min_wake_duration_usec; + int min_wake_interval_mantissa; + int min_wake_interval_exponent; + int max_wake_interval_mantissa; + int max_wake_interval_exponent; + + /* the number of allocated MAC addresses in this chip */ + int num_mac_addr; + + /* sta role index - if 0 - wlan0 primary station interface, + * if 1 - wlan2 - secondary station interface + */ + u8 sta_role_idx; + + u16 max_cmd_size; + + struct completion nvs_loading_complete; + struct completion command_complete; + + /* dynamic fw traces */ + u32 dynamic_fw_traces; + + /* buffer for sending commands to FW */ + u8 cmd_buf[CC33XX_CMD_BUFFER_SIZE]; + + /* number of keys requiring extra spare mem-blocks */ + int extra_spare_key_count; + + u8 efuse_mac_address[ETH_ALEN]; + + u32 fuse_rom_structure_version; + u32 device_part_number; + u32 pg_version; + u8 disable_5g; + u8 disable_6g; + + struct cc33xx_acx_fw_versions *fw_ver; + + u8 antenna_selection; + + /* burst mode cfg */ + u8 burst_disable; + + struct cc33xx_ant_diversity diversity; +}; + +void cc33xx_update_inconn_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct cc33xx_station *wl_sta, bool in_conn); + +void cc33xx_irq(void *cookie); + +/* Quirks */ + +/* the first start_role(sta) sometimes doesn't work on wl12xx */ +#define CC33XX_QUIRK_START_STA_FAILS BIT(1) + +/* wl127x and SPI don't support SDIO block size alignment */ +#define CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2) + +/* means aggregated Rx packets are aligned to a SDIO block */ +#define CC33XX_QUIRK_RX_BLOCKSIZE_ALIGN BIT(3) + +/* pad only the last frame in the aggregate buffer */ +#define CC33XX_QUIRK_TX_PAD_LAST_FRAME BIT(7) + +/* extra header space is required for TKIP */ +#define CC33XX_QUIRK_TKIP_HEADER_SPACE BIT(8) + +/* Some firmwares not support sched scans while connected */ +#define CC33XX_QUIRK_NO_SCHED_SCAN_WHILE_CONN BIT(9) + +/* separate probe response templates for one-shot and sched scans */ +#define CC33XX_QUIRK_DUAL_PROBE_TMPL BIT(10) + +/* Firmware requires reg domain configuration for active calibration */ +#define CC33XX_QUIRK_REGDOMAIN_CONF BIT(11) + +/* The FW only support a zero session id for AP */ +#define CC33XX_QUIRK_AP_ZERO_SESSION_ID BIT(12) + +/* TODO: move all these common registers and values elsewhere */ +#define HW_ACCESS_ELP_CTRL_REG 0x1FFFC + +enum CC33xx_FRAME_FORMAT { + CC33xx_B_SHORT = 0, + CC33xx_B_LONG, + CC33xx_LEGACY_OFDM, + CC33xx_HT_MF, + CC33xx_HT_GF, + CC33xx_HE_SU, + CC33xx_HE_MU, + CC33xx_HE_SU_ER, + CC33xx_HE_TB, + CC33xx_HE_TB_NDP_FB, + CC33xx_VHT +}; + +/* CC33xx HW Common Definitions */ + +#define HOST_SYNC_PATTERN 0x5C5C5C5C +#define DEVICE_SYNC_PATTERN 0xABCDDCBA +#define NAB_DATA_ADDR 0x0000BFF0 +#define NAB_CONTROL_ADDR 0x0000BFF8 +#define NAB_STATUS_ADDR 0x0000BFFC + +#define NAB_SEND_CMD 0x940d +#define NAB_SEND_FLAGS 0x08 +#define CC33xx_INTERNAL_DESC_SIZE 200 +#define NAB_EXTRA_BYTES 4 + +#define TX_RESULT_QUEUE_SIZE 108 + +struct control_info_descriptor { + __le16 type_and_length; +}; + +enum control_message_type { + CTRL_MSG_NONE = 0, + CTRL_MSG_EVENT = 1, + CTRL_MSG_COMMND_COMPLETE = 2 +}; + +struct core_fw_status { + u8 tx_result_queue_index; + u8 reserved1[3]; + u8 tx_result_queue[TX_RESULT_QUEUE_SIZE]; + + /* A bitmap (where each bit represents a single HLID) + * to indicate PS/Active mode of the link + */ + __le32 link_ps_bitmap; + + /* A bitmap (where each bit represents a single HLID) + * to indicate if the station is in Fast mode + */ + __le32 link_fast_bitmap; + + /* A bitmap (where each bit represents a single HLID) + * to indicate if a links is suspended/about to be suspended + */ + __le32 link_suspend_bitmap; + + /* Host TX Flow Control descriptor per AC threshold */ + u8 tx_flow_control_ac_threshold; + + /* Host TX Flow Control descriptor PS link threshold */ + u8 tx_ps_threshold; + + /* Host TX Flow Control descriptor Suspended link threshold */ + u8 tx_suspend_threshold; + + /* Host TX Flow Control descriptor Slow link threshold */ + u8 tx_slow_link_prio_threshold; + + /* Host TX Flow Control descriptor Fast link threshold */ + u8 tx_fast_link_prio_threshold; + + /* Host TX Flow Control descriptor Stop Slow link threshold */ + u8 tx_slow_stop_threshold; + + /* Host TX Flow Control descriptor Stop Fast link threshold */ + u8 tx_fast_stop_threshold; + + u8 reserved2; + /* Additional information can be added here */ +} __packed; + +struct core_status { + __le32 block_pad[28]; + __le32 host_interrupt_status; + __le32 rx_status; + struct core_fw_status fw_info; + __le32 tsf; +} __packed; + +struct NAB_header { + __le32 sync_pattern; + __le16 opcode; + __le16 len; +}; + +/* rx_status lower bytes hold the rx byte count */ +#define RX_BYTE_COUNT_MASK 0xFFFF + +#define HINT_NEW_TX_RESULT 0x1 +#define HINT_COMMAND_COMPLETE 0x2 +#define HINT_ROM_LOADER_INIT_COMPLETE 0x8 +#define HINT_SECOND_LOADER_INIT_COMPLETE 0x10 +#define HINT_FW_WAKEUP_COMPLETE 0x20 +#define HINT_FW_INIT_COMPLETE 0x40 +#define HINT_GENERAL_ERROR 0x80000000 + +#define BOOT_TIME_INTERRUPTS (\ + HINT_ROM_LOADER_INIT_COMPLETE | \ + HINT_SECOND_LOADER_INIT_COMPLETE | \ + HINT_FW_WAKEUP_COMPLETE | \ + HINT_FW_INIT_COMPLETE) + +struct NAB_tx_header { + __le32 sync; + __le16 opcode; + __le16 len; + __le16 desc_length; + u8 sd; + u8 flags; +} __packed; + +struct NAB_rx_header { + __le32 cnys; + __le16 opcode; + __le16 len; + __le32 rx_desc; + __le32 reserved; +} __packed; + +#endif /* __CC33XX_H__ */ diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx_i.h b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h new file mode 100644 index 000000000000..99a296a5e94e --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h @@ -0,0 +1,459 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __CC33XX_I_H__ +#define __CC33XX_I_H__ + +#include +#include + +#include "conf.h" + +struct cc33xx_family_data { + const char *name; + const char *nvs_name; /* nvs file */ + const char *cfg_name; /* cfg file */ +}; + +#define CC33XX_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) +#define CC33XX_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) +#define CC33XX_TX_SQN_POST_RECOVERY_PADDING 0xff +/* Use smaller padding for GEM, as some APs have issues when it's too big */ +#define CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20 + +#define CC33XX_CIPHER_SUITE_GEM 0x00147201 + +#define CC33XX_BUSY_WORD_LEN (sizeof(u32)) + +#define CC33XX_DEFAULT_BEACON_INT 100 + +#define CC33XX_MAX_ROLES 4 +#define CC33XX_INVALID_ROLE_ID 0xff +#define CC33XX_INVALID_LINK_ID 0xff + +#define CC33XX_MAX_LINKS 21 + +/* the driver supports the 2.4Ghz and 5Ghz bands */ +#define CC33XX_NUM_BANDS 2 + +#define CC33XX_MAX_RATE_POLICIES 16 + +/* Defined by FW as 0. Will not be freed or allocated. */ +#define CC33XX_SYSTEM_HLID 0 + +/* When in AP-mode, we allow (at least) this number of packets + * to be transmitted to FW for a STA in PS-mode. Only when packets are + * present in the FW buffers it will wake the sleeping STA. We want to put + * enough packets for the driver to transmit all of its buffered data before + * the STA goes to sleep again. But we don't want to take too much memory + * as it might hurt the throughput of active STAs. + */ +#define CC33XX_PS_STA_MAX_PACKETS 2 + +#define CC33XX_AP_BSS_INDEX 0 + +enum cc33xx_state { + CC33XX_STATE_OFF, + CC33XX_STATE_RESTARTING, + CC33XX_STATE_ON, +}; + +struct cc33xx; + +#define NUM_TX_QUEUES 4 + +#define CC33XX_MAX_CHANNELS 64 +struct cc33xx_scan { + struct cfg80211_scan_request *req; + unsigned long scanned_ch[BITS_TO_LONGS(CC33XX_MAX_CHANNELS)]; + bool failed; + u8 state; + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + size_t ssid_len; +}; + +struct cc33xx_if_operations { + void (*interface_claim)(struct device *child); + void (*interface_release)(struct device *child); + int __must_check (*read)(struct device *child, int addr, void *buf, + size_t len, bool fixed); + int __must_check (*write)(struct device *child, int addr, void *buf, + size_t len, bool fixed); + void (*reset)(struct device *child); + void (*init)(struct device *child); + int (*power)(struct device *child, bool enable); + void (*set_block_size)(struct device *child, unsigned int blksz); + size_t (*get_max_transaction_len)(struct device *child); + void (*set_irq_handler)(struct device *child, void *irq_handler); + void (*enable_irq)(struct device *child); + void (*disable_irq)(struct device *child); +}; + +struct cc33xx_platdev_data { + const struct cc33xx_if_operations *if_ops; + const struct cc33xx_family_data *family; + void (*irq_handler)(struct platform_device *pdev); + int gpio_irq_num; + + bool ref_clock_xtal; /* specify whether the clock is XTAL or not */ + bool pwr_in_suspend; +}; + +#define MAX_NUM_KEYS 14 +#define MAX_KEY_SIZE 32 + +struct cc33xx_ap_key { + u8 id; + u8 key_type; + u8 key_size; + u8 key[MAX_KEY_SIZE]; + u8 hlid; + u32 tx_seq_32; + u16 tx_seq_16; +}; + +enum cc33xx_flags { + CC33XX_FLAG_GPIO_POWER, + CC33XX_FLAG_TX_PENDING, + CC33XX_FLAG_IN_ELP, + CC33XX_FLAG_FW_TX_BUSY, + CC33XX_FLAG_DUMMY_PACKET_PENDING, + CC33XX_FLAG_SUSPENDED, + CC33XX_FLAG_PENDING_WORK, + CC33XX_FLAG_SOFT_GEMINI, + CC33XX_FLAG_DRIVER_REMOVED, + CC33XX_FLAG_RECOVERY_IN_PROGRESS, + CC33XX_FLAG_VIF_CHANGE_IN_PROGRESS, + CC33XX_FLAG_IO_FAILED, + CC33XX_FLAG_REINIT_TX_WDOG, +}; + +enum cc33xx_vif_flags { + WLVIF_FLAG_INITIALIZED, + WLVIF_FLAG_STA_ASSOCIATED, + 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, + WLVIF_FLAG_AP_PROBE_RESP_SET, + WLVIF_FLAG_IN_USE, + WLVIF_FLAG_ACTIVE, + WLVIF_FLAG_BEACON_DISABLED, +}; + +struct cc33xx_vif; + +struct cc33xx_link { + /* AP-mode - TX queue per AC in link */ + struct sk_buff_head tx_queue[NUM_TX_QUEUES]; + + /* accounting for allocated / freed packets in FW */ + u8 allocated_pkts; + u8 prev_freed_pkts; + + u8 addr[ETH_ALEN]; + + /* bitmap of TIDs where RX BA sessions are active for this link */ + u8 ba_bitmap; + + /* the last fw rate index we used for this link */ + u8 fw_rate_idx; + + /* the last fw rate [Mbps] we used for this link */ + u8 fw_rate_mbps; + + /* The wlvif this link belongs to. Might be null for global links */ + struct cc33xx_vif *wlvif; + + /* total freed FW packets on the link - used for tracking the + * AES/TKIP PN across recoveries. Re-initialized each time + * from the cc33xx_station structure. + */ + u64 total_freed_pkts; +}; + +#define CC33XX_MAX_RX_FILTERS 5 +#define CC33XX_RX_FILTER_MAX_FIELDS 8 + +#define CC33XX_RX_FILTER_ETH_HEADER_SIZE 14 +#define CC33XX_RX_FILTER_MAX_FIELDS_SIZE 95 +#define RX_FILTER_FIELD_OVERHEAD \ + (sizeof(struct cc33xx_rx_filter_field) - sizeof(u8 *)) +#define CC33XX_RX_FILTER_MAX_PATTERN_SIZE \ + (CC33XX_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD) + +#define CC33XX_RX_FILTER_FLAG_IP_HEADER 0 +#define CC33XX_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1) + +struct ieee80211_header { + __le16 frame_ctl; + __le16 duration_id; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[]; +} __packed; + +enum rx_filter_action { + FILTER_DROP = 0, + FILTER_SIGNAL = 1, + FILTER_FW_HANDLE = 2 +}; + +enum plt_mode { + PLT_OFF = 0, + PLT_ON = 1, + PLT_FEM_DETECT = 2, + PLT_CHIP_AWAKE = 3 +}; + +struct cc33xx_rx_filter_field { + __le16 offset; + u8 len; + u8 flags; + u8 *pattern; +} __packed; + +struct cc33xx_rx_filter { + u8 action; + int num_fields; + struct cc33xx_rx_filter_field fields[CC33XX_RX_FILTER_MAX_FIELDS]; +}; + +struct cc33xx_station { + u8 hlid; + bool in_connection; + + /* total freed FW packets on the link to the STA - used for tracking the + * AES/TKIP PN across recoveries. Re-initialized each time from the + * cc33xx_station structure. + * Used in both AP and STA mode. + */ + u64 total_freed_pkts; +}; + +struct cc33xx_vif { + struct cc33xx *cc; + struct list_head list; + unsigned long flags; + u8 bss_type; + u8 p2p; /* we are using p2p role */ + u8 role_id; + + /* sta/ibss specific */ + u8 dev_role_id; + u8 dev_hlid; + + union { + struct { + u8 hlid; + + u8 basic_rate_idx; + u8 ap_rate_idx; + u8 p2p_rate_idx; + + bool qos; + /* channel type we started the STA role with */ + enum nl80211_channel_type role_chan_type; + } sta; + struct { + u8 global_hlid; + u8 bcast_hlid; + + /* HLIDs bitmap of associated stations */ + unsigned long sta_hlid_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)]; + + /* recoreded keys - set here before AP startup */ + struct cc33xx_ap_key *recorded_keys[MAX_NUM_KEYS]; + + u8 mgmt_rate_idx; + u8 bcast_rate_idx; + u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT]; + } ap; + }; + + /* the hlid of the last transmitted skb */ + int last_tx_hlid; + + /* counters of packets per AC, across all links in the vif */ + int tx_queue_count[NUM_TX_QUEUES]; + + unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)]; + + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 ssid_len; + + /* The current band */ + enum nl80211_band band; + int channel; + enum nl80211_channel_type channel_type; + + u32 bitrate_masks[CC33XX_NUM_BANDS]; + u32 basic_rate_set; + + /* currently configured rate set: + * bits 0-15 - 802.11abg rates + * bits 16-23 - 802.11n MCS index mask + * support only 1 stream, thus only 8 bits for the MCS rates (0-7). + */ + u32 basic_rate; + u32 rate_set; + + /* probe-req template for the current AP */ + struct sk_buff *probereq; + + /* Beaconing interval (needed for ad-hoc) */ + u32 beacon_int; + + /* Default key (for WEP) */ + u32 default_key; + + /* Our association ID */ + u16 aid; + + /* retry counter for PSM entries */ + u8 psm_entry_retry; + + /* in dBm */ + int power_level; + + int rssi_thold; + int last_rssi_event; + + /* save the current encryption type for auto-arp config */ + u8 encryption_type; + __be32 ip_addr; + + /* RX BA constraint value */ + bool ba_support; + bool ba_allowed; + + bool wmm_enabled; + + bool radar_enabled; + + struct delayed_work channel_switch_work; + struct delayed_work connection_loss_work; + + /* number of in connection stations */ + int inconn_count; + + /* This vif's queues are mapped to mac80211 HW queues as: + * VO - hw_queue_base + * VI - hw_queue_base + 1 + * BE - hw_queue_base + 2 + * BK - hw_queue_base + 3 + */ + int hw_queue_base; + + /* do we have a pending auth reply? (and ROC) */ + bool ap_pending_auth_reply; + + /* time when we sent the pending auth reply */ + unsigned long pending_auth_reply_time; + + /* work for canceling ROC after pending auth reply */ + struct delayed_work pending_auth_complete_work; + + struct delayed_work roc_timeout_work; + + /* update rate control */ + enum ieee80211_sta_rx_bandwidth rc_update_bw; + struct ieee80211_sta_ht_cap rc_ht_cap; + struct work_struct rc_update_work; + + /* total freed FW packets on the link. + * For STA this holds the PN of the link to the AP. + * For AP this holds the PN of the broadcast link. + */ + u64 total_freed_pkts; + + /* for MBSSID: this BSS is a nontransmitted BSS profile + * Relevant for STA role + */ + bool nontransmitted; + + /* for MBSSID: update transmitter BSSID */ + u8 transmitter_bssid[ETH_ALEN]; + + /* for MBSSID: BSSID index */ + u8 bssid_index; + + /* for MBSSID: BSSID indicator */ + u8 bssid_indicator; + + /* for STA: if connection established and has HE support*/ + u8 sta_has_he; + + /* This struct must be last! + * data that has to be saved acrossed reconfigs (e.g. recovery) + * should be declared in this struct. + */ + u8 persistent[]; +}; + +static inline struct cc33xx_vif *cc33xx_vif_to_data(struct ieee80211_vif *vif) +{ + WARN_ON(!vif); + return (struct cc33xx_vif *)vif->drv_priv; +} + +static inline +struct ieee80211_vif *cc33xx_wlvif_to_vif(struct cc33xx_vif *wlvif) +{ + return container_of((void *)wlvif, struct ieee80211_vif, drv_priv); +} + +static inline bool cc33xx_is_p2p_mgmt(struct cc33xx_vif *wlvif) +{ + return cc33xx_wlvif_to_vif(wlvif)->type == NL80211_IFTYPE_P2P_DEVICE; +} + +#define cc33xx_for_each_wlvif(cc, wlvif) \ + list_for_each_entry(wlvif, &(cc)->wlvif_list, list) + +#define cc33xx_for_each_wlvif_continue(cc, wlvif) \ + list_for_each_entry_continue(wlvif, &(cc)->wlvif_list, list) + +#define cc33xx_for_each_wlvif_bss_type(cc, wlvif, _bss_type) \ + cc33xx_for_each_wlvif((cc), (wlvif)) \ + if ((wlvif)->bss_type == (_bss_type)) + +#define cc33xx_for_each_wlvif_sta(cc, wlvif) \ + cc33xx_for_each_wlvif_bss_type(cc, wlvif, BSS_TYPE_STA_BSS) + +#define cc33xx_for_each_wlvif_ap(cc, wlvif) \ + cc33xx_for_each_wlvif_bss_type(cc, wlvif, BSS_TYPE_AP_BSS) + +int cc33xx_plt_start(struct cc33xx *cc, const enum plt_mode plt_mode); +int cc33xx_plt_stop(struct cc33xx *cc); +void cc33xx_queue_recovery_work(struct cc33xx *cc); +void cc33xx_flush_deferred_work(struct cc33xx *cc); + +#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */ + +#define CC33XX_MAX_TXPWR 21 /* maximum power limit is 21dBm */ +#define CC33XX_MIN_TXPWR -10 /* minimum power limit is -10dBm */ + +#define CC33XX_TX_QUEUE_LOW_WATERMARK 32 +#define CC33XX_TX_QUEUE_HIGH_WATERMARK 256 + +#define CC33XX_RX_QUEUE_MAX_LEN 256 + +/* cc33xx needs a 200ms sleep after power on, and a 20ms sleep before power + * on in case is has been shut down shortly before + */ +#define CC33XX_PRE_POWER_ON_SLEEP 20 /* in milliseconds */ +#define CC33XX_POWER_ON_SLEEP 200 /* in milliseconds */ + +/* Macros to handle cc33xx.sta_rate_set */ +#define HW_HT_RATES_OFFSET 16 +#define HW_MIMO_RATES_OFFSET 24 + +#endif /* __CC33XX_I_H__ */ From patchwork Tue Oct 29 17:23:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839579 Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 503C72076D0; Tue, 29 Oct 2024 17:24:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.142 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222686; cv=none; b=iiqxnrPrx9f6RuQLga5cswDN3odznFe0TOhBSjcdwSuoApBEaekFxCyzVfMKilr5qfGifNAgQyPmwCG/tUwZUOJJWQlNyXLZG6LmPINv/ut0H75o421PZ8xxWWb3z/SPvlyIOjT/jAUJ9PE7WoFnwGZj3P3y6NBZf7YWep3ylm4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222686; c=relaxed/simple; bh=OCXE7jdnVwRbXDS81VKl78/LRJcsBXbwonohCxh/dKw=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=rnuhBmNOfNEUyFx8vMxnvFrIuLs+bS4eLJI+qMpyE/pBTPSSenstgRgM0Rinx4JCYUEZMrwtVl/dfCZGtUlRR6YPZk4KqCBquPiY07bp2BX9aCtGx4PD9jxVKoPW0RcnvOO6ACd3i4Vbf8uYS2cpOGWNbGBOimZZuc2JJTO1PIM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=naS1sv1U; arc=none smtp.client-ip=198.47.19.142 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="naS1sv1U" Received: from lelv0266.itg.ti.com ([10.180.67.225]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOWwC014520; Tue, 29 Oct 2024 12:24:32 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222672; bh=ELmaoCs3H7ROmXt6ePDqZDrp58+X0VN3FKWMwLmSGwo=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=naS1sv1UAp9q0uITrWhSAoG+cuuRuSYz+e/nzX9yVm6oL9hJUmeq9fVFPh/9+0aGi qWof+rxVkSKNWtlpeOV8ogavrnBA8tjZWbjRqyCY6UnenCS8mlPMvYnQwz9qbi5WcN OWkTT7qlAoQ4m/nxiyc2E5oOgN8IwUWco4JQhMoE= Received: from DLEE107.ent.ti.com (dlee107.ent.ti.com [157.170.170.37]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOWDR068237; Tue, 29 Oct 2024 12:24:32 -0500 Received: from DLEE102.ent.ti.com (157.170.170.32) by DLEE107.ent.ti.com (157.170.170.37) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:31 -0500 Received: from lelvsmtp5.itg.ti.com (10.180.75.250) by DLEE102.ent.ti.com (157.170.170.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:31 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp5.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOUsL006392; Tue, 29 Oct 2024 12:24:31 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 07/17] wifi: cc33xx: Add event.c, event.h Date: Tue, 29 Oct 2024 19:23:44 +0200 Message-ID: <20241029172354.4027886-8-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Unlike in wlcore, events are queued on linked list (cc->event_list) and are handled outside the IRQ context. This will be more clear when looking at main.c Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/event.c | 363 +++++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/event.h | 71 +++++ 2 files changed, 434 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/event.c create mode 100644 drivers/net/wireless/ti/cc33xx/event.h diff --git a/drivers/net/wireless/ti/cc33xx/event.c b/drivers/net/wireless/ti/cc33xx/event.c new file mode 100644 index 000000000000..48542a29323e --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/event.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "acx.h" +#include "event.h" +#include "ps.h" +#include "io.h" +#include "scan.h" + +#define CC33XX_WAIT_EVENT_FAST_POLL_COUNT 20 + +struct cc33xx_event_mailbox { + __le32 events_vector; + + u8 number_of_scan_results; + u8 number_of_sched_scan_results; + + __le16 channel_switch_role_id_bitmap; + + s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; + + /* bitmap of removed links */ + __le32 hlid_removed_bitmap; + + /* rx ba constraint */ + __le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */ + __le16 rx_ba_allowed_bitmap; + + /* bitmap of roc completed (by role id) */ + __le16 roc_completed_bitmap; + + /* bitmap of stations (by role id) with bss loss */ + __le16 bss_loss_bitmap; + + /* bitmap of stations (by HLID) which exceeded max tx retries */ + __le16 tx_retry_exceeded_bitmap; + + /* time sync high msb*/ + __le16 time_sync_tsf_high_msb; + + /* bitmap of inactive stations (by HLID) */ + __le16 inactive_sta_bitmap; + + /* time sync high lsb*/ + __le16 time_sync_tsf_high_lsb; + + /* rx BA win size indicated by RX_BA_WIN_SIZE_CHANGE_EVENT_ID */ + u8 rx_ba_role_id; + u8 rx_ba_link_id; + u8 rx_ba_win_size; + u8 padding; + + /* smart config */ + u8 sc_ssid_len; + u8 sc_pwd_len; + u8 sc_token_len; + u8 padding1; + u8 sc_ssid[32]; + u8 sc_pwd[64]; + u8 sc_token[32]; + + /* smart config sync channel */ + u8 sc_sync_channel; + u8 sc_sync_band; + + /* time sync low msb*/ + __le16 time_sync_tsf_low_msb; + + /* radar detect */ + u8 radar_channel; + u8 radar_type; + + /* time sync low lsb*/ + __le16 time_sync_tsf_low_lsb; + + u8 ble_event[260]; + +} __packed; + +struct event_node { + struct llist_node node; + struct cc33xx_event_mailbox event_data; +}; + +void deffer_event(struct cc33xx *cc, + const void *event_payload, size_t event_length) +{ + struct event_node *event_node; + bool ret; + + if (WARN_ON(event_length != sizeof(event_node->event_data))) + return; + + event_node = kzalloc(sizeof(*event_node), GFP_KERNEL); + if (WARN_ON(!event_node)) + return; + + memcpy(&event_node->event_data, + event_payload, sizeof(event_node->event_data)); + + llist_add(&event_node->node, &cc->event_list); + ret = queue_work(cc->freezable_wq, &cc->irq_deferred_work); +} + +static inline struct llist_node *get_event_list(struct cc33xx *cc) +{ + struct llist_node *node; + + node = llist_del_all(&cc->event_list); + if (!node) + return NULL; + + return llist_reverse_order(node); +} + +void flush_deferred_event_list(struct cc33xx *cc) +{ + struct event_node *event_node, *tmp; + struct llist_node *event_list; + + event_list = get_event_list(cc); + llist_for_each_entry_safe(event_node, tmp, event_list, node) { + kfree(event_node); + } +} + +static int wait_for_event_or_timeout(struct cc33xx *cc, u32 mask, bool *timeout) +{ + u32 event; + unsigned long timeout_time; + u16 poll_count = 0; + int ret = 0; + struct event_node *event_node, *tmp; + struct llist_node *event_list; + u32 vector; + + *timeout = false; + + timeout_time = jiffies + msecs_to_jiffies(CC33XX_EVENT_TIMEOUT); + + do { + if (time_after(jiffies, timeout_time)) { + *timeout = true; + goto out; + } + + poll_count++; + if (poll_count < CC33XX_WAIT_EVENT_FAST_POLL_COUNT) + usleep_range(50, 51); + else + usleep_range(1000, 5000); + + vector = 0; + event_list = get_event_list(cc); + llist_for_each_entry_safe(event_node, tmp, event_list, node) { + vector |= le32_to_cpu(event_node->event_data.events_vector); + } + + event = vector & mask; + } while (!event); + +out: + + return ret; +} + +int cc33xx_wait_for_event(struct cc33xx *cc, enum cc33xx_wait_event event, + bool *timeout) +{ + u32 local_event; + + switch (event) { + case CC33XX_EVENT_PEER_REMOVE_COMPLETE: + local_event = PEER_REMOVE_COMPLETE_EVENT_ID; + break; + + case CC33XX_EVENT_DFS_CONFIG_COMPLETE: + local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT; + break; + + default: + /* event not implemented */ + return 0; + } + return wait_for_event_or_timeout(cc, local_event, timeout); +} + +static void cc33xx_event_sched_scan_completed(struct cc33xx *cc, u8 status) +{ + if (cc->mac80211_scan_stopped) { + cc->mac80211_scan_stopped = false; + } else { + if (cc->sched_vif) { + ieee80211_sched_scan_stopped(cc->hw); + cc->sched_vif = NULL; + } + } +} + +static void cc33xx_event_channel_switch(struct cc33xx *cc, + unsigned long roles_bitmap, + bool success) +{ + struct cc33xx_vif *wlvif; + struct ieee80211_vif *vif; + + cc33xx_for_each_wlvif(cc, wlvif) { + if (wlvif->role_id == CC33XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id, &roles_bitmap)) + continue; + + if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, + &wlvif->flags)) + continue; + + vif = cc33xx_wlvif_to_vif(wlvif); + + if (wlvif->bss_type == BSS_TYPE_STA_BSS) { + ieee80211_chswitch_done(vif, success, 0); + cancel_delayed_work(&wlvif->channel_switch_work); + } else { + set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags); + ieee80211_csa_finish(vif, 0); + } + } +} + +static void cc33xx_disconnect_sta(struct cc33xx *cc, unsigned long sta_bitmap) +{ + u32 num_packets = cc->conf.host_conf.tx.max_tx_retries; + struct cc33xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + const u8 *addr; + int h; + + for_each_set_bit(h, &sta_bitmap, CC33XX_MAX_LINKS) { + bool found = false; + /* find the ap vif connected to this sta */ + cc33xx_for_each_wlvif_ap(cc, wlvif) { + if (!test_bit(h, wlvif->ap.sta_hlid_map)) + continue; + found = true; + break; + } + if (!found) + continue; + + vif = cc33xx_wlvif_to_vif(wlvif); + addr = cc->links[h].addr; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, addr); + if (sta) + ieee80211_report_low_ack(sta, num_packets); + + rcu_read_unlock(); + } +} + +static void cc33xx_event_max_tx_failure(struct cc33xx *cc, + unsigned long sta_bitmap) +{ + cc33xx_disconnect_sta(cc, sta_bitmap); +} + +static void cc33xx_event_roc_complete(struct cc33xx *cc) +{ + if (cc->roc_vif) + ieee80211_ready_on_channel(cc->hw); +} + +static void cc33xx_event_beacon_loss(struct cc33xx *cc, + unsigned long roles_bitmap) +{ + /* We are HW_MONITOR device. On beacon loss - queue + * connection loss work. Cancel it on REGAINED event. + */ + struct cc33xx_vif *wlvif; + struct ieee80211_vif *vif; + int delay = cc->conf.host_conf.conn.synch_fail_thold; + + delay *= cc->conf.host_conf.conn.bss_lose_timeout; + + cc33xx_for_each_wlvif_sta(cc, wlvif) { + if (wlvif->role_id == CC33XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id, &roles_bitmap)) + continue; + + vif = cc33xx_wlvif_to_vif(wlvif); + + /* don't attempt roaming in case of p2p */ + if (wlvif->p2p) { + ieee80211_connection_loss(vif); + continue; + } + + /* if the work is already queued, it should take place. + * We don't want to delay the connection loss + * indication any more. + */ + ieee80211_queue_delayed_work(cc->hw, + &wlvif->connection_loss_work, + msecs_to_jiffies(delay)); + + ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL); + } +} + +void process_deferred_events(struct cc33xx *cc) +{ + struct event_node *event_node, *tmp; + struct llist_node *event_list; + u32 vector; + + event_list = get_event_list(cc); + + llist_for_each_entry_safe(event_node, tmp, event_list, node) { + struct cc33xx_event_mailbox *event_data; + + event_data = &event_node->event_data; + + vector = le32_to_cpu(event_node->event_data.events_vector); + + if (vector & SCAN_COMPLETE_EVENT_ID) { + if (cc->scan_wlvif) + cc33xx_scan_completed(cc, cc->scan_wlvif); + } + + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) + cc33xx_event_sched_scan_completed(cc, 1); + + if (vector & BSS_LOSS_EVENT_ID) { + u16 bss_loss_bitmap = le16_to_cpu(event_data->bss_loss_bitmap); + + cc33xx_event_beacon_loss(cc, bss_loss_bitmap); + } + + if (vector & MAX_TX_FAILURE_EVENT_ID) { + u16 tx_retry_exceeded_bitmap = + le16_to_cpu(event_data->tx_retry_exceeded_bitmap); + + cc33xx_event_max_tx_failure(cc, tx_retry_exceeded_bitmap); + } + + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) + cc33xx_scan_sched_scan_results(cc); + + if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) { + u16 channel_switch_role_id_bitmap = + le16_to_cpu(event_data->channel_switch_role_id_bitmap); + + cc33xx_event_channel_switch(cc, channel_switch_role_id_bitmap, true); + } + + if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) + cc33xx_event_roc_complete(cc); + + kfree(event_node); + } +} diff --git a/drivers/net/wireless/ti/cc33xx/event.h b/drivers/net/wireless/ti/cc33xx/event.h new file mode 100644 index 000000000000..7952f0b7b4aa --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/event.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __EVENT_H__ +#define __EVENT_H__ + +/* Mbox events + * + * The event mechanism is based on a pair of event buffers (buffers A and + * B) at fixed locations in the target's memory. The host processes one + * buffer while the other buffer continues to collect events. If the host + * is not processing events, an interrupt is issued to signal that a buffer + * is ready. Once the host is done with processing events from one buffer, + * it signals the target (with an ACK interrupt) that the event buffer is + * free. + */ + +enum { + RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0), + RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1), + RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2), + RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3), + RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4), + RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5), + RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6), + RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7), + + EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, +}; + +enum { + SCAN_COMPLETE_EVENT_ID = BIT(8), + RADAR_DETECTED_EVENT_ID = BIT(9), + CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(10), + BSS_LOSS_EVENT_ID = BIT(11), + MAX_TX_FAILURE_EVENT_ID = BIT(12), + DUMMY_PACKET_EVENT_ID = BIT(13), + INACTIVE_STA_EVENT_ID = BIT(14), + PEER_REMOVE_COMPLETE_EVENT_ID = BIT(15), + PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(16), + BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(17), + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), + DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), + PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), + RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21), + SMART_CONFIG_SYNC_EVENT_ID = BIT(22), + SMART_CONFIG_DECODE_EVENT_ID = BIT(23), + TIME_SYNC_EVENT_ID = BIT(24), + FW_LOGGER_INDICATION = BIT(25), +}; + +/* events the driver might want to wait for */ +enum cc33xx_wait_event { + CC33XX_EVENT_ROLE_STOP_COMPLETE, + CC33XX_EVENT_PEER_REMOVE_COMPLETE, + CC33XX_EVENT_DFS_CONFIG_COMPLETE +}; + +#define NUM_OF_RSSI_SNR_TRIGGERS 8 + +struct cc33xx; + +int cc33xx_wait_for_event(struct cc33xx *cc, enum cc33xx_wait_event event, + bool *timeout); +void deffer_event(struct cc33xx *cc, const void *event_payload, size_t event_length); +void process_deferred_events(struct cc33xx *cc); +void flush_deferred_event_list(struct cc33xx *cc); + +#endif /* __EVENT_H__ */ From patchwork Tue Oct 29 17:23:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839580 Received: from lelv0143.ext.ti.com (lelv0143.ext.ti.com [198.47.23.248]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1A662076CA; Tue, 29 Oct 2024 17:24:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.248 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222684; cv=none; b=QdTMtH+FWM0ieIpTRnKgNZ8Q02x8IPk9LWBTDOnfVipqRPOU3jH5jzYHuaE/sI5ykT0lHeNmu/fQlb0N8xEfdx7yT4w7vsxGYwk/TMPTmy+diu9oIgWSfhRdYppykmywx8IRzT1cz/LfTUF/i2i1A+ZfeouP7lc+JVnrai0+h9s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222684; c=relaxed/simple; bh=a0k1wa1qqreq2w28rkenT6P63WxFlhXBlGN7dCS5o9U=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JNSs8mkOzxwLaO2N1DKGSKkt+rMhusEwd6v+zlnrgSQPOGm00teVnFkoP2/oeeJZWzNiPzDqk+D0b/ovFFcOgVvN5BtrIZ7pSiifCXMg+4hZmpxE6LsdUP8MnnpaUV1GxRRYz0+aiskyczm/DxW9JBGKx7L4Od5N32eVYoJh8Mk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=YSp0iGTB; arc=none smtp.client-ip=198.47.23.248 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="YSp0iGTB" Received: from lelv0266.itg.ti.com ([10.180.67.225]) by lelv0143.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOXbA051055; Tue, 29 Oct 2024 12:24:33 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222673; bh=k+c/Gd8lP6TLmr43PVUGJJiCdYAdoDz0yeqhgRRqme8=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=YSp0iGTBjvDzeAIyzp4DbWg1hhU+Kmjqe3S9NmzfdzoA5egawtHiNfGLGkYTazWvi ddF+ukhOe2lR8XOIfD/4fqzaJvcOJOOTJbBmuT4pbiDLUThYY0sXL1uiqdmePR7Ed/ e7x1IfC2avCEXZoy3v7x9QR/iSJJkLr2kFIlaSM0= Received: from DLEE103.ent.ti.com (dlee103.ent.ti.com [157.170.170.33]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOXbq068243; Tue, 29 Oct 2024 12:24:33 -0500 Received: from DLEE101.ent.ti.com (157.170.170.31) by DLEE103.ent.ti.com (157.170.170.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:32 -0500 Received: from lelvsmtp5.itg.ti.com (10.180.75.250) by DLEE101.ent.ti.com (157.170.170.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:32 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp5.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOWDk006401; Tue, 29 Oct 2024 12:24:32 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 08/17] wifi: cc33xx: Add boot.c, boot.h Date: Tue, 29 Oct 2024 19:23:45 +0200 Message-ID: <20241029172354.4027886-9-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Implements FW download for CC33xx. The FW comes in 2 parts - a 2nd stage bootloader (cc33xx_2nd_loader.bin) and the actual FW (cc33xx_fw.bin). Each file is requested from user space, and transferred to device chunk by chunk. A dedicated IRQ is excepted after each stage (Device power-on -> 2nd stage loader -> FW). This logic is implemnted in cc33xx_init_fw. Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/boot.c | 345 ++++++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/boot.h | 24 ++ 2 files changed, 369 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/boot.c create mode 100644 drivers/net/wireless/ti/cc33xx/boot.h diff --git a/drivers/net/wireless/ti/cc33xx/boot.c b/drivers/net/wireless/ti/cc33xx/boot.c new file mode 100644 index 000000000000..25efbe937837 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/boot.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include + +#include "boot.h" +#include "cmd.h" +#include "debug.h" +#include "init.h" +#include "io.h" + +#define CC33XX_BOOT_TIMEOUT 2000 + +struct hwinfo_bitmap { + u32 disable_5g : 1u; + u32 disable_6g : 1u; + u32 disable_ble : 1u; + u32 disable_ble_m0plus : 1u; + u32 disable_m33 : 1u; + u64 udi : 64u; + u32 pg_version : 4u; + u32 metal_version : 4u; + u32 boot_rom_version : 4u; + u32 m3_rom_version : 4u; + u32 fuse_rom_structure_version : 4u; + u64 mac_address : 48u; + u32 device_part_number : 6u; + u32 package_type : 4u; + u32 fw_rollback_protection_1 : 32u; + u32 fw_rollback_protection_2 : 32u; + u32 fw_rollback_protection_3 : 32u; + u32 reserved : 13u; +} /* Aligned with boot code, must not be __packed */; + +union hw_info { + struct hwinfo_bitmap bitmap; + u8 bytes[sizeof(struct hwinfo_bitmap)]; +}; + +/* Called from threaded irq context */ +void cc33xx_handle_boot_irqs(struct cc33xx *cc, u32 pending_interrupts) +{ + if (WARN_ON(!cc->fw_download)) + return; + + atomic_or(pending_interrupts, &cc->fw_download->pending_irqs); + complete(&cc->fw_download->wait_on_irq); +} + +static u8 *fetch_container(struct cc33xx *cc, const char *container_name, + size_t *container_len) +{ + u8 *container_data = NULL; + const struct firmware *container; + int ret; + + ret = request_firmware(&container, container_name, cc->dev); + + if (ret < 0) { + cc33xx_error("could not get container %s: (%d)", + container_name, ret); + return NULL; + } + + if (container->size % 4) { + cc33xx_error("container size is not word-aligned: %zu", + container->size); + goto out; + } + + *container_len = container->size; + container_data = vmalloc(container->size); + + if (!container_data) { + cc33xx_error("could not allocate memory for the container"); + goto out; + } + + memcpy(container_data, container->data, container->size); + +out: + release_firmware(container); + return container_data; +} + +static int cc33xx_set_power_on(struct cc33xx *cc) +{ + int ret; + + msleep(CC33XX_PRE_POWER_ON_SLEEP); + ret = cc33xx_power_on(cc); + if (ret < 0) + goto out; + msleep(CC33XX_POWER_ON_SLEEP); + cc33xx_io_reset(cc); + cc33xx_io_init(cc); + +out: + return ret; +} + +static int cc33xx_chip_wakeup(struct cc33xx *cc) +{ + int ret = 0; + + ret = cc33xx_set_power_on(cc); + if (ret < 0) + goto out; + + if (!cc33xx_set_block_size(cc)) + cc->quirks &= ~CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN; + +out: + return ret; +} + +static int wait_for_boot_irq(struct cc33xx *cc, u32 boot_irq_mask, + unsigned long timeout) +{ + int ret; + u32 pending_irqs; + struct cc33xx_fw_download *fw_download; + + fw_download = cc->fw_download; + + ret = wait_for_completion_interruptible_timeout(&fw_download->wait_on_irq, + msecs_to_jiffies(timeout)); + + /* Fetch pending IRQs while clearing them in fw_download */ + pending_irqs = atomic_fetch_and(0, &fw_download->pending_irqs); + pending_irqs &= ~HINT_COMMAND_COMPLETE; + + reinit_completion(&fw_download->wait_on_irq); + + if (ret == 0) { + cc33xx_error("boot IRQ timeout"); + return -1; + } else if (ret < 0) { + cc33xx_error("boot IRQ completion error %d", ret); + return -2; + } + + if (boot_irq_mask != pending_irqs) { + cc33xx_error("Unexpected IRQ received @ boot: 0x%x", + pending_irqs); + return -3; + } + + return 0; +} + +static int download_container(struct cc33xx *cc, u8 *container, size_t len) +{ + int ret = 0; + u8 *current_transfer; + size_t current_transfer_size; + u8 *const container_end = container + len; + size_t max_transfer_size = cc->fw_download->max_transfer_size; + bool is_last_transfer; + + current_transfer = container; + + while (current_transfer < container_end) { + current_transfer_size = container_end - current_transfer; + current_transfer_size = + min(current_transfer_size, max_transfer_size); + + is_last_transfer = (current_transfer + current_transfer_size >= container_end); + + ret = cmd_download_container_chunk(cc, + current_transfer, + current_transfer_size, + is_last_transfer); + + current_transfer += current_transfer_size; + + if (ret < 0) { + cc33xx_error("Chunk transfer failed"); + goto out; + } + } + +out: + return ret; +} + +static int container_download_and_wait(struct cc33xx *cc, + const char *container_name, + const u32 irq_wait_mask) +{ + int ret = -1; + u8 *container_data; + size_t container_len; + + container_data = fetch_container(cc, container_name, &container_len); + if (!container_data) + return ret; + + ret = download_container(cc, container_data, container_len); + if (ret < 0) { + cc33xx_error("Transfer error while downloading %s", + container_name); + goto out; + } + + ret = wait_for_boot_irq(cc, irq_wait_mask, CC33XX_BOOT_TIMEOUT); + + if (ret < 0) { + cc33xx_error("%s boot signal timeout", container_name); + goto out; + } + + ret = 0; + +out: + vfree(container_data); + return ret; +} + +static int fw_download_alloc(struct cc33xx *cc) +{ + if (WARN_ON(cc->fw_download)) + return -EFAULT; + + cc->fw_download = kzalloc(sizeof(*cc->fw_download), GFP_KERNEL); + if (!cc->fw_download) + return -ENOMEM; + + init_completion(&cc->fw_download->wait_on_irq); + + return 0; +} + +static void fw_download_free(struct cc33xx *cc) +{ + if (WARN_ON(!cc->fw_download)) + return; + + kfree(cc->fw_download); + cc->fw_download = NULL; +} + +static int get_device_info(struct cc33xx *cc) +{ + int ret; + union hw_info hw_info; + u64 mac_address; + + ret = cmd_get_device_info(cc, hw_info.bytes, sizeof(hw_info.bytes)); + if (ret < 0) + return ret; + + cc->fw_download->max_transfer_size = 640; + + mac_address = hw_info.bitmap.mac_address; + + cc->fuse_rom_structure_version = hw_info.bitmap.fuse_rom_structure_version; + cc->pg_version = hw_info.bitmap.pg_version; + cc->device_part_number = hw_info.bitmap.device_part_number; + cc->disable_5g = hw_info.bitmap.disable_5g; + cc->disable_6g = hw_info.bitmap.disable_6g; + + cc->efuse_mac_address[5] = (u8)(mac_address); + cc->efuse_mac_address[4] = (u8)(mac_address >> 8); + cc->efuse_mac_address[3] = (u8)(mac_address >> 16); + cc->efuse_mac_address[2] = (u8)(mac_address >> 24); + cc->efuse_mac_address[1] = (u8)(mac_address >> 32); + cc->efuse_mac_address[0] = (u8)(mac_address >> 40); + + return 0; +} + +int cc33xx_init_fw(struct cc33xx *cc) +{ + int ret; + + cc->max_cmd_size = CC33XX_CMD_MAX_SIZE; + + ret = fw_download_alloc(cc); + if (ret < 0) + return ret; + + reinit_completion(&cc->fw_download->wait_on_irq); + + ret = cc33xx_chip_wakeup(cc); + if (ret < 0) + goto power_off; + + cc33xx_enable_interrupts(cc); + + ret = wait_for_boot_irq(cc, HINT_ROM_LOADER_INIT_COMPLETE, + CC33XX_BOOT_TIMEOUT); + if (ret < 0) + goto disable_irq; + + ret = get_device_info(cc); + if (ret < 0) + goto disable_irq; + + ret = container_download_and_wait(cc, SECOND_LOADER_NAME, + HINT_SECOND_LOADER_INIT_COMPLETE); + if (ret < 0) + goto disable_irq; + + ret = container_download_and_wait(cc, FW_NAME, + HINT_FW_WAKEUP_COMPLETE); + if (ret < 0) + goto disable_irq; + + ret = cc33xx_download_ini_params_and_wait(cc); + + if (ret < 0) + goto disable_irq; + + ret = wait_for_boot_irq(cc, HINT_FW_INIT_COMPLETE, CC33XX_BOOT_TIMEOUT); + + if (ret < 0) + goto disable_irq; + + ret = cc33xx_hw_init(cc); + if (ret < 0) + goto disable_irq; + + /* Now we know if 11a is supported (info from the INI File), so disable + * 11a channels if not supported + */ + cc->enable_11a = cc->conf.core.enable_5ghz; + + cc->state = CC33XX_STATE_ON; + ret = 0; + goto out; + +disable_irq: + cc33xx_disable_interrupts_nosync(cc); + +power_off: + cc33xx_power_off(cc); + +out: + fw_download_free(cc); + return ret; +} diff --git a/drivers/net/wireless/ti/cc33xx/boot.h b/drivers/net/wireless/ti/cc33xx/boot.h new file mode 100644 index 000000000000..d5b7763dcd0f --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/boot.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __BOOT_H__ +#define __BOOT_H__ + +#include "cc33xx.h" + +int cc33xx_init_fw(struct cc33xx *cc); + +void cc33xx_handle_boot_irqs(struct cc33xx *cc, u32 pending_interrupts); + +#define SECOND_LOADER_NAME "ti-connectivity/cc33xx_2nd_loader.bin" +#define FW_NAME "ti-connectivity/cc33xx_fw.bin" + +struct cc33xx_fw_download { + atomic_t pending_irqs; + struct completion wait_on_irq; + size_t max_transfer_size; +}; + +#endif /* __BOOT_H__ */ From patchwork Tue Oct 29 17:23:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839578 Received: from lelv0143.ext.ti.com (lelv0143.ext.ti.com [198.47.23.248]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF44D2076D3; Tue, 29 Oct 2024 17:24:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.248 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222688; cv=none; b=ckusk/IHOHv2Mlqf0EgRdDsSBBGhs938Zj9Gqn/Hg8lgKgI+3ZJ3R89TxZDScZz2zZPKpgCU859Pp/rmlj0SDHTrhJPkiBy7MYWQI0wrAvNjVdFTSkVIUq3qGb63XKyAHTLZhnbJX9xmO+hTKRWXxBNjERR614VOBiwglrVSalo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222688; c=relaxed/simple; bh=yM1VI1Iua73e5f9PaUCH+ruJT0ZAzk2xDJVe1fAekg4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=L6q3auQzTfHW3IviDD7fwTUgRM9AYvbswTevDZJQ+diCC3I6IHe8Yg3tK7GNybppwMLXrJhkOnm2M1imKs+p4ivtw+mMr3mLrrMObHldWcibO483vZJf5o5s+eLHCijssRgRWtAbJbk5/JyaUVlOIHACwWYrcjzE8uPSVzSnoTg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=SiX4f6EN; arc=none smtp.client-ip=198.47.23.248 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="SiX4f6EN" Received: from fllv0035.itg.ti.com ([10.64.41.0]) by lelv0143.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOaTM051066; Tue, 29 Oct 2024 12:24:36 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222676; bh=CuZ5/UV380NviR7khpoggkADuPul6Z2jvwO6MnsB+48=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=SiX4f6ENQPa6HgLZwJOGRGHDmyGgNM0N1rzcgrqhy1ZSFiLIM3l0CSFfmZkGEc0r0 rzj3fEiEjs0/Pe/EBArNZgLWt+dMNqyv/7T6RTyEeoDnmgID8UWNyXUe4nSM8Xt7iB sErvFIjQ3M4KcmUmWV3GMkSCbxTOsZX1PZBaMrjY= Received: from DFLE114.ent.ti.com (dfle114.ent.ti.com [10.64.6.35]) by fllv0035.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 49THOad8092294 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 29 Oct 2024 12:24:36 -0500 Received: from DFLE110.ent.ti.com (10.64.6.31) by DFLE114.ent.ti.com (10.64.6.35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:36 -0500 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DFLE110.ent.ti.com (10.64.6.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:36 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOZMG065740; Tue, 29 Oct 2024 12:24:35 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 10/17] wifi: cc33xx: Add rx.c, rx.h Date: Tue, 29 Oct 2024 19:23:47 +0200 Message-ID: <20241029172354.4027886-11-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Code that handles parsing raw Rx data buffer from HW and, splitting it in to SKBs and handing them to MAC80211. Rx handling starts at cc33xx_rx. Full SKBs are stored at cc->deferred_rx_queue from where they are handed to MAC80211 by calling cc->netstack_work (cc33xx_netstack_work @ main.c). This allows calling ieee80211_rx_ni while new data is being read from HW. Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/rx.c | 388 ++++++++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/rx.h | 86 ++++++ 2 files changed, 474 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/rx.c create mode 100644 drivers/net/wireless/ti/cc33xx/rx.h diff --git a/drivers/net/wireless/ti/cc33xx/rx.c b/drivers/net/wireless/ti/cc33xx/rx.c new file mode 100644 index 000000000000..b6ee293fbb0b --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/rx.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "acx.h" +#include "rx.h" +#include "tx.h" +#include "io.h" + +#define RSSI_LEVEL_BITMASK 0x7F +#define ANT_DIVERSITY_BITMASK BIT(7) +#define ANT_DIVERSITY_SHIFT 7 + +/* Construct the rx status structure for upper layers */ +static void cc33xx_rx_status(struct cc33xx *cc, + struct cc33xx_rx_descriptor *desc, + struct ieee80211_rx_status *status, + u8 beacon, u8 probe_rsp) +{ + memset(status, 0, sizeof(struct ieee80211_rx_status)); + + if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_BG) + status->band = NL80211_BAND_2GHZ; + else if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_J) + status->band = NL80211_BAND_2GHZ; + else if ((desc->flags & CC33XX_RX_DESC_BAND_MASK) == CC33XX_RX_DESC_BAND_A) + status->band = NL80211_BAND_5GHZ; + else + status->band = NL80211_BAND_5GHZ; /* todo -Should be 6GHZ when added */ + + status->rate_idx = cc33xx_rate_to_idx(cc, desc->rate, status->band); + + if (desc->frame_format == CC33xx_VHT) + status->encoding = RX_ENC_VHT; + else if ((desc->frame_format == CC33xx_HT_MF) || + (desc->frame_format == CC33xx_HT_GF)) + status->encoding = RX_ENC_HT; + else if ((desc->frame_format == CC33xx_B_SHORT) || + (desc->frame_format == CC33xx_B_LONG) || + (desc->frame_format == CC33xx_LEGACY_OFDM)) + status->encoding = RX_ENC_LEGACY; + else + status->encoding = RX_ENC_HE; + + /* Read the signal level and antenna diversity indication. + * The msb in the signal level is always set as it is a + * negative number. + * The antenna indication is the msb of the rssi. + */ + status->signal = ((desc->rssi & RSSI_LEVEL_BITMASK) | BIT(7)); + status->antenna = ((desc->rssi & ANT_DIVERSITY_BITMASK) >> ANT_DIVERSITY_SHIFT); + status->freq = ieee80211_channel_to_frequency(desc->channel, + status->band); + + if (desc->flags & CC33XX_RX_DESC_ENCRYPT_MASK) { + u8 desc_err_code = desc->status & CC33XX_RX_DESC_STATUS_MASK; + + /* Frame is sent to driver with the IV (for PN replay check) + * but without the MIC + */ + status->flag |= RX_FLAG_MMIC_STRIPPED | + RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; + + if (unlikely(desc_err_code & CC33XX_RX_DESC_MIC_FAIL)) { + status->flag |= RX_FLAG_MMIC_ERROR; + cc33xx_warning("Michael MIC error. Desc: 0x%x", + desc_err_code); + } + } + + if (beacon || probe_rsp) + status->boottime_ns = ktime_get_boottime_ns(); + + if (beacon) + cc33xx_set_pending_regdomain_ch(cc, (u16)desc->channel, + status->band); + status->nss = 1; +} + +/* Copy part\ all of the descriptor. Allocate skb, or drop corrupted packet + */ +static int cc33xx_rx_get_packet_descriptor(struct cc33xx *cc, u8 *raw_buffer_ptr, + u16 *raw_buffer_len) +{ + u16 missing_desc_bytes; + u16 available_desc_bytes; + u16 pkt_data_len; + struct sk_buff *skb; + u16 prev_buffer_len = *raw_buffer_len; + + missing_desc_bytes = sizeof(struct cc33xx_rx_descriptor); + missing_desc_bytes -= cc->partial_rx.handled_bytes; + available_desc_bytes = min(*raw_buffer_len, missing_desc_bytes); + memcpy(((u8 *)(&cc->partial_rx.desc)) + cc->partial_rx.handled_bytes, + raw_buffer_ptr, available_desc_bytes); + + /* If descriptor was not completed */ + if (available_desc_bytes != missing_desc_bytes) { + cc->partial_rx.handled_bytes += *raw_buffer_len; + cc->partial_rx.status = CURR_RX_DESC; + *raw_buffer_len = 0; + goto out; + } else { + cc->partial_rx.handled_bytes += available_desc_bytes; + *raw_buffer_len -= available_desc_bytes; + } + + /* Descriptor was fully copied */ + pkt_data_len = cc->partial_rx.original_bytes; + pkt_data_len -= sizeof(struct cc33xx_rx_descriptor); + + if (unlikely(cc->partial_rx.desc.status & CC33XX_RX_DESC_DECRYPT_FAIL)) { + cc33xx_warning("corrupted packet in RX: status: 0x%x len: %d", + cc->partial_rx.desc.status & CC33XX_RX_DESC_STATUS_MASK, + pkt_data_len); + + /* If frame can be fully dropped */ + if (pkt_data_len <= *raw_buffer_len) { + *raw_buffer_len -= pkt_data_len; + cc->partial_rx.status = CURR_RX_START; + } else { + cc->partial_rx.handled_bytes += *raw_buffer_len; + cc->partial_rx.status = CURR_RX_DROP; + *raw_buffer_len = 0; + } + goto out; + } + + skb = __dev_alloc_skb(pkt_data_len, GFP_KERNEL); + if (!skb) { + cc33xx_error("Couldn't allocate RX frame"); + /* If frame can be fully dropped */ + if (pkt_data_len <= *raw_buffer_len) { + *raw_buffer_len -= pkt_data_len; + cc->partial_rx.status = CURR_RX_START; + } else { + /* Dropped partial frame */ + cc->partial_rx.handled_bytes += *raw_buffer_len; + cc->partial_rx.status = CURR_RX_DROP; + *raw_buffer_len = 0; + } + goto out; + } + + cc->partial_rx.skb = skb; + cc->partial_rx.status = CURR_RX_DATA; + +out: + /* Function return the amount of consumed bytes */ + return (prev_buffer_len - *raw_buffer_len); +} + +/* Copy part or all of the packet's data. push skb to queue if possible */ +static int cc33xx_rx_get_packet_data(struct cc33xx *cc, u8 *raw_buffer_ptr, + u16 *raw_buffer_len) +{ + u16 missing_data_bytes; + u16 available_data_bytes; + u32 defer_count; + enum cc33xx_rx_buf_align rx_align; + u16 extra_bytes; + struct ieee80211_hdr *hdr; + u8 beacon = 0; + u8 is_probe_resp = 0; + u16 seq_num; + u16 prev_buffer_len = *raw_buffer_len; + + missing_data_bytes = cc->partial_rx.original_bytes; + missing_data_bytes -= cc->partial_rx.handled_bytes; + available_data_bytes = min(missing_data_bytes, *raw_buffer_len); + + skb_put_data(cc->partial_rx.skb, raw_buffer_ptr, available_data_bytes); + + /* Check if we didn't manage to copy the entire packet - got out, + * continue next time + */ + if (available_data_bytes != missing_data_bytes) { + cc->partial_rx.handled_bytes += *raw_buffer_len; + cc->partial_rx.status = CURR_RX_DATA; + *raw_buffer_len = 0; + goto out; + } else { + *raw_buffer_len -= available_data_bytes; + } + + /* Data fully copied */ + + rx_align = cc->partial_rx.desc.header_alignment; + if (rx_align == CC33XX_RX_BUF_PADDED) + skb_pull(cc->partial_rx.skb, RX_BUF_ALIGN); + + extra_bytes = cc->partial_rx.desc.pad_len; + if (extra_bytes != 0) + skb_trim(cc->partial_rx.skb, + cc->partial_rx.skb->len - extra_bytes); + + hdr = (struct ieee80211_hdr *)cc->partial_rx.skb->data; + + if (ieee80211_is_beacon(hdr->frame_control)) + beacon = 1; + if (ieee80211_is_probe_resp(hdr->frame_control)) + is_probe_resp = 1; + + cc33xx_rx_status(cc, &cc->partial_rx.desc, + IEEE80211_SKB_RXCB(cc->partial_rx.skb), + beacon, is_probe_resp); + + seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + cc33xx_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d link id %d", + cc->partial_rx.skb, + cc->partial_rx.skb->len - cc->partial_rx.desc.pad_len, + beacon ? "beacon" : "", seq_num, cc->partial_rx.desc.hlid); + + cc33xx_debug(DEBUG_RX, "rx frame. frame type 0x%x, frame length 0x%x, frame address 0x%lx", + hdr->frame_control, cc->partial_rx.skb->len, + (unsigned long)cc->partial_rx.skb->data); + + /* Adding frame to queue */ + skb_queue_tail(&cc->deferred_rx_queue, cc->partial_rx.skb); + cc->rx_counter++; + cc->partial_rx.status = CURR_RX_START; + + /* Make sure the deferred queues don't get too long */ + defer_count = skb_queue_len(&cc->deferred_tx_queue); + defer_count += skb_queue_len(&cc->deferred_rx_queue); + if (defer_count >= CC33XX_RX_QUEUE_MAX_LEN) + cc33xx_flush_deferred_work(cc); + else + queue_work(cc->freezable_netstack_wq, &cc->netstack_work); + +out: + return (prev_buffer_len - *raw_buffer_len); +} + +static int cc33xx_rx_drop_packet_data(struct cc33xx *cc, u8 *raw_buffer_ptr, + u16 *raw_buffer_len) +{ + u16 prev_buffer_len = *raw_buffer_len; + + /* Can we drop the entire frame ? */ + if (*raw_buffer_len >= + (cc->partial_rx.original_bytes - cc->partial_rx.handled_bytes)) { + *raw_buffer_len -= cc->partial_rx.original_bytes - + cc->partial_rx.handled_bytes; + cc->partial_rx.handled_bytes = 0; + cc->partial_rx.status = CURR_RX_START; + } else { + cc->partial_rx.handled_bytes += *raw_buffer_len; + *raw_buffer_len = 0; + } + + return (prev_buffer_len - *raw_buffer_len); +} + +/* Handle single packet from the RX buffer. We don't have to be aligned to + * packet boundary (buffer may start \ end in the middle of packet) + */ +static void cc33xx_rx_handle_packet(struct cc33xx *cc, u8 *raw_buffer_ptr, + u16 *raw_buffer_len) +{ + struct cc33xx_rx_descriptor *desc; + u16 consumed_bytes; + + if (cc->partial_rx.status == CURR_RX_START) { + WARN_ON(*raw_buffer_len < 2); + desc = (struct cc33xx_rx_descriptor *)raw_buffer_ptr; + cc->partial_rx.original_bytes = le16_to_cpu(desc->length); + cc->partial_rx.handled_bytes = 0; + cc->partial_rx.status = CURR_RX_DESC; + + cc33xx_debug(DEBUG_RX, "rx frame. desc length 0x%x, alignment 0x%x, padding 0x%x", + desc->length, desc->header_alignment, desc->pad_len); + } + + /* start \ continue copy descriptor */ + if (cc->partial_rx.status == CURR_RX_DESC) { + consumed_bytes = cc33xx_rx_get_packet_descriptor(cc, + raw_buffer_ptr, + raw_buffer_len); + raw_buffer_ptr += consumed_bytes; + } + + /* Check if we are in the middle of dropped packet */ + if (unlikely(cc->partial_rx.status == CURR_RX_DROP)) { + consumed_bytes = cc33xx_rx_drop_packet_data(cc, raw_buffer_ptr, + raw_buffer_len); + raw_buffer_ptr += consumed_bytes; + } + + /* start \ continue copy descriptor */ + if (cc->partial_rx.status == CURR_RX_DATA) { + consumed_bytes = cc33xx_rx_get_packet_data(cc, raw_buffer_ptr, + raw_buffer_len); + raw_buffer_ptr += consumed_bytes; + } +} + +/* It is assumed that SDIO buffer was read prior to this function (data buffer + * is read along with the status). The RX function gets pointer to the RX data + * and its length. This buffer may contain unknown number of packets, separated + * by hif descriptor and 0-3 bytes padding if required. + * The last packet may be truncated in the middle, and should be saved for next + * iteration. + */ +int cc33xx_rx(struct cc33xx *cc, u8 *rx_buf_ptr, u16 rx_buf_len) +{ + u16 local_rx_buffer_len = rx_buf_len; + u16 pkt_offset = 0; + u16 consumed_bytes; + u16 prev_rx_buf_len; + + /* Split data into separate packets */ + while (local_rx_buffer_len > 0) { + cc33xx_debug(DEBUG_RX, "start loop. buffer length %d", + local_rx_buffer_len); + + /* the handle data call can only fail in memory-outage + * conditions, in that case the received frame will just + * be dropped. + */ + prev_rx_buf_len = local_rx_buffer_len; + cc33xx_rx_handle_packet(cc, rx_buf_ptr + pkt_offset, + &local_rx_buffer_len); + consumed_bytes = prev_rx_buf_len - local_rx_buffer_len; + + pkt_offset += consumed_bytes; + + cc33xx_debug(DEBUG_RX, "end rx loop. buffer length %d, packet counter %d, current packet status %d", + local_rx_buffer_len, cc->rx_counter, + cc->partial_rx.status); + } + + return 0; +} + +#ifdef CONFIG_PM +int cc33xx_rx_filter_enable(struct cc33xx *cc, int index, bool enable, + struct cc33xx_rx_filter *filter) +{ + int ret; + + if (!!test_bit(index, cc->rx_filter_enabled) == enable) { + cc33xx_warning("Request to enable an already enabled rx filter %d", + index); + return 0; + } + + ret = cc33xx_acx_set_rx_filter(cc, index, enable, filter); + + if (ret) { + cc33xx_error("Failed to %s rx data filter %d (err=%d)", + enable ? "enable" : "disable", index, ret); + return ret; + } + + if (enable) + __set_bit(index, cc->rx_filter_enabled); + else + __clear_bit(index, cc->rx_filter_enabled); + + return 0; +} + +int cc33xx_rx_filter_clear_all(struct cc33xx *cc) +{ + int i, ret = 0; + + for (i = 0; i < CC33XX_MAX_RX_FILTERS; i++) { + if (!test_bit(i, cc->rx_filter_enabled)) + continue; + ret = cc33xx_rx_filter_enable(cc, i, 0, NULL); + if (ret) + goto out; + } + +out: + return ret; +} +#else +int cc33xx_rx_filter_enable(struct cc33xx *cc, int index, bool enable, + struct cc33xx_rx_filter *filter) +{ + return 0; +} + +int cc33xx_rx_filter_clear_all(struct cc33xx *cc) { return 0; } +#endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/ti/cc33xx/rx.h b/drivers/net/wireless/ti/cc33xx/rx.h new file mode 100644 index 000000000000..46ff6867749f --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/rx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __RX_H__ +#define __RX_H__ + +/* RX Descriptor flags: + * + * Bits 0-1 - band + * Bit 2 - STBC + * Bit 3 - A-MPDU + * Bit 4 - HT + * Bits 5-7 - encryption + */ +#define CC33XX_RX_DESC_BAND_MASK 0x03 +#define CC33XX_RX_DESC_ENCRYPT_MASK 0xE0 + +#define CC33XX_RX_DESC_BAND_BG 0x00 +#define CC33XX_RX_DESC_BAND_J 0x01 +#define CC33XX_RX_DESC_BAND_A 0x02 + +/* RX Descriptor status + * + * Bits 0-2 - error code + * Bits 3-5 - process_id tag (AP mode FW) + * Bits 6-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 +}; + +/* Account for the padding inserted by the FW in case of RX_ALIGNMENT + * or for fixing alignment in case the packet wasn't aligned. + */ +#define RX_BUF_ALIGN 2 + +/* Describes the alignment state of a Rx buffer */ +enum cc33xx_rx_buf_align { + CC33XX_RX_BUF_ALIGNED, + CC33XX_RX_BUF_UNALIGNED, + CC33XX_RX_BUF_PADDED, +}; + +enum cc33xx_rx_curr_status { + CURR_RX_START, + CURR_RX_DROP, + CURR_RX_DESC, + CURR_RX_DATA +}; + +struct cc33xx_rx_descriptor { + __le16 length; + u8 header_alignment; + u8 status; + __le32 timestamp; + + u8 flags; + u8 rate; + u8 channel; + s8 rssi; + u8 snr; + + u8 hlid; + u8 pad_len; + u8 frame_format; +} __packed; + +struct partial_rx_frame { + struct sk_buff *skb; + struct cc33xx_rx_descriptor desc; + u16 handled_bytes; + u16 original_bytes; /* including descriptor */ + enum cc33xx_rx_curr_status status; +}; + +int cc33xx_rx(struct cc33xx *cc, u8 *rx_buf_ptr, u16 rx_buf_len); +int cc33xx_rx_filter_enable(struct cc33xx *cc, int index, bool enable, + struct cc33xx_rx_filter *filter); +int cc33xx_rx_filter_clear_all(struct cc33xx *cc); + +#endif /* __RX_H__ */ From patchwork Tue Oct 29 17:23:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839576 Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D0843208994; Tue, 29 Oct 2024 17:24:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222695; cv=none; b=qAQQXoL9aDf27pWpCgzJkWCzyE2Vc6sqOy/s2Z2Ri3/j1DKyqSPiyI8sBI6YwWuaKtM/nx10q3XRl8//cQBQR6uDQ88JfA1xmNbbh202CoAi3DTfMPcBXR3ot/cgEDe720tkGNY9lJQn1swV2DK9kKiaSq3JNZtedzkN+QCNaEk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222695; c=relaxed/simple; bh=bvp/+KPpTdUSvsAIfJCauixXnQDJ8eNEjE+po7g4dNc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Pjaiqv4lJkxxaEJUF7l6y/3RLgco2VcgJP7awh0UdTkv/sEUSkliRMwn+9lVLATjSFSoPUAuftio4yBcWf7+FJZshD15u1NbaQb7YHW5IdI9KUiACa2gzjyfi1+jmkdPzPw+w3sJWQ5ZN5jIN8DjnvSfNScU3PX1v0UBHtcZanw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=buRZGp5k; arc=none smtp.client-ip=198.47.23.249 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="buRZGp5k" Received: from fllv0035.itg.ti.com ([10.64.41.0]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOccY127631; Tue, 29 Oct 2024 12:24:38 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222678; bh=xPF/lDPC5teE7ZyOE4mb/oF/g+9eCsfnoxf/XPJp7uw=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=buRZGp5k0qVP7a22W9FN0yjN4vSu/jQrwHDzWxKEsH8luycxMVp046oOYSgqZigMG 2Tqts9WleW4eDB6ts8XFYyYC4hjaqmffqTJGRsw8wOHbTMURlZmDmKDScJOZMs0gA4 fWdRJzjLy90woem1I7nD/Mnwb37ZFwtCPkql7EMg= Received: from DFLE114.ent.ti.com (dfle114.ent.ti.com [10.64.6.35]) by fllv0035.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 49THOc1B092307 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 29 Oct 2024 12:24:38 -0500 Received: from DFLE110.ent.ti.com (10.64.6.31) by DFLE114.ent.ti.com (10.64.6.35) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:37 -0500 Received: from lelvsmtp5.itg.ti.com (10.180.75.250) by DFLE110.ent.ti.com (10.64.6.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:37 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp5.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOaDH006455; Tue, 29 Oct 2024 12:24:37 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 11/17] wifi: cc33xx: Add tx.c, tx.h Date: Tue, 29 Oct 2024 19:23:48 +0200 Message-ID: <20241029172354.4027886-12-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Tx of data frames starts either with MAC80211 tx op (cc33xx_op_tx) or from the deferred IRQ context (irq_deferred_work). Both trigger cc->tx_work (cc33xx_tx_work @ tx.c) which will call cc33xx_tx_work_locked where data from MAC80211 SKBs will be packed and transferred to HW. An interrupt will then be received from HW indicating that a given frame was transmitted or expired so that its SKB can be freed (cc33xx_tx_immediate_complete). Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/tx.c | 1409 +++++++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/tx.h | 160 +++ 2 files changed, 1569 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/tx.c create mode 100644 drivers/net/wireless/ti/cc33xx/tx.h diff --git a/drivers/net/wireless/ti/cc33xx/tx.c b/drivers/net/wireless/ti/cc33xx/tx.c new file mode 100644 index 000000000000..37f8b63c99e0 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/tx.c @@ -0,0 +1,1409 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include "acx.h" +#include "debug.h" +#include "io.h" +#include "ps.h" +#include "tx.h" +#include "cc33xx.h" + +static int cc33xx_set_default_wep_key(struct cc33xx *cc, + struct cc33xx_vif *wlvif, u8 id) +{ + int ret; + bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); + + if (is_ap) + ret = cc33xx_cmd_set_default_wep_key(cc, id, + wlvif->ap.bcast_hlid); + else + ret = cc33xx_cmd_set_default_wep_key(cc, id, wlvif->sta.hlid); + + if (ret < 0) + return ret; + + cc33xx_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id); + return 0; +} + +static int cc33xx_alloc_tx_id(struct cc33xx *cc, struct sk_buff *skb) +{ + int id; + + id = find_first_zero_bit(cc->tx_frames_map, CC33XX_NUM_TX_DESCRIPTORS); + if (id >= CC33XX_NUM_TX_DESCRIPTORS) + return -EBUSY; + + __set_bit(id, cc->tx_frames_map); + cc->tx_frames[id] = skb; + cc->tx_frames_cnt++; + cc33xx_debug(DEBUG_TX, "alloc desc ID. id - %d, frames count %d", + id, cc->tx_frames_cnt); + return id; +} + +void cc33xx_free_tx_id(struct cc33xx *cc, int id) +{ + if (__test_and_clear_bit(id, cc->tx_frames_map)) { + if (unlikely(cc->tx_frames_cnt == CC33XX_NUM_TX_DESCRIPTORS)) + clear_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags); + + cc->tx_frames[id] = NULL; + cc->tx_frames_cnt--; + } + cc33xx_debug(DEBUG_TX, "free desc ID. id - %d, frames count %d", + id, cc->tx_frames_cnt); +} +EXPORT_SYMBOL(cc33xx_free_tx_id); + +static void cc33xx_tx_ap_update_inconnection_sta(struct cc33xx *cc, + struct cc33xx_vif *wlvif, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)(skb->data + + sizeof(struct cc33xx_tx_hw_descr)); + if (!ieee80211_is_auth(hdr->frame_control)) + return; + + /* ROC for 1 second on the AP channel for completing the connection. + * Note the ROC will be continued by the update_sta_state callbacks + * once the station reaches the associated state. + */ + cc33xx_update_inconn_sta(cc, wlvif, NULL, true); + wlvif->pending_auth_reply_time = jiffies; + cancel_delayed_work(&wlvif->pending_auth_complete_work); + ieee80211_queue_delayed_work(cc->hw, + &wlvif->pending_auth_complete_work, + msecs_to_jiffies(CC33XX_PEND_AUTH_ROC_TIMEOUT)); +} + +static void cc33xx_tx_regulate_link(struct cc33xx *cc, + struct cc33xx_vif *wlvif, + u8 hlid) +{ + bool fw_ps; + u8 tx_pkts; + + if (WARN_ON(!test_bit(hlid, wlvif->links_map))) + return; + + fw_ps = test_bit(hlid, &cc->ap_fw_ps_map); + tx_pkts = cc->links[hlid].allocated_pkts; + + /* if in FW PS and there is enough data in FW we can put the link + * into high-level PS and clean out its TX queues. + * Make an exception if this is the only connected link. In this + * case FW-memory congestion is less of a problem. + * Note that a single connected STA means 2*ap_count + 1 active links, + * since we must account for the global and broadcast AP links + * for each AP. The "fw_ps" check assures us the other link is a STA + * connected to the AP. Otherwise the FW would not set the PSM bit. + */ + if (cc->active_link_count > (cc->ap_count * 2 + 1) && fw_ps && + tx_pkts >= CC33XX_PS_STA_MAX_PACKETS) + cc33xx_ps_link_start(cc, wlvif, hlid, true); +} + +inline bool cc33xx_is_dummy_packet(struct cc33xx *cc, struct sk_buff *skb) +{ + return cc->dummy_packet == skb; +} +EXPORT_SYMBOL(cc33xx_is_dummy_packet); + +static u8 cc33xx_tx_get_hlid_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_hdr *hdr; + + if (sta) { + struct cc33xx_station *wl_sta; + + wl_sta = (struct cc33xx_station *)sta->drv_priv; + return wl_sta->hlid; + } + + if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) + return CC33XX_SYSTEM_HLID; + + hdr = (struct ieee80211_hdr *)skb->data; + if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) + return wlvif->ap.bcast_hlid; + else + return wlvif->ap.global_hlid; +} + +u8 cc33xx_tx_get_hlid(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *control; + + if (wlvif->bss_type == BSS_TYPE_AP_BSS) + return cc33xx_tx_get_hlid_ap(cc, wlvif, skb, sta); + + control = IEEE80211_SKB_CB(skb); + if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + return wlvif->dev_hlid; + + return wlvif->sta.hlid; +} + +unsigned int cc33xx_calc_packet_alignment(struct cc33xx *cc, + unsigned int packet_length) +{ + if ((cc->quirks & CC33XX_QUIRK_TX_PAD_LAST_FRAME) || + !(cc->quirks & CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN)) + return ALIGN(packet_length, CC33XX_TX_ALIGN_TO); + else + return ALIGN(packet_length, CC33XX_BUS_BLOCK_SIZE); +} +EXPORT_SYMBOL(cc33xx_calc_packet_alignment); + +static u32 cc33xx_calc_tx_blocks(struct cc33xx *cc, u32 len, u32 spare_blks) +{ + u32 blk_size = CC33XX_TX_HW_BLOCK_SIZE; + /* In CC33xx the packet will be stored along with its internal descriptor. + * the descriptor is not part of the host transaction, but should be + * considered as part of the allocate memory blocks in the device + */ + len = len + CC33xx_INTERNAL_DESC_SIZE; + return (len + blk_size - 1) / blk_size + spare_blks; +} + +static inline void cc33xx_set_tx_desc_blocks(struct cc33xx *cc, + struct cc33xx_tx_hw_descr *desc, + u32 blks, u32 spare_blks) +{ + desc->cc33xx_mem.total_mem_blocks = blks; +} + +static void cc33xx_set_tx_desc_data_len(struct cc33xx *cc, + struct cc33xx_tx_hw_descr *desc, + struct sk_buff *skb) +{ + desc->length = cpu_to_le16(skb->len); + + /* if only the last frame is to be padded, we unset this bit on Tx */ + if (cc->quirks & CC33XX_QUIRK_TX_PAD_LAST_FRAME) + desc->cc33xx_mem.ctrl = CC33XX_TX_CTRL_NOT_PADDED; + else + desc->cc33xx_mem.ctrl = 0; + + cc33xx_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d", + desc->hlid, le16_to_cpu(desc->length), + le16_to_cpu(desc->life_time), + desc->cc33xx_mem.total_mem_blocks); +} + +static int cc33xx_get_spare_blocks(struct cc33xx *cc, bool is_gem) +{ + /* If we have keys requiring extra spare, indulge them */ + if (cc->extra_spare_key_count) + return CC33XX_TX_HW_EXTRA_BLOCK_SPARE; + + return CC33XX_TX_HW_BLOCK_SPARE; +} + +int cc33xx_tx_get_queue(int queue) +{ + switch (queue) { + case 0: + return CONF_TX_AC_VO; + case 1: + return CONF_TX_AC_VI; + case 2: + return CONF_TX_AC_BE; + case 3: + return CONF_TX_AC_BK; + default: + return CONF_TX_AC_BE; + } +} + +static int cc33xx_tx_allocate(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, u32 extra, u32 buf_offset, + u8 hlid, bool is_gem, + struct NAB_tx_header *nab_cmd) +{ + struct cc33xx_tx_hw_descr *desc; + u32 total_blocks; + int id, ret = -EBUSY, ac; + u32 spare_blocks; + u32 total_skb_len = skb->len + extra; + /* Add NAB command required for CC33xx architecture */ + u32 total_len = sizeof(struct NAB_tx_header); + + total_skb_len += sizeof(struct cc33xx_tx_hw_descr); + total_len += total_skb_len; + + cc33xx_debug(DEBUG_TX, "cc->tx_blocks_available %d", + cc->tx_blocks_available); + + if (buf_offset + total_len > cc->aggr_buf_size) + return -EAGAIN; + + spare_blocks = cc33xx_get_spare_blocks(cc, is_gem); + + /* allocate free identifier for the packet */ + id = cc33xx_alloc_tx_id(cc, skb); + if (id < 0) + return id; + + /* memblocks should not include nab descriptor */ + total_blocks = cc33xx_calc_tx_blocks(cc, total_skb_len, spare_blocks); + cc33xx_debug(DEBUG_TX, "total blocks %d", total_blocks); + + if (total_blocks <= cc->tx_blocks_available) { + /** + * In CC33XX the packet starts with NAB command, + * only then the descriptor. + */ + nab_cmd->sync = cpu_to_le32(HOST_SYNC_PATTERN); + nab_cmd->opcode = cpu_to_le16(NAB_SEND_CMD); + + /** + * length should include the following 4 bytes + * of the NAB command. + */ + nab_cmd->len = cpu_to_le16(total_len - + sizeof(struct NAB_header)); + nab_cmd->desc_length = cpu_to_le16(total_len - + sizeof(struct NAB_tx_header)); + nab_cmd->sd = 0; + nab_cmd->flags = NAB_SEND_FLAGS; + + desc = skb_push(skb, total_skb_len - skb->len); + + cc33xx_set_tx_desc_blocks(cc, desc, total_blocks, spare_blocks); + + desc->id = id; + + cc33xx_debug(DEBUG_TX, + "tx allocate id %u skb 0x%p tx_memblocks %d", + id, skb, desc->cc33xx_mem.total_mem_blocks); + + cc->tx_blocks_available -= total_blocks; + cc->tx_allocated_blocks += total_blocks; + + /* If the FW was empty before, arm the Tx watchdog. Also do + * this on the first Tx after resume, as we always cancel the + * watchdog on suspend. + */ + if (cc->tx_allocated_blocks == total_blocks || + test_and_clear_bit(CC33XX_FLAG_REINIT_TX_WDOG, &cc->flags)) + cc33xx_rearm_tx_watchdog_locked(cc); + + ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb)); + desc->ac = ac; + cc->tx_allocated_pkts[ac]++; + + if (test_bit(hlid, cc->links_map)) + cc->links[hlid].allocated_pkts++; + + ret = 0; + + cc33xx_debug(DEBUG_TX, + "tx_allocate: size: %d, blocks: %d, id: %d", + total_len, total_blocks, id); + } else { + cc33xx_free_tx_id(cc, id); + } + + return ret; +} + +static void cc33xx_tx_fill_hdr(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, u32 extra, + struct ieee80211_tx_info *control, u8 hlid) +{ + struct cc33xx_tx_hw_descr *desc; + int ac, rate_idx; + u16 tx_attr = 0; + __le16 frame_control; + struct ieee80211_hdr *hdr; + u8 *frame_start; + bool is_dummy; + + desc = (struct cc33xx_tx_hw_descr *)skb->data; + + frame_start = (u8 *)(desc + 1); + hdr = (struct ieee80211_hdr *)(frame_start + extra); + frame_control = hdr->frame_control; + + /* relocate space for security header */ + if (extra) { + int hdrlen = ieee80211_hdrlen(frame_control); + + memmove(frame_start, hdr, hdrlen); + skb_set_network_header(skb, skb_network_offset(skb) + extra); + } + + is_dummy = cc33xx_is_dummy_packet(cc, skb); + if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS) + desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU); + else + desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU); + + /* queue */ + ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb)); + desc->tid = skb->priority; + + if (is_dummy) { + /* FW expects the dummy packet to have an invalid session id - + * any session id that is different than the one set in the join + */ + tx_attr = (SESSION_COUNTER_INVALID << + TX_HW_ATTR_OFST_SESSION_COUNTER) & + TX_HW_ATTR_SESSION_COUNTER; + + tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; + } else if (wlvif) { + u8 session_id = cc->session_ids[hlid]; + + if (cc->quirks & CC33XX_QUIRK_AP_ZERO_SESSION_ID && + wlvif->bss_type == BSS_TYPE_AP_BSS) + session_id = 0; + + /* configure the tx attributes */ + tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER; + } + + desc->hlid = hlid; + if (is_dummy || !wlvif) { + rate_idx = 0; + } else if (wlvif->bss_type != BSS_TYPE_AP_BSS) { + /* if the packets are data packets + * send them with AP rate policies (EAPOLs are an exception), + * otherwise use default basic rates + */ + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + rate_idx = wlvif->sta.basic_rate_idx; + else if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE) + rate_idx = wlvif->sta.p2p_rate_idx; + else if (ieee80211_is_data(frame_control)) + rate_idx = wlvif->sta.ap_rate_idx; + else + rate_idx = wlvif->sta.basic_rate_idx; + } else { + if (hlid == wlvif->ap.global_hlid) + rate_idx = wlvif->ap.mgmt_rate_idx; + else if (hlid == wlvif->ap.bcast_hlid || + skb->protocol == cpu_to_be16(ETH_P_PAE) || + !ieee80211_is_data(frame_control)) + /* send non-data, bcast and EAPOLs using the + * min basic rate + */ + rate_idx = wlvif->ap.bcast_rate_idx; + else + rate_idx = wlvif->ap.ucast_rate_idx[ac]; + } + + tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; + + /* for WEP shared auth - no fw encryption is needed */ + if (ieee80211_is_auth(frame_control) && + ieee80211_has_protected(frame_control)) + tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; + + /* send EAPOL frames as voice */ + if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) + tx_attr |= TX_HW_ATTR_EAPOL_FRAME; + + desc->tx_attr = cpu_to_le16(tx_attr); + + cc33xx_set_tx_desc_data_len(cc, desc, skb); +} + +/* caller must hold cc->mutex */ +static int cc33xx_prepare_tx_frame(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, u32 buf_offset, u8 hlid) +{ + struct ieee80211_tx_info *info; + u32 extra = 0; + int ret = 0; + u32 total_len; + bool is_dummy; + bool is_gem = false; + struct NAB_tx_header nab_cmd; + + if (!skb) { + cc33xx_error("discarding null skb"); + return -EINVAL; + } + + if (hlid == CC33XX_INVALID_LINK_ID) { + cc33xx_error("invalid hlid. dropping skb 0x%p", skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + + is_dummy = cc33xx_is_dummy_packet(cc, skb); + + if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + extra = CC33XX_EXTRA_SPACE_TKIP; + + if (info->control.hw_key) { + bool is_wep; + u8 idx = info->control.hw_key->hw_key_idx; + u32 cipher = info->control.hw_key->cipher; + + is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104); + + if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) { + ret = cc33xx_set_default_wep_key(cc, wlvif, idx); + if (ret < 0) + return ret; + wlvif->default_key = idx; + } + + is_gem = (cipher == CC33XX_CIPHER_SUITE_GEM); + } + + /* Add 4 bytes gap, may be filled later on by the PMAC. */ + extra += IEEE80211_HT_CTL_LEN; + ret = cc33xx_tx_allocate(cc, wlvif, skb, extra, buf_offset, hlid, + is_gem, &nab_cmd); + cc33xx_debug(DEBUG_TX, "cc33xx_tx_allocate %d", ret); + + if (ret < 0) + return ret; + + cc33xx_tx_fill_hdr(cc, wlvif, skb, extra, info, hlid); + + if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) { + cc33xx_tx_ap_update_inconnection_sta(cc, wlvif, skb); + cc33xx_tx_regulate_link(cc, wlvif, hlid); + } + + /* The length of each packet is stored in terms of + * words. Thus, we must pad the skb data to make sure its + * length is aligned. The number of padding bytes is computed + * and set in cc33xx_tx_fill_hdr. + * In special cases, we want to align to a specific block size + * (eg. for wl128x with SDIO we align to 256). + */ + total_len = cc33xx_calc_packet_alignment(cc, skb->len); + + memcpy(cc->aggr_buf + buf_offset, + &nab_cmd, sizeof(struct NAB_tx_header)); + memcpy(cc->aggr_buf + buf_offset + sizeof(struct NAB_tx_header), + skb->data, skb->len); + memset(cc->aggr_buf + buf_offset + sizeof(struct NAB_tx_header) + + skb->len, 0, total_len - skb->len); + + /* Revert side effects in the dummy packet skb, so it can be reused */ + if (is_dummy) + skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr)); + + return (total_len + sizeof(struct NAB_tx_header)); +} + +u32 cc33xx_tx_enabled_rates_get(struct cc33xx *cc, u32 rate_set, + enum nl80211_band rate_band) +{ + struct ieee80211_supported_band *band; + u32 enabled_rates = 0; + int bit; + + band = cc->hw->wiphy->bands[rate_band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + if (rate_set & 0x1) + enabled_rates |= band->bitrates[bit].hw_value; + rate_set >>= 1; + } + + /* MCS rates indication are on bits 16 - 31 */ + rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates; + + for (bit = 0; bit < 16; bit++) { + if (rate_set & 0x1) + enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit); + rate_set >>= 1; + } + + return enabled_rates; +} + +static inline int cc33xx_tx_get_mac80211_queue(struct cc33xx_vif *wlvif, + int queue) +{ + int mac_queue = wlvif->hw_queue_base; + + switch (queue) { + case CONF_TX_AC_VO: + return mac_queue + 0; + case CONF_TX_AC_VI: + return mac_queue + 1; + case CONF_TX_AC_BE: + return mac_queue + 2; + case CONF_TX_AC_BK: + return mac_queue + 3; + default: + return mac_queue + 2; + } +} + +static void cc33xx_wake_queue(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 queue, enum cc33xx_queue_stop_reason reason) +{ + unsigned long flags; + int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue); + + spin_lock_irqsave(&cc->cc_lock, flags); + + /* queue should not be clear for this reason */ + WARN_ON_ONCE(!test_and_clear_bit(reason, &cc->queue_stop_reasons[hwq])); + + if (cc->queue_stop_reasons[hwq]) + goto out; + + ieee80211_wake_queue(cc->hw, hwq); + +out: + spin_unlock_irqrestore(&cc->cc_lock, flags); +} + +void cc33xx_handle_tx_low_watermark(struct cc33xx *cc) +{ + int i; + struct cc33xx_vif *wlvif; + + cc33xx_for_each_wlvif(cc, wlvif) { + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (cc33xx_is_queue_stopped_by_reason(cc, wlvif, i, + CC33XX_QUEUE_STOP_REASON_WATERMARK) && + wlvif->tx_queue_count[i] <= + CC33XX_TX_QUEUE_LOW_WATERMARK) + /* firmware buffer has space, restart queues */ + cc33xx_wake_queue(cc, wlvif, i, + CC33XX_QUEUE_STOP_REASON_WATERMARK); + } + } +} + +static int cc33xx_select_ac(struct cc33xx *cc) +{ + int i, q = -1, ac; + u32 min_pkts = 0xffffffff; + + /* Find a non-empty ac where: + * 1. There are packets to transmit + * 2. The FW has the least allocated blocks + * + * We prioritize the ACs according to VO>VI>BE>BK + */ + for (i = 0; i < NUM_TX_QUEUES; i++) { + ac = cc33xx_tx_get_queue(i); + if (cc->tx_queue_count[ac] && + cc->tx_allocated_pkts[ac] < min_pkts) { + q = ac; + min_pkts = cc->tx_allocated_pkts[q]; + } + } + + return q; +} + +static struct sk_buff *cc33xx_lnk_dequeue(struct cc33xx *cc, + struct cc33xx_link *lnk, u8 q) +{ + struct sk_buff *skb; + unsigned long flags; + + skb = skb_dequeue(&lnk->tx_queue[q]); + if (skb) { + spin_lock_irqsave(&cc->cc_lock, flags); + WARN_ON_ONCE(cc->tx_queue_count[q] <= 0); + cc->tx_queue_count[q]--; + if (lnk->wlvif) { + WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0); + lnk->wlvif->tx_queue_count[q]--; + } + spin_unlock_irqrestore(&cc->cc_lock, flags); + } + + return skb; +} + +static bool cc33xx_lnk_high_prio(struct cc33xx *cc, u8 hlid, + struct cc33xx_link *lnk) +{ + u8 thold; + struct core_fw_status *core_fw_status = &cc->core_status->fw_info; + unsigned long suspend_bitmap, fast_bitmap, ps_bitmap; + + suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap); + fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap); + ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap); + + /* suspended links are never high priority */ + if (test_bit(hlid, &suspend_bitmap)) + return false; + + /* the priority thresholds are taken from FW */ + if (test_bit(hlid, &fast_bitmap) && !test_bit(hlid, &ps_bitmap)) + thold = core_fw_status->tx_fast_link_prio_threshold; + else + thold = core_fw_status->tx_slow_link_prio_threshold; + + return lnk->allocated_pkts < thold; +} + +static bool cc33xx_lnk_low_prio(struct cc33xx *cc, u8 hlid, + struct cc33xx_link *lnk) +{ + u8 thold; + struct core_fw_status *core_fw_status = &cc->core_status->fw_info; + unsigned long suspend_bitmap, fast_bitmap, ps_bitmap; + + suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap); + fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap); + ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap); + + if (test_bit(hlid, &suspend_bitmap)) + thold = core_fw_status->tx_suspend_threshold; + else if (test_bit(hlid, &fast_bitmap) && !test_bit(hlid, &ps_bitmap)) + thold = core_fw_status->tx_fast_stop_threshold; + else + thold = core_fw_status->tx_slow_stop_threshold; + + return lnk->allocated_pkts < thold; +} + +static struct sk_buff *cc33xx_lnk_dequeue_high_prio(struct cc33xx *cc, + u8 hlid, u8 ac, + u8 *low_prio_hlid) +{ + struct cc33xx_link *lnk = &cc->links[hlid]; + + if (!cc33xx_lnk_high_prio(cc, hlid, lnk)) { + if (*low_prio_hlid == CC33XX_INVALID_LINK_ID && + !skb_queue_empty(&lnk->tx_queue[ac]) && + cc33xx_lnk_low_prio(cc, hlid, lnk)) + /* we found the first non-empty low priority queue */ + *low_prio_hlid = hlid; + + return NULL; + } + + return cc33xx_lnk_dequeue(cc, lnk, ac); +} + +static struct sk_buff *cc33xx_vif_dequeue_high_prio(struct cc33xx *cc, + struct cc33xx_vif *wlvif, + u8 ac, u8 *hlid, + u8 *low_prio_hlid) +{ + struct sk_buff *skb = NULL; + int i, h, start_hlid; + + /* start from the link after the last one */ + start_hlid = (wlvif->last_tx_hlid + 1) % CC33XX_MAX_LINKS; + + /* dequeue according to AC, round robin on each link */ + for (i = 0; i < CC33XX_MAX_LINKS; i++) { + h = (start_hlid + i) % CC33XX_MAX_LINKS; + + /* only consider connected stations */ + if (!test_bit(h, wlvif->links_map)) + continue; + + skb = cc33xx_lnk_dequeue_high_prio(cc, h, ac, low_prio_hlid); + if (!skb) + continue; + + wlvif->last_tx_hlid = h; + break; + } + + if (!skb) + wlvif->last_tx_hlid = 0; + + *hlid = wlvif->last_tx_hlid; + return skb; +} + +static struct sk_buff *cc33xx_skb_dequeue(struct cc33xx *cc, u8 *hlid) +{ + unsigned long flags; + struct cc33xx_vif *wlvif = cc->last_wlvif; + struct sk_buff *skb = NULL; + int ac; + u8 low_prio_hlid = CC33XX_INVALID_LINK_ID; + + ac = cc33xx_select_ac(cc); + if (ac < 0) + goto out; + + /* continue from last wlvif (round robin) */ + if (wlvif) { + cc33xx_for_each_wlvif_continue(cc, wlvif) { + if (!wlvif->tx_queue_count[ac]) + continue; + + skb = cc33xx_vif_dequeue_high_prio(cc, wlvif, ac, hlid, + &low_prio_hlid); + if (!skb) + continue; + + cc->last_wlvif = wlvif; + break; + } + } + + /* dequeue from the system HLID before the restarting wlvif list */ + if (!skb) { + skb = cc33xx_lnk_dequeue_high_prio(cc, CC33XX_SYSTEM_HLID, + ac, &low_prio_hlid); + if (skb) { + *hlid = CC33XX_SYSTEM_HLID; + cc->last_wlvif = NULL; + } + } + + /* Do a new pass over the wlvif list. But no need to continue + * after last_wlvif. The previous pass should have found it. + */ + if (!skb) { + cc33xx_for_each_wlvif(cc, wlvif) { + if (!wlvif->tx_queue_count[ac]) + goto next; + + skb = cc33xx_vif_dequeue_high_prio(cc, wlvif, ac, hlid, + &low_prio_hlid); + if (skb) { + cc->last_wlvif = wlvif; + break; + } + +next: + if (wlvif == cc->last_wlvif) + break; + } + } + + /* no high priority skbs found - but maybe a low priority one? */ + if (!skb && low_prio_hlid != CC33XX_INVALID_LINK_ID) { + struct cc33xx_link *lnk = &cc->links[low_prio_hlid]; + + skb = cc33xx_lnk_dequeue(cc, lnk, ac); + + WARN_ON(!skb); /* we checked this before */ + *hlid = low_prio_hlid; + + /* ensure proper round robin in the vif/link levels */ + cc->last_wlvif = lnk->wlvif; + if (lnk->wlvif) + lnk->wlvif->last_tx_hlid = low_prio_hlid; + } + +out: + if (!skb && + test_and_clear_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &cc->flags)) { + int q; + + skb = cc->dummy_packet; + *hlid = CC33XX_SYSTEM_HLID; + q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb)); + spin_lock_irqsave(&cc->cc_lock, flags); + WARN_ON_ONCE(cc->tx_queue_count[q] <= 0); + cc->tx_queue_count[q]--; + spin_unlock_irqrestore(&cc->cc_lock, flags); + } + + return skb; +} + +static void cc33xx_skb_queue_head(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, u8 hlid) +{ + unsigned long flags; + int q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb)); + + if (cc33xx_is_dummy_packet(cc, skb)) { + set_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &cc->flags); + } else { + skb_queue_head(&cc->links[hlid].tx_queue[q], skb); + + /* make sure we dequeue the same packet next time */ + wlvif->last_tx_hlid = (hlid + CC33XX_MAX_LINKS - 1) % + CC33XX_MAX_LINKS; + } + + spin_lock_irqsave(&cc->cc_lock, flags); + cc->tx_queue_count[q]++; + if (wlvif) + wlvif->tx_queue_count[q]++; + spin_unlock_irqrestore(&cc->cc_lock, flags); +} + +static inline bool cc33xx_tx_is_data_present(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); + + return ieee80211_is_data_present(hdr->frame_control); +} + +/* Returns failure values only in case of failed bus ops within this function. + * cc33xx_prepare_tx_frame retvals won't be returned in order to avoid + * triggering recovery by higher layers when not necessary. + * In case a FW command fails within cc33xx_prepare_tx_frame fails a recovery + * will be queued in cc33xx_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame + * can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING + * within prepare_tx_frame code but there's nothing we should do about those + * as well. + */ +int cc33xx_tx_work_locked(struct cc33xx *cc) +{ + struct cc33xx_vif *wlvif; + struct sk_buff *skb; + struct cc33xx_tx_hw_descr *desc; + u32 buf_offset = 0, last_len = 0; + u32 transfer_len = 0; + u32 padding_size = 0; + bool sent_packets = false; + unsigned long active_hlids[BITS_TO_LONGS(CC33XX_MAX_LINKS)] = {0}; + int ret = 0; + int bus_ret = 0; + u8 hlid; + + memset(cc->aggr_buf, 0, 0x300); + if (unlikely(cc->state != CC33XX_STATE_ON)) + return 0; + + while ((skb = cc33xx_skb_dequeue(cc, &hlid))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool has_data = false; + + cc33xx_debug(DEBUG_TX, "skb dequeue skb: 0x%p data %#lx head %#lx tail %#lx end %#lx", + skb, + (unsigned long)skb->data, (unsigned long)skb->head, + (unsigned long)skb->tail, (unsigned long)skb->end); + wlvif = NULL; + if (!cc33xx_is_dummy_packet(cc, skb)) + wlvif = cc33xx_vif_to_data(info->control.vif); + else + hlid = CC33XX_SYSTEM_HLID; + + has_data = wlvif && cc33xx_tx_is_data_present(skb); + ret = cc33xx_prepare_tx_frame(cc, wlvif, skb, buf_offset, + hlid); + + if (ret == -EAGAIN) { + /* Aggregation buffer is full. + * Flush buffer and try again. + */ + cc33xx_skb_queue_head(cc, wlvif, skb, hlid); + + transfer_len = __ALIGN_MASK(buf_offset, + CC33XX_BUS_BLOCK_SIZE * 2 - 1); + + padding_size = transfer_len - buf_offset; + memset(cc->aggr_buf + buf_offset, 0x33, padding_size); + + cc33xx_debug(DEBUG_TX, "sdio transaction length: %d ", + transfer_len); + + bus_ret = cc33xx_write(cc, NAB_DATA_ADDR, cc->aggr_buf, + transfer_len, true); + if (bus_ret < 0) + goto out; + + sent_packets = true; + buf_offset = 0; + continue; + } else if (ret == -EBUSY) { + /* Firmware buffer is full. + * Queue back last skb, and stop aggregating. + */ + cc33xx_skb_queue_head(cc, wlvif, skb, hlid); + /* No work left, avoid scheduling redundant tx work */ + set_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags); + goto out_ack; + } else if (ret < 0) { + if (cc33xx_is_dummy_packet(cc, skb)) + /* fw still expects dummy packet, + * so re-enqueue it + */ + cc33xx_skb_queue_head(cc, wlvif, skb, hlid); + else + ieee80211_free_txskb(cc->hw, skb); + goto out_ack; + } + + last_len = ret; + buf_offset += last_len; + + if (has_data) { + desc = (struct cc33xx_tx_hw_descr *)skb->data; + __set_bit(desc->hlid, active_hlids); + } + } + +out_ack: + if (buf_offset) { + transfer_len = __ALIGN_MASK(buf_offset, + CC33XX_BUS_BLOCK_SIZE * 2 - 1); + + padding_size = transfer_len - buf_offset; + memset(cc->aggr_buf + buf_offset, 0x33, padding_size); + + cc33xx_debug(DEBUG_TX, "sdio transaction (926) length: %d ", + transfer_len); + + bus_ret = cc33xx_write(cc, NAB_DATA_ADDR, cc->aggr_buf, + transfer_len, true); + if (bus_ret < 0) + goto out; + + sent_packets = true; + } + + if (sent_packets) + cc33xx_handle_tx_low_watermark(cc); + +out: + return bus_ret; +} + +void cc33xx_tx_work(struct work_struct *work) +{ + struct cc33xx *cc = container_of(work, struct cc33xx, tx_work); + int ret; + + mutex_lock(&cc->mutex); + + ret = cc33xx_tx_work_locked(cc); + if (ret < 0) { + cc33xx_queue_recovery_work(cc); + goto out; + } + +out: + mutex_unlock(&cc->mutex); +} + +void cc33xx_tx_reset_link_queues(struct cc33xx *cc, u8 hlid) +{ + struct sk_buff *skb; + int i; + unsigned long flags; + struct ieee80211_tx_info *info; + int total[NUM_TX_QUEUES]; + struct cc33xx_link *lnk = &cc->links[hlid]; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + total[i] = 0; + while ((skb = skb_dequeue(&lnk->tx_queue[i]))) { + cc33xx_debug(DEBUG_TX, "link freeing skb 0x%p", skb); + + if (!cc33xx_is_dummy_packet(cc, skb)) { + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + ieee80211_tx_status_ni(cc->hw, skb); + } + + total[i]++; + } + } + + spin_lock_irqsave(&cc->cc_lock, flags); + for (i = 0; i < NUM_TX_QUEUES; i++) { + cc->tx_queue_count[i] -= total[i]; + if (lnk->wlvif) + lnk->wlvif->tx_queue_count[i] -= total[i]; + } + spin_unlock_irqrestore(&cc->cc_lock, flags); + + cc33xx_handle_tx_low_watermark(cc); +} + +/* caller must hold cc->mutex and TX must be stopped */ +void cc33xx_tx_reset_wlvif(struct cc33xx *cc, struct cc33xx_vif *wlvif) +{ + int i; + + /* TX failure */ + for_each_set_bit(i, wlvif->links_map, CC33XX_MAX_LINKS) { + if (wlvif->bss_type == BSS_TYPE_AP_BSS && + i != wlvif->ap.bcast_hlid && + i != wlvif->ap.global_hlid) { + /* this calls cc33xx_clear_link */ + cc33xx_free_sta(cc, wlvif, i); + } else { + u8 hlid = i; + + cc33xx_clear_link(cc, wlvif, &hlid); + } + } + + wlvif->last_tx_hlid = 0; + + for (i = 0; i < NUM_TX_QUEUES; i++) + wlvif->tx_queue_count[i] = 0; +} + +int cc33xx_tx_total_queue_count(struct cc33xx *cc) +{ + int i, count = 0; + + for (i = 0; i < NUM_TX_QUEUES; i++) + count += cc->tx_queue_count[i]; + + return count; +} + +/* caller must hold cc->mutex and TX must be stopped */ +void cc33xx_tx_reset(struct cc33xx *cc) +{ + int i; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + + /* only reset the queues if something bad happened */ + if (cc33xx_tx_total_queue_count(cc) != 0) { + for (i = 0; i < CC33XX_MAX_LINKS; i++) + cc33xx_tx_reset_link_queues(cc, i); + + for (i = 0; i < NUM_TX_QUEUES; i++) + cc->tx_queue_count[i] = 0; + } + + /* Make sure the driver is at a consistent state, in case this + * function is called from a context other than interface removal. + * This call will always wake the TX queues. + */ + cc33xx_handle_tx_low_watermark(cc); + + for (i = 0; i < CC33XX_NUM_TX_DESCRIPTORS; i++) { + if (!cc->tx_frames[i]) + continue; + + skb = cc->tx_frames[i]; + cc33xx_free_tx_id(cc, i); + cc33xx_debug(DEBUG_TX, "freeing skb 0x%p", skb); + + if (!cc33xx_is_dummy_packet(cc, skb)) { + /* Remove private headers before passing the skb to + * mac80211 + */ + info = IEEE80211_SKB_CB(skb); + skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr)); + if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && + info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP, + skb->data, hdrlen); + skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP); + } + + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + + ieee80211_tx_status_ni(cc->hw, skb); + } + } +} + +#define CC33XX_TX_FLUSH_TIMEOUT 500000 + +/* caller must *NOT* hold cc->mutex */ +void cc33xx_tx_flush(struct cc33xx *cc) +{ + unsigned long timeout, start_time; + int i; + + start_time = jiffies; + timeout = start_time + usecs_to_jiffies(CC33XX_TX_FLUSH_TIMEOUT); + + /* only one flush should be in progress, for consistent queue state */ + mutex_lock(&cc->flush_mutex); + + mutex_lock(&cc->mutex); + if (cc->tx_frames_cnt == 0 && cc33xx_tx_total_queue_count(cc) == 0) { + mutex_unlock(&cc->mutex); + goto out; + } + + cc33xx_stop_queues(cc, CC33XX_QUEUE_STOP_REASON_FLUSH); + + while (!time_after(jiffies, timeout)) { + cc33xx_debug(DEBUG_MAC80211, "flushing tx buffer: %d %d", + cc->tx_frames_cnt, + cc33xx_tx_total_queue_count(cc)); + + /* force Tx and give the driver some time to flush data */ + mutex_unlock(&cc->mutex); + if (cc33xx_tx_total_queue_count(cc)) + cc33xx_tx_work(&cc->tx_work); + msleep(20); + mutex_lock(&cc->mutex); + + if (cc->tx_frames_cnt == 0 && cc33xx_tx_total_queue_count(cc) == 0) { + cc33xx_debug(DEBUG_MAC80211, "tx flush took %d ms", + jiffies_to_msecs(jiffies - start_time)); + goto out_wake; + } + } + + cc33xx_warning("Unable to flush all TX buffers, timed out (timeout %d ms", + CC33XX_TX_FLUSH_TIMEOUT / 1000); + + /* forcibly flush all Tx buffers on our queues */ + for (i = 0; i < CC33XX_MAX_LINKS; i++) + cc33xx_tx_reset_link_queues(cc, i); + +out_wake: + cc33xx_wake_queues(cc, CC33XX_QUEUE_STOP_REASON_FLUSH); + mutex_unlock(&cc->mutex); +out: + mutex_unlock(&cc->flush_mutex); +} + +u32 cc33xx_tx_min_rate_get(struct cc33xx *cc, u32 rate_set) +{ + if (WARN_ON(!rate_set)) + return 0; + + return BIT(__ffs(rate_set)); +} + +void cc33xx_stop_queue_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 queue, enum cc33xx_queue_stop_reason reason) +{ + int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue); + bool stopped = !!cc->queue_stop_reasons[hwq]; + + /* queue should not be stopped for this reason */ + WARN_ON_ONCE(test_and_set_bit(reason, &cc->queue_stop_reasons[hwq])); + + if (stopped) + return; + + ieee80211_stop_queue(cc->hw, hwq); +} + +void cc33xx_stop_queues(struct cc33xx *cc, + enum cc33xx_queue_stop_reason reason) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&cc->cc_lock, flags); + + /* mark all possible queues as stopped */ + for (i = 0; i < CC33XX_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) { + WARN_ON_ONCE(test_and_set_bit(reason, + &cc->queue_stop_reasons[i])); + } + + /* use the global version to make sure all vifs in mac80211 we don't + * know are stopped. + */ + ieee80211_stop_queues(cc->hw); + + spin_unlock_irqrestore(&cc->cc_lock, flags); +} + +void cc33xx_wake_queues(struct cc33xx *cc, + enum cc33xx_queue_stop_reason reason) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&cc->cc_lock, flags); + + /* mark all possible queues as awake */ + for (i = 0; i < CC33XX_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) { + WARN_ON_ONCE(!test_and_clear_bit(reason, + &cc->queue_stop_reasons[i])); + } + + /* use the global version to make sure all vifs in mac80211 we don't + * know are woken up. + */ + ieee80211_wake_queues(cc->hw); + + spin_unlock_irqrestore(&cc->cc_lock, flags); +} + +bool cc33xx_is_queue_stopped_by_reason(struct cc33xx *cc, + struct cc33xx_vif *wlvif, u8 queue, + enum cc33xx_queue_stop_reason reason) +{ + unsigned long flags; + bool stopped; + + spin_lock_irqsave(&cc->cc_lock, flags); + stopped = cc33xx_is_queue_stopped_by_reason_locked(cc, wlvif, queue, + reason); + spin_unlock_irqrestore(&cc->cc_lock, flags); + + return stopped; +} + +bool cc33xx_is_queue_stopped_by_reason_locked(struct cc33xx *cc, + struct cc33xx_vif *wlvif, u8 queue, + enum cc33xx_queue_stop_reason reason) +{ + int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue); + + assert_spin_locked(&cc->cc_lock); + return test_bit(reason, &cc->queue_stop_reasons[hwq]); +} + +bool cc33xx_is_queue_stopped_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 queue) +{ + int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue); + + assert_spin_locked(&cc->cc_lock); + return !!cc->queue_stop_reasons[hwq]; +} + +static void cc33xx_tx_complete_packet(struct cc33xx *cc, u8 tx_stat_byte, + struct core_fw_status *core_fw_status) +{ + struct ieee80211_tx_info *info; + struct sk_buff *skb; + int id = tx_stat_byte & CC33XX_TX_STATUS_DESC_ID_MASK; + bool tx_success; + struct cc33xx_tx_hw_descr *tx_desc; + u16 desc_session_idx; + + /* check for id legality */ + if (unlikely(id >= CC33XX_NUM_TX_DESCRIPTORS || + !cc->tx_frames[id])) { + cc33xx_warning("illegal id in tx completion: %d", id); + + print_hex_dump(KERN_DEBUG, "fw_info local:", + DUMP_PREFIX_OFFSET, 16, 4, (u8 *)(core_fw_status), + sizeof(struct core_fw_status), false); + + cc33xx_queue_recovery_work(cc); + return; + } + + /* a zero bit indicates Tx success */ + tx_success = !(tx_stat_byte & BIT(CC33XX_TX_STATUS_STAT_BIT_IDX)); + + skb = cc->tx_frames[id]; + info = IEEE80211_SKB_CB(skb); + tx_desc = (struct cc33xx_tx_hw_descr *)skb->data; + + if (cc33xx_is_dummy_packet(cc, skb)) { + cc33xx_free_tx_id(cc, id); + return; + } + + /* update the TX status info */ + if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.rates[0].count = 1; /* no data about retries */ + info->status.ack_signal = -1; + + if (!tx_success) + cc->stats.retry_count++; + + /* remove private header from packet */ + skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr)); + + /* remove TKIP header space if present */ + if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP, skb->data, hdrlen); + skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP); + } + + cc33xx_debug(DEBUG_TX, + "tx status id %u skb 0x%p success %d, tx_memblocks %d", + id, skb, tx_success, tx_desc->cc33xx_mem.total_mem_blocks); + + /** + * in order to update the memory management + * we should have total_blocks, ac, and hlid + */ + cc->tx_blocks_available += tx_desc->cc33xx_mem.total_mem_blocks; + cc->tx_allocated_blocks -= tx_desc->cc33xx_mem.total_mem_blocks; + /* per queue */ + + /* prevent wrap-around in freed-packets counter */ + cc->tx_allocated_pkts[tx_desc->ac]--; + + /* per link */ + desc_session_idx = (le16_to_cpu(tx_desc->tx_attr) & TX_HW_ATTR_SESSION_COUNTER) >> + TX_HW_ATTR_OFST_SESSION_COUNTER; + + if (cc->session_ids[tx_desc->hlid] == desc_session_idx) + cc->links[tx_desc->hlid].allocated_pkts--; + + cc33xx_free_tx_id(cc, id); + + /* new mem blocks are available now */ + clear_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags); + + /* return the packet to the stack */ + skb_queue_tail(&cc->deferred_tx_queue, skb); + queue_work(cc->freezable_wq, &cc->netstack_work); +} + +void cc33xx_tx_immediate_complete(struct cc33xx *cc) +{ + u8 tx_result_queue_index; + struct core_fw_status core_fw_status; + u8 i; + + claim_core_status_lock(cc); + memcpy(&core_fw_status, &cc->core_status->fw_info, + sizeof(struct core_fw_status)); + + tx_result_queue_index = cc->core_status->fw_info.tx_result_queue_index; + /* Lock guarantees we shadow tx_result_queue_index NOT during + * an active transaction. Subsequent references to fw_info can be done + * without locking as long we do not pass this index. + */ + release_core_status_lock(cc); + + cc33xx_debug(DEBUG_TX, "last released desc = %d, current idx = %d", + cc->last_fw_rls_idx, tx_result_queue_index); + + /* nothing to do here */ + if (cc->last_fw_rls_idx == tx_result_queue_index) + return; + + /* freed Tx descriptors */ + + if (tx_result_queue_index >= TX_RESULT_QUEUE_SIZE) { + cc33xx_error("invalid desc release index %d", + tx_result_queue_index); + WARN_ON(1); + return; + } + + cc33xx_debug(DEBUG_TX, "TX result queue! priv last fw idx %d, current resut index %d ", + cc->last_fw_rls_idx, tx_result_queue_index); + + for (i = cc->last_fw_rls_idx; i != tx_result_queue_index; + i = (i + 1) % TX_RESULT_QUEUE_SIZE) { + cc33xx_tx_complete_packet(cc, core_fw_status.tx_result_queue[i], + &core_fw_status); + } + + cc->last_fw_rls_idx = tx_result_queue_index; +} diff --git a/drivers/net/wireless/ti/cc33xx/tx.h b/drivers/net/wireless/ti/cc33xx/tx.h new file mode 100644 index 000000000000..9062d50c7c8e --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/tx.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __TX_H__ +#define __TX_H__ + +#define CC33XX_TX_HW_BLOCK_SPARE 1 +/* for special cases - namely, TKIP and GEM */ +#define CC33XX_TX_HW_EXTRA_BLOCK_SPARE 2 +#define CC33XX_TX_HW_BLOCK_SIZE 256 + +#define CC33XX_TX_STATUS_DESC_ID_MASK 0x7F +#define CC33XX_TX_STATUS_STAT_BIT_IDX 7 + +/* Indicates this TX HW frame is not padded to SDIO block size */ +#define CC33XX_TX_CTRL_NOT_PADDED BIT(7) + +#define TX_HW_MGMT_PKT_LIFETIME_TU 2000 +#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000 + +#define TX_HW_ATTR_SESSION_COUNTER (BIT(2) | BIT(3) | BIT(4)) +#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) +#define TX_HW_ATTR_HOST_ENCRYPT BIT(14) +#define TX_HW_ATTR_EAPOL_FRAME BIT(15) + +#define TX_HW_ATTR_OFST_SESSION_COUNTER 2 +#define TX_HW_ATTR_OFST_RATE_POLICY 5 + +#define CC33XX_TX_ALIGN_TO 4 +#define CC33XX_EXTRA_SPACE_TKIP 4 +#define CC33XX_EXTRA_SPACE_AES 8 +#define CC33XX_EXTRA_SPACE_MAX 8 + +#define CC33XX_TX_EXTRA_HEADROOM \ + (sizeof(struct cc33xx_tx_hw_descr) + IEEE80211_HT_CTL_LEN) + +/* Used for management frames and dummy packets */ +#define CC33XX_TID_MGMT 7 + +/* stop a ROC for pending authentication reply after this time (ms) */ +#define CC33XX_PEND_AUTH_ROC_TIMEOUT 1000 +#define CC33xx_PEND_ROC_COMPLETE_TIMEOUT 2000 + +struct cc33xx_tx_mem { + /* Total number of memory blocks allocated by the host for + * this packet. + */ + u8 total_mem_blocks; + + /* control bits + */ + u8 ctrl; +} __packed; + +/* On cc33xx based devices, when TX packets are aggregated, each packet + * size must be aligned to the SDIO block size. The maximum block size + * is bounded by the type of the padded bytes field that is sent to the + * FW. The HW maximum block size is 256 bytes. We use 128 to utilize the + * SDIO built-in busy signal when the FIFO is full. + */ +#define CC33XX_BUS_BLOCK_SIZE 128 + +struct cc33xx_tx_hw_descr { + /* Length of packet in words, including descriptor+header+data */ + __le16 length; + + struct cc33xx_tx_mem cc33xx_mem; + + /* Packet identifier used also in the Tx-Result. */ + u8 id; + /* The packet TID value (as User-Priority) */ + u8 tid; + /* host link ID (HLID) */ + u8 hlid; + u8 ac; + /* Max delay in TUs until transmission. The last device time the + * packet can be transmitted is: start_time + (1024 * life_time) + */ + __le16 life_time; + /* Bitwise fields - see TX_ATTR... definitions above. */ + __le16 tx_attr; +} __packed; + +struct cc33xx_tx_hw_res_descr { + /* Packet Identifier - same value used in the Tx descriptor.*/ + u8 id; + /* The status of the transmission, indicating success or one of + * several possible reasons for failure. + */ + u8 status; + /* Total air access duration including all retrys and overheads.*/ + __le16 medium_usage; + /* The time passed from host xfer to Tx-complete.*/ + __le32 fw_handling_time; + /* Total media delay + * (from 1st EDCA AIFS counter until TX Complete). + */ + __le32 medium_delay; + /* LS-byte of last TKIP seq-num (saved per AC for recovery). */ + u8 tx_security_sequence_number_lsb; + /* Retry count - number of transmissions without successful ACK.*/ + u8 ack_failures; + /* The rate that succeeded getting ACK + * (Valid only if status=SUCCESS). + */ + u8 rate_class_index; + /* for 4-byte alignment. */ + u8 spare; +} __packed; + +enum cc33xx_queue_stop_reason { + CC33XX_QUEUE_STOP_REASON_WATERMARK, + CC33XX_QUEUE_STOP_REASON_FW_RESTART, + CC33XX_QUEUE_STOP_REASON_FLUSH, + CC33XX_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */ +}; + +int cc33xx_tx_get_queue(int queue); +int cc33xx_tx_total_queue_count(struct cc33xx *cc); +void cc33xx_tx_immediate_complete(struct cc33xx *cc); +void cc33xx_tx_work(struct work_struct *work); +int cc33xx_tx_work_locked(struct cc33xx *cc); +void cc33xx_tx_reset_wlvif(struct cc33xx *cc, struct cc33xx_vif *wlvif); +void cc33xx_tx_reset(struct cc33xx *cc); +void cc33xx_tx_flush(struct cc33xx *cc); +u8 cc33xx_rate_to_idx(struct cc33xx *cc, u8 rate, enum nl80211_band band); +u32 cc33xx_tx_enabled_rates_get(struct cc33xx *cc, u32 rate_set, + enum nl80211_band rate_band); +u32 cc33xx_tx_min_rate_get(struct cc33xx *cc, u32 rate_set); +u8 cc33xx_tx_get_hlid(struct cc33xx *cc, struct cc33xx_vif *wlvif, + struct sk_buff *skb, struct ieee80211_sta *sta); +void cc33xx_tx_reset_link_queues(struct cc33xx *cc, u8 hlid); +void cc33xx_handle_tx_low_watermark(struct cc33xx *cc); +bool cc33xx_is_dummy_packet(struct cc33xx *cc, struct sk_buff *skb); +unsigned int cc33xx_calc_packet_alignment(struct cc33xx *cc, + unsigned int packet_length); +void cc33xx_free_tx_id(struct cc33xx *cc, int id); +void cc33xx_stop_queue_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 queue, enum cc33xx_queue_stop_reason reason); +void cc33xx_stop_queues(struct cc33xx *cc, + enum cc33xx_queue_stop_reason reason); +void cc33xx_wake_queues(struct cc33xx *cc, + enum cc33xx_queue_stop_reason reason); +bool cc33xx_is_queue_stopped_by_reason(struct cc33xx *cc, + struct cc33xx_vif *wlvif, u8 queue, + enum cc33xx_queue_stop_reason reason); +bool cc33xx_is_queue_stopped_by_reason_locked(struct cc33xx *cc, + struct cc33xx_vif *wlvif, + u8 queue, + enum cc33xx_queue_stop_reason reason); +bool cc33xx_is_queue_stopped_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif, + u8 queue); + +/* from main.c */ +void cc33xx_free_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 hlid); +void cc33xx_rearm_tx_watchdog_locked(struct cc33xx *cc); + +#endif /* __TX_H__ */ From patchwork Tue Oct 29 17:23:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839577 Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DEF1320820E; Tue, 29 Oct 2024 17:24:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222689; cv=none; b=hZkOttjsJNg4F141kAP0ceRNg/mrNoyewkmhUSvL8CPAfnzgRow8t75Zq1se9BxskSKFl8dGL9LiEldteuibV9puC0hCwjE8GyyruTOYodP8zRFjw2+hIIM239DaeRZBqAdl/Lku6EE7KTQtU/TpEwYgLJUVVH8A1vWEy9jvaco= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222689; c=relaxed/simple; bh=Hf/7G60jaXIYK35ZCiFWQtP4c0TpBzXdq4DbMODo9/I=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FKxiFODOvMloKBThrCM7KyEjw5WqSai26CkA+QGU6OzFtRPNmoyIR6QU5w4mjJH0N5XzKN4uRsTfswSzgdZrhzXKI9ZQYh2XtqvV0Uf57HEg/6NtcO3+Kd0EAysRBgf4C9BVKqErpjZ8IchqkS7SWVcVC7qdKtPScxqBPyw640I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=x6JtvwCr; arc=none smtp.client-ip=198.47.23.249 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="x6JtvwCr" Received: from lelv0266.itg.ti.com ([10.180.67.225]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOde2127636; Tue, 29 Oct 2024 12:24:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222679; bh=pfy1yy2gwNFOPkKPmfB3NTYFBRwvCDDq0Cd7DByNqtk=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=x6JtvwCrCPZIUsxXEV7cufdQHR8LeT2jTua8QBAVNTWRsL4dqR3mE645/EWau2Hfx jbZ5BlMPrVN0UD0KyNV+W7GF8CF+vTgXNMN8NazWmZN4SURxyr/hin57y8r9AaoqgS NBHhhaTjedPIBNt76DWbYDyXjmjeadeSnkiZ7mps= Received: from DLEE115.ent.ti.com (dlee115.ent.ti.com [157.170.170.26]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOdBQ068272; Tue, 29 Oct 2024 12:24:39 -0500 Received: from DLEE114.ent.ti.com (157.170.170.25) by DLEE115.ent.ti.com (157.170.170.26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:38 -0500 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE114.ent.ti.com (157.170.170.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:39 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOcOc065793; Tue, 29 Oct 2024 12:24:38 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 12/17] wifi: cc33xx: Add init.c, init.h Date: Tue, 29 Oct 2024 19:23:49 +0200 Message-ID: <20241029172354.4027886-13-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea High-level init code for new vifs Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/init.c | 231 ++++++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/init.h | 15 ++ 2 files changed, 246 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/init.c create mode 100644 drivers/net/wireless/ti/cc33xx/init.h diff --git a/drivers/net/wireless/ti/cc33xx/init.c b/drivers/net/wireless/ti/cc33xx/init.c new file mode 100644 index 000000000000..ff1fab1e0104 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/init.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include "acx.h" +#include "cmd.h" +#include "conf.h" +#include "event.h" +#include "tx.h" +#include "init.h" + +static int cc33xx_init_phy_vif_config(struct cc33xx *cc, + struct cc33xx_vif *wlvif) +{ + int ret; + + ret = cc33xx_acx_slot(cc, wlvif, DEFAULT_SLOT_TIME); + if (ret < 0) + return ret; + + return 0; +} + +static int cc33xx_init_sta_beacon_filter(struct cc33xx *cc, + struct cc33xx_vif *wlvif) +{ + int ret; + + ret = cc33xx_acx_beacon_filter_table(cc, wlvif); + if (ret < 0) + return ret; + + /* disable beacon filtering until we get the first beacon */ + ret = cc33xx_acx_beacon_filter_opt(cc, wlvif, false); + if (ret < 0) + return ret; + + return 0; +} + +static int cc33xx_ap_init_templates(struct cc33xx *cc, + struct ieee80211_vif *vif) +{ + struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif); + int ret; + + /* when operating as AP we want to receive external beacons for + * configuring ERP protection. + */ + ret = cc33xx_acx_beacon_filter_opt(cc, wlvif, false); + if (ret < 0) + return ret; + + return 0; +} + +static void cc33xx_set_ba_policies(struct cc33xx *cc, struct cc33xx_vif *wlvif) +{ + /* Reset the BA RX indicators */ + wlvif->ba_allowed = true; + cc->ba_rx_session_count = 0; + + /* BA is supported in STA/AP modes */ + wlvif->ba_support = (wlvif->bss_type != BSS_TYPE_AP_BSS && + wlvif->bss_type != BSS_TYPE_STA_BSS); +} + +/* vif-specifc initialization */ +static int cc33xx_init_sta_role(struct cc33xx *cc, struct cc33xx_vif *wlvif) +{ + int ret = cc33xx_acx_group_address_tbl(cc, true, NULL, 0); + + if (ret < 0) + return ret; + + /* Beacon filtering */ + ret = cc33xx_init_sta_beacon_filter(cc, wlvif); + if (ret < 0) + return ret; + + return 0; +} + +/* vif-specific initialization */ +static int cc33xx_init_ap_role(struct cc33xx *cc, struct cc33xx_vif *wlvif) +{ + int ret; + + /* initialize Tx power */ + ret = cc33xx_acx_tx_power(cc, wlvif, wlvif->power_level); + if (ret < 0) + return ret; + + if (cc->radar_debug_mode) + cc33xx_cmd_generic_cfg(cc, wlvif, + CC33XX_CFG_FEATURE_RADAR_DEBUG, + cc->radar_debug_mode, 0); + + return 0; +} + +int cc33xx_init_vif_specific(struct cc33xx *cc, struct ieee80211_vif *vif) +{ + struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif); + struct conf_tx_settings *tx_settings = &cc->conf.host_conf.tx; + struct conf_tx_ac_category *conf_ac = &tx_settings->ac_conf0; + bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); + u8 ps_scheme = cc->conf.mac.ps_scheme; + int ret, i; + + /* consider all existing roles before configuring psm. */ + + if (cc->ap_count == 0 && is_ap) { /* first AP */ + ret = cc33xx_acx_sleep_auth(cc, CC33XX_PSM_ELP); + if (ret < 0) + return ret; + + /* unmask ap events */ + cc->event_mask |= cc->ap_event_mask; + + /* first STA, no APs */ + } else if (cc->sta_count == 0 && cc->ap_count == 0 && !is_ap) { + u8 sta_auth = cc->conf.host_conf.conn.sta_sleep_auth; + /* Configure for power according to debugfs */ + if (sta_auth != CC33XX_PSM_ILLEGAL) + ret = cc33xx_acx_sleep_auth(cc, sta_auth); + /* Configure for ELP power saving */ + else + ret = cc33xx_acx_sleep_auth(cc, CC33XX_PSM_ELP); + + if (ret < 0) + return ret; + } + + /* Mode specific init */ + if (is_ap) { + ret = cc33xx_init_ap_role(cc, wlvif); + if (ret < 0) + return ret; + } else { + ret = cc33xx_init_sta_role(cc, wlvif); + if (ret < 0) + return ret; + } + + cc33xx_init_phy_vif_config(cc, wlvif); + + /* Default TID/AC configuration */ + if (WARN_ON(tx_settings->ac_conf_count != tx_settings->tid_conf_count) || + WARN_ON(tx_settings->ac_conf_count != CONF_TX_MAX_AC_COUNT)) + return -EINVAL; + + for (i = 0; i < tx_settings->tid_conf_count; i++) { + /* If no ps poll is used, send legacy ps scheme in cmd */ + if (ps_scheme == PS_SCHEME_NOPSPOLL) + ps_scheme = PS_SCHEME_LEGACY; + + ret = cc33xx_tx_param_cfg(cc, wlvif, conf_ac->ac, + conf_ac->cw_min, conf_ac->cw_max, + conf_ac->aifsn, conf_ac->tx_op_limit, + false, ps_scheme, conf_ac->is_mu_edca, + conf_ac->mu_edca_aifs, + conf_ac->mu_edca_ecw_min_max, + conf_ac->mu_edca_timer); + + if (ret < 0) + return ret; + + conf_ac++; + } + + /* Mode specific init - post mem init */ + if (is_ap) + ret = cc33xx_ap_init_templates(cc, vif); + + if (ret < 0) + return ret; + + /* Configure initiator BA sessions policies */ + cc33xx_set_ba_policies(cc, wlvif); + + return 0; +} + +int cc33xx_hw_init(struct cc33xx *cc) +{ + cc33xx_acx_init_mem_config(cc); + + cc->last_fw_rls_idx = 0; + cc->partial_rx.status = CURR_RX_START; + return 0; +} + +int cc33xx_download_ini_params_and_wait(struct cc33xx *cc) +{ + struct cc33xx_cmd_ini_params_download *cmd; + size_t command_size = ALIGN((sizeof(*cmd) + sizeof(cc->conf)), 4); + int ret; + + cc33xx_set_max_buffer_size(cc, INI_MAX_BUFFER_SIZE); + + cc33xx_debug(DEBUG_ACX, + "Downloading INI configurations to FW, payload Length: %zu", + sizeof(cc->conf)); + + cmd = kzalloc(command_size, GFP_KERNEL); + if (!cmd) { + cc33xx_set_max_buffer_size(cc, CMD_MAX_BUFFER_SIZE); + return -ENOMEM; + } + + cmd->length = cpu_to_le32(sizeof(cc->conf)); + + /* copy INI file params payload */ + memcpy((cmd->payload), &cc->conf, sizeof(cc->conf)); + + ret = cc33xx_cmd_send(cc, CMD_DOWNLOAD_INI_PARAMS, + cmd, command_size, 0); + if (ret < 0) { + cc33xx_warning("download INI params to FW command sending failed: %d", + ret); + } else { + cc33xx_debug(DEBUG_BOOT, "INI Params downloaded successfully"); + } + + cc33xx_set_max_buffer_size(cc, CMD_MAX_BUFFER_SIZE); + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/ti/cc33xx/init.h b/drivers/net/wireless/ti/cc33xx/init.h new file mode 100644 index 000000000000..b0bc6a548611 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/init.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __INIT_H__ +#define __INIT_H__ + +#include "cc33xx.h" + +int cc33xx_hw_init(struct cc33xx *cc); +int cc33xx_download_ini_params_and_wait(struct cc33xx *cc); +int cc33xx_init_vif_specific(struct cc33xx *cc, struct ieee80211_vif *vif); + +#endif /* __INIT_H__ */ From patchwork Tue Oct 29 17:23:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839574 Received: from fllv0015.ext.ti.com (fllv0015.ext.ti.com [198.47.19.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DAA3209F5D; Tue, 29 Oct 2024 17:24:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222697; cv=none; b=Jdu9GSNyNo+AZ5/EPZPaCX4b73vNCy5h0DYAboTxXC1UAb1NId6FtZqCcd0Z9p0ZknrRiwiSZHVGGhyyaZCJQIYYJK3W4nbwxRpVgY59ZqtXyXFyCattJBq2cjB3aSgrZmvyqE7V6plzo8QAuK+ftth73cKhau3D3ZBxoCHcAAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222697; c=relaxed/simple; bh=THwDVJycWD85EIuVm6b31JKn4pPiivNrQZ2Jehv5PFU=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=pSF8QrPEQ6Er8s5LVFJjb6kpsiJSo/0YPzGAPyianNZadOo910lNM02wCzRdt74fXugloo9N98JHzFb1jjKkqhth5aqOixF0LcMZ1EJIVFoUUzRNGP+u9IEN60GSAut41Ek8vR80qaiKQdiWDl5UArFVYt85Vv1MVYSsmgkwi1Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=p8RHZLeR; arc=none smtp.client-ip=198.47.19.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="p8RHZLeR" Received: from fllv0035.itg.ti.com ([10.64.41.0]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOgxb027146; Tue, 29 Oct 2024 12:24:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222682; bh=zzYTimKlJmYJPJPK7k4y8QpACJoWgJ+dEn8661bKzAA=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=p8RHZLeRguVZ4xIBrTW0IVpazew2iA1Enr0wRebb0pTdGJHjQLndbIg0fVYvL4MnO d65o1Ry6SmxUuhiu9VJXC7LyTMHkK4sC4xkvsoT4q0E/XGaEtNImgF4Eyb3t3qq4tk xeZoowM4F71EwbHlsYHUUnhY4qQkU67QfZ01ndF0= Received: from DLEE112.ent.ti.com (dlee112.ent.ti.com [157.170.170.23]) by fllv0035.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 49THOgB9092337 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 29 Oct 2024 12:24:42 -0500 Received: from DLEE110.ent.ti.com (157.170.170.21) by DLEE112.ent.ti.com (157.170.170.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:41 -0500 Received: from lelvsmtp5.itg.ti.com (10.180.75.250) by DLEE110.ent.ti.com (157.170.170.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:41 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp5.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOfqO006530; Tue, 29 Oct 2024 12:24:41 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 14/17] wifi: cc33xx: Add conf.h Date: Tue, 29 Oct 2024 19:23:51 +0200 Message-ID: <20241029172354.4027886-15-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Various HW / FW / Driver controls unique for the CC33xx that can be set by OEMs. Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/conf.h | 1246 +++++++++++++++++++++++++ 1 file changed, 1246 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/conf.h diff --git a/drivers/net/wireless/ti/cc33xx/conf.h b/drivers/net/wireless/ti/cc33xx/conf.h new file mode 100644 index 000000000000..bc6eeb7a82c4 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/conf.h @@ -0,0 +1,1246 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __CONF_H__ +#define __CONF_H__ + +struct cc33xx_conf_header { + __le32 magic; + __le32 version; + __le32 checksum; +} __packed; + +#define CC33XX_CONF_MAGIC 0x10e100ca +#define CC33XX_CONF_VERSION 0x01070069 +#define CC33XX_CONF_MASK 0x0000ffff +#define CC33X_CONF_SIZE (sizeof(struct cc33xx_conf_file)) + +enum { + CONF_HW_BIT_RATE_1MBPS = BIT(1), + CONF_HW_BIT_RATE_2MBPS = BIT(2), + CONF_HW_BIT_RATE_5_5MBPS = BIT(3), + CONF_HW_BIT_RATE_11MBPS = BIT(4), + CONF_HW_BIT_RATE_6MBPS = BIT(5), + CONF_HW_BIT_RATE_9MBPS = BIT(6), + CONF_HW_BIT_RATE_12MBPS = BIT(7), + CONF_HW_BIT_RATE_18MBPS = BIT(8), + CONF_HW_BIT_RATE_24MBPS = BIT(9), + CONF_HW_BIT_RATE_36MBPS = BIT(10), + CONF_HW_BIT_RATE_48MBPS = BIT(11), + CONF_HW_BIT_RATE_54MBPS = BIT(12), + CONF_HW_BIT_RATE_MCS_0 = BIT(13), + CONF_HW_BIT_RATE_MCS_1 = BIT(14), + CONF_HW_BIT_RATE_MCS_2 = BIT(15), + CONF_HW_BIT_RATE_MCS_3 = BIT(16), + CONF_HW_BIT_RATE_MCS_4 = BIT(17), + CONF_HW_BIT_RATE_MCS_5 = BIT(18), + CONF_HW_BIT_RATE_MCS_6 = BIT(19), + CONF_HW_BIT_RATE_MCS_7 = BIT(20) +}; + +enum { + CONF_HW_RATE_INDEX_1MBPS = 1, + CONF_HW_RATE_INDEX_2MBPS = 2, + CONF_HW_RATE_INDEX_5_5MBPS = 3, + CONF_HW_RATE_INDEX_11MBPS = 4, + CONF_HW_RATE_INDEX_6MBPS = 5, + CONF_HW_RATE_INDEX_9MBPS = 6, + CONF_HW_RATE_INDEX_12MBPS = 7, + CONF_HW_RATE_INDEX_18MBPS = 8, + CONF_HW_RATE_INDEX_24MBPS = 9, + CONF_HW_RATE_INDEX_36MBPS = 10, + CONF_HW_RATE_INDEX_48MBPS = 11, + CONF_HW_RATE_INDEX_54MBPS = 12, + CONF_HW_RATE_INDEX_MCS0 = 13, + CONF_HW_RATE_INDEX_MCS1 = 14, + CONF_HW_RATE_INDEX_MCS2 = 15, + CONF_HW_RATE_INDEX_MCS3 = 16, + CONF_HW_RATE_INDEX_MCS4 = 17, + CONF_HW_RATE_INDEX_MCS5 = 18, + CONF_HW_RATE_INDEX_MCS6 = 19, + CONF_HW_RATE_INDEX_MCS7 = 20, + + CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_MCS7, +}; + +enum { + CONF_PREAMBLE_TYPE_SHORT = 0, + CONF_PREAMBLE_TYPE_LONG = 1, + CONF_PREAMBLE_TYPE_OFDM = 2, + CONF_PREAMBLE_TYPE_N_MIXED_MODE = 3, + CONF_PREAMBLE_TYPE_GREENFIELD = 4, + CONF_PREAMBLE_TYPE_AX_SU = 5, + CONF_PREAMBLE_TYPE_AX_MU = 6, + CONF_PREAMBLE_TYPE_AX_SU_ER = 7, + CONF_PREAMBLE_TYPE_AX_TB = 8, + CONF_PREAMBLE_TYPE_AX_TB_NDP_FB = 9, + CONF_PREAMBLE_TYPE_AC_VHT = 10, + CONF_PREAMBLE_TYPE_BE_EHT_MU = 13, + CONF_PREAMBLE_TYPE_BE_EHT_TB = 14, + CONF_PREAMBLE_TYPE_INVALID = 0xFF +}; + +#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff + +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 */ +}; + +struct cc33xx_clk_cfg { + u32 n; + u32 m; + u32 p; + u32 q; + 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. + * + * Range: enum conf_tx_ac + */ + u8 ac; + + /* The contention window minimum size (in slots) for the access + * class. + * + * Range: uint8_t + */ + u8 cw_min; + + /* The contention window maximum size (in slots) for the access + * class. + * + * Range: uint8_t + */ + u16 cw_max; + + /* The AIF value (in slots) for the access class. + * + * Range: uint8_t + */ + u8 aifsn; + + /* The TX Op Limit (in microseconds) for the access class. + * + * Range: uint16_t + */ + u16 tx_op_limit; + + /* Is the MU EDCA configured + * + * Range: uint8_t + */ + u8 is_mu_edca; + + /* The AIFSN value for the corresponding access class + * + * Range: uint8_t + */ + u8 mu_edca_aifs; + + /* The ECWmin and ECWmax value is indicating contention window maximum + * size (in slots) for the access + * + * Range: uint8_t + */ + u8 mu_edca_ecw_min_max; + + /* The MU EDCA timer (in microseconds) obtaining an EDCA TXOP + * for STA using MU EDCA parameters + * + * Range: uint8_t + */ + u8 mu_edca_timer; +} __packed; + +#define CONF_TX_MAX_TID_COUNT 8 + +/* Allow TX BA on all TIDs but 6,7. These are currently reserved in the FW */ +#define CONF_TX_BA_ENABLED_TID_BITMAP 0x3F + +enum { + CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/ + CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/ + CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/ +}; + +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_ACK_POLICY_LEGACY = 0, + CONF_ACK_POLICY_NO_ACK = 1, + CONF_ACK_POLICY_BLOCK = 2, +}; + +struct conf_tx_tid { + u8 queue_id; + u8 channel_type; + u8 tsid; + u8 ps_scheme; + u8 ack_policy; + u32 apsd_conf[2]; +} __packed; + +struct conf_tx_settings { + /* The TX ED value for TELEC Enable/Disable. + * + * Range: 0, 1 + */ + u8 tx_energy_detection; + + /* 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; + + /* Configuration for access categories for TX rate control. + */ + u8 ac_conf_count; + /*struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT];*/ + struct conf_tx_ac_category ac_conf0; + struct conf_tx_ac_category ac_conf1; + struct conf_tx_ac_category ac_conf2; + struct conf_tx_ac_category ac_conf3; + + /* AP-mode - allow this number of TX retries to a station before an + * event is triggered from FW. + * In AP-mode the hlids of unreachable stations are given in the + * "sta_tx_retry_exceeded" member in the event mailbox. + */ + 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; + /* struct conf_tx_tid tid_conf[]; */ + struct conf_tx_tid tid_conf0; + struct conf_tx_tid tid_conf1; + struct conf_tx_tid tid_conf2; + struct conf_tx_tid tid_conf3; + struct conf_tx_tid tid_conf4; + struct conf_tx_tid tid_conf5; + 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 + */ + u32 basic_rate; + + /* The rate used for control messages and scanning on the 5GHz band + * + * Range: CONF_HW_BIT_RATE_* bit mask + */ + 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. + * + * Range: uint8_t + */ + u8 ie; + + /* Rule to associate with the specific ie. + * + * Range: CONF_BCN_RULE_PASS_ON_* + */ + u8 rule; + + /* OUI for the vendor specifie IE (221) + */ + u8 oui[3]; + + /* Type for the vendor specifie IE (221) + */ + u8 type; + + /* Version for the vendor specifie IE (221) + */ + 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. + * + * Range: CONF_BCN_FILT_MODE_* + */ + u8 bcn_filt_mode; + + /* Configure Beacon filter pass-through rules. + */ + u8 bcn_filt_ie_count; + /*struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT];*/ + /* struct conf_bcn_filt_rule bcn_filt_ie[32]; */ + struct conf_bcn_filt_rule bcn_filt_ie0; + struct conf_bcn_filt_rule bcn_filt_ie1; + struct conf_bcn_filt_rule bcn_filt_ie2; + struct conf_bcn_filt_rule bcn_filt_ie3; + struct conf_bcn_filt_rule bcn_filt_ie4; + struct conf_bcn_filt_rule bcn_filt_ie5; + struct conf_bcn_filt_rule bcn_filt_ie6; + struct conf_bcn_filt_rule bcn_filt_ie7; + struct conf_bcn_filt_rule bcn_filt_ie8; + struct conf_bcn_filt_rule bcn_filt_ie9; + struct conf_bcn_filt_rule bcn_filt_ie10; + struct conf_bcn_filt_rule bcn_filt_ie11; + struct conf_bcn_filt_rule bcn_filt_ie12; + struct conf_bcn_filt_rule bcn_filt_ie13; + struct conf_bcn_filt_rule bcn_filt_ie14; + struct conf_bcn_filt_rule bcn_filt_ie15; + struct conf_bcn_filt_rule bcn_filt_ie16; + struct conf_bcn_filt_rule bcn_filt_ie17; + struct conf_bcn_filt_rule bcn_filt_ie18; + struct conf_bcn_filt_rule bcn_filt_ie19; + struct conf_bcn_filt_rule bcn_filt_ie20; + struct conf_bcn_filt_rule bcn_filt_ie21; + struct conf_bcn_filt_rule bcn_filt_ie22; + struct conf_bcn_filt_rule bcn_filt_ie23; + struct conf_bcn_filt_rule bcn_filt_ie24; + struct conf_bcn_filt_rule bcn_filt_ie25; + struct conf_bcn_filt_rule bcn_filt_ie26; + struct conf_bcn_filt_rule bcn_filt_ie27; + struct conf_bcn_filt_rule bcn_filt_ie28; + struct conf_bcn_filt_rule bcn_filt_ie29; + struct conf_bcn_filt_rule bcn_filt_ie30; + struct conf_bcn_filt_rule bcn_filt_ie31; + + /* The number of consecutive beacons to lose, before the firmware + * becomes out of synch. + * + * Range: uint32_t + */ + u32 synch_fail_thold; + + /* After out-of-synch, the number of TU's to wait without a further + * received beacon (or probe response) before issuing the BSS_EVENT_LOSE + * event. + * + * Range: uint32_t + */ + 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 + */ + u8 max_listen_interval; + + /* Default sleep authorization for a new STA interface. This determines + * whether we can go to ELP. + */ + u8 sta_sleep_auth; + + /* Default RX BA Activity filter configuration + */ + 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. + * + * Range: uint32_t tu/1000 + */ + u32 min_dwell_time_active; + + /* The maximum time to wait on each channel for active scans + * This value will be currently used whenever there's a + * connected interface. It shouldn't exceed 30000 (~30ms) to avoid + * possible interference of voip traffic going on while scanning. + * + * Range: uint32_t tu/1000 + */ + u32 max_dwell_time_active; + + /* The minimum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * Currently this is used whenever we're idle on all interfaces. + * Longer dwell times improve detection of networks within a + * single scan. + * + * Range: uint32_t tu/1000 + */ + u32 min_dwell_time_active_long; + + /* The maximum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * See min_dwell_time_active_long + * + * Range: uint32_t tu/1000 + */ + u32 max_dwell_time_active_long; + + /* time to wait on the channel for passive scans (in TU/1000) */ + u32 dwell_time_passive; + + /* time to wait on the channel for DFS scans (in TU/1000) */ + u32 dwell_time_dfs; + + /* Number of probe requests to transmit on each active scan channel + * + * Range: uint8_t + */ + u16 num_probe_reqs; + + /* Scan trigger (split scan) timeout. The FW will split the scan + * operation into slices of the given time and allow the FW to schedule + * other tasks in between. + * + * Range: uint32_t Microsecs + */ + u32 split_scan_timeout; +} __packed; + +struct conf_sched_scan_settings { + /* The base time to wait on the channel for active scans (in TU/1000). + * The minimum dwell time is calculated according to this: + * min_dwell_time = base + num_of_probes_to_be_sent * delta_per_probe + * The maximum dwell time is calculated according to this: + * max_dwell_time = min_dwell_time + max_dwell_time_delta + */ + u32 base_dwell_time; + + /* The delta between the min dwell time and max dwell time for + * active scans (in TU/1000s). The max dwell time is used by the FW once + * traffic is detected on the channel. + */ + u32 max_dwell_time_delta; + + /* Delta added to min dwell time per each probe in 2.4 GHz (TU/1000) */ + u32 dwell_time_delta_per_probe; + + /* Delta added to min dwell time per each probe in 5 GHz (TU/1000) */ + u32 dwell_time_delta_per_probe_5; + + /* time to wait on the channel for passive scans (in TU/1000) */ + u32 dwell_time_passive; + + /* time to wait on the channel for DFS scans (in TU/1000) */ + u32 dwell_time_dfs; + + /* number of probe requests to send on each channel in active scans */ + u8 num_probe_reqs; + + /* RSSI threshold to be used for filtering */ + s8 rssi_threshold; + + /* SNR threshold to be used for filtering */ + s8 snr_threshold; + + /* number of short intervals scheduled scan cycles before + * switching to long intervals + */ + u8 num_short_intervals; + + /* interval between each long scheduled scan cycle (in ms) */ + u16 long_interval; +} __packed; + +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; + + /* Number of memory blocks dedicated for the FW logger + * + * Range: 2-16, or 0 to disable the FW logger + */ + u8 mem_blocks; + + /* Minimum log level threshold */ + u8 severity; + + /* Include/exclude timestamps from the log messages */ + u8 timestamp; + + /* See enum cc33xx_fwlogger_output */ + u8 output; + + /* Regulates the frequency of log messages */ + 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, + + /* Wide - use SISO40 */ + HT_MODE_WIDE = 1, + + /* Use SISO20 */ + 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 + * + * Range: 1 - YES, 0 - NO + */ + u8 disable_coex; + /* Yes/No Choose if External SoC entity is connected + * + * Range: 1 - YES, 0 - NO + */ + u8 is_ext_soc_enable; + /* External SoC grant polarity + * + * 0 - Active Low + * + * 1 - Active High (Default) + */ + u8 ext_soc_grant_polarity; + /* External SoC priority polarity + * + * 0 - Active Low (Default) + * + * 1 - Active High + */ + u8 ext_soc_priority_polarity; + /* External SoC request polarity + * + * 0 - Active Low (Default) + * + * 1 - Active High + */ + u8 ext_soc_request_polarity; + u16 ext_soc_min_grant_time; + u16 ext_soc_max_grant_time; + /* Range: 0 - 20 us + */ + u8 ext_soc_t2_time; + + u8 ext_soc_to_wifi_grant_delay; + u8 ext_soc_to_ble_grant_delay; +} __packed; + +struct conf_iomux_configuration { + /* For any iomux pull value: + * 1: Pull up + * 2: Pull down + * 3: Pull disable + * ff: Default value set by HW + * ANY other value is invalid + */ + u8 slow_clock_in_pull_val; + u8 sdio_clk_pull_val; + u8 sdio_cmd_pull_val; + u8 sdio_d0_pull_val; + u8 sdio_d1_pull_val; + u8 sdio_d2_pull_val; + u8 sdio_d3_pull_val; + u8 host_irq_wl_pull_val; + u8 uart1_tx_pull_val; + u8 uart1_rx_pull_val; + u8 uart1_cts_pull_val; + u8 uart1_rts_pull_val; + u8 coex_priority_pull_val; + u8 coex_req_pull_val; + u8 coex_grant_pull_val; + u8 host_irq_ble_pull_val; + u8 fast_clk_req_pull_val; + u8 ant_sel_pull_val; +} __packed; + +struct conf_ant_diversity { + /* First beacons after antenna switch. + * In this window we asses our satisfaction from the new antenna. + */ + u8 fast_switching_window; + + /* Deltas above this threshold between the curiosity score and + * the average RSSI will lead to antenna switch. + */ + u8 rssi_delta_for_switching; + + /* Used in the first beacons after antenna switch: + * Deltas above this threshold between the average RSSI and + * the curiosity score will make us switch back the antennas. + */ + u8 rssi_delta_for_fast_switching; + + /* Curiosity punishment in beacon timeout after an antenna switch. + */ + u8 curiosity_punish; + + /* Curiosity raise in beacon timeout not after an antenna switch. + */ + u8 curiosity_raise; + + /* Used for the average RSSI punishment in beacon timeout + * not after antenna switch. + */ + u8 consecutive_missed_beacons_threshold; + + /* Used in the curiosity metric. + */ + u8 compensation_log; + + /* Used in the average RSSI metric. + */ + u8 log_alpha; + + /* Curiosity initialization score. + */ + s8 initial_curiosity; + + /* MR configuration: should the AP follow the STA antenna or use the default antenna. + */ + u8 ap_follows_sta; + + /* MR configuration: should the BLE follow the STA antenna or use the default antenna. + */ + u8 ble_follows_sta; + + /* The antenna to use when the diversity mechanism is not in charge. + */ + u8 default_antenna; +} __packed; + +struct cc33xx_core_conf { + u8 enable_5ghz; + u8 enable_ble; + u8 enable_at_test_debug; + u8 disable_beamforming_fftp; + u32 ble_uart_baudrate; + u8 enable_flow_ctrl; + u8 listen_interval; + u8 wake_up_event; + u8 suspend_listen_interval; + u8 suspend_wake_up_event; + u8 per_channel_power_limit[507]; + u32 internal_slowclk_wakeup_earlier; + u32 internal_slowclk_open_window_longer; + u32 external_slowclk_wakeup_earlier; + u32 external_slowclk_open_window_longer; + struct conf_coex_configuration coex_configuration; + /* Prevent HW recovery. FW will remain stuck. */ + u8 no_recovery; + u8 disable_logger; + u8 mixed_mode_support; + u8 sram_ldo_voltage_trimming; + u32 xtal_settling_time_usec; + struct conf_ant_diversity ant_diversity; + struct conf_iomux_configuration iomux_configuration; +} __packed; + +struct cc33xx_mac_conf { + u8 ps_scheme; + u8 he_enable; + u8 ap_max_num_stations; +} __packed; + +struct cc33xx_phy_conf { + u8 insertion_loss_2_4ghz[2]; + u8 insertion_loss_5ghz[2]; + u8 reserved_0[2]; + u8 ant_gain_2_4ghz[2]; + u8 ant_gain_5ghz[2]; + u8 reserved_1[2]; + u8 ble_ch_lim_1m[40]; + u8 ble_ch_lim_2m[40]; + u8 one_time_calibration_only; + u8 is_diplexer_present; + u8 num_of_antennas; + u8 reg_domain; + u16 calib_period; +} __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; + +struct cc33xx_conf_file { + struct cc33xx_conf_header header; + struct cc33xx_phy_conf phy; + struct cc33xx_mac_conf mac; + struct cc33xx_core_conf core; + struct cc33xx_host_conf host_conf; +} __packed; + +#endif From patchwork Tue Oct 29 17:23:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Nemanov, Michael" X-Patchwork-Id: 839575 Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A4AF20A5D7; Tue, 29 Oct 2024 17:24:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222695; cv=none; b=kBhqWRnILyDBf2yNPuvtVMKh8tyCQAcf5XZlYtN6QJZary86UT9uXX3AADLiFZcJg7E1z8JfQ77OSryqJd0cCwzCIMNF+Ar+EY3iRMYrsxB6FW3cJfg6vPuRXnSxvDHHyth8nSOCP8+9/dfjjENwPtobUHrzcwdpIrhjvmGz23w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730222695; c=relaxed/simple; bh=R6tpvRyIpwX4pXsdqHoRg3bE5Z7lDq0QLETfgR6SyRs=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=nJzVhrwu5tLXm2E1o+h11PAHAXlvC2ruYfGMPCHMFbd/rzIcrt05Fq+uq/umImt9wvQxZNmiwZW6hws5qn1Gn94vefmA3PunvTlXh3WsL94EAep3blrIjbYGm8AMk7MgwWvJ9+sgq/J5UrbB5NCZujN5dPMhSedV4+rczwpDa0k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=FBWd1QQY; arc=none smtp.client-ip=198.47.23.249 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="FBWd1QQY" Received: from fllv0034.itg.ti.com ([10.64.40.246]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id 49THOjEG127653; Tue, 29 Oct 2024 12:24:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1730222685; bh=fVF+Yae9zuym8XN0sRP46oIer23L3hr3RjHm+d4FVXs=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=FBWd1QQYH8IdfaMbDEdG5xOeiaBUXLjoTumbEgLnTBziKBWQxGSc067/HTfAbZ4G9 W0u/l5d7t2bKDyXKvsP3w7FTmRcK7m4ABU0ZyyvBj93NO0cWJhk4kh5+tzwHF3aNpk WUwmYxeTdhmB04B9SxxXnhQTj8XYUYFGd47t/h3s= Received: from DLEE114.ent.ti.com (dlee114.ent.ti.com [157.170.170.25]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 49THOj5m072591 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 29 Oct 2024 12:24:45 -0500 Received: from DLEE103.ent.ti.com (157.170.170.33) by DLEE114.ent.ti.com (157.170.170.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 29 Oct 2024 12:24:44 -0500 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE103.ent.ti.com (157.170.170.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 29 Oct 2024 12:24:44 -0500 Received: from localhost (udb0389739.dhcp.ti.com [137.167.1.149]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 49THOhlb065882; Tue, 29 Oct 2024 12:24:44 -0500 From: Michael Nemanov To: Kalle Valo , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , , , , CC: Sabeeh Khan , Michael Nemanov Subject: [PATCH v4 16/17] wifi: cc33xx: Add testmode.c, testmode.h Date: Tue, 29 Oct 2024 19:23:53 +0200 Message-ID: <20241029172354.4027886-17-michael.nemanov@ti.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241029172354.4027886-1-michael.nemanov@ti.com> References: <20241029172354.4027886-1-michael.nemanov@ti.com> Precedence: bulk X-Mailing-List: linux-wireless@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-C2ProcessedOrg: 333ef613-75bf-4e12-a4b1-8e3623f5dcea Allows a user-space tools to access FW APIs via CFG80211_TESTMODE infrastructure. Signed-off-by: Michael Nemanov --- drivers/net/wireless/ti/cc33xx/testmode.c | 349 ++++++++++++++++++++++ drivers/net/wireless/ti/cc33xx/testmode.h | 12 + 2 files changed, 361 insertions(+) create mode 100644 drivers/net/wireless/ti/cc33xx/testmode.c create mode 100644 drivers/net/wireless/ti/cc33xx/testmode.h diff --git a/drivers/net/wireless/ti/cc33xx/testmode.c b/drivers/net/wireless/ti/cc33xx/testmode.c new file mode 100644 index 000000000000..b845610c5a30 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/testmode.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include + +#include "cc33xx.h" +#include "acx.h" +#include "io.h" +#include "testmode.h" + +#define CC33XX_TM_MAX_DATA_LENGTH 1024 + +enum cc33xx_tm_commands { + CC33XX_TM_CMD_UNSPEC, + CC33XX_TM_CMD_TEST, + CC33XX_TM_CMD_INTERROGATE, + CC33XX_TM_CMD_CONFIGURE, + CC33XX_TM_CMD_NVS_PUSH, /* Not in use. Keep to not break ABI */ + CC33XX_TM_CMD_SET_PLT_MODE, + CC33XX_TM_CMD_RECOVER, /* Not in use. Keep to not break ABI */ + CC33XX_TM_CMD_GET_MAC, + + __CC33XX_TM_CMD_AFTER_LAST +}; + +enum cc33xx_tm_attrs { + CC33XX_TM_ATTR_UNSPEC, + CC33XX_TM_ATTR_CMD_ID, + CC33XX_TM_ATTR_ANSWER, + CC33XX_TM_ATTR_DATA, + CC33XX_TM_ATTR_IE_ID, + CC33XX_TM_ATTR_PLT_MODE, + + __CC33XX_TM_ATTR_AFTER_LAST +}; + +#define CC33XX_TM_ATTR_MAX (__CC33XX_TM_ATTR_AFTER_LAST - 1) + +static struct nla_policy cc33xx_tm_policy[CC33XX_TM_ATTR_MAX + 1] = { + [CC33XX_TM_ATTR_CMD_ID] = { .type = NLA_U32 }, + [CC33XX_TM_ATTR_ANSWER] = { .type = NLA_U8 }, + [CC33XX_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = CC33XX_TM_MAX_DATA_LENGTH }, + [CC33XX_TM_ATTR_IE_ID] = { .type = NLA_U32 }, + [CC33XX_TM_ATTR_PLT_MODE] = { .type = NLA_U32 }, +}; + +static int cc33xx_tm_cmd_test(struct cc33xx *cc, struct nlattr *tb[]) +{ + int ret, len; + u16 buf_len; + struct sk_buff *skb; + void *buf; + u8 answer = 0; + + if (!tb[CC33XX_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[CC33XX_TM_ATTR_DATA]); + buf_len = nla_len(tb[CC33XX_TM_ATTR_DATA]); + + if (tb[CC33XX_TM_ATTR_ANSWER]) + answer = nla_get_u8(tb[CC33XX_TM_ATTR_ANSWER]); + + if (buf_len > sizeof(struct cc33xx_command)) + return -EMSGSIZE; + + mutex_lock(&cc->mutex); + + if (unlikely(cc->state != CC33XX_STATE_ON)) { + ret = -EINVAL; + goto out; + } + + ret = cc33xx_cmd_test(cc, buf, buf_len, answer); + if (ret < 0) { + cc33xx_warning("testmode cmd test failed: %d", ret); + goto out; + } + + if (answer) { + /* If we got bip calibration answer print radio status */ + struct cc33xx_cmd_cal_p2g *params = + (struct cc33xx_cmd_cal_p2g *)buf; + s16 radio_status = (s16)le16_to_cpu(params->radio_status); + + if (params->test.id == TEST_CMD_P2G_CAL && radio_status < 0) + cc33xx_warning("testmode cmd: radio status=%d", + radio_status); + else + cc33xx_info("testmode cmd: radio status=%d", + radio_status); + + len = nla_total_size(buf_len); + skb = cfg80211_testmode_alloc_reply_skb(cc->hw->wiphy, len); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + if (nla_put(skb, CC33XX_TM_ATTR_DATA, buf_len, buf)) { + kfree_skb(skb); + ret = -EMSGSIZE; + goto out; + } + + ret = cfg80211_testmode_reply(skb); + } + +out: + mutex_unlock(&cc->mutex); + + return ret; +} + +static int cc33xx_tm_cmd_interrogate(struct cc33xx *cc, struct nlattr *tb[]) +{ + int ret; + struct cc33xx_command *cmd; + struct sk_buff *skb; + u8 ie_id; + + if (!tb[CC33XX_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[CC33XX_TM_ATTR_IE_ID]); + + mutex_lock(&cc->mutex); + + if (unlikely(cc->state != CC33XX_STATE_ON)) { + ret = -EINVAL; + goto out; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + ret = cc33xx_cmd_debug_inter(cc, ie_id, cmd, + sizeof(struct acx_header), sizeof(*cmd)); + if (ret < 0) { + cc33xx_warning("testmode cmd interrogate failed: %d", ret); + goto out_free; + } + + skb = cfg80211_testmode_alloc_reply_skb(cc->hw->wiphy, sizeof(*cmd)); + if (!skb) { + ret = -ENOMEM; + goto out_free; + } + + if (nla_put(skb, CC33XX_TM_ATTR_DATA, sizeof(*cmd), cmd)) { + kfree_skb(skb); + ret = -EMSGSIZE; + goto out_free; + } + + ret = cfg80211_testmode_reply(skb); + if (ret < 0) + goto out_free; + +out_free: + kfree(cmd); + +out: + mutex_unlock(&cc->mutex); + + return ret; +} + +static int cc33xx_tm_cmd_configure(struct cc33xx *cc, struct nlattr *tb[]) +{ + int ret; + u16 buf_len; + void *buf; + u8 ie_id; + + if (!tb[CC33XX_TM_ATTR_DATA]) + return -EINVAL; + if (!tb[CC33XX_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[CC33XX_TM_ATTR_IE_ID]); + buf = nla_data(tb[CC33XX_TM_ATTR_DATA]); + buf_len = nla_len(tb[CC33XX_TM_ATTR_DATA]); + + if (buf_len > sizeof(struct cc33xx_command)) + return -EMSGSIZE; + + mutex_lock(&cc->mutex); + ret = cc33xx_cmd_debug(cc, ie_id, buf, buf_len); + mutex_unlock(&cc->mutex); + + if (ret < 0) { + cc33xx_warning("testmode cmd configure failed: %d", ret); + return ret; + } + + return 0; +} + +static int cc33xx_tm_detect_fem(struct cc33xx *cc, struct nlattr *tb[]) +{ + /* return FEM type */ + int ret, len; + struct sk_buff *skb; + + ret = cc33xx_plt_start(cc, PLT_FEM_DETECT); + if (ret < 0) + goto out; + + mutex_lock(&cc->mutex); + + len = nla_total_size(sizeof(cc->fem_manuf)); + skb = cfg80211_testmode_alloc_reply_skb(cc->hw->wiphy, len); + if (!skb) { + ret = -ENOMEM; + goto out_mutex; + } + + if (nla_put(skb, CC33XX_TM_ATTR_DATA, sizeof(cc->fem_manuf), + &cc->fem_manuf)) { + kfree_skb(skb); + ret = -EMSGSIZE; + goto out_mutex; + } + + ret = cfg80211_testmode_reply(skb); + +out_mutex: + mutex_unlock(&cc->mutex); + + /* We always stop plt after DETECT mode */ + cc33xx_plt_stop(cc); +out: + return ret; +} + +static int cc33xx_tm_cmd_set_plt_mode(struct cc33xx *cc, struct nlattr *tb[]) +{ + u32 val; + int ret; + + if (!tb[CC33XX_TM_ATTR_PLT_MODE]) + return -EINVAL; + + val = nla_get_u32(tb[CC33XX_TM_ATTR_PLT_MODE]); + + switch (val) { + case PLT_OFF: + ret = cc33xx_plt_stop(cc); + break; + case PLT_ON: + case PLT_CHIP_AWAKE: + ret = cc33xx_plt_start(cc, val); + break; + case PLT_FEM_DETECT: + ret = cc33xx_tm_detect_fem(cc, tb); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int cc33xx_tm_cmd_get_mac(struct cc33xx *cc, struct nlattr *tb[]) +{ + struct sk_buff *skb; + u8 zero_mac[ETH_ALEN] = {0}; + int ret = 0; + + mutex_lock(&cc->mutex); + + if (!cc->plt) { + ret = -EINVAL; + goto out; + } + + if (memcmp(zero_mac, cc->efuse_mac_address, ETH_ALEN) == 0) { + ret = -EOPNOTSUPP; + goto out; + } + + skb = cfg80211_testmode_alloc_reply_skb(cc->hw->wiphy, ETH_ALEN); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + if (nla_put(skb, CC33XX_TM_ATTR_DATA, + ETH_ALEN, cc->efuse_mac_address)) { + kfree_skb(skb); + ret = -EMSGSIZE; + goto out; + } + + ret = cfg80211_testmode_reply(skb); + if (ret < 0) + goto out; + +out: + mutex_unlock(&cc->mutex); + return ret; +} + +int cc33xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct cc33xx *cc = hw->priv; + struct nlattr *tb[CC33XX_TM_ATTR_MAX + 1]; + u32 nla_cmd; + int err; + + err = nla_parse_deprecated(tb, CC33XX_TM_ATTR_MAX, data, len, + cc33xx_tm_policy, NULL); + if (err) + return err; + + if (!tb[CC33XX_TM_ATTR_CMD_ID]) + return -EINVAL; + + nla_cmd = nla_get_u32(tb[CC33XX_TM_ATTR_CMD_ID]); + + /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */ + if (cc->plt_mode == PLT_CHIP_AWAKE && + nla_cmd != CC33XX_TM_CMD_SET_PLT_MODE) + return -EOPNOTSUPP; + + switch (nla_cmd) { + case CC33XX_TM_CMD_TEST: + return cc33xx_tm_cmd_test(cc, tb); + case CC33XX_TM_CMD_INTERROGATE: + return cc33xx_tm_cmd_interrogate(cc, tb); + case CC33XX_TM_CMD_CONFIGURE: + return cc33xx_tm_cmd_configure(cc, tb); + case CC33XX_TM_CMD_SET_PLT_MODE: + return cc33xx_tm_cmd_set_plt_mode(cc, tb); + case CC33XX_TM_CMD_GET_MAC: + return cc33xx_tm_cmd_get_mac(cc, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ti/cc33xx/testmode.h b/drivers/net/wireless/ti/cc33xx/testmode.h new file mode 100644 index 000000000000..58f336202925 --- /dev/null +++ b/drivers/net/wireless/ti/cc33xx/testmode.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef __TESTMODE_H__ +#define __TESTMODE_H__ + +int cc33xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#endif /* __TESTMODE_H__ */