/* * This function writes data into PCIE card register.
*/ staticinlinevoid
mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data)
{ struct pcie_service_card *card = adapter->card;
/* This function reads data from PCIE card register.
*/ staticint mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
{ struct pcie_service_card *card = adapter->card;
/* This function reads u8 data from PCIE card register. */ staticint mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, int reg, u8 *data)
{ struct pcie_service_card *card = adapter->card;
*data = ioread8(card->pci_mmap1 + reg);
return 0;
}
/* * This function reads sleep cookie and checks if FW is ready
*/ staticbool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
{
u32 cookie_value; struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
if (!reg->sleep_cookie) returntrue;
if (card->sleep_cookie_vbase) {
cookie_value = get_unaligned_le32(card->sleep_cookie_vbase);
mwifiex_dbg(adapter, INFO, "info: ACCESS_HW: sleep cookie=0x%x\n",
cookie_value); if (cookie_value == FW_AWAKE_COOKIE) returntrue;
}
returnfalse;
}
#ifdef CONFIG_PM_SLEEP /* * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a host * sleep activate request to the firmware and turns off the traffic.
*/ staticint mwifiex_pcie_suspend(struct device *dev)
{ struct mwifiex_adapter *adapter; struct pcie_service_card *card = dev_get_drvdata(dev);
/* Might still be loading firmware */
wait_for_completion(&card->fw_done);
adapter = card->adapter; if (!adapter) {
dev_err(dev, "adapter is not valid\n"); return 0;
}
mwifiex_enable_wake(adapter);
/* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) {
mwifiex_dbg(adapter, ERROR, "cmd: failed to suspend\n");
clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags);
mwifiex_disable_wake(adapter); return -EFAULT;
}
/* * Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not resumed, this function turns on the traffic and * sends a host sleep cancel request to the firmware.
*/ staticint mwifiex_pcie_resume(struct device *dev)
{ struct mwifiex_adapter *adapter; struct pcie_service_card *card = dev_get_drvdata(dev);
if (!card->adapter) {
dev_err(dev, "adapter structure is not valid\n"); return 0;
}
/* * This function probes an mwifiex device and registers it. It allocates * the card structure, enables PCIE function number and initiates the * device registration and initialization procedure by adding a logical * interface.
*/ staticint mwifiex_pcie_probe(struct pci_dev *pdev, conststruct pci_device_id *ent)
{ struct pcie_service_card *card; int ret;
/* device tree node parsing and platform specific configuration*/ if (pdev->dev.of_node) {
ret = mwifiex_pcie_probe_of(&pdev->dev); if (ret) return ret;
}
/* On MS Surface gen4+ devices FLR isn't effective to recover from * hangups, so we power-cycle the card instead.
*/ if (card->quirks & QUIRK_FW_RST_D3COLD)
mwifiex_pcie_reset_d3cold_quirk(pdev);
/* * Kernel stores and restores PCIe function context before and after performing * FLR respectively. Reconfigure the software and firmware including firmware * redownload.
*/ staticvoid mwifiex_pcie_reset_done(struct pci_dev *pdev)
{ struct pcie_service_card *card = pci_get_drvdata(pdev); struct mwifiex_adapter *adapter = card->adapter; int ret;
if (!adapter) {
dev_err(&pdev->dev, "%s: adapter structure is not valid\n",
__func__); return;
}
/* * This function adds delay loop to ensure FW is awake before proceeding.
*/ staticvoid mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
{ int i = 0;
while (mwifiex_pcie_ok_to_access_hw(adapter)) {
i++;
usleep_range(10, 20); /* 50ms max wait */ if (i == 5000) break;
}
/* This function wakes up the card by reading fw_status register. */ staticint mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; int retval __maybe_unused;
if (reg->sleep_cookie)
mwifiex_pcie_dev_wakeup_delay(adapter);
/* The 88W8897 PCIe+USB firmware (latest version 15.68.19.p21) sometimes * appears to ignore or miss our wakeup request, so we continue trying * until we receive an interrupt from the card.
*/ if (read_poll_timeout(mwifiex_write_reg_rpt, retval,
READ_ONCE(adapter->int_status) != 0,
500, 500 * N_WAKEUP_TRIES_SHORT_INTERVAL, false,
adapter, reg->fw_status, FIRMWARE_READY_PCIE)) { if (read_poll_timeout(mwifiex_write_reg_rpt, retval,
READ_ONCE(adapter->int_status) != 0,
10000, 10000 * N_WAKEUP_TRIES_LONG_INTERVAL, false,
adapter, reg->fw_status, FIRMWARE_READY_PCIE)) {
mwifiex_dbg(adapter, ERROR, "Firmware didn't wake up\n"); return -EIO;
}
}
/* * This function is called after the card has woken up. * * The card configuration register is reset.
*/ staticint mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
{
mwifiex_dbg(adapter, CMD, "cmd: Wakeup device completed\n");
return 0;
}
/* * This function disables the host interrupt. * * The host interrupt mask is read, the disable bit is reset and * written back to the card host interrupt mask register.
*/ staticvoid mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter)
{ if (mwifiex_pcie_ok_to_access_hw(adapter))
mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, 0x00000000);
atomic_set(&adapter->tx_hw_pending, 0);
}
/* * This function enables the host interrupt. * * The host interrupt enable mask is written to the card * host interrupt mask register.
*/ staticint mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter)
{ if (mwifiex_pcie_ok_to_access_hw(adapter)) /* Simply write the mask to the register */
mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK);
return 0;
}
/* * This function initializes TX buffer ring descriptors
*/ staticint mwifiex_init_txq_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2; int i;
/* This function initializes RX buffer ring descriptors. Each SKB is allocated * here and after mapping PCI memory, its physical address is assigned to * PCIE Rx buffer descriptor's physical address.
*/ staticint mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; struct sk_buff *skb; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2;
dma_addr_t buf_pa; int i;
for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { /* Allocate skb here so that firmware can DMA data from it */
skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
GFP_KERNEL); if (!skb) {
mwifiex_dbg(adapter, ERROR, "Unable to allocate skb for RX ring.\n"); return -ENOMEM;
}
if (mwifiex_map_pci_memory(adapter, skb,
MWIFIEX_RX_DATA_BUF_SIZE,
DMA_FROM_DEVICE)) {
kfree_skb(skb); return -ENOMEM;
}
/* This function initializes event buffer ring descriptors. Each SKB is * allocated here and after mapping PCI memory, its physical address is assigned * to PCIE Rx buffer descriptor's physical address
*/ staticint mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; struct mwifiex_evt_buf_desc *desc; struct sk_buff *skb;
dma_addr_t buf_pa; int i;
for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { /* Allocate skb here so that firmware can DMA data from it */
skb = dev_alloc_skb(MAX_EVENT_SIZE); if (!skb) {
mwifiex_dbg(adapter, ERROR, "Unable to allocate skb for EVENT buf.\n"); return -ENOMEM;
}
skb_put(skb, MAX_EVENT_SIZE);
if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE,
DMA_FROM_DEVICE)) {
kfree_skb(skb); return -ENOMEM;
}
/* This function cleans up TX buffer rings. If any of the buffer list has valid * SKB address, associated SKB is freed.
*/ staticvoid mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; struct sk_buff *skb; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2; int i;
for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { if (reg->pfu_enabled) {
desc2 = card->txbd_ring[i]; if (card->tx_buf_list[i]) {
skb = card->tx_buf_list[i];
mwifiex_unmap_pci_memory(adapter, skb,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
}
memset(desc2, 0, sizeof(*desc2));
} else {
desc = card->txbd_ring[i]; if (card->tx_buf_list[i]) {
skb = card->tx_buf_list[i];
mwifiex_unmap_pci_memory(adapter, skb,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
}
memset(desc, 0, sizeof(*desc));
}
card->tx_buf_list[i] = NULL;
}
atomic_set(&adapter->tx_hw_pending, 0); return;
}
/* This function cleans up RX buffer rings. If any of the buffer list has valid * SKB address, associated SKB is freed.
*/ staticvoid mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2; struct sk_buff *skb; int i;
for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { if (reg->pfu_enabled) {
desc2 = card->rxbd_ring[i]; if (card->rx_buf_list[i]) {
skb = card->rx_buf_list[i];
mwifiex_unmap_pci_memory(adapter, skb,
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
memset(desc2, 0, sizeof(*desc2));
} else {
desc = card->rxbd_ring[i]; if (card->rx_buf_list[i]) {
skb = card->rx_buf_list[i];
mwifiex_unmap_pci_memory(adapter, skb,
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
memset(desc, 0, sizeof(*desc));
}
card->rx_buf_list[i] = NULL;
}
return;
}
/* This function cleans up event buffer rings. If any of the buffer list has * valid SKB address, associated SKB is freed.
*/ staticvoid mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; struct mwifiex_evt_buf_desc *desc; struct sk_buff *skb; int i;
for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) {
desc = card->evtbd_ring[i]; if (card->evt_buf_list[i]) {
skb = card->evt_buf_list[i];
mwifiex_unmap_pci_memory(adapter, skb,
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
card->evt_buf_list[i] = NULL;
memset(desc, 0, sizeof(*desc));
}
return;
}
/* This function creates buffer descriptor ring for TX
*/ staticint mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
/* * driver maintaines the write pointer and firmware maintaines the read * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set
*/
card->txbd_wrptr = 0;
if (reg->pfu_enabled)
card->txbd_rdptr = 0; else
card->txbd_rdptr |= reg->tx_rollover_ind;
/* allocate shared memory for the BD ring and divide the same in to
several descriptors */ if (reg->pfu_enabled)
card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) *
MWIFIEX_MAX_TXRX_BD; else
card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) *
MWIFIEX_MAX_TXRX_BD;
/* * This function creates buffer descriptor ring for RX
*/ staticint mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter)
{ int ret; struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
/* * driver maintaines the read pointer and firmware maintaines the write * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set
*/
card->rxbd_wrptr = 0;
card->rxbd_rdptr = reg->rx_rollover_ind;
/* * This function creates buffer descriptor ring for Events
*/ staticint mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter)
{ int ret; struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
/* * driver maintaines the read pointer and firmware maintaines the write * pointer. The write pointer starts at 0 (zero) while the read pointer * starts at zero with rollover bit set
*/
card->evtbd_wrptr = 0;
card->evtbd_rdptr = reg->evt_rollover_ind;
/* * This function allocates a buffer for CMDRSP
*/ staticint mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; struct sk_buff *skb;
/* Allocate memory for receiving command response data */
skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); if (!skb) {
mwifiex_dbg(adapter, ERROR, "Unable to allocate skb for command response data.\n"); return -ENOMEM;
}
skb_put(skb, MWIFIEX_UPLD_SIZE); if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
DMA_FROM_DEVICE)) {
kfree_skb(skb); return -1;
}
card->cmdrsp_buf = skb;
return 0;
}
/* * This function deletes a buffer for CMDRSP
*/ staticint mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card;
/* This function flushes the TX buffer descriptor ring * This function defined as handler is also called while cleaning TXRX * during disconnect/ bss stop.
*/ staticvoid mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card;
if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) {
card->txbd_flush = 1; /* write pointer already set at last send * send dnld-rdy intr again, wait for completion.
*/
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
CPU_INTR_DNLD_RDY);
}
}
if (!mwifiex_pcie_ok_to_access_hw(adapter))
mwifiex_pm_wakeup_card(adapter);
/* Read the TX ring read pointer set by firmware */ if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) {
mwifiex_dbg(adapter, ERROR, "SEND COMP: failed to read reg->tx_rdptr\n"); return -1;
}
if (card->txbd_flush) { if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr))
card->txbd_flush = 0; else
mwifiex_clean_pcie_ring_buf(adapter);
}
return 0;
}
/* This function sends data buffer to device. First 4 bytes of payload * are filled with payload length and payload type. Then this payload * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. * Download ready interrupt to FW is deffered if Tx ring is not full and * additional payload can be accomodated. * Caller must ensure tx_param parameter to this function is not NULL.
*/ staticint
mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, struct mwifiex_tx_param *tx_param)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
u32 wrindx, num_tx_buffs, rx_val;
dma_addr_t buf_pa; struct mwifiex_pcie_buf_desc *desc = NULL; struct mwifiex_pfu_buf_desc *desc2 = NULL;
rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; /* Write the TX ring write pointer in to reg->tx_wrptr */
mwifiex_write_reg(adapter, reg->tx_wrptr,
card->txbd_wrptr | rx_val);
/* The firmware (latest version 15.68.19.p21) of the 88W8897 PCIe+USB card * seems to crash randomly after setting the TX ring write pointer when * ASPM powersaving is enabled. A workaround seems to be keeping the bus * busy by reading a random register afterwards.
*/
mwifiex_read_reg(adapter, PCI_VENDOR_ID, &rx_val);
if ((mwifiex_pcie_txbd_not_full(card)) &&
tx_param->next_pkt_len) { /* have more packets and TxBD still can hold more */
mwifiex_dbg(adapter, DATA, "SEND DATA: delay dnld-rdy interrupt.\n");
adapter->data_sent = false;
} else { /* Send the TX ready interrupt */
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
CPU_INTR_DNLD_RDY);
}
mwifiex_dbg(adapter, DATA, "info: SEND DATA: Updated "%#x> and sent packet to firmware successfully\n",
card->txbd_rdptr, card->txbd_wrptr);
} else {
mwifiex_dbg(adapter, DATA, "info: TX Ring full, can't send packets to fw\n");
adapter->data_sent = true; /* Send the TX ready interrupt */
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
CPU_INTR_DNLD_RDY); return -EBUSY;
}
return -EINPROGRESS;
}
/* * This function handles received buffer ring and * dispatches packets to upper
*/ staticint mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
u32 wrptr, rd_index, tx_val;
dma_addr_t buf_pa; int ret = 0; struct sk_buff *skb_tmp = NULL; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2;
if (!mwifiex_pcie_ok_to_access_hw(adapter))
mwifiex_pm_wakeup_card(adapter);
/* Read the RX ring Write pointer set by firmware */ if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
mwifiex_dbg(adapter, ERROR, "RECV DATA: failed to read reg->rx_wrptr\n");
ret = -1; goto done;
}
card->rxbd_wrptr = wrptr;
tx_val = card->txbd_wrptr & reg->tx_wrap_mask; /* Write the RX ring read pointer in to reg->rx_rdptr */
mwifiex_write_reg(adapter, reg->rx_rdptr,
card->rxbd_rdptr | tx_val);
/* Read the RX ring Write pointer set by firmware */ if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) {
mwifiex_dbg(adapter, ERROR, "RECV DATA: failed to read reg->rx_wrptr\n");
ret = -1; goto done;
}
mwifiex_dbg(adapter, DATA, "info: RECV DATA: Rcvd packet from fw successfully\n");
card->rxbd_wrptr = wrptr;
}
done: return ret;
}
/* * This function downloads the boot command to device
*/ staticint
mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
{
dma_addr_t buf_pa; struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
if (!(skb->data && skb->len)) {
mwifiex_dbg(adapter, ERROR, "Invalid parameter in %s <%p. len %d>\n",
__func__, skb->data, skb->len); return -1;
}
if (mwifiex_map_pci_memory(adapter, skb, skb->len, DMA_TO_DEVICE)) return -1;
buf_pa = MWIFIEX_SKB_DMA_ADDR(skb);
/* Write the lower 32bits of the physical address to low command * address scratch register
*/
mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa);
/* Write the upper 32bits of the physical address to high command * address scratch register
*/
mwifiex_write_reg(adapter, reg->cmd_addr_hi, (u32)((u64)buf_pa >> 32));
/* Write the command length to cmd_size scratch register */
mwifiex_write_reg(adapter, reg->cmd_size, skb->len);
/* Ring the door bell */
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DOOR_BELL);
return 0;
}
/* This function init rx port in firmware which in turn enables to receive data * from device before transmitting any packet.
*/ staticvoid mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg; int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask;
/* Write the RX ring read pointer in to reg->rx_rdptr */
mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | tx_wrap);
}
/* This function downloads commands to the device
*/ staticint
mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb)
{ struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
dma_addr_t cmd_buf_pa, cmdrsp_buf_pa;
u8 *payload = (u8 *)skb->data;
if (!(skb->data && skb->len)) {
mwifiex_dbg(adapter, ERROR, "Invalid parameter in %s <%p, %#x>\n",
__func__, skb->data, skb->len); return -1;
}
/* Make sure a command response buffer is available */ if (!card->cmdrsp_buf) {
mwifiex_dbg(adapter, ERROR, "No response buffer available, send command failed\n"); return -EBUSY;
}
if (!mwifiex_pcie_ok_to_access_hw(adapter))
mwifiex_pm_wakeup_card(adapter);
if (mwifiex_map_pci_memory(adapter, skb, skb->len, DMA_TO_DEVICE)) return -1;
card->cmd_buf = skb; /* * Need to keep a reference, since core driver might free up this * buffer before we've unmapped it.
*/
skb_get(skb);
/* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to cmd response address low + cmd response address high 2. Ring the door bell (i.e. set the door bell interrupt)
In response to door bell interrupt, the firmware will perform the DMA of the command packet (first header to obtain the total length and then rest of the command).
*/
if (card->cmdrsp_buf) {
cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); /* Write the lower 32bits of the cmdrsp buffer physical
address */
mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo,
(u32)cmdrsp_buf_pa);
/* Write the upper 32bits of the cmdrsp buffer physical
address */
mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi,
(u32)((u64)cmdrsp_buf_pa >> 32));
}
cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf);
/* Write the lower 32bits of the physical address to reg->cmd_addr_lo */
mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)cmd_buf_pa);
/* Write the upper 32bits of the physical address to reg->cmd_addr_hi */
mwifiex_write_reg(adapter, reg->cmd_addr_hi,
(u32)((u64)cmd_buf_pa >> 32));
/* Write the command length to reg->cmd_size */
mwifiex_write_reg(adapter, reg->cmd_size, card->cmd_buf->len);
/* Ring the door bell */
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, CPU_INTR_DOOR_BELL);
if (adapter->curr_cmd)
mwifiex_unmap_pci_memory(adapter, skb, DMA_FROM_DEVICE); else
dma_sync_single_for_cpu(&card->dev->dev,
MWIFIEX_SKB_DMA_ADDR(skb),
MWIFIEX_UPLD_SIZE, DMA_FROM_DEVICE);
/* Unmap the command as a response has been received. */ if (card->cmd_buf) {
mwifiex_unmap_pci_memory(adapter, card->cmd_buf,
DMA_TO_DEVICE);
dev_kfree_skb_any(card->cmd_buf);
card->cmd_buf = NULL;
}
if (!adapter->curr_cmd) { if (adapter->ps_state == PS_STATE_SLEEP_CFM) {
dma_sync_single_for_device(&card->dev->dev,
MWIFIEX_SKB_DMA_ADDR(skb),
MWIFIEX_SLEEP_COOKIE_SIZE,
DMA_FROM_DEVICE);
mwifiex_write_reg(adapter,
PCIE_CPU_INT_EVENT,
CPU_INTR_SLEEP_CFM_DONE);
mwifiex_delay_for_sleep_cookie(adapter,
MWIFIEX_MAX_DELAY_COUNT);
mwifiex_unmap_pci_memory(adapter, skb,
DMA_FROM_DEVICE);
skb_pull(skb, adapter->intf_hdr_len); while (reg->sleep_cookie && (count++ < 10) &&
mwifiex_pcie_ok_to_access_hw(adapter))
usleep_range(50, 60);
mwifiex_pcie_enable_host_int(adapter);
mwifiex_process_sleep_confirm_resp(adapter, skb->data,
skb->len);
} else {
mwifiex_dbg(adapter, ERROR, "There is no command but got cmdrsp\n");
}
memcpy(adapter->upld_buf, skb->data,
min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len));
skb_push(skb, adapter->intf_hdr_len); if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
DMA_FROM_DEVICE)) return -1;
} elseif (mwifiex_pcie_ok_to_access_hw(adapter)) {
skb_pull(skb, adapter->intf_hdr_len);
adapter->curr_cmd->resp_skb = skb;
adapter->cmd_resp_received = true; /* Take the pointer and set it to CMD node and will
return in the response complete callback */
card->cmdrsp_buf = NULL;
/* Clear the cmd-rsp buffer address in scratch registers. This will prevent firmware from writing to the same response
buffer again. */
mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0);
/* Write the upper 32bits of the cmdrsp buffer physical
address */
mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0);
}
/* Read the event ring write pointer set by firmware */ if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
mwifiex_dbg(adapter, ERROR, "EventReady: failed to read reg->evt_wrptr\n"); return -1;
}
/* Take the pointer and set it to event pointer in adapter
and will return back after event handling callback */
card->evt_buf_list[rdptr] = NULL;
desc = card->evtbd_ring[rdptr];
memset(desc, 0, sizeof(*desc));
event = get_unaligned_le32(
&skb_cmd->data[adapter->intf_hdr_len]);
adapter->event_cause = event; /* The first 4bytes will be the event transfer header
len is 2 bytes followed by type which is 2 bytes */
memcpy(&data_len, skb_cmd->data, sizeof(__le16));
evt_len = le16_to_cpu(data_len);
skb_trim(skb_cmd, evt_len);
skb_pull(skb_cmd, adapter->intf_hdr_len);
mwifiex_dbg(adapter, EVENT, "info: Event length: %d\n", evt_len);
/* Do not update the event read pointer here, wait till the buffer is released. This is just to make things simpler, we need to find a better method of managing these buffers.
*/
} else {
mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT,
CPU_INTR_EVENT_DONE);
}
/* Read the event ring write pointer set by firmware */ if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) {
mwifiex_dbg(adapter, ERROR, "event_complete: failed to read reg->evt_wrptr\n"); return -1;
}
/* Combo firmware image is a combination of * (1) combo crc heaer, start with CMD5 * (2) bluetooth image, start with CMD7, end with CMD6, data wrapped in CMD1. * (3) wifi image. * * This function bypass the header and bluetooth part, return * the offset of tail wifi-only part. If the image is already wifi-only, * that is start with CMD1, return 0.
*/
/* Skip past header */
offset += sizeof(fwdata->header);
switch (dnld_cmd) { case MWIFIEX_FW_DNLD_CMD_1: if (offset + data_len < data_len) {
mwifiex_dbg(adapter, ERROR, "bad FW parse\n");
ret = -1; goto done;
}
/* Image start with cmd1, already wifi-only firmware */ if (!first_cmd) {
mwifiex_dbg(adapter, MSG, "input wifi-only firmware\n"); return 0;
}
if (!cmd7_before) {
mwifiex_dbg(adapter, ERROR, "no cmd7 before cmd1!\n");
ret = -1; goto done;
}
offset += data_len; break; case MWIFIEX_FW_DNLD_CMD_5:
first_cmd = true; /* Check for integer overflow */ if (offset + data_len < data_len) {
mwifiex_dbg(adapter, ERROR, "bad FW parse\n");
ret = -1; goto done;
}
offset += data_len; break; case MWIFIEX_FW_DNLD_CMD_6:
first_cmd = true; /* Check for integer overflow */ if (offset + data_len < data_len) {
mwifiex_dbg(adapter, ERROR, "bad FW parse\n");
ret = -1; goto done;
}
offset += data_len; if (offset >= firmware_len) {
mwifiex_dbg(adapter, ERROR, "extract wifi-only fw failure!\n");
ret = -1;
} else {
ret = offset;
} goto done; case MWIFIEX_FW_DNLD_CMD_7:
first_cmd = true;
cmd7_before = true; break; default:
mwifiex_dbg(adapter, ERROR, "unknown dnld_cmd %d\n",
dnld_cmd);
ret = -1; goto done;
}
}
done: return ret;
}
/* * This function downloads the firmware to the card. * * Firmware is downloaded to the card in blocks. Every block download * is tested for CRC errors, and retried a number of times before * returning failure.
*/ staticint mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw)
{ int ret;
u8 *firmware = fw->fw_buf;
u32 firmware_len = fw->fw_len;
u32 offset = 0; struct sk_buff *skb;
u32 txlen, tx_blocks = 0, tries, len, val;
u32 block_retry_cnt = 0; struct pcie_service_card *card = adapter->card; conststruct mwifiex_pcie_card_reg *reg = card->pcie.reg;
if (!firmware || !firmware_len) {
mwifiex_dbg(adapter, ERROR, "No firmware image found! Terminating download\n"); return -1;
}
/* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { if (mwifiex_read_reg(adapter, reg->fw_status,
&firmware_stat))
ret = -1; else
ret = 0;
mwifiex_dbg(adapter, INFO, "Try %d if FW is ready <%d,%#x>",
tries, ret, firmware_stat);
if (ret) continue; if (firmware_stat == FIRMWARE_READY_PCIE) {
ret = 0; break;
} else {
msleep(100);
ret = -1;
}
}
return ret;
}
/* This function checks if WLAN is the winner.
*/ staticint
mwifiex_check_winner_status(struct mwifiex_adapter *adapter)
{
u32 winner = 0; int ret = 0; struct pcie_service_card *card = adapter->card;
--> --------------------
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.