|
@@ -19,8 +19,11 @@
|
|
|
#include <linux/mmc/core.h>
|
|
|
#include <linux/mmc/sdio_func.h>
|
|
|
#include <linux/mmc/sdio_ids.h>
|
|
|
+#include <linux/mmc/card.h>
|
|
|
#include <linux/suspend.h>
|
|
|
#include <linux/errno.h>
|
|
|
+#include <linux/sched.h> /* request_irq() */
|
|
|
+#include <net/cfg80211.h>
|
|
|
|
|
|
#include <defs.h>
|
|
|
#include <brcm_hw_ids.h>
|
|
@@ -30,6 +33,8 @@
|
|
|
#include "bcmsdbus.h"
|
|
|
#include "dngl_stats.h"
|
|
|
#include "dhd.h"
|
|
|
+#include "dhd_dbg.h"
|
|
|
+#include "wl_cfg80211.h"
|
|
|
|
|
|
#define BLOCK_SIZE_64 64
|
|
|
#define BLOCK_SIZE_512 512
|
|
@@ -41,20 +46,95 @@
|
|
|
|
|
|
#define CLIENT_INTR 0x100 /* Get rid of this! */
|
|
|
|
|
|
+#if !defined(SDIO_VENDOR_ID_BROADCOM)
|
|
|
+#define SDIO_VENDOR_ID_BROADCOM 0x02d0
|
|
|
+#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */
|
|
|
+
|
|
|
+#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000
|
|
|
+
|
|
|
+#define DMA_ALIGN_MASK 0x03
|
|
|
+
|
|
|
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
|
|
|
+#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */
|
|
|
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
|
|
|
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
|
|
|
+#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493
|
|
|
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
|
|
|
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
|
|
|
+#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
|
|
|
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
|
|
|
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
|
|
|
+#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319
|
|
|
+#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
|
|
|
+
|
|
|
+struct sdos_info {
|
|
|
+ struct sdioh_info *sd;
|
|
|
+ spinlock_t lock;
|
|
|
+};
|
|
|
+
|
|
|
static void brcmf_sdioh_irqhandler(struct sdio_func *func);
|
|
|
static void brcmf_sdioh_irqhandler_f2(struct sdio_func *func);
|
|
|
static int brcmf_sdioh_get_cisaddr(struct sdioh_info *sd, u32 regaddr);
|
|
|
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
|
|
|
+ const struct sdio_device_id *id);
|
|
|
+static void brcmf_ops_sdio_remove(struct sdio_func *func);
|
|
|
+
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static int brcmf_sdio_suspend(struct device *dev);
|
|
|
+static int brcmf_sdio_resume(struct device *dev);
|
|
|
+#endif /* CONFIG_PM */
|
|
|
|
|
|
uint sd_f2_blocksize = 512; /* Default blocksize */
|
|
|
|
|
|
uint sd_msglevel = 0x01;
|
|
|
+
|
|
|
+/* module param defaults */
|
|
|
+static int clockoverride;
|
|
|
+
|
|
|
+module_param(clockoverride, int, 0644);
|
|
|
+MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
|
|
|
+
|
|
|
+struct brcmf_sdmmc_instance *gInstance;
|
|
|
+
|
|
|
+struct device sdmmc_dev;
|
|
|
+
|
|
|
+/* devices we support, null terminated */
|
|
|
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
|
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT)},
|
|
|
+ {SDIO_DEVICE
|
|
|
+ (SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)},
|
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325)},
|
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
|
|
|
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319)},
|
|
|
+ { /* end: all zeroes */ },
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
|
|
|
+ .suspend = brcmf_sdio_suspend,
|
|
|
+ .resume = brcmf_sdio_resume,
|
|
|
+};
|
|
|
+#endif /* CONFIG_PM */
|
|
|
+
|
|
|
+static struct sdio_driver brcmf_sdmmc_driver = {
|
|
|
+ .probe = brcmf_ops_sdio_probe,
|
|
|
+ .remove = brcmf_ops_sdio_remove,
|
|
|
+ .name = "brcmfmac",
|
|
|
+ .id_table = brcmf_sdmmc_ids,
|
|
|
+#ifdef CONFIG_PM
|
|
|
+ .drv = {
|
|
|
+ .pm = &brcmf_sdio_pm_ops,
|
|
|
+ },
|
|
|
+#endif /* CONFIG_PM */
|
|
|
+};
|
|
|
+
|
|
|
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
|
|
|
+
|
|
|
BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
|
|
|
BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
|
|
|
BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
|
|
|
BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
|
|
|
|
|
|
-#define DMA_ALIGN_MASK 0x03
|
|
|
-
|
|
|
static int
|
|
|
brcmf_sdioh_card_regread(struct sdioh_info *sd, int func, u32 regaddr,
|
|
|
int regsize, u32 *data);
|
|
@@ -904,3 +984,165 @@ static void brcmf_sdioh_irqhandler_f2(struct sdio_func *func)
|
|
|
|
|
|
sd = gInstance->sd;
|
|
|
}
|
|
|
+
|
|
|
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
|
|
|
+ const struct sdio_device_id *id)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ static struct sdio_func sdio_func_0;
|
|
|
+ sd_trace(("sdio_probe: %s Enter\n", __func__));
|
|
|
+ sd_trace(("sdio_probe: func->class=%x\n", func->class));
|
|
|
+ sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
|
|
|
+ sd_trace(("sdio_device: 0x%04x\n", func->device));
|
|
|
+ sd_trace(("Function#: 0x%04x\n", func->num));
|
|
|
+
|
|
|
+ if (func->num == 1) {
|
|
|
+ sdio_func_0.num = 0;
|
|
|
+ sdio_func_0.card = func->card;
|
|
|
+ gInstance->func[0] = &sdio_func_0;
|
|
|
+ if (func->device == 0x4) { /* 4318 */
|
|
|
+ gInstance->func[2] = NULL;
|
|
|
+ sd_trace(("NIC found, calling brcmf_sdio_probe...\n"));
|
|
|
+ ret = brcmf_sdio_probe(&sdmmc_dev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gInstance->func[func->num] = func;
|
|
|
+
|
|
|
+ if (func->num == 2) {
|
|
|
+ brcmf_cfg80211_sdio_func(func);
|
|
|
+ sd_trace(("F2 found, calling brcmf_sdio_probe...\n"));
|
|
|
+ ret = brcmf_sdio_probe(&sdmmc_dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
|
|
|
+{
|
|
|
+ sd_trace(("%s Enter\n", __func__));
|
|
|
+ sd_info(("func->class=%x\n", func->class));
|
|
|
+ sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
|
|
|
+ sd_info(("sdio_device: 0x%04x\n", func->device));
|
|
|
+ sd_info(("Function#: 0x%04x\n", func->num));
|
|
|
+
|
|
|
+ if (func->num == 2) {
|
|
|
+ sd_trace(("F2 found, calling brcmf_sdio_remove...\n"));
|
|
|
+ brcmf_sdio_remove(&sdmmc_dev);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static int brcmf_sdio_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ mmc_pm_flag_t sdio_flags;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ sd_trace(("%s\n", __func__));
|
|
|
+
|
|
|
+ sdio_flags = sdio_get_host_pm_caps(gInstance->func[1]);
|
|
|
+ if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
|
|
|
+ sd_err(("Host can't keep power while suspended\n"));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = sdio_set_host_pm_flags(gInstance->func[1], MMC_PM_KEEP_POWER);
|
|
|
+ if (ret) {
|
|
|
+ sd_err(("Failed to set pm_flags\n"));
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ brcmf_sdio_wdtmr_enable(false);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int brcmf_sdio_resume(struct device *dev)
|
|
|
+{
|
|
|
+ brcmf_sdio_wdtmr_enable(true);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif /* CONFIG_PM */
|
|
|
+
|
|
|
+int brcmf_sdioh_osinit(struct sdioh_info *sd)
|
|
|
+{
|
|
|
+ struct sdos_info *sdos;
|
|
|
+
|
|
|
+ sdos = kmalloc(sizeof(struct sdos_info), GFP_ATOMIC);
|
|
|
+ sd->sdos_info = (void *)sdos;
|
|
|
+ if (sdos == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ sdos->sd = sd;
|
|
|
+ spin_lock_init(&sdos->lock);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void brcmf_sdioh_osfree(struct sdioh_info *sd)
|
|
|
+{
|
|
|
+ struct sdos_info *sdos;
|
|
|
+
|
|
|
+ sdos = (struct sdos_info *)sd->sdos_info;
|
|
|
+ kfree(sdos);
|
|
|
+}
|
|
|
+
|
|
|
+/* Interrupt enable/disable */
|
|
|
+int brcmf_sdioh_interrupt_set(struct sdioh_info *sd, bool enable)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ struct sdos_info *sdos;
|
|
|
+
|
|
|
+ sd_trace(("%s: %s\n", __func__, enable ? "Enabling" : "Disabling"));
|
|
|
+
|
|
|
+ sdos = (struct sdos_info *)sd->sdos_info;
|
|
|
+
|
|
|
+ if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
|
|
|
+ sd_err(("%s: no handler registered, will not enable\n",
|
|
|
+ __func__));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Ensure atomicity for enable/disable calls */
|
|
|
+ spin_lock_irqsave(&sdos->lock, flags);
|
|
|
+
|
|
|
+ sd->client_intr_enabled = enable;
|
|
|
+ if (enable)
|
|
|
+ brcmf_sdioh_dev_intr_on(sd);
|
|
|
+ else
|
|
|
+ brcmf_sdioh_dev_intr_off(sd);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&sdos->lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * module init
|
|
|
+*/
|
|
|
+int brcmf_sdio_function_init(void)
|
|
|
+{
|
|
|
+ int error = 0;
|
|
|
+ sd_trace(("brcmf_sdio_function_init: %s Enter\n", __func__));
|
|
|
+
|
|
|
+ gInstance = kzalloc(sizeof(struct brcmf_sdmmc_instance), GFP_KERNEL);
|
|
|
+ if (!gInstance)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ memset(&sdmmc_dev, 0, sizeof(sdmmc_dev));
|
|
|
+ error = sdio_register_driver(&brcmf_sdmmc_driver);
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * module cleanup
|
|
|
+*/
|
|
|
+void brcmf_sdio_function_cleanup(void)
|
|
|
+{
|
|
|
+ sd_trace(("%s Enter\n", __func__));
|
|
|
+
|
|
|
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
|
|
|
+
|
|
|
+ kfree(gInstance);
|
|
|
+}
|