mwifiex/mxm_wifiex/wlan_src/mlinux/moal_pcie.c
Sherry Sun f1382ccbd3 mxm_wifiex: remove MODULE_DEVICE_TABLE to avoid wifi driver auto load
Load wifi driver need to use specific load parameters for differnet wifi
chips, so avoid wifi driver auto load, user can load the wifi driver
along with the specific load parameters.

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
2023-06-01 16:07:43 +08:00

2647 lines
68 KiB
C

/** @file moal_pcie.c
*
* @brief This file contains PCIE IF (interface) module
* related functions.
*
*
* Copyright 2008-2022 NXP
*
* This software file (the File) is distributed by NXP
* under the terms of the GNU General Public License Version 2, June 1991
* (the License). You may use, redistribute and/or modify the File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/********************************************************
Change log:
02/01/2012: initial version
********************************************************/
#include <linux/firmware.h>
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#include "moal_cfg80211.h"
#endif
#include "moal_pcie.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 70)
#ifdef IMX_SUPPORT
#include <linux/busfreq-imx.h>
#endif
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
#if IS_ENABLED(CONFIG_IPV6)
#include <net/addrconf.h>
#endif
#endif
/********************************************************
Local Variables
********************************************************/
#define DRV_NAME "NXP mdriver PCIe"
/* PCIE resume handler */
static int woal_pcie_resume(struct pci_dev *pdev);
static void woal_pcie_reg_dbg(moal_handle *phandle);
static void woal_pcie_unregister_dev(moal_handle *handle);
static void woal_pcie_cleanup(pcie_service_card *card);
static mlan_status woal_pcie_init(pcie_service_card *card);
/** WLAN IDs */
static const struct pci_device_id wlan_ids[] = {
#ifdef PCIE8897
{
PCIE_VENDOR_ID_MRVL,
PCIE_DEVICE_ID_88W8897P,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#endif
#ifdef PCIE8997
{
PCIE_VENDOR_ID_MRVL,
PCIE_DEVICE_ID_88W8997P,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
{
PCIE_VENDOR_ID_V2_MRVL,
PCIE_DEVICE_ID_88W8997P,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#endif
#ifdef PCIE9097
{
PCIE_VENDOR_ID_V2_MRVL,
PCIE_DEVICE_ID_88W9097,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#endif
#ifdef PCIE9098
{
PCIE_VENDOR_ID_V2_MRVL,
PCIE_DEVICE_ID_88W9098P_FN0,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
{
PCIE_VENDOR_ID_V2_MRVL,
PCIE_DEVICE_ID_88W9098P_FN1,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#endif
{
PCIE_VENDOR_ID_NXP,
PCIE_DEVICE_ID_88WAW693_FN0,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
{
PCIE_VENDOR_ID_NXP,
PCIE_DEVICE_ID_88WAW693_FN1,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#ifdef PCIEIW62X
{
PCIE_VENDOR_ID_NXP,
PCIE_DEVICE_ID_88WIW62X,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
},
#endif
{},
};
/* moal interface ops */
static moal_if_ops pcie_ops;
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
static mlan_status woal_pcie_preinit(struct pci_dev *pdev);
static rdwr_status woal_pcie_rdwr_firmware(moal_handle *phandle, t_u8 doneflag,
t_u8 resetflag);
/** @brief This function updates the card types
*
* @param handle A Pointer to the moal_handle structure
* @param card A Pointer to card
*
* @return N/A
*/
static t_u16 woal_update_card_type(t_void *card)
{
pcie_service_card *cardp_pcie = (pcie_service_card *)card;
t_u16 card_type = 0;
/* Update card type */
#ifdef PCIE8897
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88W8897P) {
card_type = CARD_TYPE_PCIE8897;
moal_memcpy_ext(NULL, driver_version, CARD_PCIE8897,
strlen(CARD_PCIE8897), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V15, strlen(V15),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#endif
#ifdef PCIE8997
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88W8997P) {
card_type = CARD_TYPE_PCIE8997;
moal_memcpy_ext(NULL, driver_version, CARD_PCIE8997,
strlen(CARD_PCIE8997), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V16, strlen(V16),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#endif
#ifdef PCIE9097
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88W9097) {
card_type = CARD_TYPE_PCIE9097;
moal_memcpy_ext(NULL, driver_version, CARD_PCIE9097,
strlen(CARD_PCIE9097), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V17, strlen(V17),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#endif
#ifdef PCIE9098
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88W9098P_FN0 ||
cardp_pcie->dev->device == PCIE_DEVICE_ID_88W9098P_FN1) {
card_type = CARD_TYPE_PCIE9098;
moal_memcpy_ext(NULL, driver_version, CARD_PCIE9098,
strlen(CARD_PCIE9098), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V17, strlen(V17),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#endif
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88WAW693_FN0 ||
cardp_pcie->dev->device == PCIE_DEVICE_ID_88WAW693_FN1) {
card_type = CARD_TYPE_PCIEAW693;
moal_memcpy_ext(NULL, driver_version, CARD_PCIEAW693,
strlen(CARD_PCIEAW693), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V17, strlen(V17),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#ifdef PCIEIW62X
if (cardp_pcie->dev->device == PCIE_DEVICE_ID_88WIW62X) {
card_type = CARD_TYPE_PCIEIW62X;
moal_memcpy_ext(NULL, driver_version, CARD_PCIEIW62X,
strlen(CARD_PCIEIW62X), strlen(driver_version));
moal_memcpy_ext(NULL,
driver_version + strlen(INTF_CARDTYPE) +
strlen(KERN_VERSION),
V18, strlen(V18),
strlen(driver_version) - strlen(INTF_CARDTYPE) -
strlen(KERN_VERSION));
}
#endif
return card_type;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
/**
* @brief Function to process pre/post PCIe function level reset
*
* @param handle A pointer to moal_handle structure
* @param prepare True :- its a pre FLR call from the kernel
* False :- its a post FLR call from the kernel
* @param flr True: call from FLR
*
* Note: This function is mix of woal_switch_drv_mode() and
* remove_card(). Idea is to cleanup the software only without
* touching the PCIe specific code. Likewise, during init init
* everything, including hw, but do not reinitiate PCIe stack
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_do_flr(moal_handle *handle, bool prepare, bool flr_flag)
{
unsigned int i;
int index = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
moal_private *priv = NULL;
pcie_service_card *card = NULL;
int fw_serial_bkp = 0;
ENTER();
if (!handle) {
PRINTM(MINFO, "\n Handle null during prepare=%d\n", prepare);
LEAVE();
return status;
}
card = (pcie_service_card *)handle->card;
if (card == NULL) {
PRINTM(MERROR, "The parameter 'card' is NULL\n");
LEAVE();
return (mlan_status)MLAN_STATUS_FAILURE;
}
if (!IS_PCIE8997(handle->card_type) &&
!IS_PCIE9097(handle->card_type) &&
!IS_PCIEIW62X(handle->card_type) &&
!IS_PCIEAW693(handle->card_type) &&
!IS_PCIE9098(handle->card_type)) {
LEAVE();
return status;
}
if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem))
goto exit_sem_err;
if (!prepare)
goto perform_init;
/* Reset all interfaces */
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE);
woal_clean_up(handle);
/* Shutdown firmware */
handle->init_wait_q_woken = MFALSE;
status = mlan_shutdown_fw(handle->pmlan_adapter);
if (status == MLAN_STATUS_PENDING)
wait_event_interruptible(handle->init_wait_q,
handle->init_wait_q_woken);
if (atomic_read(&handle->rx_pending) ||
atomic_read(&handle->tx_pending) ||
atomic_read(&handle->ioctl_pending)) {
PRINTM(MERROR,
"ERR: rx_pending=%d,tx_pending=%d,ioctl_pending=%d\n",
atomic_read(&handle->rx_pending),
atomic_read(&handle->tx_pending),
atomic_read(&handle->ioctl_pending));
}
unregister_inetaddr_notifier(&handle->woal_notifier);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&handle->woal_inet6_notifier);
#endif
#endif
#ifdef WIFI_DIRECT_SUPPORT
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
/* Remove virtual interface */
woal_remove_virtual_interface(handle);
#endif
#endif
#endif
/* Remove interface */
for (i = 0; i < handle->priv_num; i++)
woal_remove_interface(handle, i);
/* Unregister mlan */
if (handle->pmlan_adapter) {
mlan_unregister(handle->pmlan_adapter);
if (atomic_read(&handle->lock_count) ||
atomic_read(&handle->malloc_count) ||
atomic_read(&handle->mbufalloc_count)) {
PRINTM(MERROR,
"mlan has memory leak: lock_count=%d,"
" malloc_count=%d, mbufalloc_count=%d\n",
atomic_read(&handle->lock_count),
atomic_read(&handle->malloc_count),
atomic_read(&handle->mbufalloc_count));
}
if (atomic_read(&handle->malloc_cons_count)) {
PRINTM(MERROR,
"mlan has memory leak: malloc_cons_count=%d\n",
atomic_read(&handle->malloc_cons_count));
}
handle->pmlan_adapter = NULL;
}
goto exit;
perform_init:
handle->priv_num = 0;
/* Init SW */
if (woal_init_sw(handle)) {
PRINTM(MFATAL, "Software Init Failed\n");
goto err_init_fw;
}
if ((card->dev->device == PCIE_DEVICE_ID_88W9098P_FN1) ||
(card->dev->device == PCIE_DEVICE_ID_88WAW693_FN1))
mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 1);
else
/* Update pcie_int_mode in mlan adapter */
mlan_set_int_mode(handle->pmlan_adapter,
handle->params.pcie_int_mode, 0);
/* Init FW and HW */
/* Load wlan only binary */
if (flr_flag) {
fw_serial_bkp = moal_extflg_isset(handle, EXT_FW_SERIAL);
moal_extflg_clear(handle, EXT_FW_SERIAL);
woal_update_firmware_name(handle);
}
if (woal_init_fw(handle)) {
PRINTM(MFATAL, "Firmware Init Failed\n");
woal_pcie_reg_dbg(handle);
if (fw_serial_bkp)
moal_extflg_set(handle, EXT_FW_SERIAL);
goto err_init_fw;
}
if (flr_flag && fw_serial_bkp)
moal_extflg_set(handle, EXT_FW_SERIAL);
if (IS_PCIE9098(handle->card_type))
handle->event_fw_dump = MTRUE;
exit:
MOAL_REL_SEMAPHORE(&AddRemoveCardSem);
exit_sem_err:
LEAVE();
return status;
err_init_fw:
if ((handle->hardware_status == HardwareStatusFwReady) ||
(handle->hardware_status == HardwareStatusReady)) {
PRINTM(MINFO, "shutdown mlan\n");
handle->init_wait_q_woken = MFALSE;
status = mlan_shutdown_fw(handle->pmlan_adapter);
if (status == MLAN_STATUS_PENDING)
wait_event_interruptible(handle->init_wait_q,
handle->init_wait_q_woken);
}
#ifdef ANDROID_KERNEL
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
wakeup_source_trash(&handle->ws);
#else
wake_lock_destroy(&handle->wake_lock);
#endif
#endif
#ifdef CONFIG_PROC_FS
woal_proc_exit(handle);
#endif
/* Unregister device */
PRINTM(MINFO, "unregister device\n");
woal_pcie_unregister_dev(handle);
handle->surprise_removed = MTRUE;
#ifdef REASSOCIATION
if (handle->reassoc_thread.pid)
wake_up_interruptible(&handle->reassoc_thread.wait_q);
/* waiting for main thread quit */
while (handle->reassoc_thread.pid)
woal_sched_timeout(2);
#endif /* REASSOCIATION */
woal_terminate_workqueue(handle);
woal_free_moal_handle(handle);
for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
if (m_handle[index] == handle)
break;
}
if (index < MAX_MLAN_ADAPTER)
m_handle[index] = NULL;
card->handle = NULL;
MOAL_REL_SEMAPHORE(&AddRemoveCardSem);
LEAVE();
return (mlan_status)MLAN_STATUS_FAILURE;
}
#endif
/**
* @brief This function handles PCIE driver probe
*
* @param pdev A pointer to pci_dev structure
* @param id A pointer to pci_device_id structure
*
* @return error code
*/
static int woal_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
pcie_service_card *card = NULL;
t_u16 card_type = 0;
int ret = 0;
ENTER();
PRINTM(MINFO, "vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor,
pdev->device, pdev->revision);
/* Preinit PCIE device so allocate PCIE memory can be successful */
if (woal_pcie_preinit(pdev)) {
PRINTM(MFATAL, "MOAL PCIE preinit failed\n");
LEAVE();
return -EFAULT;
}
card = kzalloc(sizeof(pcie_service_card), GFP_KERNEL);
if (!card) {
PRINTM(MERROR, "%s: failed to alloc memory\n", __func__);
ret = -ENOMEM;
goto err;
}
card->dev = pdev;
card_type = woal_update_card_type(card);
if (!card_type) {
PRINTM(MERROR, "pcie probe: woal_update_card_type() failed\n");
ret = MLAN_STATUS_FAILURE;
goto err;
}
if (MLAN_STATUS_SUCCESS != woal_pcie_init(card)) {
PRINTM(MERROR, "woal_pcie_init failed\n");
ret = -EFAULT;
goto err;
}
if (woal_add_card(card, &card->dev->dev, &pcie_ops, card_type) ==
NULL) {
woal_pcie_cleanup(card);
PRINTM(MERROR, "%s: failed\n", __func__);
ret = -EFAULT;
goto err;
}
#ifdef IMX_SUPPORT
woal_regist_oob_wakeup_irq(card->handle);
#endif /* IMX_SUPPORT */
LEAVE();
return ret;
err:
kfree(card);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
LEAVE();
return ret;
}
/**
* @brief This function handles PCIE driver remove
*
* @param pdev A pointer to pci_dev structure
*
* @return error code
*/
static void woal_pcie_remove(struct pci_dev *dev)
{
pcie_service_card *card;
moal_handle *handle;
ENTER();
card = pci_get_drvdata(dev);
if (!card) {
PRINTM(MINFO, "PCIE card removed from slot\n");
LEAVE();
return;
}
handle = card->handle;
if (!handle || !handle->priv_num) {
PRINTM(MINFO, "PCIE card handle removed\n");
LEAVE();
return;
}
handle->surprise_removed = MTRUE;
#ifdef IMX_SUPPORT
woal_unregist_oob_wakeup_irq(card->handle);
#endif /* IMX_SUPPORT */
woal_remove_card(card);
woal_pcie_cleanup(card);
kfree(card);
LEAVE();
return;
}
/**
* @brief This function handles PCIE driver remove
*
* @param pdev A pointer to pci_dev structure
*
* @return error code
*/
static void woal_pcie_shutdown(struct pci_dev *dev)
{
pcie_service_card *card;
moal_handle *handle;
ENTER();
PRINTM(MCMND, "<--- Enter woal_pcie_shutdown --->\n");
card = pci_get_drvdata(dev);
if (!card) {
PRINTM(MINFO, "PCIE card removed from slot\n");
LEAVE();
return;
}
handle = card->handle;
if (handle->second_mac)
goto done;
if (IS_PCIE9098(handle->card_type) || IS_PCIEIW62X(handle->card_type) ||
IS_PCIEAW693(handle->card_type) || IS_PCIE9097(handle->card_type)) {
if (RDWR_STATUS_FAILURE !=
woal_pcie_rdwr_firmware(handle, 0, 1))
PRINTM(MMSG, "wlan: start in-bound IR...\n");
}
done:
handle->surprise_removed = MTRUE;
pci_disable_device(dev);
PRINTM(MCMND, "<--- Leave woal_pcie_shutdown --->\n");
LEAVE();
return;
}
/**
* @brief Handle suspend
*
* @param pdev A pointer to pci_dev structure
* @param state PM state message
*
* @return error code
*/
static int woal_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
{
pcie_service_card *cardp;
moal_handle *handle = NULL;
moal_handle *ref_handle = NULL;
int i;
int ret = MLAN_STATUS_SUCCESS;
int hs_actived;
mlan_ds_ps_info pm_info;
int keep_power = 0;
ENTER();
if (pdev) {
cardp = (pcie_service_card *)pci_get_drvdata(pdev);
if (!cardp || !cardp->handle) {
LEAVE();
return MLAN_STATUS_SUCCESS;
}
} else {
PRINTM(MERROR, "PCIE device is not specified\n");
LEAVE();
return -ENOSYS;
}
handle = cardp->handle;
if (handle->second_mac)
PRINTM(MCMND, "<--- Enter woal_pcie_suspend# --->\n");
else
PRINTM(MCMND, "<--- Enter woal_pcie_suspend --->\n");
if (handle->is_suspended == MTRUE) {
PRINTM(MWARN, "Device already suspended\n");
LEAVE();
return MLAN_STATUS_SUCCESS;
}
if (handle->fw_dump) {
PRINTM(MMSG, "suspend not allowed while FW dump!");
ret = -EBUSY;
goto done;
}
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
if (handle->priv[i] &&
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA))
woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT);
}
handle->suspend_fail = MFALSE;
memset(&pm_info, 0, sizeof(pm_info));
#define MAX_RETRY_NUM 8
for (i = 0; i < MAX_RETRY_NUM; i++) {
if (MLAN_STATUS_SUCCESS ==
woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY),
&pm_info)) {
if (pm_info.is_suspend_allowed == MTRUE)
break;
else
PRINTM(MMSG,
"Suspend not allowed and retry again\n");
}
woal_sched_timeout(100);
}
if (pm_info.is_suspend_allowed == MFALSE) {
PRINTM(MMSG, "Suspend not allowed\n");
ret = -EBUSY;
goto done;
}
for (i = 0; i < handle->priv_num; i++)
netif_device_detach(handle->priv[i]->netdev);
if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER))
keep_power = MTRUE;
else
keep_power = MFALSE;
if (keep_power) {
/* Enable Host Sleep */
hs_actived = woal_enable_hs(
woal_get_priv(handle, MLAN_BSS_ROLE_ANY));
if (hs_actived == MTRUE) {
/* Indicate device suspended */
handle->is_suspended = MTRUE;
} else {
PRINTM(MMSG, "HS not actived, suspend fail!");
handle->suspend_fail = MTRUE;
for (i = 0; i < handle->priv_num; i++)
netif_device_attach(handle->priv[i]->netdev);
ret = -EBUSY;
goto done;
}
}
woal_flush_workqueue(handle);
if (!keep_power) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
woal_do_flr(handle, true, false);
#endif
handle->surprise_removed = MTRUE;
handle->is_suspended = MTRUE;
}
#ifdef IMX_SUPPORT
woal_enable_oob_wakeup_irq(handle);
#endif /* IMX_SUPPORT */
pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
pci_save_state(pdev);
ref_handle = (moal_handle *)handle->pref_mac;
if (ref_handle && ref_handle->is_suspended)
pci_set_power_state(pdev, pci_choose_state(pdev, state));
done:
PRINTM(MCMND, "<--- Leave woal_pcie_suspend --->\n");
LEAVE();
return ret;
}
/**
* @brief Handle resume
*
* @param pdev A pointer to pci_dev structure
*
* @return error code
*/
static int woal_pcie_resume(struct pci_dev *pdev)
{
moal_handle *handle;
pcie_service_card *cardp;
int keep_power = 0;
int i;
ENTER();
if (pdev) {
cardp = (pcie_service_card *)pci_get_drvdata(pdev);
if (!cardp || !cardp->handle) {
PRINTM(MERROR, "Card or handle is not valid\n");
LEAVE();
return MLAN_STATUS_SUCCESS;
}
} else {
PRINTM(MERROR, "PCIE device is not specified\n");
LEAVE();
return -ENOSYS;
}
handle = cardp->handle;
if (handle->second_mac)
PRINTM(MCMND, "<--- Enter woal_pcie_resume# --->\n");
else
PRINTM(MCMND, "<--- Enter woal_pcie_resume --->\n");
if (handle->is_suspended == MFALSE) {
PRINTM(MWARN, "Device already resumed\n");
goto done;
}
handle->is_suspended = MFALSE;
if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER))
keep_power = MTRUE;
else
keep_power = MFALSE;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
pci_enable_wake(pdev, PCI_D0, 0);
if (!keep_power) {
handle->surprise_removed = MFALSE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
woal_do_flr(handle, false, false);
#endif
} else {
if (woal_check_driver_status(handle)) {
PRINTM(MERROR, "Resuem, device is in hang state\n");
LEAVE();
return MLAN_STATUS_SUCCESS;
}
for (i = 0; i < handle->priv_num; i++)
netif_device_attach(handle->priv[i]->netdev);
woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY),
MOAL_NO_WAIT);
#ifdef IMX_SUPPORT
woal_disable_oob_wakeup_irq(handle);
#endif /* IMX_SUPPORT */
}
done:
PRINTM(MCMND, "<--- Leave woal_pcie_resume --->\n");
LEAVE();
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
/**
* @brief Pcie reset prepare handler
*
* @param pdev A pointer to pci_dev structure
*/
static void woal_pcie_reset_prepare(struct pci_dev *pdev)
{
pcie_service_card *card;
moal_handle *handle;
moal_handle *ref_handle = NULL;
ENTER();
card = pci_get_drvdata(pdev);
if (!card) {
PRINTM(MINFO, "PCIE card removed from slot\n");
LEAVE();
return;
}
handle = card->handle;
if (!handle) {
PRINTM(MINFO, "Invalid handle\n");
LEAVE();
return;
}
PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d Pre-FLR\n",
__func__, pdev->vendor, pdev->device, pdev->revision);
/* Kernel would be performing FLR after this notification.
* Cleanup up all software withouth cleaning anything related to
* PCIe and HW.
* Note. FW might not be healthy.
*/
// handle-> mac0 , ref_handle->second mac
if (handle->pref_mac) {
if (handle->second_mac) {
handle = (moal_handle *)handle->pref_mac;
ref_handle = (moal_handle *)handle->pref_mac;
} else {
ref_handle = (moal_handle *)handle->pref_mac;
}
}
handle->surprise_removed = MTRUE;
woal_do_flr(handle, true, true);
if (ref_handle) {
ref_handle->surprise_removed = MTRUE;
woal_do_flr(ref_handle, true, true);
}
LEAVE();
}
/**
* @brief Pcie reset done handler
*
* @param pdev A pointer to pci_dev structure
*/
static void woal_pcie_reset_done(struct pci_dev *pdev)
{
pcie_service_card *card;
moal_handle *handle;
moal_handle *ref_handle = NULL;
ENTER();
card = pci_get_drvdata(pdev);
if (!card) {
PRINTM(MINFO, "PCIE card removed from slot\n");
LEAVE();
return;
}
handle = card->handle;
if (!handle) {
PRINTM(MINFO, "Invalid handle\n");
LEAVE();
return;
}
PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d Post-FLR\n",
__func__, pdev->vendor, pdev->device, pdev->revision);
/* Kernel stores and restores PCIe function context before and
* after performing FLR, respectively.
*
* Reconfigure the sw and fw including fw redownload
*/
// handle-> mac0 , ref_handle->second mac
if (handle->pref_mac) {
if (handle->second_mac) {
handle = (moal_handle *)handle->pref_mac;
ref_handle = (moal_handle *)handle->pref_mac;
} else {
ref_handle = (moal_handle *)handle->pref_mac;
}
}
handle->surprise_removed = MFALSE;
woal_do_flr(handle, false, true);
if (ref_handle) {
ref_handle->surprise_removed = MFALSE;
woal_do_flr(ref_handle, false, true);
}
LEAVE();
}
#else
static void woal_pcie_reset_notify(struct pci_dev *pdev, bool prepare)
{
pcie_service_card *card;
moal_handle *handle;
moal_handle *ref_handle = NULL;
ENTER();
card = pci_get_drvdata(pdev);
if (!card) {
PRINTM(MINFO, "PCIE card removed from slot\n");
LEAVE();
return;
}
handle = card->handle;
if (!handle) {
PRINTM(MINFO, "Invalid handle\n");
LEAVE();
return;
}
PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d %s\n",
__func__, pdev->vendor, pdev->device, pdev->revision,
prepare ? "Pre-FLR" : "Post-FLR");
// handle-> mac0 , ref_handle->second mac
if (handle->pref_mac) {
if (handle->second_mac) {
handle = (moal_handle *)handle->pref_mac;
ref_handle = (moal_handle *)handle->pref_mac;
} else {
ref_handle = (moal_handle *)handle->pref_mac;
}
}
if (prepare) {
/* Kernel would be performing FLR after this notification.
* Cleanup up all software withouth cleaning anything related to
* PCIe and HW.
* Note. FW might not be healthy.
*/
handle->surprise_removed = MTRUE;
woal_do_flr(handle, prepare, true);
if (ref_handle) {
ref_handle->surprise_removed = MTRUE;
woal_do_flr(ref_handle, prepare, true);
}
} else {
/* Kernel stores and restores PCIe function context before and
* after performing FLR, respectively.
*
* Reconfigure the sw and fw including fw redownload
*/
handle->surprise_removed = MFALSE;
woal_do_flr(handle, prepare, true);
if (ref_handle) {
ref_handle->surprise_removed = MFALSE;
woal_do_flr(ref_handle, prepare, true);
}
}
LEAVE();
}
#endif
static const struct pci_error_handlers woal_pcie_err_handler[] = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
{
.reset_prepare = woal_pcie_reset_prepare,
.reset_done = woal_pcie_reset_done,
},
#else
{
.reset_notify = woal_pcie_reset_notify,
},
#endif
};
#endif // KERNEL_VERSION(3.18.0)
/* PCI Device Driver */
static struct pci_driver REFDATA wlan_pcie = {
.name = "wlan_pcie",
.id_table = wlan_ids,
.probe = woal_pcie_probe,
.remove = woal_pcie_remove,
.shutdown = woal_pcie_shutdown,
#ifdef CONFIG_PM
/* Power Management Hooks */
.suspend = woal_pcie_suspend,
.resume = woal_pcie_resume,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
.err_handler = woal_pcie_err_handler,
#endif
};
/********************************************************
Global Functions
********************************************************/
/**
* @brief This function writes data into card register
*
* @param handle A Pointer to the moal_handle structure
* @param reg Register offset
* @param data Value
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_write_reg(moal_handle *handle, t_u32 reg,
t_u32 data)
{
pcie_service_card *card = (pcie_service_card *)handle->card;
iowrite32(data, card->pci_mmap1 + reg);
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function reads data from card register
*
* @param handle A Pointer to the moal_handle structure
* @param reg Register offset
* @param data Value
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_read_reg(moal_handle *handle, t_u32 reg,
t_u32 *data)
{
pcie_service_card *card = (pcie_service_card *)handle->card;
*data = ioread32(card->pci_mmap1 + reg);
if (*data == MLAN_STATUS_FAILURE)
return MLAN_STATUS_FAILURE;
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function writes multiple bytes into card memory
*
* @param handle A Pointer to the moal_handle structure
* @param pmbuf Pointer to mlan_buffer structure
* @param port Port
* @param timeout Time out value
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_write_data_sync(moal_handle *handle,
mlan_buffer *pmbuf, t_u32 port,
t_u32 timeout)
{
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function reads multiple bytes from card memory
*
* @param handle A Pointer to the moal_handle structure
* @param pmbuf Pointer to mlan_buffer structure
* @param port Port
* @param timeout Time out value
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_read_data_sync(moal_handle *handle,
mlan_buffer *pmbuf, t_u32 port,
t_u32 timeout)
{
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function handles the interrupt.
*
* @param irq The irq no. of PCIE device
* @param dev_id A pointer to the pci_dev structure
*
* @return IRQ_HANDLED
*/
static irqreturn_t woal_pcie_interrupt(int irq, void *dev_id)
{
struct pci_dev *pdev;
pcie_service_card *card;
moal_handle *handle;
mlan_status ret = MLAN_STATUS_SUCCESS;
pdev = (struct pci_dev *)dev_id;
if (!pdev) {
PRINTM(MFATAL, "%s: pdev is NULL\n", (t_u8 *)pdev);
goto exit;
}
card = (pcie_service_card *)pci_get_drvdata(pdev);
if (!card || !card->handle) {
PRINTM(MFATAL, "%s: card=%p handle=%p\n", __func__, card,
card ? card->handle : NULL);
goto exit;
}
handle = card->handle;
if (handle->surprise_removed == MTRUE) {
ret = MLAN_STATUS_FAILURE;
goto exit;
}
PRINTM(MINFO, "*** IN PCIE IRQ ***\n");
handle->main_state = MOAL_RECV_INT;
if (handle->second_mac)
PRINTM(MINTR, "**\n");
else
PRINTM(MINTR, "*\n");
ret = mlan_interrupt(0xffff, handle->pmlan_adapter);
if (handle->is_suspended) {
PRINTM(MINTR, "Receive interrupt in hs_suspended\n");
goto exit;
}
queue_work(handle->workqueue, &handle->main_work);
exit:
if (ret == MLAN_STATUS_SUCCESS)
return IRQ_HANDLED;
else
return IRQ_NONE;
}
/**
* @brief This function handles the MSI-X interrupt.
*
* @param irq The irq no. of PCIE device
* @param dev_id A pointer to the msix_context structure
*
* @return IRQ_HANDLED
*/
static irqreturn_t woal_pcie_msix_interrupt(int irq, void *dev_id)
{
struct pci_dev *pdev;
pcie_service_card *card;
moal_handle *handle;
msix_context *ctx = (msix_context *)dev_id;
mlan_status ret = MLAN_STATUS_SUCCESS;
if (!ctx) {
PRINTM(MFATAL, "%s: ctx=%p is NULL\n", __func__, ctx);
goto exit;
}
pdev = ctx->dev;
if (!pdev) {
PRINTM(MFATAL, "%s: pdev is NULL\n", (t_u8 *)pdev);
goto exit;
}
card = (pcie_service_card *)pci_get_drvdata(pdev);
if (!card || !card->handle) {
PRINTM(MFATAL, "%s: card=%p handle=%p\n", __func__, card,
card ? card->handle : NULL);
goto exit;
}
handle = card->handle;
if (handle->surprise_removed == MTRUE) {
ret = MLAN_STATUS_FAILURE;
goto exit;
}
PRINTM(MINFO, "*** IN PCIE IRQ ***\n");
handle->main_state = MOAL_RECV_INT;
if (handle->second_mac)
PRINTM(MINTR, "**\n");
else
PRINTM(MINTR, "*\n");
ret = mlan_interrupt(ctx->msg_id, handle->pmlan_adapter);
queue_work(handle->workqueue, &handle->main_work);
exit:
if (ret == MLAN_STATUS_SUCCESS)
return IRQ_HANDLED;
else
return IRQ_NONE;
}
/**
* @brief This function pre-initializes the PCI-E host
* memory space, etc.
*
* @param handle A pointer to moal_handle structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_preinit(struct pci_dev *pdev)
{
int ret;
if (pdev->multifunction)
device_disable_async_suspend(&pdev->dev);
ret = pci_enable_device(pdev);
if (ret)
goto err_enable_dev;
pci_set_master(pdev);
PRINTM(MINFO, "Try set_consistent_dma_mask(32)\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
#else
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
#endif
if (ret) {
PRINTM(MERROR, "set_dma_mask(32) failed\n");
goto err_set_dma_mask;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
#else
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
#endif
if (ret) {
PRINTM(MERROR, "set_consistent_dma_mask(64) failed\n");
goto err_set_dma_mask;
}
return MLAN_STATUS_SUCCESS;
err_set_dma_mask:
pci_disable_device(pdev);
err_enable_dev:
return MLAN_STATUS_FAILURE;
}
/**
* @brief This function initializes the PCI-E host
* memory space, etc.
*
* @param card A pointer to pcie_service_card structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_init(pcie_service_card *card)
{
struct pci_dev *pdev = NULL;
int ret;
pdev = card->dev;
pci_set_drvdata(pdev, card);
#if 0
ret = pci_enable_device(pdev);
if (ret)
goto err_enable_dev;
pci_set_master(pdev);
PRINTM(MINFO, "Try set_consistent_dma_mask(32)\n");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
#else
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
#endif
if (ret) {
PRINTM(MERROR, "set_dma_mask(32) failed\n");
goto err_set_dma_mask;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
#else
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
#endif
if (ret) {
PRINTM(MERROR, "set_consistent_dma_mask(64) failed\n");
goto err_set_dma_mask;
}
#endif
ret = pci_request_region(pdev, 0, DRV_NAME);
if (ret) {
PRINTM(MERROR, "req_reg(0) error\n");
goto err_req_region0;
}
card->pci_mmap = pci_iomap(pdev, 0, 0);
if (!card->pci_mmap) {
PRINTM(MERROR, "iomap(0) error\n");
goto err_iomap0;
}
ret = pci_request_region(pdev, 2, DRV_NAME);
if (ret) {
PRINTM(MERROR, "req_reg(2) error\n");
goto err_req_region2;
}
card->pci_mmap1 = pci_iomap(pdev, 2, 0);
if (!card->pci_mmap1) {
PRINTM(MERROR, "iomap(2) error\n");
goto err_iomap2;
}
PRINTM(MMSG,
"PCI memory map Virt0: %p PCI memory map Virt2: "
"%p\n",
card->pci_mmap, card->pci_mmap1);
return MLAN_STATUS_SUCCESS;
err_iomap2:
pci_release_region(pdev, 2);
err_req_region2:
pci_iounmap(pdev, card->pci_mmap);
err_iomap0:
pci_release_region(pdev, 0);
err_req_region0:
#if 0
err_set_dma_mask:
#endif
#if 0
err_enable_dev:
#endif
pci_set_drvdata(pdev, NULL);
return MLAN_STATUS_FAILURE;
}
/**
* @brief This function registers the PCIE device
*
* @param handle A pointer to moal_handle structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_pcie_register_dev(moal_handle *handle)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
pcie_service_card *card = NULL;
struct pci_dev *pdev = NULL;
unsigned char nvec;
unsigned char i, j;
ENTER();
if (!handle || !handle->card) {
PRINTM(MINFO, "%s: handle=%p card=%p\n", __FUNCTION__, handle,
handle ? handle->card : NULL);
LEAVE();
return MLAN_STATUS_FAILURE;
}
card = (pcie_service_card *)handle->card;
pdev = card->dev;
/* save adapter pointer in card */
card->handle = handle;
switch (pcie_int_mode) {
case PCIE_INT_MODE_MSIX:
pcie_int_mode = PCIE_INT_MODE_MSIX;
nvec = PCIE_NUM_MSIX_VECTORS;
for (i = 0; i < nvec; i++) {
card->msix_entries[i].entry = i;
}
/* Try to enable msix */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
ret = pci_enable_msix_exact(pdev, card->msix_entries, nvec);
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) */
ret = pci_enable_msix(pdev, card->msix_entries, nvec);
#endif
if (ret == 0) {
for (i = 0; i < nvec; i++) {
card->msix_contexts[i].dev = pdev;
card->msix_contexts[i].msg_id = i;
ret = request_irq(card->msix_entries[i].vector,
woal_pcie_msix_interrupt, 0,
"mrvl_pcie_msix",
&(card->msix_contexts[i]));
if (ret) {
PRINTM(MFATAL,
"request_irq failed: ret=%d\n",
ret);
for (j = 0; j < i; j++)
free_irq(card->msix_entries[j]
.vector,
&(card->msix_contexts
[i]));
pci_disable_msix(pdev);
break;
}
}
if (i == nvec)
break;
}
// follow through
/* fall through */
case PCIE_INT_MODE_MSI:
pcie_int_mode = PCIE_INT_MODE_MSI;
ret = pci_enable_msi(pdev);
if (ret == 0) {
ret = request_irq(pdev->irq, woal_pcie_interrupt, 0,
"mrvl_pcie_msi", pdev);
if (ret) {
PRINTM(MFATAL, "request_irq failed: ret=%d\n",
ret);
pci_disable_msi(pdev);
} else {
break;
}
}
// follow through
/* fall through */
case PCIE_INT_MODE_LEGACY:
pcie_int_mode = PCIE_INT_MODE_LEGACY;
ret = request_irq(pdev->irq, woal_pcie_interrupt, IRQF_SHARED,
"mrvl_pcie", pdev);
if (ret) {
PRINTM(MFATAL, "request_irq failed: ret=%d\n", ret);
ret = MLAN_STATUS_FAILURE;
goto done;
}
break;
default:
PRINTM(MFATAL, "pcie_int_mode %d failed\n", pcie_int_mode);
ret = MLAN_STATUS_FAILURE;
goto done;
break;
}
if ((card->dev->device == PCIE_DEVICE_ID_88W9098P_FN1) ||
(card->dev->device == PCIE_DEVICE_ID_88WAW693_FN1))
mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 1);
else
mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 0);
done:
LEAVE();
return ret;
}
/**
* @brief This function cleans up the host memory spaces
*
* @param card A pointer to pcie_service_card structure
*
* @return N/A
*/
static void woal_pcie_cleanup(pcie_service_card *card)
{
struct pci_dev *pdev = NULL;
pdev = card->dev;
PRINTM(MINFO, "Clearing driver ready signature\n");
if (pdev) {
pci_iounmap(pdev, card->pci_mmap);
pci_iounmap(pdev, card->pci_mmap1);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
pci_release_region(pdev, 0);
pci_release_region(pdev, 2);
pci_set_drvdata(pdev, NULL);
}
}
/**
* @brief This function unregisters the PCIE device
*
* @param handle A pointer to moal_handle structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static void woal_pcie_unregister_dev(moal_handle *handle)
{
pcie_service_card *card =
handle ? (pcie_service_card *)handle->card : NULL;
struct pci_dev *pdev = NULL;
unsigned char i;
unsigned char nvec;
ENTER();
if (card) {
pdev = card->dev;
PRINTM(MINFO, "%s(): calling free_irq()\n", __func__);
switch (pcie_int_mode) {
case PCIE_INT_MODE_MSIX:
nvec = PCIE_NUM_MSIX_VECTORS;
for (i = 0; i < nvec; i++)
synchronize_irq(card->msix_entries[i].vector);
for (i = 0; i < nvec; i++)
free_irq(card->msix_entries[i].vector,
&(card->msix_contexts[i]));
pci_disable_msix(pdev);
break;
case PCIE_INT_MODE_MSI:
free_irq(card->dev->irq, pdev);
pci_disable_msi(pdev);
break;
case PCIE_INT_MODE_LEGACY:
free_irq(card->dev->irq, pdev);
break;
default:
PRINTM(MFATAL, "pcie_int_mode %d failed\n",
pcie_int_mode);
break;
}
card->handle = NULL;
}
LEAVE();
}
/**
* @brief This function registers the IF module in bus driver
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status woal_pcie_bus_register(void)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* API registers the NXP PCIE driver */
if (pci_register_driver(&wlan_pcie)) {
PRINTM(MFATAL, "PCIE Driver Registration Failed \n");
ret = MLAN_STATUS_FAILURE;
}
LEAVE();
return ret;
}
/**
* @brief This function de-registers the IF module in bus driver
*
* @return N/A
*/
void woal_pcie_bus_unregister(void)
{
ENTER();
/* PCIE Driver Unregistration */
pci_unregister_driver(&wlan_pcie);
LEAVE();
}
#define PCIE9098_DUMP_CTRL_REG 0x1C94
#define PCIE9098_DUMP_START_REG 0x1C98
#define PCIE9098_DUMP_END_REG 0x1C9F
#if defined(PCIE8897) || defined(PCIE8997)
#define DEBUG_DUMP_CTRL_REG 0xCF4
#define DEBUG_DUMP_START_REG 0xCF8
#define DEBUG_DUMP_END_REG 0xCFF
#endif
#define PCIE9098_SCRATCH_12_REG 0x1C90
#define PCIE9098_SCRATCH_14_REG 0x1C98
#define PCIE9098_SCRATCH_15_REG 0x1C9C
#define PCIE9098_DUMP_REG_START 0x1C20
#define PCIE9098_DUMP_REG_END 0x1C9C
#if defined(PCIE8997) || defined(PCIE8897)
#define PCIE_SCRATCH_12_REG 0x0CF0;
#define PCIE_SCRATCH_14_REG 0x0CF8;
#define PCIE_SCRATCH_15_REG 0x0CFC;
#define PCIE_DUMP_START_REG 0xC00
#define PCIE_DUMP_END_REG 0xCFC
#endif
/**
* @brief This function save the log of pcie register value
*
* @param phandle A pointer to moal_handle
* @param buffer A pointer to buffer saving log
*
* @return The length of this log
*/
static int woal_pcie_dump_reg_info(moal_handle *phandle, t_u8 *buffer)
{
char *drv_ptr = (char *)buffer;
t_u32 reg = 0, value = 0;
t_u8 i;
char buf[256], *ptr;
pcie_service_card *card = (pcie_service_card *)phandle->card;
int config_reg_table[] = {0x00, 0x04, 0x10, 0x18, 0x2c,
0x3c, 0x44, 0x80, 0x98, 0x170};
t_u32 dump_start_reg = 0;
t_u32 dump_end_reg = 0;
t_u32 scratch_14_reg = 0;
t_u32 scratch_15_reg = 0;
/* Tx/Rx/Event AMDA start address */
t_u32 adma_reg_table[] = {0x10000, 0x10800, 0x10880, 0x11000, 0x11080};
t_u8 j;
ENTER();
mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE);
drv_ptr += sprintf(drv_ptr,
"------------PCIe Registers dump-------------\n");
drv_ptr += sprintf(drv_ptr, "Config Space Registers:\n");
for (i = 0; i < ARRAY_SIZE(config_reg_table); i++) {
pci_read_config_dword(card->dev, config_reg_table[i], &value);
drv_ptr += sprintf(drv_ptr, "reg:0x%02x value=0x%08x\n",
config_reg_table[i], value);
}
drv_ptr += sprintf(drv_ptr, "FW Scrach Registers:\n");
#if defined(PCIE8897) || defined(PCIE8997)
if (IS_PCIE8897(phandle->card_type) ||
IS_PCIE8997(phandle->card_type)) {
reg = PCIE_SCRATCH_12_REG;
dump_start_reg = PCIE_DUMP_START_REG;
dump_end_reg = PCIE_DUMP_END_REG;
scratch_14_reg = PCIE_SCRATCH_14_REG;
scratch_15_reg = PCIE_SCRATCH_15_REG;
}
#endif
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
reg = PCIE9098_SCRATCH_12_REG;
dump_start_reg = PCIE9098_DUMP_REG_START;
dump_end_reg = PCIE9098_DUMP_REG_END;
scratch_14_reg = PCIE9098_SCRATCH_14_REG;
scratch_15_reg = PCIE9098_SCRATCH_15_REG;
}
woal_pcie_read_reg(phandle, reg, &value);
drv_ptr += sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value);
for (i = 0; i < 2; i++) {
reg = scratch_14_reg;
woal_pcie_read_reg(phandle, reg, &value);
drv_ptr +=
sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value);
reg = scratch_15_reg;
woal_pcie_read_reg(phandle, reg, &value);
drv_ptr +=
sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value);
mdelay(100);
}
drv_ptr +=
sprintf(drv_ptr,
"Interface registers dump from offset 0x%x to 0x%x\n",
dump_start_reg, dump_end_reg);
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = dump_start_reg; reg <= dump_end_reg; reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
drv_ptr += sprintf(drv_ptr, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
drv_ptr += sprintf(
drv_ptr,
"PCIE registers from offset 0x1c20 to 0x1c9c:\n");
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = 0x1c20; reg <= 0x1c9c; reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
drv_ptr += sprintf(drv_ptr, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
drv_ptr += sprintf(drv_ptr, "%s\n", buf);
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
drv_ptr += sprintf(drv_ptr,
"ADMA Tx/Rx/Event/Cmd/CmdResp registers:\n");
for (j = 0; j < ARRAY_SIZE(adma_reg_table); j++) {
drv_ptr += sprintf(
drv_ptr,
"ADMA registers dump from offset 0x%x to 0x%x\n",
adma_reg_table[j], adma_reg_table[j] + 0x68);
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = adma_reg_table[j];
reg <= (adma_reg_table[j] + 0x68); reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
drv_ptr +=
sprintf(drv_ptr, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
drv_ptr += sprintf(drv_ptr, "%s\n", buf);
}
}
drv_ptr += sprintf(drv_ptr,
"-----------PCIe Registers dump End-----------\n");
mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE);
LEAVE();
return drv_ptr - (char *)buffer;
}
/**
* @brief This function reads and displays PCIE scratch registers for debugging
*
* @param phandle A pointer to moal_handle
*
* @return N/A
*/
static void woal_pcie_reg_dbg(moal_handle *phandle)
{
t_u32 reg = 0, value = 0;
t_u8 i;
char buf[256], *ptr;
pcie_service_card *card = (pcie_service_card *)phandle->card;
int config_reg_table[] = {0x00, 0x04, 0x10, 0x18, 0x2c,
0x3c, 0x44, 0x80, 0x98, 0x170};
t_u32 dump_start_reg = 0;
t_u32 dump_end_reg = 0;
t_u32 scratch_14_reg = 0;
t_u32 scratch_15_reg = 0;
/* Tx/Rx/Event AMDA start address */
t_u32 adma_reg_table[] = {0x10000, 0x10800, 0x10880, 0x11000, 0x11080};
t_u8 j;
mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE);
PRINTM(MMSG, "Config Space Registers:\n");
for (i = 0; i < ARRAY_SIZE(config_reg_table); i++) {
pci_read_config_dword(card->dev, config_reg_table[i], &value);
PRINTM(MERROR, "reg:0x%02x value=0x%08x\n", config_reg_table[i],
value);
}
PRINTM(MMSG, "FW Scrach Registers:\n");
#if defined(PCIE8897) || defined(PCIE8997)
if (IS_PCIE8897(phandle->card_type) ||
IS_PCIE8997(phandle->card_type)) {
reg = PCIE_SCRATCH_12_REG;
dump_start_reg = PCIE_DUMP_START_REG;
dump_end_reg = PCIE_DUMP_END_REG;
scratch_14_reg = PCIE_SCRATCH_14_REG;
scratch_15_reg = PCIE_SCRATCH_15_REG;
}
#endif
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
reg = PCIE9098_SCRATCH_12_REG;
dump_start_reg = PCIE9098_DUMP_START_REG;
dump_end_reg = PCIE9098_DUMP_END_REG;
scratch_14_reg = PCIE9098_SCRATCH_14_REG;
scratch_15_reg = PCIE9098_SCRATCH_15_REG;
}
woal_pcie_read_reg(phandle, reg, &value);
PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value);
for (i = 0; i < 2; i++) {
reg = scratch_14_reg;
woal_pcie_read_reg(phandle, reg, &value);
PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value);
reg = scratch_15_reg;
woal_pcie_read_reg(phandle, reg, &value);
PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value);
mdelay(100);
}
PRINTM(MMSG, "Interface registers dump from offset 0x%x to 0x%x\n",
dump_start_reg, dump_end_reg);
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = dump_start_reg; reg <= dump_end_reg; reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
PRINTM(MMSG, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
PRINTM(MMSG, "PCIE registers from offset 0x1c20 to 0x1c9c:\n");
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = 0x1c20; reg <= 0x1c9c; reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
PRINTM(MMSG, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
PRINTM(MMSG, "%s\n", buf);
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
PRINTM(MMSG, "ADMA Tx/Rx/Event/Cmd/CmdResp registers:\n");
for (j = 0; j < ARRAY_SIZE(adma_reg_table); j++) {
PRINTM(MMSG,
"ADMA registers dump from offset 0x%x to 0x%x\n",
adma_reg_table[j], adma_reg_table[j] + 0x68);
memset(buf, 0, sizeof(buf));
ptr = buf;
i = 1;
for (reg = adma_reg_table[j];
reg <= (adma_reg_table[j] + 0x68); reg += 4) {
woal_pcie_read_reg(phandle, reg, &value);
ptr += sprintf(ptr, "%08x ", value);
if (!(i % 8)) {
PRINTM(MMSG, "%s\n", buf);
memset(buf, 0, sizeof(buf));
ptr = buf;
}
i++;
}
PRINTM(MMSG, "%s\n", buf);
}
}
mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE);
}
#define DEBUG_FW_DONE 0xFF
#define MAX_POLL_TRIES 100
typedef enum {
DUMP_TYPE_ITCM = 0,
DUMP_TYPE_DTCM = 1,
DUMP_TYPE_SQRAM = 2,
DUMP_TYPE_IRAM = 3,
DUMP_TYPE_APU = 4,
DUMP_TYPE_CIU = 5,
DUMP_TYPE_ICU = 6,
DUMP_TYPE_MAC = 7,
} dumped_mem_type;
#define MAX_NAME_LEN 8
typedef struct {
t_u8 mem_name[MAX_NAME_LEN];
t_u8 *mem_Ptr;
struct file *pfile_mem;
t_u8 done_flag;
t_u8 type;
} memory_type_mapping;
#ifdef PCIE8897
#define DEBUG_HOST_READY_8897 0xEE
#define DEBUG_MEMDUMP_FINISH_8897 0xFE
static memory_type_mapping mem_type_mapping_tbl_8897[] = {
{"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM},
{"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM},
{"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM},
{"IRAM", NULL, NULL, 0xF3, FW_DUMP_TYPE_MEM_IRAM},
{"APU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_APU},
{"CIU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_CIU},
{"ICU", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_ICU},
{"MAC", NULL, NULL, 0xF7, FW_DUMP_TYPE_REG_MAC},
};
#endif
#define DEBUG_HOST_READY_8997 0xCC
#define DEBUG_HOST_EVENT_READY 0xAA
#define DEBUG_HOST_RESET_READY 0x99
static memory_type_mapping mem_type_mapping_tbl_8997 = {"DUMP", NULL, NULL,
0xDD, 0x00};
/**
* @brief This function reads data by 8 bit from card register
*
* @param handle A Pointer to the moal_handle structure
* @param reg Register offset
* @param data Value
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status woal_read_reg_eight_bit(moal_handle *handle, t_u32 reg,
t_u8 *data)
{
pcie_service_card *card = (pcie_service_card *)handle->card;
*data = ioread8(card->pci_mmap1 + reg);
return MLAN_STATUS_SUCCESS;
}
/**
* @brief This function read/write firmware
*
* @param phandle A pointer to moal_handle
* @param doneflag done flag
* @param resetflag reset flag;
*
* @return MLAN_STATUS_SUCCESS
*/
static rdwr_status woal_pcie_rdwr_firmware(moal_handle *phandle, t_u8 doneflag,
t_u8 resetflag)
{
int ret = 0;
int tries = 0;
t_u8 ctrl_data = 0;
t_u32 reg_data = 0;
t_u32 debug_host_ready = 0;
t_u32 dump_ctrl_reg = 0;
#ifdef PCIE8897
if (IS_PCIE8897(phandle->card_type)) {
debug_host_ready = DEBUG_HOST_READY_8897;
dump_ctrl_reg = DEBUG_DUMP_CTRL_REG;
}
#endif
#if defined(PCIE8997)
if (IS_PCIE8997(phandle->card_type)) {
debug_host_ready = DEBUG_HOST_READY_8997;
dump_ctrl_reg = DEBUG_DUMP_CTRL_REG;
}
#endif
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
if (phandle->event_fw_dump)
debug_host_ready = DEBUG_HOST_EVENT_READY;
else
debug_host_ready = DEBUG_HOST_READY_8997;
if (resetflag)
debug_host_ready = DEBUG_HOST_RESET_READY;
dump_ctrl_reg = PCIE9098_DUMP_CTRL_REG;
}
ret = woal_pcie_write_reg(phandle, dump_ctrl_reg, debug_host_ready);
if (ret) {
PRINTM(MERROR, "PCIE Write ERR, reg=0x%x debug_reay=0x%x\n",
dump_ctrl_reg, debug_host_ready);
return RDWR_STATUS_FAILURE;
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
if (phandle->event_fw_dump || resetflag)
return RDWR_STATUS_SUCCESS;
}
ret = woal_pcie_read_reg(phandle, dump_ctrl_reg, &reg_data);
if (ret) {
PRINTM(MERROR, "PCIE Read DEBUG_DUMP_CTRL_REG 0x%x fail\n",
dump_ctrl_reg);
return RDWR_STATUS_FAILURE;
}
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
ret = woal_read_reg_eight_bit(phandle, dump_ctrl_reg,
&ctrl_data);
if (ret) {
PRINTM(MERROR, "PCIE READ reg 0x%x 8bit ERR\n",
dump_ctrl_reg);
return RDWR_STATUS_FAILURE;
}
if (ctrl_data == DEBUG_FW_DONE)
break;
if (doneflag && ctrl_data == doneflag)
return RDWR_STATUS_DONE;
if (ctrl_data != debug_host_ready) {
PRINTM(MMSG,
"The ctrl reg was changed, ctrl_data=0x%x, host_ready:0x%x try again!\n",
ctrl_data, debug_host_ready);
ret = woal_pcie_write_reg(phandle, dump_ctrl_reg,
debug_host_ready);
if (ret) {
PRINTM(MERROR, "PCIE Write ERR\n");
return RDWR_STATUS_FAILURE;
}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
usleep_range(99, 100);
#else
udelay(100);
#endif
}
if (ctrl_data == debug_host_ready) {
PRINTM(MERROR, "Fail to pull ctrl_data=0x%x host_ready=0x%x\n",
ctrl_data, debug_host_ready);
return RDWR_STATUS_FAILURE;
}
return RDWR_STATUS_SUCCESS;
}
#ifdef PCIE8897
/**
* @brief This function dump firmware memory to file
*
* @param phandle A pointer to moal_handle
*
* @return N/A
*/
static void woal_pcie_dump_fw_info_v1(moal_handle *phandle)
{
int ret = 0;
unsigned int reg, reg_start, reg_end;
t_u8 *dbg_ptr = NULL;
t_u32 sec, usec;
t_u8 dump_num = 0;
t_u8 idx = 0;
t_u8 doneflag = 0;
rdwr_status stat;
t_u8 i = 0;
t_u8 read_reg = 0;
t_u32 memory_size = 0;
t_u32 memdump_finsh = 0;
t_u8 *end_ptr = NULL;
memory_type_mapping *mem_type_mapping_tbl = mem_type_mapping_tbl_8897;
if (!phandle) {
PRINTM(MERROR, "Could not dump firmwware info\n");
return;
}
if (!phandle->fw_dump_buf) {
ret = moal_vmalloc(phandle, FW_DUMP_INFO_LEN,
&(phandle->fw_dump_buf));
if (ret != MLAN_STATUS_SUCCESS || !phandle->fw_dump_buf) {
PRINTM(MERROR, "Failed to vmalloc fw dump bufffer\n");
return;
}
} else {
memset(phandle->fw_dump_buf, 0x00, FW_DUMP_INFO_LEN);
}
phandle->fw_dump_len = 0;
/* start dump fw memory */
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT START: %u.%06u ====\n", sec,
usec);
/* read the number of the memories which will dump */
if (RDWR_STATUS_FAILURE ==
woal_pcie_rdwr_firmware(phandle, doneflag, 0))
goto done;
reg = DEBUG_DUMP_START_REG;
ret = woal_read_reg_eight_bit(phandle, reg, &dump_num);
if (ret) {
PRINTM(MMSG, "PCIE READ MEM NUM ERR\n");
goto done;
}
/* read the length of every memory which will dump */
for (idx = 0;
idx < dump_num && idx < ARRAY_SIZE(mem_type_mapping_tbl_8897);
idx++) {
if (RDWR_STATUS_FAILURE ==
woal_pcie_rdwr_firmware(phandle, doneflag, 0))
goto done;
memory_size = 0;
reg = DEBUG_DUMP_START_REG;
for (i = 0; i < 4; i++) {
ret = woal_read_reg_eight_bit(phandle, reg, &read_reg);
if (ret) {
PRINTM(MMSG, "PCIE READ ERR\n");
goto done;
}
memory_size |= (read_reg << i * 8);
reg++;
}
if (memory_size == 0) {
PRINTM(MMSG, "Firmware Dump Finished!\n");
ret = woal_pcie_write_reg(phandle, DEBUG_DUMP_CTRL_REG,
memdump_finsh);
if (ret) {
PRINTM(MERROR,
"PCIE Write MEMDUMP_FINISH ERR\n");
goto done;
}
break;
} else {
PRINTM(MMSG, "%s_SIZE=0x%x\n",
mem_type_mapping_tbl[idx].mem_name, memory_size);
ret = moal_vmalloc(
phandle, memory_size + 1,
(t_u8 **)&mem_type_mapping_tbl[idx].mem_Ptr);
if ((ret != MLAN_STATUS_SUCCESS) ||
!mem_type_mapping_tbl[idx].mem_Ptr) {
PRINTM(MERROR,
"Error: vmalloc %s buffer failed!!!\n",
mem_type_mapping_tbl[idx].mem_name);
goto done;
}
dbg_ptr = mem_type_mapping_tbl[idx].mem_Ptr;
end_ptr = dbg_ptr + memory_size;
}
doneflag = mem_type_mapping_tbl[idx].done_flag;
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n",
mem_type_mapping_tbl[idx].mem_name, sec, usec);
do {
stat = woal_pcie_rdwr_firmware(phandle, doneflag, 0);
if (RDWR_STATUS_FAILURE == stat)
goto done;
reg_start = DEBUG_DUMP_START_REG;
reg_end = DEBUG_DUMP_END_REG;
for (reg = reg_start; reg <= reg_end; reg++) {
ret = woal_read_reg_eight_bit(phandle, reg,
dbg_ptr);
if (ret) {
PRINTM(MMSG, "PCIE READ ERR\n");
goto done;
}
if (dbg_ptr < end_ptr)
dbg_ptr++;
else
PRINTM(MINFO,
"pre-allocced buf is not enough\n");
}
if (RDWR_STATUS_DONE == stat) {
PRINTM(MMSG, "%s done: size=0x%x\n",
mem_type_mapping_tbl[idx].mem_name,
(unsigned int)(dbg_ptr -
mem_type_mapping_tbl[idx]
.mem_Ptr));
woal_save_dump_info_to_buf(
phandle,
mem_type_mapping_tbl[idx].mem_Ptr,
memory_size,
mem_type_mapping_tbl[idx].type);
moal_vfree(phandle,
mem_type_mapping_tbl[idx].mem_Ptr);
mem_type_mapping_tbl[idx].mem_Ptr = NULL;
break;
}
} while (1);
}
woal_append_end_block(phandle);
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT END: %u.%06u ====\n", sec,
usec);
/* end dump fw memory */
done:
for (idx = 0;
idx < dump_num && idx < ARRAY_SIZE(mem_type_mapping_tbl_8897);
idx++) {
if (mem_type_mapping_tbl[idx].mem_Ptr) {
moal_vfree(phandle, mem_type_mapping_tbl[idx].mem_Ptr);
mem_type_mapping_tbl[idx].mem_Ptr = NULL;
}
}
return;
}
#endif
/**
* @brief This function dump firmware memory to file
*
* @param phandle A pointer to moal_handle
*
* @return N/A
*/
static void woal_pcie_dump_fw_info_v2(moal_handle *phandle)
{
int ret = 0;
unsigned int reg, reg_start, reg_end;
t_u8 *dbg_ptr = NULL;
t_u8 *tmp_ptr = NULL;
t_u32 sec, usec;
t_u8 dump_num = 0;
t_u8 doneflag = 0;
rdwr_status stat;
t_u32 memory_size = 0;
t_u8 *end_ptr = NULL;
memory_type_mapping *mem_type_mapping_tbl = &mem_type_mapping_tbl_8997;
t_u32 dump_start_reg = 0;
t_u32 dump_end_reg = 0;
if (!phandle) {
PRINTM(MERROR, "Could not dump firmwware info\n");
return;
}
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
if (phandle->event_fw_dump) {
if (RDWR_STATUS_FAILURE !=
woal_pcie_rdwr_firmware(phandle, doneflag, 0)) {
PRINTM(MMSG,
"====PCIE FW DUMP EVENT MODE START ====\n");
return;
}
}
}
/* start dump fw memory */
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT START: %u.%06u ====\n", sec,
usec);
/* read the number of the memories which will dump */
if (RDWR_STATUS_FAILURE ==
woal_pcie_rdwr_firmware(phandle, doneflag, 0))
goto done;
if (IS_PCIE9098(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
dump_start_reg = PCIE9098_DUMP_START_REG;
dump_end_reg = PCIE9098_DUMP_END_REG;
}
#ifdef PCIE8997
if (IS_PCIE8997(phandle->card_type)) {
dump_start_reg = DEBUG_DUMP_START_REG;
dump_end_reg = DEBUG_DUMP_END_REG;
}
#endif
reg = dump_start_reg;
ret = woal_read_reg_eight_bit(phandle, reg, &dump_num);
if (ret) {
PRINTM(MMSG, "PCIE READ MEM NUM ERR\n");
goto done;
}
memory_size = 0x80000;
ret = moal_vmalloc(phandle, memory_size + 1,
(t_u8 **)&mem_type_mapping_tbl->mem_Ptr);
if ((ret != MLAN_STATUS_SUCCESS) || !mem_type_mapping_tbl->mem_Ptr) {
PRINTM(MERROR, "Error: vmalloc %s buffer failed!!!\n",
mem_type_mapping_tbl->mem_name);
goto done;
}
dbg_ptr = mem_type_mapping_tbl->mem_Ptr;
end_ptr = dbg_ptr + memory_size;
doneflag = mem_type_mapping_tbl->done_flag;
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n",
mem_type_mapping_tbl->mem_name, sec, usec);
do {
stat = woal_pcie_rdwr_firmware(phandle, doneflag, 0);
if (RDWR_STATUS_FAILURE == stat)
goto done;
reg_start = dump_start_reg;
reg_end = dump_end_reg;
for (reg = reg_start; reg <= reg_end; reg++) {
ret = woal_read_reg_eight_bit(phandle, reg, dbg_ptr);
if (ret) {
PRINTM(MMSG, "PCIE READ ERR\n");
goto done;
}
dbg_ptr++;
if (dbg_ptr >= end_ptr) {
PRINTM(MINFO,
"pre-allocced buf is not enough\n");
ret = moal_vmalloc(phandle,
memory_size + 0x4000 + 1,
(t_u8 **)&tmp_ptr);
if ((ret != MLAN_STATUS_SUCCESS) || !tmp_ptr) {
PRINTM(MERROR,
"Error: vmalloc buffer failed!!!\n");
goto done;
}
moal_memcpy_ext(phandle, tmp_ptr,
mem_type_mapping_tbl->mem_Ptr,
memory_size,
memory_size + 0x4000);
moal_vfree(phandle,
mem_type_mapping_tbl->mem_Ptr);
mem_type_mapping_tbl->mem_Ptr = tmp_ptr;
tmp_ptr = NULL;
dbg_ptr = mem_type_mapping_tbl->mem_Ptr +
memory_size;
memory_size += 0x4000;
end_ptr = mem_type_mapping_tbl->mem_Ptr +
memory_size;
}
}
if (RDWR_STATUS_DONE == stat) {
#ifdef MLAN_64BIT
PRINTM(MMSG,
"%s done:"
"size = 0x%lx\n",
mem_type_mapping_tbl->mem_name,
dbg_ptr - mem_type_mapping_tbl->mem_Ptr);
#else
PRINTM(MMSG,
"%s done:"
"size = 0x%x\n",
mem_type_mapping_tbl->mem_name,
dbg_ptr - mem_type_mapping_tbl->mem_Ptr);
#endif
if (phandle->fw_dump_buf) {
moal_vfree(phandle, phandle->fw_dump_buf);
phandle->fw_dump_buf = NULL;
phandle->fw_dump_len = 0;
}
phandle->fw_dump_buf = mem_type_mapping_tbl->mem_Ptr;
phandle->fw_dump_len =
dbg_ptr - mem_type_mapping_tbl->mem_Ptr;
mem_type_mapping_tbl->mem_Ptr = NULL;
break;
}
} while (1);
moal_get_system_time(phandle, &sec, &usec);
PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT END: %u.%06u ====\n", sec,
usec);
/* end dump fw memory */
done:
if (mem_type_mapping_tbl->mem_Ptr) {
moal_vfree(phandle, mem_type_mapping_tbl->mem_Ptr);
mem_type_mapping_tbl->mem_Ptr = NULL;
}
return;
}
/**
* @brief This function check if this is second mac
*
* @param handle A pointer to moal_handle structure
* @return MTRUE/MFALSE
*
*/
static t_u8 woal_pcie_is_second_mac(moal_handle *handle)
{
pcie_service_card *card = (pcie_service_card *)handle->card;
if ((card->dev->device == PCIE_DEVICE_ID_88W9098P_FN1) ||
(card->dev->device == PCIE_DEVICE_ID_88WAW693_FN1))
return MTRUE;
return MFALSE;
}
static void woal_pcie_dump_fw_info(moal_handle *phandle)
{
if (phandle->fw_dump_buf) {
PRINTM(MERROR, "FW dump already exist\n");
return;
}
mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE);
phandle->fw_dump = MTRUE;
#ifdef PCIE8897
if (IS_PCIE8897(phandle->card_type))
woal_pcie_dump_fw_info_v1(phandle);
#endif
if (IS_PCIE8997(phandle->card_type) ||
IS_PCIEAW693(phandle->card_type) ||
IS_PCIEIW62X(phandle->card_type) ||
IS_PCIE9098(phandle->card_type) ||
IS_PCIE9097(phandle->card_type)) {
woal_pcie_dump_fw_info_v2(phandle);
if (phandle->event_fw_dump) {
phandle->event_fw_dump = MFALSE;
queue_work(phandle->workqueue, &phandle->main_work);
phandle->is_fw_dump_timer_set = MTRUE;
woal_mod_timer(&phandle->fw_dump_timer, MOAL_TIMER_5S);
return;
}
}
phandle->fw_dump = MFALSE;
if (!phandle->priv_num)
return;
woal_send_fw_dump_complete_event(
woal_get_priv(phandle, MLAN_BSS_ROLE_ANY));
mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE);
queue_work(phandle->workqueue, &phandle->main_work);
woal_process_hang(phandle);
}
static mlan_status woal_pcie_get_fw_name(moal_handle *handle)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
pcie_service_card *card = (pcie_service_card *)handle->card;
moal_handle *ref_handle = NULL;
t_u32 rev_id_reg = handle->card_info->rev_id_reg;
t_u32 revision_id = 0;
t_u32 host_strap_reg = handle->card_info->host_strap_reg;
t_u32 magic_reg = handle->card_info->magic_reg;
t_u32 strap = 0;
t_u32 magic = 0;
#ifdef PCIEIW62X
t_u32 boot_mode_reg = handle->card_info->boot_mode_reg;
t_u32 boot_mode;
#endif
ENTER();
if (handle->params.fw_name) {
#ifdef PCIE9097
if (IS_PCIE9097(handle->card_type)) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
revision_id &= 0xff;
PRINTM(MCMND, "revision_id=0x%x\n", revision_id);
switch (revision_id) {
case PCIE9097_A0:
break;
case PCIE9097_B0:
case PCIE9097_B1:
handle->card_rev = CHIP_9097_REV_B0;
break;
default:
break;
}
}
#endif
goto done;
}
#ifdef PCIE8997
if (IS_PCIE8997(handle->card_type)) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
woal_pcie_read_reg(handle, host_strap_reg, &strap);
woal_pcie_read_reg(handle, magic_reg, &magic);
revision_id &= 0xff;
strap &= 0x7;
magic &= 0xff;
PRINTM(MCMND, "magic=0x%x, strap=0x%x, revision_id=0x%x\n",
magic, strap, revision_id);
if ((revision_id == PCIE8997_A1) &&
(magic == CHIP_MAGIC_VALUE)) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info->fw_name,
PCIEUART8997_DEFAULT_COMBO_FW_NAME);
else
strcpy(handle->card_info->fw_name,
PCIEUSB8997_DEFAULT_COMBO_FW_NAME);
}
}
#endif
#ifdef PCIE9098
if (IS_PCIE9098(handle->card_type)) {
if (card->dev->device == PCIE_DEVICE_ID_88W9098P_FN0) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
woal_pcie_read_reg(handle, host_strap_reg, &strap);
woal_pcie_read_reg(handle, magic_reg, &magic);
revision_id &= 0xff;
strap &= 0x7;
magic &= 0xff;
PRINTM(MCMND,
"magic=0x%x, strap=0x%x, revision_id=0x%x\n",
magic, strap, revision_id);
switch (revision_id) {
case PCIE9098_Z1Z2:
if (magic == CHIP_MAGIC_VALUE) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info
->fw_name,
PCIEUART9098_DEFAULT_COMBO_FW_NAME);
else if (strap == CARD_TYPE_PCIE_PCIE)
strcpy(handle->card_info
->fw_name,
PCIEPCIE9098_DEFAULT_COMBO_FW_NAME);
else
strcpy(handle->card_info
->fw_name,
PCIEUSB9098_DEFAULT_COMBO_FW_NAME);
}
strcpy(handle->card_info->fw_name_wlan,
PCIE9098_DEFAULT_WLAN_FW_NAME);
break;
case PCIE9098_A0:
case PCIE9098_A1:
case PCIE9098_A2:
if (magic == CHIP_MAGIC_VALUE) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info
->fw_name,
PCIEUART9098_COMBO_V1_FW_NAME);
else if (strap == CARD_TYPE_PCIE_PCIE)
strcpy(handle->card_info
->fw_name,
PCIEPCIE9098_COMBO_V1_FW_NAME);
else
strcpy(handle->card_info
->fw_name,
PCIEUSB9098_COMBO_V1_FW_NAME);
}
strcpy(handle->card_info->fw_name_wlan,
PCIE9098_WLAN_V1_FW_NAME);
break;
default:
break;
}
} else {
ref_handle = (moal_handle *)handle->pref_mac;
if (ref_handle) {
strcpy(handle->card_info->fw_name,
ref_handle->card_info->fw_name);
strcpy(handle->card_info->fw_name_wlan,
ref_handle->card_info->fw_name_wlan);
}
}
}
#endif
#ifdef PCIE9097
if (IS_PCIE9097(handle->card_type)) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
woal_pcie_read_reg(handle, host_strap_reg, &strap);
woal_pcie_read_reg(handle, magic_reg, &magic);
revision_id &= 0xff;
strap &= 0x7;
magic &= 0xff;
PRINTM(MCMND, "magic=0x%x, strap=0x%x, revision_id=0x%x\n",
magic, strap, revision_id);
switch (revision_id) {
case PCIE9097_A0:
if (magic == CHIP_MAGIC_VALUE) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info->fw_name,
PCIEUART9097_DEFAULT_COMBO_FW_NAME);
else
strcpy(handle->card_info->fw_name,
PCIEUSB9097_DEFAULT_COMBO_FW_NAME);
}
strcpy(handle->card_info->fw_name_wlan,
PCIE9097_DEFAULT_WLAN_FW_NAME);
break;
case PCIE9097_B0:
case PCIE9097_B1:
if (magic == CHIP_MAGIC_VALUE) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info->fw_name,
PCIEUART9097_COMBO_V1_FW_NAME);
else
strcpy(handle->card_info->fw_name,
PCIEUSB9097_COMBO_V1_FW_NAME);
}
strcpy(handle->card_info->fw_name_wlan,
PCIE9097_WLAN_V1_FW_NAME);
handle->card_rev = CHIP_9097_REV_B0;
break;
default:
break;
}
}
#endif
if (IS_PCIEAW693(handle->card_type)) {
if (card->dev->device == PCIE_DEVICE_ID_88WAW693_FN0) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
woal_pcie_read_reg(handle, host_strap_reg, &strap);
woal_pcie_read_reg(handle, magic_reg, &magic);
revision_id &= 0xff;
strap &= 0x7;
magic &= 0xff;
PRINTM(MCMND,
"magic=0x%x, strap=0x%x, revision_id=0x%x\n",
magic, strap, revision_id);
if (magic == CHIP_MAGIC_VALUE) {
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info->fw_name,
PCIEUARTAW693_DEFAULT_COMBO_FW_NAME);
else
strcpy(handle->card_info->fw_name,
PCIEAW693_DEFAULT_COMBO_FW_NAME);
}
} else {
ref_handle = (moal_handle *)handle->pref_mac;
if (ref_handle) {
strcpy(handle->card_info->fw_name,
ref_handle->card_info->fw_name);
strcpy(handle->card_info->fw_name_wlan,
ref_handle->card_info->fw_name_wlan);
}
}
}
#ifdef PCIEIW62X
if (IS_PCIEIW62X(handle->card_type)) {
woal_pcie_read_reg(handle, rev_id_reg, &revision_id);
woal_pcie_read_reg(handle, host_strap_reg, &strap);
woal_pcie_read_reg(handle, magic_reg, &magic);
woal_pcie_read_reg(handle, boot_mode_reg, &boot_mode);
revision_id &= 0xff;
strap &= 0x7;
magic &= 0xff;
boot_mode &= 0x03;
PRINTM(MCMND,
"magic=0x%x boot_mode=0x%x, strap=0x%x, revision_id=0x%x\n",
magic, boot_mode, strap, revision_id);
if (boot_mode == 0x03)
PRINTM(MMSG, "wlan: PCIE-IW62X in secure-boot mode\n");
if (strap == CARD_TYPE_PCIE_UART)
strcpy(handle->card_info->fw_name,
PCIEUARTIW62X_DEFAULT_COMBO_FW_NAME);
else
strcpy(handle->card_info->fw_name,
PCIEUSBIW62X_DEFAULT_COMBO_FW_NAME);
}
#endif
done:
PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name,
handle->card_info->fw_name_wlan);
LEAVE();
return ret;
}
static moal_if_ops pcie_ops = {
.register_dev = woal_pcie_register_dev,
.unregister_dev = woal_pcie_unregister_dev,
.read_reg = woal_pcie_read_reg,
.write_reg = woal_pcie_write_reg,
.read_data_sync = woal_pcie_read_data_sync,
.write_data_sync = woal_pcie_write_data_sync,
.get_fw_name = woal_pcie_get_fw_name,
.dump_fw_info = woal_pcie_dump_fw_info,
.reg_dbg = woal_pcie_reg_dbg,
.dump_reg_info = woal_pcie_dump_reg_info,
.is_second_mac = woal_pcie_is_second_mac,
};