summaryrefslogtreecommitdiffstats
path: root/drivers/staging/csr/sdio_mmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/csr/sdio_mmc.c')
-rw-r--r--drivers/staging/csr/sdio_mmc.c1288
1 files changed, 0 insertions, 1288 deletions
diff --git a/drivers/staging/csr/sdio_mmc.c b/drivers/staging/csr/sdio_mmc.c
deleted file mode 100644
index 2b503c23efae..000000000000
--- a/drivers/staging/csr/sdio_mmc.c
+++ /dev/null
@@ -1,1288 +0,0 @@
-/*
- * ---------------------------------------------------------------------------
- *
- * FILE: sdio_mmc.c
- *
- * PURPOSE: SDIO driver interface for generic MMC stack.
- *
- * Copyright (C) 2008-2009 by Cambridge Silicon Radio Ltd.
- *
- * ---------------------------------------------------------------------------
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
-#include <linux/mmc/core.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/sdio.h>
-#include <linux/suspend.h>
-
-#include "unifi_priv.h"
-
-#ifdef ANDROID_BUILD
-struct wake_lock unifi_sdio_wake_lock; /* wakelock to prevent suspend while resuming */
-#endif
-
-static CsrSdioFunctionDriver *sdio_func_drv;
-
-#ifdef CONFIG_PM
-static int uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *ptr);
-#endif
-
-/*
- * We need to keep track of the power on/off because we can not call
- * mmc_power_restore_host() when the card is already powered.
- * Even then, we need to patch the MMC driver to add a power_restore handler
- * in the mmc_sdio_ops structure. If the MMC driver before 2.6.37 is not patched,
- * mmc_power_save_host() and mmc_power_restore_host() are no-ops in the kernel,
- * returning immediately (at least on x86).
- */
-static int card_is_powered = 1;
-
-/* MMC uses ENOMEDIUM to indicate card gone away */
-
-static CsrResult
-ConvertSdioToCsrSdioResult(int r)
-{
- CsrResult csrResult = CSR_RESULT_FAILURE;
-
- switch (r) {
- case 0:
- csrResult = CSR_RESULT_SUCCESS;
- break;
- case -EIO:
- case -EILSEQ:
- csrResult = CSR_SDIO_RESULT_CRC_ERROR;
- break;
- /* Timeout errors */
- case -ETIMEDOUT:
- case -EBUSY:
- csrResult = CSR_SDIO_RESULT_TIMEOUT;
- break;
- case -ENODEV:
- case -ENOMEDIUM:
- csrResult = CSR_SDIO_RESULT_NO_DEVICE;
- break;
- case -EINVAL:
- csrResult = CSR_SDIO_RESULT_INVALID_VALUE;
- break;
- case -ENOMEM:
- case -ENOSYS:
- case -ERANGE:
- case -ENXIO:
- csrResult = CSR_RESULT_FAILURE;
- break;
- default:
- unifi_warning(NULL, "Unrecognised SDIO error code: %d\n", r);
- break;
- }
-
- return csrResult;
-}
-
-
-static int
-csr_io_rw_direct(struct mmc_card *card, int write, uint8_t fn,
- uint32_t addr, uint8_t in, uint8_t* out)
-{
- struct mmc_command cmd;
- int err;
-
- BUG_ON(!card);
- BUG_ON(fn > 7);
-
- memset(&cmd, 0, sizeof(struct mmc_command));
-
- cmd.opcode = SD_IO_RW_DIRECT;
- cmd.arg = write ? 0x80000000 : 0x00000000;
- cmd.arg |= fn << 28;
- cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
- cmd.arg |= addr << 9;
- cmd.arg |= in;
- cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
-
- err = mmc_wait_for_cmd(card->host, &cmd, 0);
- if (err)
- return err;
-
- /* this function is not exported, so we will need to sort it out here
- * for now, lets hard code it to sdio */
- if (0) {
- /* old arg (mmc_host_is_spi(card->host)) { */
- /* host driver already reported errors */
- } else {
- if (cmd.resp[0] & R5_ERROR) {
- printk(KERN_ERR "%s: r5 error 0x%02x\n",
- __FUNCTION__, cmd.resp[0]);
- return -EIO;
- }
- if (cmd.resp[0] & R5_FUNCTION_NUMBER)
- return -EINVAL;
- if (cmd.resp[0] & R5_OUT_OF_RANGE)
- return -ERANGE;
- }
-
- if (out) {
- if (0) { /* old argument (mmc_host_is_spi(card->host)) */
- *out = (cmd.resp[0] >> 8) & 0xFF;
- }
- else {
- *out = cmd.resp[0] & 0xFF;
- }
- }
-
- return CSR_RESULT_SUCCESS;
-}
-
-
-CsrResult
-CsrSdioRead8(CsrSdioFunction *function, u32 address, u8 *data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
- _sdio_claim_host(func);
- *data = sdio_readb(func, address, &err);
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioRead8() */
-
-CsrResult
-CsrSdioWrite8(CsrSdioFunction *function, u32 address, u8 data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
- _sdio_claim_host(func);
- sdio_writeb(func, data, address, &err);
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioWrite8() */
-
-CsrResult
-CsrSdioRead16(CsrSdioFunction *function, u32 address, u16 *data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
- uint8_t b0, b1;
-
- _sdio_claim_host(func);
- b0 = sdio_readb(func, address, &err);
- if (err) {
- _sdio_release_host(func);
- return ConvertSdioToCsrSdioResult(err);
- }
-
- b1 = sdio_readb(func, address+1, &err);
- if (err) {
- _sdio_release_host(func);
- return ConvertSdioToCsrSdioResult(err);
- }
- _sdio_release_host(func);
-
- *data = ((uint16_t)b1 << 8) | b0;
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioRead16() */
-
-
-CsrResult
-CsrSdioWrite16(CsrSdioFunction *function, u32 address, u16 data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
- uint8_t b0, b1;
-
- _sdio_claim_host(func);
- b1 = (data >> 8) & 0xFF;
- sdio_writeb(func, b1, address+1, &err);
- if (err) {
- _sdio_release_host(func);
- return ConvertSdioToCsrSdioResult(err);
- }
-
- b0 = data & 0xFF;
- sdio_writeb(func, b0, address, &err);
- if (err) {
- _sdio_release_host(func);
- return ConvertSdioToCsrSdioResult(err);
- }
-
- _sdio_release_host(func);
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioWrite16() */
-
-
-CsrResult
-CsrSdioF0Read8(CsrSdioFunction *function, u32 address, u8 *data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
- _sdio_claim_host(func);
-#ifdef MMC_QUIRK_LENIENT_FN0
- *data = sdio_f0_readb(func, address, &err);
-#else
- err = csr_io_rw_direct(func->card, 0, 0, address, 0, data);
-#endif
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioF0Read8() */
-
-CsrResult
-CsrSdioF0Write8(CsrSdioFunction *function, u32 address, u8 data)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
- _sdio_claim_host(func);
-#ifdef MMC_QUIRK_LENIENT_FN0
- sdio_f0_writeb(func, data, address, &err);
-#else
- err = csr_io_rw_direct(func->card, 1, 0, address, data, NULL);
-#endif
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioF0Write8() */
-
-
-CsrResult
-CsrSdioRead(CsrSdioFunction *function, u32 address, void *data, u32 length)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
-
- _sdio_claim_host(func);
- err = sdio_readsb(func, data, address, length);
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioRead() */
-
-CsrResult
-CsrSdioWrite(CsrSdioFunction *function, u32 address, const void *data, u32 length)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
-
- _sdio_claim_host(func);
- err = sdio_writesb(func, address, (void*)data, length);
- _sdio_release_host(func);
-
- if (err) {
- return ConvertSdioToCsrSdioResult(err);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioWrite() */
-
-
-static int
-csr_sdio_enable_hs(struct mmc_card *card)
-{
- int ret;
- u8 speed;
-
- if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) {
- /* We've asked for HS clock rates, but controller doesn't
- * claim to support it. We should limit the clock
- * to 25MHz via module parameter.
- */
- printk(KERN_INFO "unifi: request HS but not MMC_CAP_SD_HIGHSPEED");
- return 0;
- }
-
- if (!card->cccr.high_speed)
- return 0;
-
-#if 1
- ret = csr_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
- if (ret)
- return ret;
-
- speed |= SDIO_SPEED_EHS;
-#else
- /* Optimisation: Eliminate read by always assuming SHS and that reserved bits can be zero */
- speed = SDIO_SPEED_EHS | SDIO_SPEED_SHS;
-#endif
-
- ret = csr_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
- if (ret)
- return ret;
-
- mmc_card_set_highspeed(card);
- card->host->ios.timing = MMC_TIMING_SD_HS;
- card->host->ops->set_ios(card->host, &card->host->ios);
-
- return 0;
-}
-
-static int
-csr_sdio_disable_hs(struct mmc_card *card)
-{
- int ret;
- u8 speed;
-
- if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
- return 0;
-
- if (!card->cccr.high_speed)
- return 0;
-#if 1
- ret = csr_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
- if (ret)
- return ret;
-
- speed &= ~SDIO_SPEED_EHS;
-#else
- /* Optimisation: Eliminate read by always assuming SHS and that reserved bits can be zero */
- speed = SDIO_SPEED_SHS; /* clear SDIO_SPEED_EHS */
-#endif
-
- ret = csr_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
- if (ret)
- return ret;
-
- card->state &= ~MMC_STATE_HIGHSPEED;
- card->host->ios.timing = MMC_TIMING_LEGACY;
- card->host->ops->set_ios(card->host, &card->host->ios);
-
- return 0;
-}
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioMaxBusClockFrequencySet
- *
- * Set the maximum SDIO bus clock speed to use.
- *
- * Arguments:
- * sdio SDIO context pointer
- * maxFrequency maximum clock speed in Hz
- *
- * Returns:
- * an error code.
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioMaxBusClockFrequencySet(CsrSdioFunction *function, u32 maxFrequency)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- struct mmc_host *host = func->card->host;
- struct mmc_ios *ios = &host->ios;
- unsigned int max_hz;
- int err;
- u32 max_khz = maxFrequency/1000;
-
- if (!max_khz || max_khz > sdio_clock) {
- max_khz = sdio_clock;
- }
-
- _sdio_claim_host(func);
- max_hz = 1000 * max_khz;
- if (max_hz > host->f_max) {
- max_hz = host->f_max;
- }
-
- if (max_hz > 25000000) {
- err = csr_sdio_enable_hs(func->card);
- } else {
- err = csr_sdio_disable_hs(func->card);
- }
- if (err) {
- printk(KERN_ERR "SDIO warning: Failed to configure SDIO clock mode\n");
- _sdio_release_host(func);
- return CSR_RESULT_SUCCESS;
- }
-
- ios->clock = max_hz;
- host->ops->set_ios(host, ios);
-
- _sdio_release_host(func);
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioMaxBusClockFrequencySet() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioInterruptEnable
- * CsrSdioInterruptDisable
- *
- * Enable or disable the SDIO interrupt.
- * The driver disables the SDIO interrupt until the i/o thread can
- * process it.
- * The SDIO interrupt can be disabled by modifying the SDIO_INT_ENABLE
- * register in the Card Common Control Register block, but this requires
- * two CMD52 operations. A better solution is to mask the interrupt at
- * the host controller.
- *
- * Arguments:
- * sdio SDIO context pointer
- *
- * Returns:
- * Zero on success or a UniFi driver error code.
- *
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioInterruptEnable(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
-#ifdef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD
- sdio_unblock_card_irq(func);
-#else
- _sdio_claim_host(func);
- /* Write the Int Enable in CCCR block */
-#ifdef MMC_QUIRK_LENIENT_FN0
- sdio_f0_writeb(func, 0x3, SDIO_CCCR_IENx, &err);
-#else
- err = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x03, NULL);
-#endif
- _sdio_release_host(func);
-
- if (err) {
- printk(KERN_ERR "unifi: %s: error %d writing IENx\n", __FUNCTION__, err);
- return ConvertSdioToCsrSdioResult(err);
- }
-#endif
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioInterruptEnable() */
-
-CsrResult
-CsrSdioInterruptDisable(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err = 0;
-
-#ifdef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD
- sdio_block_card_irq(func);
-#else
- _sdio_claim_host(func);
- /* Write the Int Enable in CCCR block */
-#ifdef MMC_QUIRK_LENIENT_FN0
- sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &err);
-#else
- err = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x00, NULL);
-#endif
- _sdio_release_host(func);
-
- if (err) {
- printk(KERN_ERR "unifi: %s: error %d writing IENx\n", __FUNCTION__, err);
- return ConvertSdioToCsrSdioResult(err);
- }
-#endif
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioInterruptDisable() */
-
-
-void CsrSdioInterruptAcknowledge(CsrSdioFunction *function)
-{
-}
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioFunctionEnable
- *
- * Enable i/o on function 1.
- *
- * Arguments:
- * sdio SDIO context pointer
- *
- * Returns:
- * UniFi driver error code.
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioFunctionEnable(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
-
- /* Enable UniFi function 1 (the 802.11 part). */
- _sdio_claim_host(func);
- err = sdio_enable_func(func);
- _sdio_release_host(func);
- if (err) {
- unifi_error(NULL, "Failed to enable SDIO function %d\n", func->num);
- }
-
- return ConvertSdioToCsrSdioResult(err);
-} /* CsrSdioFunctionEnable() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioFunctionDisable
- *
- * Enable i/o on function 1.
- *
- * Arguments:
- * sdio SDIO context pointer
- *
- * Returns:
- * UniFi driver error code.
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioFunctionDisable(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int err;
-
- /* Disable UniFi function 1 (the 802.11 part). */
- _sdio_claim_host(func);
- err = sdio_disable_func(func);
- _sdio_release_host(func);
- if (err) {
- unifi_error(NULL, "Failed to disable SDIO function %d\n", func->num);
- }
-
- return ConvertSdioToCsrSdioResult(err);
-} /* CsrSdioFunctionDisable() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioFunctionActive
- *
- * No-op as the bus goes to an active state at the start of every
- * command.
- *
- * Arguments:
- * sdio SDIO context pointer
- * ---------------------------------------------------------------------------
- */
-void
-CsrSdioFunctionActive(CsrSdioFunction *function)
-{
-} /* CsrSdioFunctionActive() */
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioFunctionIdle
- *
- * Set the function as idle.
- *
- * Arguments:
- * sdio SDIO context pointer
- * ---------------------------------------------------------------------------
- */
-void
-CsrSdioFunctionIdle(CsrSdioFunction *function)
-{
-} /* CsrSdioFunctionIdle() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioPowerOn
- *
- * Power on UniFi.
- *
- * Arguments:
- * sdio SDIO context pointer
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioPowerOn(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- struct mmc_host *host = func->card->host;
-
- _sdio_claim_host(func);
- if (!card_is_powered) {
- mmc_power_restore_host(host);
- card_is_powered = 1;
- } else {
- printk(KERN_INFO "SDIO: Skip power on; card is already powered.\n");
- }
- _sdio_release_host(func);
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioPowerOn() */
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioPowerOff
- *
- * Power off UniFi.
- *
- * Arguments:
- * sdio SDIO context pointer
- * ---------------------------------------------------------------------------
- */
-void
-CsrSdioPowerOff(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- struct mmc_host *host = func->card->host;
-
- _sdio_claim_host(func);
- if (card_is_powered) {
- mmc_power_save_host(host);
- card_is_powered = 0;
- } else {
- printk(KERN_INFO "SDIO: Skip power off; card is already powered off.\n");
- }
- _sdio_release_host(func);
-} /* CsrSdioPowerOff() */
-
-
-static int
-sdio_set_block_size_ignore_first_error(struct sdio_func *func, unsigned blksz)
-{
- int ret;
-
- if (blksz > func->card->host->max_blk_size)
- return -EINVAL;
-
- if (blksz == 0) {
- blksz = min(func->max_blksize, func->card->host->max_blk_size);
- blksz = min(blksz, 512u);
- }
-
- /*
- * Ignore -ERANGE (OUT_OF_RANGE in R5) on the first byte as
- * the block size may be invalid until both bytes are written.
- */
- ret = csr_io_rw_direct(func->card, 1, 0,
- SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
- blksz & 0xff, NULL);
- if (ret && ret != -ERANGE)
- return ret;
- ret = csr_io_rw_direct(func->card, 1, 0,
- SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
- (blksz >> 8) & 0xff, NULL);
- if (ret)
- return ret;
- func->cur_blksize = blksz;
-
- return 0;
-}
-
-CsrResult
-CsrSdioBlockSizeSet(CsrSdioFunction *function, u16 blockSize)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int r = 0;
-
- /* Module parameter overrides */
- if (sdio_block_size > -1) {
- blockSize = sdio_block_size;
- }
-
- unifi_trace(NULL, UDBG1, "Set SDIO function block size to %d\n",
- blockSize);
-
- _sdio_claim_host(func);
- r = sdio_set_block_size(func, blockSize);
- _sdio_release_host(func);
-
- /*
- * The MMC driver for kernels prior to 2.6.32 may fail this request
- * with -ERANGE. In this case use our workaround.
- */
- if (r == -ERANGE) {
- _sdio_claim_host(func);
- r = sdio_set_block_size_ignore_first_error(func, blockSize);
- _sdio_release_host(func);
- }
- if (r) {
- unifi_error(NULL, "Error %d setting block size\n", r);
- }
-
- /* Determine the achieved block size to pass to the core */
- function->blockSize = func->cur_blksize;
-
- return ConvertSdioToCsrSdioResult(r);
-} /* CsrSdioBlockSizeSet() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioHardReset
- *
- * Hard Resets UniFi is possible.
- *
- * Arguments:
- * sdio SDIO context pointer
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioHardReset(CsrSdioFunction *function)
-{
- return CSR_RESULT_FAILURE;
-} /* CsrSdioHardReset() */
-
-
-
-/*
- * ---------------------------------------------------------------------------
- * uf_glue_sdio_int_handler
- *
- * Interrupt callback function for SDIO interrupts.
- * This is called in kernel context (i.e. not interrupt context).
- *
- * Arguments:
- * func SDIO context pointer
- *
- * Returns:
- * None.
- *
- * Note: Called with host already claimed.
- * ---------------------------------------------------------------------------
- */
-static void
-uf_glue_sdio_int_handler(struct sdio_func *func)
-{
- CsrSdioFunction *sdio_ctx;
- CsrSdioInterruptDsrCallback func_dsr_callback;
- int r;
-
- sdio_ctx = sdio_get_drvdata(func);
- if (!sdio_ctx) {
- return;
- }
-
-#ifndef CSR_CONFIG_MMC_INT_BYPASS_KSOFTIRQD
- /*
- * Normally, we are not allowed to do any SDIO commands here.
- * However, this is called in a thread context and with the SDIO lock
- * so we disable the interrupts here instead of trying to do complicated
- * things with the SDIO lock.
- */
-#ifdef MMC_QUIRK_LENIENT_FN0
- sdio_f0_writeb(func, 0, SDIO_CCCR_IENx, &r);
-#else
- r = csr_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, 0x00, NULL);
-#endif
- if (r) {
- printk(KERN_ERR "UniFi MMC Int handler: Failed to disable interrupts %d\n", r);
- }
-#endif
-
- /* If the function driver has registered a handler, call it */
- if (sdio_func_drv && sdio_func_drv->intr) {
-
- func_dsr_callback = sdio_func_drv->intr(sdio_ctx);
-
- /* If interrupt handle returns a DSR handle, call it */
- if (func_dsr_callback) {
- func_dsr_callback(sdio_ctx);
- }
- }
-
-} /* uf_glue_sdio_int_handler() */
-
-
-
-/*
- * ---------------------------------------------------------------------------
- * csr_sdio_linux_remove_irq
- *
- * Unregister the interrupt handler.
- * This means that the linux layer can not process interrupts any more.
- *
- * Arguments:
- * sdio SDIO context pointer
- *
- * Returns:
- * Status of the removal.
- * ---------------------------------------------------------------------------
- */
-int csr_sdio_linux_remove_irq(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int r;
-
- unifi_trace(NULL, UDBG1, "csr_sdio_linux_remove_irq\n");
-
- sdio_claim_host(func);
- r = sdio_release_irq(func);
- sdio_release_host(func);
-
- return r;
-
-} /* csr_sdio_linux_remove_irq() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * csr_sdio_linux_install_irq
- *
- * Register the interrupt handler.
- * This means that the linux layer can process interrupts.
- *
- * Arguments:
- * sdio SDIO context pointer
- *
- * Returns:
- * Status of the removal.
- * ---------------------------------------------------------------------------
- */
-int csr_sdio_linux_install_irq(CsrSdioFunction *function)
-{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int r;
-
- unifi_trace(NULL, UDBG1, "csr_sdio_linux_install_irq\n");
-
- /* Register our interrupt handle */
- sdio_claim_host(func);
- r = sdio_claim_irq(func, uf_glue_sdio_int_handler);
- sdio_release_host(func);
-
- /* If the interrupt was installed earlier, is fine */
- if (r == -EBUSY)
- r = 0;
-
- return r;
-} /* csr_sdio_linux_install_irq() */
-
-#ifdef CONFIG_PM
-
-/*
- * Power Management notifier
- */
-struct uf_sdio_mmc_pm_notifier
-{
- struct list_head list;
-
- CsrSdioFunction *sdio_ctx;
- struct notifier_block pm_notifier;
-};
-
-/* PM notifier list head */
-static struct uf_sdio_mmc_pm_notifier uf_sdio_mmc_pm_notifiers = {
- .sdio_ctx = NULL,
-};
-
-/*
- * ---------------------------------------------------------------------------
- * uf_sdio_mmc_register_pm_notifier
- * uf_sdio_mmc_unregister_pm_notifier
- *
- * Register/unregister for power management events. A list is used to
- * allow multiple card instances to be supported.
- *
- * Arguments:
- * sdio_ctx - CSR SDIO context to associate PM notifier to
- *
- * Returns:
- * Register function returns NULL on error
- * ---------------------------------------------------------------------------
- */
-static struct uf_sdio_mmc_pm_notifier *
-uf_sdio_mmc_register_pm_notifier(CsrSdioFunction *sdio_ctx)
-{
- /* Allocate notifier context for this card instance */
- struct uf_sdio_mmc_pm_notifier *notifier_ctx = kmalloc(sizeof(struct uf_sdio_mmc_pm_notifier), GFP_KERNEL);
-
- if (notifier_ctx)
- {
- notifier_ctx->sdio_ctx = sdio_ctx;
- notifier_ctx->pm_notifier.notifier_call = uf_sdio_mmc_power_event;
-
- list_add(&notifier_ctx->list, &uf_sdio_mmc_pm_notifiers.list);
-
- if (register_pm_notifier(&notifier_ctx->pm_notifier)) {
- printk(KERN_ERR "unifi: register_pm_notifier failed\n");
- }
- }
-
- return notifier_ctx;
-}
-
-static void
-uf_sdio_mmc_unregister_pm_notifier(CsrSdioFunction *sdio_ctx)
-{
- struct uf_sdio_mmc_pm_notifier *notifier_ctx;
- struct list_head *node, *q;
-
- list_for_each_safe(node, q, &uf_sdio_mmc_pm_notifiers.list) {
- notifier_ctx = list_entry(node, struct uf_sdio_mmc_pm_notifier, list);
-
- /* If it matches, unregister and free the notifier context */
- if (notifier_ctx && notifier_ctx->sdio_ctx == sdio_ctx)
- {
- if (unregister_pm_notifier(&notifier_ctx->pm_notifier)) {
- printk(KERN_ERR "unifi: unregister_pm_notifier failed\n");
- }
-
- /* Remove from list */
- notifier_ctx->sdio_ctx = NULL;
- list_del(node);
- kfree(notifier_ctx);
- }
- }
-}
-
-/*
- * ---------------------------------------------------------------------------
- * uf_sdio_mmc_power_event
- *
- * Handler for power management events.
- *
- * We need to handle suspend/resume events while the userspace is unsuspended
- * to allow the SME to run its suspend/resume state machines.
- *
- * Arguments:
- * event event ID
- *
- * Returns:
- * Status of the event handling
- * ---------------------------------------------------------------------------
- */
-static int
-uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct uf_sdio_mmc_pm_notifier *notifier_ctx = container_of(this,
- struct uf_sdio_mmc_pm_notifier,
- pm_notifier);
-
- /* Call the CSR SDIO function driver's suspend/resume method
- * while the userspace is unsuspended.
- */
- switch (event) {
- case PM_POST_HIBERNATION:
- case PM_POST_SUSPEND:
- printk(KERN_INFO "%s:%d resume\n", __FUNCTION__, __LINE__ );
- if (sdio_func_drv && sdio_func_drv->resume) {
- sdio_func_drv->resume(notifier_ctx->sdio_ctx);
- }
- break;
-
- case PM_HIBERNATION_PREPARE:
- case PM_SUSPEND_PREPARE:
- printk(KERN_INFO "%s:%d suspend\n", __FUNCTION__, __LINE__ );
- if (sdio_func_drv && sdio_func_drv->suspend) {
- sdio_func_drv->suspend(notifier_ctx->sdio_ctx);
- }
- break;
- }
- return NOTIFY_DONE;
-}
-
-#endif /* CONFIG_PM */
-
-/*
- * ---------------------------------------------------------------------------
- * uf_glue_sdio_probe
- *
- * Card insert callback.
- *
- * Arguments:
- * func Our (glue layer) context pointer.
- *
- * Returns:
- * UniFi driver error code.
- * ---------------------------------------------------------------------------
- */
-static int
-uf_glue_sdio_probe(struct sdio_func *func,
- const struct sdio_device_id *id)
-{
- int instance;
- CsrSdioFunction *sdio_ctx;
-
- /* First of all claim the SDIO driver */
- sdio_claim_host(func);
-
- /* Assume that the card is already powered */
- card_is_powered = 1;
-
- /* Assumes one card per host, which is true for SDIO */
- instance = func->card->host->index;
- printk("sdio bus_id: %16s - UniFi card 0x%X inserted\n",
- sdio_func_id(func), instance);
-
- /* Allocate context */
- sdio_ctx = kmalloc(sizeof(CsrSdioFunction), GFP_KERNEL);
- if (sdio_ctx == NULL) {
- sdio_release_host(func);
- return -ENOMEM;
- }
-
- /* Initialise the context */
- sdio_ctx->sdioId.manfId = func->vendor;
- sdio_ctx->sdioId.cardId = func->device;
- sdio_ctx->sdioId.sdioFunction = func->num;
- sdio_ctx->sdioId.sdioInterface = func->class;
- sdio_ctx->blockSize = func->cur_blksize;
- sdio_ctx->priv = (void *)func;
- sdio_ctx->features = 0;
-
- /* Module parameter enables byte mode */
- if (sdio_byte_mode) {
- sdio_ctx->features |= CSR_SDIO_FEATURE_BYTE_MODE;
- }
-
- if (func->card->host->caps & MMC_CAP_SD_HIGHSPEED) {
- unifi_trace(NULL, UDBG1, "MMC_CAP_SD_HIGHSPEED is available\n");
- }
-
-#ifdef MMC_QUIRK_LENIENT_FN0
- func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-#endif
-
- /* Pass context to the SDIO driver */
- sdio_set_drvdata(func, sdio_ctx);
-
-#ifdef CONFIG_PM
- /* Register to get PM events */
- if (uf_sdio_mmc_register_pm_notifier(sdio_ctx) == NULL) {
- unifi_error(NULL, "%s: Failed to register for PM events\n", __FUNCTION__);
- }
-#endif
-
- /* Register this device with the SDIO function driver */
- /* Call the main UniFi driver inserted handler */
- if (sdio_func_drv && sdio_func_drv->inserted) {
- uf_add_os_device(instance, &func->dev);
- sdio_func_drv->inserted(sdio_ctx);
- }
-
- /* We have finished, so release the SDIO driver */
- sdio_release_host(func);
-
-#ifdef ANDROID_BUILD
- /* Take the wakelock */
- unifi_trace(NULL, UDBG1, "probe: take wake lock\n");
- wake_lock(&unifi_sdio_wake_lock);
-#endif
-
- return 0;
-} /* uf_glue_sdio_probe() */
-
-
-/*
- * ---------------------------------------------------------------------------
- * uf_glue_sdio_remove
- *
- * Card removal callback.
- *
- * Arguments:
- * func Our (glue layer) context pointer.
- *
- * Returns:
- * UniFi driver error code.
- * ---------------------------------------------------------------------------
- */
-static void
-uf_glue_sdio_remove(struct sdio_func *func)
-{
- CsrSdioFunction *sdio_ctx;
-
- sdio_ctx = sdio_get_drvdata(func);
- if (!sdio_ctx) {
- return;
- }
-
- unifi_info(NULL, "UniFi card removed\n");
-
- /* Clean up the SDIO function driver */
- if (sdio_func_drv && sdio_func_drv->removed) {
- uf_remove_os_device(func->card->host->index);
- sdio_func_drv->removed(sdio_ctx);
- }
-
-#ifdef CONFIG_PM
- /* Unregister for PM events */
- uf_sdio_mmc_unregister_pm_notifier(sdio_ctx);
-#endif
-
- kfree(sdio_ctx);
-
-} /* uf_glue_sdio_remove */
-
-
-/*
- * SDIO ids *must* be statically declared, so we can't take
- * them from the list passed in csr_sdio_register_driver().
- */
-static const struct sdio_device_id unifi_ids[] = {
- { SDIO_DEVICE(SDIO_MANF_ID_CSR, SDIO_CARD_ID_UNIFI_3) },
- { SDIO_DEVICE(SDIO_MANF_ID_CSR, SDIO_CARD_ID_UNIFI_4) },
- { /* end: all zeroes */ },
-};
-
-MODULE_DEVICE_TABLE(sdio, unifi_ids);
-
-#ifdef CONFIG_PM
-
-/*
- * ---------------------------------------------------------------------------
- * uf_glue_sdio_suspend
- *
- * Card suspend callback. The userspace will already be suspended.
- *
- * Arguments:
- * dev The struct device owned by the MMC driver
- *
- * Returns:
- * None
- * ---------------------------------------------------------------------------
- */
-static int
-uf_glue_sdio_suspend(struct device *dev)
-{
- unifi_trace(NULL, UDBG1, "uf_glue_sdio_suspend");
-
- return 0;
-} /* uf_glue_sdio_suspend */
-
-
-/*
- * ---------------------------------------------------------------------------
- * uf_glue_sdio_resume
- *
- * Card resume callback. The userspace will still be suspended.
- *
- * Arguments:
- * dev The struct device owned by the MMC driver
- *
- * Returns:
- * None
- * ---------------------------------------------------------------------------
- */
-static int
-uf_glue_sdio_resume(struct device *dev)
-{
- unifi_trace(NULL, UDBG1, "uf_glue_sdio_resume");
-
-#ifdef ANDROID_BUILD
- unifi_trace(NULL, UDBG1, "resume: take wakelock\n");
- wake_lock(&unifi_sdio_wake_lock);
-#endif
-
- return 0;
-
-} /* uf_glue_sdio_resume */
-
-static struct dev_pm_ops unifi_pm_ops = {
- .suspend = uf_glue_sdio_suspend,
- .resume = uf_glue_sdio_resume,
-};
-
-#define UNIFI_PM_OPS (&unifi_pm_ops)
-
-#else
-
-#define UNIFI_PM_OPS NULL
-
-#endif /* CONFIG_PM */
-
-static struct sdio_driver unifi_driver = {
- .probe = uf_glue_sdio_probe,
- .remove = uf_glue_sdio_remove,
- .name = "unifi",
- .id_table = unifi_ids,
- .drv.pm = UNIFI_PM_OPS,
-};
-
-
-/*
- * ---------------------------------------------------------------------------
- * CsrSdioFunctionDriverRegister
- * CsrSdioFunctionDriverUnregister
- *
- * These functions are called from the main module load and unload
- * functions. They perform the appropriate operations for the
- * linux MMC/SDIO driver.
- *
- * Arguments:
- * sdio_drv Pointer to the function driver's SDIO structure.
- *
- * Returns:
- * None.
- * ---------------------------------------------------------------------------
- */
-CsrResult
-CsrSdioFunctionDriverRegister(CsrSdioFunctionDriver *sdio_drv)
-{
- int r;
-
- printk("UniFi: Using native Linux MMC driver for SDIO.\n");
-
- if (sdio_func_drv) {
- unifi_error(NULL, "sdio_mmc: UniFi driver already registered\n");
- return CSR_SDIO_RESULT_INVALID_VALUE;
- }
-
-#ifdef ANDROID_BUILD
- wake_lock_init(&unifi_sdio_wake_lock, WAKE_LOCK_SUSPEND, "unifi_sdio_work");
-#endif
-
- /* Save the registered driver description */
- /*
- * FIXME:
- * Need a table here to handle a call to register for just one function.
- * mmc only allows us to register for the whole device
- */
- sdio_func_drv = sdio_drv;
-
-#ifdef CONFIG_PM
- /* Initialise PM notifier list */
- INIT_LIST_HEAD(&uf_sdio_mmc_pm_notifiers.list);
-#endif
-
- /* Register ourself with mmc_core */
- r = sdio_register_driver(&unifi_driver);
- if (r) {
- printk(KERN_ERR "unifi_sdio: Failed to register UniFi SDIO driver: %d\n", r);
- return ConvertSdioToCsrSdioResult(r);
- }
-
- return CSR_RESULT_SUCCESS;
-} /* CsrSdioFunctionDriverRegister() */
-
-
-
-void
-CsrSdioFunctionDriverUnregister(CsrSdioFunctionDriver *sdio_drv)
-{
- printk(KERN_INFO "UniFi: unregister from MMC sdio\n");
-
-#ifdef ANDROID_BUILD
- wake_lock_destroy(&unifi_sdio_wake_lock);
-#endif
- sdio_unregister_driver(&unifi_driver);
-
- sdio_func_drv = NULL;
-
-} /* CsrSdioFunctionDriverUnregister() */
-