/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2026 Morse Micro */ #ifndef _MM81X_CORE_H_ #define _MM81X_CORE_H_ #include #include #include #include #include #include #include #include #include #include #include "yaps.h" #include "yaps_hw.h" #include "hw.h" #include "fw.h" #include "rc.h" #define MM81X_DRIVER_SEMVER_MAJOR 56 #define MM81X_DRIVER_SEMVER_MINOR 3 #define MM81X_DRIVER_SEMVER_PATCH 0 #define MM81X_SEMVER_GET_MAJOR(x) (((x) >> 22) & 0x3FF) #define MM81X_SEMVER_GET_MINOR(x) (((x) >> 10) & 0xFFF) #define MM81X_SEMVER_GET_PATCH(x) ((x) & 0x3FF) #define DRV_VERSION __stringify(MM81X_VERSION) #define MM8108_FW_BASE "mm8108" #define BCF_SIZE_MAX 48 #define KHZ100_TO_MHZ(x) ((x) / 10) #define KHZ100_TO_KHZ(freq) ((freq) * 100) #define KHZ100_TO_HZ(freq) ((freq) * 100000) #define QDBM_TO_MBM(gain) (((gain) * 100) >> 2) #define MBM_TO_QDBM(gain) (((gain) << 2) / 100) #define QDBM_TO_DBM(gain) ((gain) / 4) #define BPS_TO_KBPS(x) ((x) / 1000) #define NSS_IDX_TO_NSS(x) ((x) + 1) #define NSS_TO_NSS_IDX(x) ((x) - 1) #define ROUND_BYTES_TO_WORD(_nbytes) \ (((_nbytes) + 3) & ~((typeof(_nbytes))0x03)) struct mm81x_bus_ops; struct mm81x_hif_ops; #define MM81X_CAPS_MAX_FW_VAL (128) /* Max number of interfaces */ #define MM81X_MAX_IF (2) enum mm81x_caps_flags { MM81X_CAPS_FW_START = 0, MM81X_CAPS_2MHZ = MM81X_CAPS_FW_START, MM81X_CAPS_4MHZ, MM81X_CAPS_8MHZ, MM81X_CAPS_16MHZ, MM81X_CAPS_SGI, MM81X_CAPS_S1G_LONG, MM81X_CAPS_TRAVELING_PILOT_ONE_STREAM, MM81X_CAPS_TRAVELING_PILOT_TWO_STREAM, MM81X_CAPS_MU_BEAMFORMEE, MM81X_CAPS_MU_BEAMFORMER, MM81X_CAPS_RD_RESPONDER, MM81X_CAPS_STA_TYPE_SENSOR, MM81X_CAPS_STA_TYPE_NON_SENSOR, MM81X_CAPS_GROUP_AID, MM81X_CAPS_NON_TIM, MM81X_CAPS_TIM_ADE, MM81X_CAPS_BAT, MM81X_CAPS_DYNAMIC_AID, MM81X_CAPS_UPLINK_SYNC, MM81X_CAPS_FLOW_CONTROL, MM81X_CAPS_AMPDU, MM81X_CAPS_AMSDU, MM81X_CAPS_1MHZ_CONTROL_RESPONSE_PREAMBLE, MM81X_CAPS_PAGE_SLICING, MM81X_CAPS_RAW, MM81X_CAPS_MCS8, MM81X_CAPS_MCS9, MM81X_CAPS_ASYMMETRIC_BA_SUPPORT, MM81X_CAPS_DAC, MM81X_CAPS_CAC, MM81X_CAPS_TXOP_SHARING_IMPLICIT_ACK, MM81X_CAPS_NDP_PSPOLL, MM81X_CAPS_FRAGMENT_BA, MM81X_CAPS_OBSS_MITIGATION, MM81X_CAPS_TMP_PS_MODE_SWITCH, MM81X_CAPS_SECTOR_TRAINING, MM81X_CAPS_UNSOLICIT_DYNAMIC_AID, MM81X_CAPS_NDP_BEAMFORMING_REPORT, MM81X_CAPS_MCS_NEGOTIATION, MM81X_CAPS_DUPLICATE_1MHZ, MM81X_CAPS_TACK_AS_PSPOLL, MM81X_CAPS_PV1, MM81X_CAPS_TWT_RESPONDER, MM81X_CAPS_TWT_REQUESTER, MM81X_CAPS_BDT, MM81X_CAPS_TWT_GROUPING, MM81X_CAPS_LINK_ADAPTATION_WO_NDP_CMAC, MM81X_CAPS_LONG_MPDU, MM81X_CAPS_TXOP_SECTORIZATION, MM81X_CAPS_GROUP_SECTORIZATION, MM81X_CAPS_HTC_VHT, MM81X_CAPS_HTC_VHT_MFB, MM81X_CAPS_HTC_VHT_MRQ, MM81X_CAPS_2SS, MM81X_CAPS_3SS, MM81X_CAPS_4SS, MM81X_CAPS_SU_BEAMFORMEE, MM81X_CAPS_SU_BEAMFORMER, MM81X_CAPS_RX_STBC, MM81X_CAPS_TX_STBC, MM81X_CAPS_RX_LDPC, MM81X_CAPS_HW_FRAGMENT, MM81X_CAPS_FW_END = MM81X_CAPS_MAX_FW_VAL, MM81X_CAPS_LAST = MM81X_CAPS_FW_END, }; struct mm81x_fw_caps { u32 flags[FW_CAPABILITIES_FLAGS_WIDTH]; u8 ampdu_mss; u8 beamformee_sts_capability; u8 number_sounding_dimensions; u8 maximum_ampdu_length_exponent; u8 mm81x_mmss_offset; }; #define MM81X_FW_SUPP(MM81X_CAPS, CAPABILITY) \ mm81x_caps_supported(MM81X_CAPS, MM81X_CAPS_##CAPABILITY) static inline bool mm81x_caps_supported(struct mm81x_fw_caps *caps, enum mm81x_caps_flags flag) { const unsigned long *flags_ptr = (unsigned long *)caps->flags; return test_bit(flag, flags_ptr); } struct mm81x_ps { u32 wakers; bool enable; bool suspended; /* PS state lock */ struct mutex lock; struct delayed_work delayed_eval_work; }; enum mm81x_page_aci { MM81X_ACI_BE = 0, MM81X_ACI_BK = 1, MM81X_ACI_VI = 2, MM81X_ACI_VO = 3, }; enum mm81x_qos_tid_up_index { MM81X_QOS_TID_UP_BK = 1, MM81X_QOS_TID_UP_XX = 2, MM81X_QOS_TID_UP_BE = 0, MM81X_QOS_TID_UP_EE = 3, MM81X_QOS_TID_UP_CL = 4, MM81X_QOS_TID_UP_VI = 5, MM81X_QOS_TID_UP_VO = 6, MM81X_QOS_TID_UP_NC = 7, MM81X_QOS_TID_UP_LOWEST = MM81X_QOS_TID_UP_BK, MM81X_QOS_TID_UP_HIGHEST = MM81X_QOS_TID_UP_NC }; struct mm81x_sw_version { u8 major; u8 minor; u8 patch; }; struct mm81x_sta { const struct ieee80211_vif *vif; u8 addr[ETH_ALEN]; enum ieee80211_sta_state state; bool tid_tx[IEEE80211_NUM_TIDS]; bool tid_start_tx[IEEE80211_NUM_TIDS]; u8 tid_params[IEEE80211_NUM_TIDS]; int max_bw_mhz; struct mm81x_rc_sta rc; struct mmrc_rate last_sta_tx_rate; s16 avg_rssi; bool tx_ps_filter_en; }; struct mm81x_vif { struct mm81x *mors; u16 id; union { struct { bool is_assoc; } sta; struct { u32 num_stas; struct work_struct beacon_work; } ap; } u; }; struct mm81x_stale_tx_status { /* Stale Tx lock */ spinlock_t lock; struct timer_list timer; }; struct mcast_filter { u8 count; /* * Integer representation of the last four bytes of a multicast MAC * address. The first two bytes are always 0x0100 (IPv4) or 0x3333 * (IPv6). */ __le32 addr_list[]; }; enum mm81x_hw_scan_op { MM81X_HW_SCAN_OP_START, MM81X_HW_SCAN_OP_STOP, }; struct mm81x_hw_scan_params { struct ieee80211_hw *hw; /* vif which initiated the scan */ struct ieee80211_vif *vif; bool has_directed_ssid; u32 dwell_time_ms; u32 dwell_on_home_ms; enum mm81x_hw_scan_op operation; bool store; struct sk_buff *probe_req; u16 num_chans; u16 allocated_chans; struct { struct ieee80211_channel *channel; /* Index into @ref powers_qdbm for the power of this channel */ u8 power_idx; } *channels; s32 *powers_qdbm; u8 n_powers; }; enum mm81x_hw_scan_state { HW_SCAN_STATE_IDLE, HW_SCAN_STATE_RUNNING, HW_SCAN_STATE_ABORTING, }; struct mm81x_hw_scan { enum mm81x_hw_scan_state state; struct completion scan_done; struct mm81x_hw_scan_params *params; struct delayed_work timeout; u32 home_dwell_ms; }; enum mm81x_hif_event_flags { MM81X_HIF_EVT_RX_PEND, MM81X_HIF_EVT_PAGE_RETURN_PEND, MM81X_HIF_EVT_TX_COMMAND_PEND, MM81X_HIF_EVT_TX_BEACON_PEND, MM81X_HIF_EVT_TX_MGMT_PEND, MM81X_HIF_EVT_TX_DATA_PEND, MM81X_HIF_EVT_TX_PACKET_FREED_UP_PEND, MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND, MM81X_HIF_EVT_DATA_TRAFFIC_RESUME_PEND, MM81X_HIF_EVT_UPDATE_HW_CLOCK_REFERENCE, }; enum mm81x_state_flags { MM81X_STATE_CHIP_UNRESPONSIVE, MM81X_STATE_DATA_QS_STOPPED, MM81X_STATE_DATA_TX_STOPPED, MM81X_STATE_REGDOM_SET_BY_USER, MM81X_STATE_REGDOM_SET_BY_OTP, MM81X_STATE_RELOAD_FW_AFTER_START, MM81X_STATE_HOST_TO_CHIP_TX_BLOCKED, MM81X_STATE_HOST_TO_CHIP_CMD_BLOCKED, }; #define MM81X_COUNTRY_LEN (3) #define INVALID_VIF_INDEX 0xFF struct mm81x { u32 chip_id; u32 host_table_ptr; /* Refer to @enum mm81x_bus_type */ u32 bus_type; u32 bcf_address; /* * Parsed from the release tag, which should be in the format * 'rel___'. If the tag is not in this format * then corresponding version field will be 0. */ struct mm81x_sw_version sw_ver; u8 macaddr[ETH_ALEN]; u8 country[MM81X_COUNTRY_LEN]; /* Mask of type @enum host_table_firmware_flags */ u32 fw_flags; u32 fw_major; struct mm81x_fw_caps fw_caps; bool started; bool chip_was_reset; struct wiphy *wiphy; struct mm81x_hw_scan hw_scan; struct ieee80211_hw *hw; struct device *dev; struct ieee80211_vif __rcu *vifs[MM81X_MAX_IF]; /* @mm81x_state_flags */ unsigned long state_flags; u16 cmd_seq; struct completion *cmd_comp; /* Serialises commands */ struct mutex cmd_lock; /* Serialises command completion */ struct mutex cmd_wait; const struct mm81x_regs *regs; struct { union { struct mm81x_yaps yaps; } u; const struct mm81x_hif_ops *ops; /* See @enum mm81x_hif_event_flags for values */ unsigned long event_flags; bool validate_skb_checksum; } hif; struct workqueue_struct *chip_wq; struct work_struct hif_work; struct work_struct usb_irq_work; struct mm81x_stale_tx_status stale_status; bool config_ps; struct mm81x_ps ps; /* Tx power in mBm received from the FW before association */ s32 tx_power_mbm; s32 tx_max_power_mbm; const struct mm81x_bus_ops *bus_ops; struct mm81x_rc mrc; int rts_threshold; struct workqueue_struct *net_wq; struct work_struct tx_stale_work; wait_queue_head_t tx_empty_waitq; struct cfg80211_chan_def chandef; struct mcast_filter *mcast_filter; atomic_t num_bcn_vifs; unsigned long beacon_irqs_enabled; u8 drv_priv[] __aligned(sizeof(void *)); }; /* Map from mac80211 queue to Morse ACI value for page metadata */ static inline u8 map_mac80211q_2_mm81x_aci(u16 mac80211queue) { switch (mac80211queue) { case IEEE80211_AC_VO: return MM81X_ACI_VO; case IEEE80211_AC_VI: return MM81X_ACI_VI; case IEEE80211_AC_BK: return MM81X_ACI_BK; default: return MM81X_ACI_BE; } } static inline enum mm81x_page_aci dot11_tid_to_ac(enum mm81x_qos_tid_up_index tid) { switch (tid) { case MM81X_QOS_TID_UP_BK: case MM81X_QOS_TID_UP_XX: return MM81X_ACI_BK; case MM81X_QOS_TID_UP_CL: case MM81X_QOS_TID_UP_VI: return MM81X_ACI_VI; case MM81X_QOS_TID_UP_VO: case MM81X_QOS_TID_UP_NC: return MM81X_ACI_VO; case MM81X_QOS_TID_UP_BE: case MM81X_QOS_TID_UP_EE: default: return MM81X_ACI_BE; } } static inline bool mm81x_is_data_tx_allowed(struct mm81x *mors) { return !test_bit(MM81X_STATE_DATA_TX_STOPPED, &mors->state_flags) && !test_bit(MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND, &mors->hif.event_flags); } static inline struct ieee80211_vif * mm81x_vif_to_ieee80211_vif(struct mm81x_vif *mors_vif) { return container_of((void *)mors_vif, struct ieee80211_vif, drv_priv); } static inline struct mm81x_vif * ieee80211_vif_to_mors_vif(struct ieee80211_vif *vif) { return (struct mm81x_vif *)vif->drv_priv; } static inline struct mm81x *mm81x_vif_to_mors(struct mm81x_vif *mors_vif) { return mors_vif->mors; } static inline u32 mm81x_generate_cssid(const u8 *ssid, u8 len) { return ~crc32(~0, ssid, len); } int mm81x_beacon_init(struct mm81x_vif *mors_vif); void mm81x_beacon_finish(struct mm81x_vif *mors_vif); void mm81x_beacon_irq_handle(struct mm81x *mors, u32 status); char *mm81x_core_get_fw_path(u32 chip_id, u32 fw_ver); struct mm81x *mm81x_core_alloc(size_t priv_size, struct device *dev); int mm81x_core_init(struct mm81x *mors); int mm81x_core_register(struct mm81x *mors); void mm81x_core_unregister(struct mm81x *mors); void mm81x_core_deinit(struct mm81x *mors); void mm81x_core_free(struct mm81x *mors); #endif /* !_MM81X_MM81X_H_ */