diff options
author | Amitkumar Karwar <akarwar@marvell.com> | 2011-10-11 17:41:21 -0700 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-10-12 14:41:44 -0400 |
commit | d930faee141bd0a6a4873791996c5354c9a85ca7 (patch) | |
tree | c0ce085b87900d346cd3442cb1756aea060d416f /drivers | |
parent | ec205999d31bbb1208ab59c3b6d22c47cca74257 (diff) |
mwifiex: add support for Marvell pcie8766 chipset
This patch supports 88W8766P chipset with a PCIe interface.
The corresponding firmware image file is located at:
"mrvl/pcie8766_uapsta.bin"
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Ramesh Radhakrishnan <rramesh@marvell.com>
Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com>
Signed-off-by: Kiran Divekar <dkiran@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Frank Huang <frankh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/mwifiex/11n_aggr.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/cmdevt.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/fw.h | 30 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/init.c | 17 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 20 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.c | 1948 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/pcie.h | 148 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sdio.c | 19 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sdio.h | 24 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_cmd.c | 64 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_cmdresp.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_tx.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/txrx.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/util.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/wmm.c | 4 |
18 files changed, 2284 insertions, 37 deletions
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 9e63d16365e..079e5532e68 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -246,8 +246,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, tx_param.next_pkt_len = 0; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb_aggr->data, - skb_aggr->len, &tx_param); + skb_aggr, &tx_param); switch (ret) { case -EBUSY: spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index 86962920cef..8f2797aa0c6 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -19,3 +19,14 @@ config MWIFIEX_SDIO If you choose to build it as a module, it will be called mwifiex_sdio. + +config MWIFIEX_PCIE + tristate "Marvell WiFi-Ex Driver for PCIE 8766" + depends on MWIFIEX && PCI + select FW_LOADER + ---help--- + This adds support for wireless adapters based on Marvell + 8766 chipset with PCIe interface. + + If you choose to build it as a module, it will be called + mwifiex_pcie. diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index 42cb733ea33..b0257ad1bbe 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -39,3 +39,6 @@ obj-$(CONFIG_MWIFIEX) += mwifiex.o mwifiex_sdio-y += sdio.o obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o + +mwifiex_pcie-y += pcie.o +obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index d12e25d0c88..508de7f6d9c 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -94,7 +94,7 @@ mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, skb_trim(cmd_node->cmd_skb, 0); if (cmd_node->resp_skb) { - dev_kfree_skb_any(cmd_node->resp_skb); + adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); cmd_node->resp_skb = NULL; } } @@ -176,8 +176,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - cmd_node->cmd_skb->data, - cmd_node->cmd_skb->len, NULL); + cmd_node->cmd_skb, NULL); skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); @@ -238,8 +237,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - adapter->sleep_cfm->data, - adapter->sleep_cfm->len, NULL); + adapter->sleep_cfm, NULL); skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); if (ret == -1) { @@ -402,8 +400,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) adapter->event_cause = 0; adapter->event_skb = NULL; - - dev_kfree_skb_any(skb); + adapter->if_ops.event_complete(adapter, skb); return ret; } diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index f23ec72ed4f..71c61b7e74e 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -84,7 +84,8 @@ enum KEY_TYPE_ID { #define MAX_FIRMWARE_POLL_TRIES 100 -#define FIRMWARE_READY 0xfedc +#define FIRMWARE_READY_SDIO 0xfedc +#define FIRMWARE_READY_PCIE 0xfedcba00 enum MWIFIEX_802_11_PRIVACY_FILTER { MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, @@ -221,7 +222,7 @@ enum MWIFIEX_802_11_WEP_STATUS { #define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 - +#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa enum ENH_PS_MODES { EN_PS = 1, @@ -1137,6 +1138,30 @@ struct host_cmd_ds_set_bss_mode { u8 con_type; } __packed; +struct host_cmd_ds_pcie_details { + /* TX buffer descriptor ring address */ + u32 txbd_addr_lo; + u32 txbd_addr_hi; + /* TX buffer descriptor ring count */ + u32 txbd_count; + + /* RX buffer descriptor ring address */ + u32 rxbd_addr_lo; + u32 rxbd_addr_hi; + /* RX buffer descriptor ring count */ + u32 rxbd_count; + + /* Event buffer descriptor ring address */ + u32 evtbd_addr_lo; + u32 evtbd_addr_hi; + /* Event buffer descriptor ring count */ + u32 evtbd_count; + + /* Sleep cookie buffer physical address */ + u32 sleep_cookie_addr_lo; + u32 sleep_cookie_addr_hi; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -1184,6 +1209,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_rf_reg_access rf_reg; struct host_cmd_ds_pmic_reg_access pmic_reg; struct host_cmd_ds_set_bss_mode bss_mode; + struct host_cmd_ds_pcie_details pcie_host_spec; struct host_cmd_ds_802_11_eeprom_access eeprom; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index e1076b46401..0ce72fc9143 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -191,7 +191,12 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) (adapter->sleep_cfm->data); adapter->cmd_sent = false; - adapter->data_sent = true; + + if (adapter->iface_type == MWIFIEX_PCIE) + adapter->data_sent = false; + else + adapter->data_sent = true; + adapter->cmd_resp_received = false; adapter->event_received = false; adapter->data_received = false; @@ -581,11 +586,13 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *pmfw) { - int ret, winner; + int ret; u32 poll_num = 1; + adapter->winner = 0; + /* Check if firmware is already running */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num, &winner); + ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (!ret) { dev_notice(adapter->dev, "WLAN FW already running! Skip FW download\n"); @@ -594,7 +601,7 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, poll_num = MAX_FIRMWARE_POLL_TRIES; /* Check if we are the winner for downloading FW */ - if (!winner) { + if (!adapter->winner) { dev_notice(adapter->dev, "Other interface already running!" " Skip FW download\n"); @@ -612,7 +619,7 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, poll_fw: /* Check if the firmware is downloaded successfully or not */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num, NULL); + ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (ret) { dev_err(adapter->dev, "FW failed to be active in time\n"); return -1; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 277ea84a05f..849144d9c94 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -661,7 +661,7 @@ mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) */ int mwifiex_add_card(void *card, struct semaphore *sem, - struct mwifiex_if_ops *if_ops) + struct mwifiex_if_ops *if_ops, u8 iface_type) { struct mwifiex_adapter *adapter; char fmt[64]; @@ -675,6 +675,8 @@ mwifiex_add_card(void *card, struct semaphore *sem, goto err_init_sw; } + adapter->iface_type = iface_type; + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; adapter->surprise_removed = false; init_waitqueue_head(&adapter->init_wait_q); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 907ab746dc4..ec45607c5f9 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -37,6 +37,7 @@ #include "ioctl.h" #include "util.h" #include "fw.h" +#include "pcie.h" extern const char driver_version[]; @@ -107,6 +108,8 @@ enum { #define MAX_FREQUENCY_BAND_BG 2484 +#define MWIFIEX_EVENT_HEADER_LEN 4 + struct mwifiex_dbg { u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; @@ -156,6 +159,11 @@ enum MWIFIEX_PS_STATE { PS_STATE_SLEEP }; +enum mwifiex_iface_type { + MWIFIEX_SDIO, + MWIFIEX_PCIE, +}; + struct mwifiex_add_ba_param { u32 tx_win_size; u32 rx_win_size; @@ -517,27 +525,31 @@ struct cmd_ctrl_node { struct mwifiex_if_ops { int (*init_if) (struct mwifiex_adapter *); void (*cleanup_if) (struct mwifiex_adapter *); - int (*check_fw_status) (struct mwifiex_adapter *, u32, int *); + int (*check_fw_status) (struct mwifiex_adapter *, u32); int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); int (*register_dev) (struct mwifiex_adapter *); void (*unregister_dev) (struct mwifiex_adapter *); int (*enable_int) (struct mwifiex_adapter *); int (*process_int_status) (struct mwifiex_adapter *); - int (*host_to_card) (struct mwifiex_adapter *, u8, - u8 *payload, u32 pkt_len, + int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, struct mwifiex_tx_param *); int (*wakeup) (struct mwifiex_adapter *); int (*wakeup_complete) (struct mwifiex_adapter *); + /* Interface specific functions */ void (*update_mp_end_port) (struct mwifiex_adapter *, u16); void (*cleanup_mpa_buf) (struct mwifiex_adapter *); + int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); }; struct mwifiex_adapter { + u8 iface_type; struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; u8 priv_num; const struct firmware *firmware; char fw_name[32]; + int winner; struct device *dev; bool surprise_removed; u32 fw_release_number; @@ -872,7 +884,7 @@ struct mwifiex_private *mwifiex_bss_index_to_priv(struct mwifiex_adapter *adapter, u8 bss_index); int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); -int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *); +int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c new file mode 100644 index 00000000000..d34acf082d3 --- /dev/null +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -0,0 +1,1948 @@ +/* + * Marvell Wireless LAN device driver: PCIE specific handling + * + * Copyright (C) 2011, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this 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. + */ + +#include <linux/firmware.h> + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "pcie.h" + +#define PCIE_VERSION "1.0" +#define DRV_NAME "Marvell mwifiex PCIe" + +static u8 user_rmmod; + +static struct mwifiex_if_ops pcie_ops; + +static struct semaphore add_remove_card_sem; +static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter); +static int mwifiex_pcie_resume(struct pci_dev *pdev); + +/* + * This function is called after skb allocation to update + * "skb->cb" with physical address of data pointer. + */ +static phys_addr_t *mwifiex_update_sk_buff_pa(struct sk_buff *skb) +{ + phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb); + + *buf_pa = (phys_addr_t)virt_to_phys(skb->data); + + return buf_pa; +} + +/* + * This function reads sleep cookie and checks if FW is ready + */ +static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) +{ + u32 *cookie_addr; + struct pcie_service_card *card = adapter->card; + + if (card->sleep_cookie) { + cookie_addr = (u32 *)card->sleep_cookie->data; + dev_dbg(adapter->dev, "info: ACCESS_HW: sleep cookie=0x%x\n", + *cookie_addr); + if (*cookie_addr == FW_AWAKE_COOKIE) + return true; + } + + return false; +} + +/* + * 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. + */ +static int mwifiex_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pcie_service_card *card; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", + pdev->vendor, pdev->device, pdev->revision); + + card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); + if (!card) { + pr_err("%s: failed to alloc memory\n", __func__); + return -ENOMEM; + } + + card->dev = pdev; + + if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, + MWIFIEX_PCIE)) { + pr_err("%s failed\n", __func__); + kfree(card); + return -1; + } + + return 0; +} + +/* + * This function removes the interface and frees up the card structure. + */ +static void mwifiex_pcie_remove(struct pci_dev *pdev) +{ + struct pcie_service_card *card; + struct mwifiex_adapter *adapter; + int i; + + card = pci_get_drvdata(pdev); + if (!card) + return; + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + if (user_rmmod) { +#ifdef CONFIG_PM + if (adapter->is_suspended) + mwifiex_pcie_resume(pdev); +#endif + + for (i = 0; i < adapter->priv_num; i++) + if ((GET_BSS_ROLE(adapter->priv[i]) == + MWIFIEX_BSS_ROLE_STA) && + adapter->priv[i]->media_connected) + mwifiex_deauthenticate(adapter->priv[i], NULL); + + mwifiex_disable_auto_ds(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY)); + + mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_remove_card(card->adapter, &add_remove_card_sem); + kfree(card); +} + +/* + * 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. + */ +static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + int hs_actived, i; + + if (pdev) { + card = (struct pcie_service_card *) pci_get_drvdata(pdev); + if (!card || card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + hs_actived = mwifiex_enable_hs(adapter); + + /* Indicate device suspended */ + adapter->is_suspended = true; + + for (i = 0; i < adapter->priv_num; i++) + netif_carrier_off(adapter->priv[i]->netdev); + + return 0; +} + +/* + * 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. + */ +static int mwifiex_pcie_resume(struct pci_dev *pdev) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + int i; + + if (pdev) { + card = (struct pcie_service_card *) pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + dev_warn(adapter->dev, "Device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->media_connected) + netif_carrier_on(adapter->priv[i]->netdev); + + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +#define PCIE_VENDOR_ID_MARVELL (0x11ab) +#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) + +static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = { + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, mwifiex_ids); + +/* PCI Device Driver */ +static struct pci_driver __refdata mwifiex_pcie = { + .name = "mwifiex_pcie", + .id_table = mwifiex_ids, + .probe = mwifiex_pcie_probe, + .remove = mwifiex_pcie_remove, +#ifdef CONFIG_PM + /* Power Management Hooks */ + .suspend = mwifiex_pcie_suspend, + .resume = mwifiex_pcie_resume, +#endif +}; + +/* + * This function writes data into PCIE card register. + */ +static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) +{ + struct pcie_service_card *card = adapter->card; + + iowrite32(data, card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function reads data from PCIE card register. + */ +static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread32(card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function wakes up the card. + * + * A host power up command is written to the card configuration + * register to wake up the card. + */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + int i = 0; + + while (mwifiex_pcie_ok_to_access_hw(adapter)) { + i++; + udelay(10); + /* 50ms max wait */ + if (i == 50000) + break; + } + + dev_dbg(adapter->dev, "event: Wakeup device...\n"); + + /* Enable interrupts or any chip access will wakeup device */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) { + dev_warn(adapter->dev, "Enable host interrupt failed\n"); + return -1; + } + + dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n"); + adapter->ps_state = PS_STATE_AWAKE; + + return 0; +} + +/* + * This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + dev_dbg(adapter->dev, "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. + */ +static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + 0x00000000)) { + dev_warn(adapter->dev, "Disable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Simply write the mask to the register */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + HOST_INTR_MASK)) { + dev_warn(adapter->dev, "Enable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function creates buffer descriptor ring for TX + */ +static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; + int i; + phys_addr_t *buf_pa; + + /* + * 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; + card->txbd_rdptr |= MWIFIEX_BD_FLAG_ROLLOVER_IND; + + /* allocate shared memory for the BD ring and divide the same in to + several descriptors */ + card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + dev_dbg(adapter->dev, "info: txbd_ring: Allocating %d bytes\n", + card->txbd_ring_size); + card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL); + if (!card->txbd_ring_vbase) { + dev_err(adapter->dev, "Unable to allocate buffer for txbd ring.\n"); + kfree(card->txbd_ring_vbase); + return -1; + } + card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase); + + dev_dbg(adapter->dev, "info: txbd_ring - base: %p, pbase: %#x:%x," + "len: %x\n", card->txbd_ring_vbase, + (u32)card->txbd_ring_pbase, + (u32)((u64)card->txbd_ring_pbase >> 32), + card->txbd_ring_size); + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + card->txbd_ring[i] = (struct mwifiex_pcie_buf_desc *) + (card->txbd_ring_vbase + + (sizeof(struct mwifiex_pcie_buf_desc) * i)); + + /* Allocate buffer here so that firmware can DMA data from it */ + skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); + if (!skb) { + dev_err(adapter->dev, "Unable to allocate skb for TX ring.\n"); + kfree(card->txbd_ring_vbase); + return -ENOMEM; + } + buf_pa = mwifiex_update_sk_buff_pa(skb); + + skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); + dev_dbg(adapter->dev, "info: TX ring: add new skb base: %p, " + "buf_base: %p, buf_pbase: %#x:%x, " + "buf_len: %#x\n", skb, skb->data, + (u32)*buf_pa, (u32)(((u64)*buf_pa >> 32)), + skb->len); + + card->tx_buf_list[i] = skb; + card->txbd_ring[i]->paddr = *buf_pa; + card->txbd_ring[i]->len = (u16)skb->len; + card->txbd_ring[i]->flags = 0; + } + + return 0; +} + +static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (card->tx_buf_list[i]) + dev_kfree_skb_any(card->tx_buf_list[i]); + card->tx_buf_list[i] = NULL; + card->txbd_ring[i]->paddr = 0; + card->txbd_ring[i]->len = 0; + card->txbd_ring[i]->flags = 0; + card->txbd_ring[i] = NULL; + } + + kfree(card->txbd_ring_vbase); + card->txbd_ring_size = 0; + card->txbd_wrptr = 0; + card->txbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; + card->txbd_ring_vbase = NULL; + + return 0; +} + +/* + * This function creates buffer descriptor ring for RX + */ +static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; + int i; + phys_addr_t *buf_pa; + + /* + * 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 |= MWIFIEX_BD_FLAG_ROLLOVER_IND; + + card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + dev_dbg(adapter->dev, "info: rxbd_ring: Allocating %d bytes\n", + card->rxbd_ring_size); + card->rxbd_ring_vbase = kzalloc(card->rxbd_ring_size, GFP_KERNEL); + if (!card->rxbd_ring_vbase) { + dev_err(adapter->dev, "Unable to allocate buffer for " + "rxbd_ring.\n"); + return -1; + } + card->rxbd_ring_pbase = virt_to_phys(card->rxbd_ring_vbase); + + dev_dbg(adapter->dev, "info: rxbd_ring - base: %p, pbase: %#x:%x," + "len: %#x\n", card->rxbd_ring_vbase, + (u32)card->rxbd_ring_pbase, + (u32)((u64)card->rxbd_ring_pbase >> 32), + card->rxbd_ring_size); + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + card->rxbd_ring[i] = (struct mwifiex_pcie_buf_desc *) + (card->rxbd_ring_vbase + + (sizeof(struct mwifiex_pcie_buf_desc) * i)); + + /* Allocate skb here so that firmware can DMA data from it */ + skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); + if (!skb) { + dev_err(adapter->dev, "Unable to allocate skb for RX ring.\n"); + kfree(card->rxbd_ring_vbase); + return -ENOMEM; + } + buf_pa = mwifiex_update_sk_buff_pa(skb); + skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); + + dev_dbg(adapter->dev, "info: RX ring: add new skb base: %p, " + "buf_base: %p, buf_pbase: %#x:%x, " + "buf_len: %#x\n", skb, skb->data, + (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), + skb->len); + + card->rx_buf_list[i] = skb; + card->rxbd_ring[i]->paddr = *buf_pa; + card->rxbd_ring[i]->len = (u16)skb->len; + card->rxbd_ring[i]->flags = 0; + } + + return 0; +} + +/* + * This function deletes Buffer descriptor ring for RX + */ +static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (card->rx_buf_list[i]) + dev_kfree_skb_any(card->rx_buf_list[i]); + card->rx_buf_list[i] = NULL; + card->rxbd_ring[i]->paddr = 0; + card->rxbd_ring[i]->len = 0; + card->rxbd_ring[i]->flags = 0; + card->rxbd_ring[i] = NULL; + } + + kfree(card->rxbd_ring_vbase); + card->rxbd_ring_size = 0; + card->rxbd_wrptr = 0; + card->rxbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; + card->rxbd_ring_vbase = NULL; + + return 0; +} + +/* + * This function creates buffer descriptor ring for Events + */ +static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; + int i; + phys_addr_t *buf_pa; + + /* + * 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 |= MWIFIEX_BD_FLAG_ROLLOVER_IND; + + card->evtbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_EVT_BD; + dev_dbg(adapter->dev, "info: evtbd_ring: Allocating %d bytes\n", + card->evtbd_ring_size); + card->evtbd_ring_vbase = kzalloc(card->evtbd_ring_size, GFP_KERNEL); + if (!card->evtbd_ring_vbase) { + dev_err(adapter->dev, "Unable to allocate buffer. " + "Terminating download\n"); + return -1; + } + card->evtbd_ring_pbase = virt_to_phys(card->evtbd_ring_vbase); + + dev_dbg(adapter->dev, "info: CMDRSP/EVT bd_ring - base: %p, " + "pbase: %#x:%x, len: %#x\n", card->evtbd_ring_vbase, + (u32)card->evtbd_ring_pbase, + (u32)((u64)card->evtbd_ring_pbase >> 32), + card->evtbd_ring_size); + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + card->evtbd_ring[i] = (struct mwifiex_pcie_buf_desc *) + (card->evtbd_ring_vbase + + (sizeof(struct mwifiex_pcie_buf_desc) * i)); + + /* Allocate skb here so that firmware can DMA data from it */ + skb = dev_alloc_skb(MAX_EVENT_SIZE); + if (!skb) { + dev_err(adapter->dev, "Unable to allocate skb for EVENT buf.\n"); + kfree(card->evtbd_ring_vbase); + return -ENOMEM; + } + buf_pa = mwifiex_update_sk_buff_pa(skb); + skb_put(skb, MAX_EVENT_SIZE); + + dev_dbg(adapter->dev, "info: Evt ring: add new skb. base: %p, " + "buf_base: %p, buf_pbase: %#x:%x, " + "buf_len: %#x\n", skb, skb->data, + (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), + skb->len); + + card->evt_buf_list[i] = skb; + card->evtbd_ring[i]->paddr = *buf_pa; + card->evtbd_ring[i]->len = (u16)skb->len; + card->evtbd_ring[i]->flags = 0; + } + + return 0; +} + +/* + * This function deletes Buffer descriptor ring for Events + */ +static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + if (card->evt_buf_list[i]) + dev_kfree_skb_any(card->evt_buf_list[i]); + card->evt_buf_list[i] = NULL; + card->evtbd_ring[i]->paddr = 0; + card->evtbd_ring[i]->len = 0; + card->evtbd_ring[i]->flags = 0; + card->evtbd_ring[i] = NULL; + } < |