/*
* Linux DHD Bus Module for PCIE
*
* Copyright (C) 1999-2015, Broadcom Corporation
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
* $Id: dhd_pcie_linux.c 530336 2015-01-29 22:52:35Z $
*/
/* include files */
#include <typedefs.h>
#include <bcmutils.h>
#include <bcmdevs.h>
#include <siutils.h>
#include <hndsoc.h>
#include <hndpmu.h>
#include <sbchipc.h>
#if defined(DHD_DEBUG)
#include <hnd_armtrap.h>
#include <hnd_cons.h>
#endif /* defined(DHD_DEBUG) */
#include <dngl_stats.h>
#include <pcie_core.h>
#include <dhd.h>
#include <dhd_bus.h>
#include <dhd_proto.h>
#include <dhd_dbg.h>
#include <dhdioctl.h>
#include <bcmmsgbuf.h>
#include <pcicfg.h>
#include <dhd_pcie.h>
#include <dhd_linux.h>
#ifdef CONFIG_ARCH_MSM
#ifdef CONFIG_ARCH_MSM8994
#include <linux/msm_pcie.h>
#else
#include <mach/msm_pcie.h>
#endif
#endif /* CONFIG_ARCH_MSM */
#ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
#include "dhd_custom_sysfs_tegra.h"
#endif
#define PCI_CFG_RETRY 10
#define OS_HANDLE_MAGIC 0x1234abcd /* Magic # to recognize osh */
#define BCM_MEM_FILENAME_LEN 24 /* Mem. filename length */
#define OSL_PKTTAG_CLEAR(p) \
do { \
struct sk_buff *s = (struct sk_buff *)(p); \
ASSERT(OSL_PKTTAG_SZ == 32); \
*(uint32 *)(&s->cb[0]) = 0; *(uint32 *)(&s->cb[4]) = 0; \
*(uint32 *)(&s->cb[8]) = 0; *(uint32 *)(&s->cb[12]) = 0; \
*(uint32 *)(&s->cb[16]) = 0; *(uint32 *)(&s->cb[20]) = 0; \
*(uint32 *)(&s->cb[24]) = 0; *(uint32 *)(&s->cb[28]) = 0; \
} while (0)
/* user defined data structures */
typedef struct dhd_pc_res {
uint32 bar0_size;
void* bar0_addr;
uint32 bar1_size;
void* bar1_addr;
} pci_config_res, *pPci_config_res;
typedef bool (*dhdpcie_cb_fn_t)(void *);
typedef struct dhdpcie_info
{
dhd_bus_t *bus;
osl_t *osh;
struct pci_dev *dev; /* pci device handle */
volatile char *regs; /* pci device memory va */
volatile char *tcm; /* pci device memory va */
uint32 tcm_size; /* pci device memory size */
struct pcos_info *pcos_info;
uint16 last_intrstatus; /* to cache intrstatus */
int irq;
char pciname[32];
struct pci_saved_state* default_state;
struct pci_saved_state* state;
#ifdef BCMPCIE_OOB_HOST_WAKE
void *os_cxt; /* Pointer to per-OS private data */
#endif /* BCMPCIE_OOB_HOST_WAKE */
} dhdpcie_info_t;
struct pcos_info {
dhdpcie_info_t *pc;
spinlock_t lock;
wait_queue_head_t intr_wait_queue;
struct timer_list tuning_timer;
int tuning_timer_exp;
atomic_t timer_enab;
struct tasklet_struct tuning_tasklet;
};
#ifdef BCMPCIE_OOB_HOST_WAKE
typedef struct dhdpcie_os_info {
int oob_irq_num; /* valid when hardware or software oob in use */
unsigned long oob_irq_flags; /* valid when hardware or software oob in use */
bool oob_irq_registered;
bool oob_irq_enabled;
bool oob_irq_wake_enabled;
spinlock_t oob_irq_spinlock;
void *dev; /* handle to the underlying device */
} dhdpcie_os_info_t;
#endif /* BCMPCIE_OOB_HOST_WAKE */
/* function declarations */
static int __devinit
dhdpcie_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
static void __devexit
dhdpcie_pci_remove(struct pci_dev *pdev);
static int dhdpcie_init(struct pci_dev *pdev);
static irqreturn_t dhdpcie_isr(int irq, void *arg);
/* OS Routine functions for PCI suspend/resume */
static int dhdpcie_pci_suspend(struct pci_dev *dev, pm_message_t state);
static int dhdpcie_set_suspend_resume(struct pci_dev *dev, bool state);
static int dhdpcie_pci_resume(struct pci_dev *dev);
static int dhdpcie_resume_dev(struct pci_dev *dev);
static int dhdpcie_suspend_dev(struct pci_dev *dev);
static struct pci_device_id dhdpcie_pci_devid[] __devinitdata = {
{ vendor: 0x14e4,
device: PCI_ANY_ID,
subvendor: PCI_ANY_ID,
subdevice: PCI_ANY_ID,
class: PCI_CLASS_NETWORK_OTHER << 8,
class_mask: 0xffff00,
driver_data: 0,
},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, dhdpcie_pci_devid);
static struct pci_driver dhdpcie_driver = {
node: {},
name: "pcieh",
id_table: dhdpcie_pci_devid,
probe: dhdpcie_pci_probe,
remove: dhdpcie_pci_remove,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
save_state: NULL,
#endif
suspend: dhdpcie_pci_suspend,
resume: dhdpcie_pci_resume,
};
int dhdpcie_init_succeeded = FALSE;
static int dhdpcie_set_suspend_resume(struct pci_dev *pdev, bool state)
{
int ret = 0;
dhdpcie_info_t *pch = pci_get_drvdata(pdev);
dhd_bus_t *bus = NULL;
if (pch) {
bus = pch->bus;
}
/* When firmware is not loaded do the PCI bus */
/* suspend/resume only */
if (bus && (bus->dhd->busstate == DHD_BUS_DOWN) &&
#ifdef CONFIG_MACH_UNIVERSAL5
|