aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmitkumar Karwar <akarwar@marvell.com>2011-10-11 17:41:21 -0700
committerJohn W. Linville <linville@tuxdriver.com>2011-10-12 14:41:44 -0400
commitd930faee141bd0a6a4873791996c5354c9a85ca7 (patch)
treec0ce085b87900d346cd3442cb1756aea060d416f
parentec205999d31bbb1208ab59c3b6d22c47cca74257 (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>
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c3
-rw-r--r--drivers/net/wireless/mwifiex/Kconfig11
-rw-r--r--drivers/net/wireless/mwifiex/Makefile3
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c11
-rw-r--r--drivers/net/wireless/mwifiex/fw.h30
-rw-r--r--drivers/net/wireless/mwifiex/init.c17
-rw-r--r--drivers/net/wireless/mwifiex/main.c4
-rw-r--r--drivers/net/wireless/mwifiex/main.h20
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c1948
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h148
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c19
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h24
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c64
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c2
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c2
-rw-r--r--drivers/net/wireless/mwifiex/util.h9
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c4
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;
+ }
+
+ kfree(card->evtbd_ring_vbase);
+ card->evtbd_wrptr = 0;
+ card->evtbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND;
+ card->evtbd_ring_size = 0;
+ card->evtbd_ring_vbase = NULL;
+
+ return 0;
+}