// SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
bool debug_fw; /* = false; */
module_param(debug_fw, bool, 0444);
MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");
static u8 oob_mode;
module_param(oob_mode, byte, 0444);
MODULE_PARM_DESC(oob_mode, " enable out of the box (OOB) mode in FW, for diagnostics and certification");
/* if not set via modparam, will be set to default value of 1/8 of * rx ring size during init flow
*/ unsignedshort rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT;
module_param(rx_ring_overflow_thrsh, ushort, 0444);
MODULE_PARM_DESC(rx_ring_overflow_thrsh, " RX ring overflow threshold in descriptors.");
/* We allow allocation of more than 1 page buffers to support large packets. * It is suboptimal behavior performance wise in case MTU above page size.
*/ unsignedint mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; staticint mtu_max_set(constchar *val, conststruct kernel_param *kp)
{ int ret;
/* sets mtu_max directly. no need to restore it in case of * illegal value since we assume this will fail insmod
*/
ret = param_set_uint(val, kp); if (ret) return ret;
if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU)
ret = -EINVAL;
#define RST_DELAY (20) /* msec, for loop in @wil_wait_device_ready */ #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
#define PMU_READY_DELAY_MS (4) /* ms, for sleep in @wil_wait_device_ready */
#define OTP_HW_DELAY (200) /* usec, loop in @wil_wait_device_ready_talyn_mb */ /* round up to be above 2 ms total */ #define OTP_HW_COUNT (1 + 2000 / OTP_HW_DELAY)
/* * Due to a hardware issue, * one has to read/write to/from NIC in 32-bit chunks; * regular memcpy_fromio and siblings will * not work on 64-bit platform - it uses 64-bit transactions * * Force 32-bit transactions to enable NIC on 64-bit platforms * * To avoid byte swap on big endian host, __raw_{read|write}l * should be used - {read|write}l would swap bytes to provide * little endian on PCI value in host endianness.
*/ void wil_memcpy_fromio_32(void *dst, constvolatilevoid __iomem *src,
size_t count)
{
u32 *d = dst; constvolatile u32 __iomem *s = src;
for (; count >= 4; count -= 4)
__raw_writel(*s++, d++);
if (unlikely(count)) { /* count can be 1..3 */
u32 tmp = 0;
memcpy(&tmp, s, count);
__raw_writel(tmp, d);
}
}
/* Device memory access is prohibited while reset or suspend. * wil_mem_access_lock protects accessing device memory in these cases
*/ int wil_mem_access_lock(struct wil6210_priv *wil)
{ if (!down_read_trylock(&wil->mem_lock)) return -EBUSY;
spin_lock_bh(&txdata->lock);
txdata->dot1x_open = false;
txdata->mid = U8_MAX;
txdata->enabled = 0; /* no Tx can be in progress or start anew */
spin_unlock_bh(&txdata->lock); /* napi_synchronize waits for completion of the current NAPI but will * not prevent the next NAPI run. * Add a memory barrier to guarantee that txdata->enabled is zeroed * before napi_synchronize so that the next scheduled NAPI will not * handle this vring
*/
wmb(); /* make sure NAPI won't touch this vring */ if (test_bit(wil_status_napi_en, wil->status))
napi_synchronize(&wil->napi_tx);
wil->txrx_ops.ring_fini_tx(wil, ring);
}
staticbool wil_vif_is_connected(struct wil6210_priv *wil, u8 mid)
{ int i;
for (i = 0; i < wil->max_assoc_sta; i++) { if (wil->sta[i].mid == mid &&
wil->sta[i].status == wil_sta_connected) returntrue;
}
/* Cases are: * - disconnect single STA, still connected * - disconnect single STA, already disconnected * - disconnect all * * For "disconnect all", there are 3 options: * - bssid == NULL * - bssid is broadcast address (ff:ff:ff:ff:ff:ff) * - bssid is our MAC address
*/ if (bssid && !is_broadcast_ether_addr(bssid) &&
!ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
cid = wil_find_cid(wil, vif->mid, bssid);
wil_dbg_misc(wil, "Disconnect complete %pM, CID=%d, reason=%d\n",
bssid, cid, reason_code); if (wil_cid_valid(wil, cid)) /* disconnect 1 peer */
wil_disconnect_cid_complete(vif, cid, reason_code);
} else { /* all */
wil_dbg_misc(wil, "Disconnect complete all\n"); for (cid = 0; cid < wil->max_assoc_sta; cid++)
wil_disconnect_cid_complete(vif, cid, reason_code);
}
/* link state */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT:
wil_bcast_fini(vif);
wil_update_net_queues_bh(wil, vif, NULL, true);
netif_carrier_off(ndev); if (!wil_has_other_active_ifaces(wil, ndev, false, true))
wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
/* Cases are: * - disconnect single STA, still connected * - disconnect single STA, already disconnected * - disconnect all * * For "disconnect all", there are 3 options: * - bssid == NULL * - bssid is broadcast address (ff:ff:ff:ff:ff:ff) * - bssid is our MAC address
*/ if (bssid && !is_broadcast_ether_addr(bssid) &&
!ether_addr_equal_unaligned(ndev->dev_addr, bssid)) {
cid = wil_find_cid(wil, vif->mid, bssid);
wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n",
bssid, cid, reason_code); if (wil_cid_valid(wil, cid)) /* disconnect 1 peer */
wil_disconnect_cid(vif, cid, reason_code);
} else { /* all */
wil_dbg_misc(wil, "Disconnect all\n"); for (cid = 0; cid < wil->max_assoc_sta; cid++)
wil_disconnect_cid(vif, cid, reason_code);
}
/* call event handler manually after processing wmi_call, * to avoid deadlock - disconnect event handler acquires * wil->mutex while it is already held here
*/
_wil6210_disconnect_complete(vif, bssid, reason_code);
}
if (!ndev || !(ndev->flags & IFF_UP)) {
wil_info(wil, "No recovery - interface is down\n"); return;
}
wdev = ndev->ieee80211_ptr;
/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO * passed since last recovery attempt
*/ if (time_is_after_jiffies(wil->last_fw_recovery +
WIL6210_FW_RECOVERY_TO))
wil->recovery_count++; else
wil->recovery_count = 1; /* fw was alive for a long time */
if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
wil_err(wil, "too many recovery attempts (%d), giving up\n",
wil->recovery_count); return;
}
wil->last_fw_recovery = jiffies;
wil_info(wil, "fw error recovery requested (try %d)...\n",
wil->recovery_count); if (!no_fw_recovery)
wil->recovery_state = fw_recovery_running; if (wil_wait_for_recovery(wil) != 0) return;
rtnl_lock();
mutex_lock(&wil->mutex); /* Needs adaptation for multiple VIFs * need to go over all VIFs and consider the appropriate * recovery because each one can have different iftype.
*/ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: /* silent recovery, upper layers will see disconnect */
__wil_down(wil);
__wil_up(wil); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (no_fw_recovery) /* upper layers do recovery */ break; /* silent recovery, upper layers will see disconnect */
__wil_down(wil);
__wil_up(wil);
mutex_unlock(&wil->mutex);
wil_cfg80211_ap_recovery(wil);
mutex_lock(&wil->mutex);
wil_info(wil, "... completed\n"); break; default:
wil_err(wil, "No recovery - unknown interface type %d\n",
wdev->iftype); break;
}
mutex_unlock(&wil->mutex);
rtnl_unlock();
}
staticint wil_find_free_ring(struct wil6210_priv *wil)
{ int i; int min_ring_id = wil_get_min_tx_ring_id(wil);
for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { if (!wil->ring_tx[i].va) return i;
} return -EINVAL;
}
int wil_ring_init_tx(struct wil6210_vif *vif, int cid)
{ struct wil6210_priv *wil = vif_to_wil(vif); int rc = -EINVAL, ringid;
if (cid < 0) {
wil_err(wil, "No connection pending\n"); goto out;
}
ringid = wil_find_free_ring(wil); if (ringid < 0) {
wil_err(wil, "No free vring found\n"); goto out;
}
wil_dbg_wmi(wil, "Configure for connection CID %d MID %d ring %d\n",
cid, vif->mid, ringid);
/* edma configuration can be updated via debugfs before allocation */
wil->num_rx_status_rings = WIL_DEFAULT_NUM_RX_STATUS_RINGS;
wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT;
/* Rx status ring size should be bigger than the number of RX buffers * in order to prevent backpressure on the status ring, which may * cause HW freeze.
*/
wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT; /* Number of RX buffer IDs should be bigger than the RX descriptor * ring size as in HW reorder flow, the HW can consume additional * buffers before releasing the previous ones.
*/
wil->rx_buff_id_count = WIL_RX_BUFF_ARR_SIZE_DEFAULT;
val = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v1,
bl_shutdown_handshake)); if (val & BL_SHUTDOWN_HS_RTD) {
wil_dbg_misc(wil, "BL is ready for halt\n"); return;
}
wil_err(wil, "BL did not report ready for halt\n");
}
/* this format is used by ARC embedded CPU for instruction memory */ staticinline u32 ARC_me_imm32(u32 d)
{ return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16);
}
/* defines access to interrupt vectors for wil_freeze_bl */ #define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8) /* ARC long jump instruction */ #define ARC_JAL_INST (0x20200f80)
/* prevent the target from entering deep sleep * and disabling memory access
*/
saved = wil_r(wil, RGF_USER_USAGE_8);
wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP);
usleep_range(20, 25); /* let the BL process the bit */
/* redirect to endless loop in the INT_L1 context and let it trap */
wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3));
usleep_range(20, 25); /* let the BL get into the trap */
/* verify the BL is frozen */
upc = wil_r(wil, RGF_USER_CPU_PC); if (upc < ivt3 || (upc > (ivt3 + 8)))
wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc);
/* before halting device CPU driver must make sure BL is not accessing * host memory. This is done differently depending on BL version: * 1. For very old BL versions the procedure is skipped * (not supported). * 2. For old BL version we use a special trick to freeze the BL * 3. For new BL versions we shutdown the BL using handshake procedure.
*/
tmp = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0,
boot_loader_struct_version)); if (!tmp) {
wil_dbg_misc(wil, "old BL, skipping halt preparation\n"); return;
}
/* Wait for OTP signature test to complete */
usleep_range(2000, 2200);
wil->boot_config = WIL_BOOT_ERR;
/* Poll until OTP signature status is valid. * In vanilla and development modes, when signature test is complete * HW sets BIT_OTP_SIGNATURE_ERR_TALYN_MB. * In production mode BIT_OTP_SIGNATURE_ERR_TALYN_MB remains 0, poll * for signature status change to 2 or 3.
*/ do {
otp_hw = wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1);
signature_status = WIL_GET_BITS(otp_hw, 8, 9);
otp_signature_err = otp_hw & BIT_OTP_SIGNATURE_ERR_TALYN_MB;
if (otp_signature_err &&
signature_status == WIL_SIG_STATUS_VANILLA) {
wil->boot_config = WIL_BOOT_VANILLA; break;
} if (otp_signature_err &&
signature_status == WIL_SIG_STATUS_DEVELOPMENT) {
wil->boot_config = WIL_BOOT_DEVELOPMENT; break;
} if (!otp_signature_err &&
signature_status == WIL_SIG_STATUS_PRODUCTION) {
wil->boot_config = WIL_BOOT_PRODUCTION; break;
} if (!otp_signature_err &&
signature_status ==
WIL_SIG_STATUS_CORRUPTED_PRODUCTION) { /* Unrecognized OTP signature found. Possibly a * corrupted production signature, access control * is applied as in production mode, therefore * do not fail
*/
wil->boot_config = WIL_BOOT_PRODUCTION; break;
} if (delay++ > OTP_HW_COUNT) break;
/* enable fix for HW bug related to the SA/DA swap in AP Rx */
wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
/* construct actual board file name to use */ void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len)
{ constchar *board_file; constchar *wil_talyn_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN :
WIL_FW_NAME_TALYN;
if (wil->board_file) {
board_file = wil->board_file;
} else { /* If specific FW file is used for Talyn, * use specific board file
*/ if (strcmp(wil->wil_fw_name, wil_talyn_fw_name) == 0)
board_file = WIL_BRD_NAME_TALYN; else
board_file = WIL_BOARD_FILE_NAME;
}
staticvoid wil_pre_fw_config(struct wil6210_priv *wil)
{
wil_clear_fw_log_addr(wil); /* Mark FW as loaded from host */
wil_s(wil, RGF_USER_USAGE_6, 1);
/* clear any interrupts which on-card-firmware * may have set
*/
wil6210_clear_irq(wil); /* CAF_ICR - clear and mask */ /* it is W1C, clear by writing back same value */ if (wil->hw_version < HW_VER_TALYN_MB) {
wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
} /* clear PAL_UNIT_ICR (potential D0->D3 leftover) * In Talyn-MB host cannot access this register due to * access control, hence PAL_UNIT_ICR is cleared by the FW
*/ if (wil->hw_version < HW_VER_TALYN_MB)
wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR),
0);
if (wil->fw_calib_result > 0) {
__le32 val = cpu_to_le32(wil->fw_calib_result |
(CALIB_RESULT_SIGNATURE << 8));
wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val);
}
}
staticint wil_restore_vifs(struct wil6210_priv *wil)
{ struct wil6210_vif *vif; struct net_device *ndev; struct wireless_dev *wdev; int i, rc;
for (i = 0; i < GET_MAX_VIFS(wil); i++) {
vif = wil->vifs[i]; if (!vif) continue;
vif->ap_isolate = 0; if (vif->mid) {
ndev = vif_to_ndev(vif);
wdev = vif_to_wdev(vif);
rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
wdev->iftype); if (rc) {
wil_err(wil, "fail to restore VIF %d type %d, rc %d\n",
i, wdev->iftype, rc); return rc;
}
}
}
return 0;
}
/* * Clear FW and ucode log start addr to indicate FW log is not ready. The host * driver clears the addresses before FW starts and FW initializes the address * when it is ready to send logs.
*/ void wil_clear_fw_log_addr(struct wil6210_priv *wil)
{ /* FW log addr */
wil_w(wil, RGF_USER_USAGE_1, 0); /* ucode log addr */
wil_w(wil, RGF_USER_USAGE_2, 0);
wil_dbg_misc(wil, "Cleared FW and ucode log address");
}
/* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload * the firmware.
*/ int wil_reset(struct wil6210_priv *wil, bool load_fw)
{ int rc, i; unsignedlong status_flags = BIT(wil_status_resetting); int no_flash; struct wil6210_vif *vif;
/* Enable OFU rdy valid bug fix, to prevent hang in oful34_rx * while there is back-pressure from Host during RX
*/ if (wil->hw_version >= HW_VER_TALYN_MB)
wil_s(wil, RGF_DMA_MISC_CTL,
BIT_OFUL34_RDY_VALID_BUG_FIX_EN);
rc = wil_restore_vifs(wil); if (rc) {
wil_err(wil, "failed to restore vifs, rc %d\n", rc); return rc;
}
wil_collect_fw_info(wil);
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
wil_ps_update(wil, wil->ps_profile);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.