diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2007-02-11 19:57:36 +0100 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-05-01 13:04:17 +0200 |
commit | 1c6a0718f0bfdab0d9b7da5f7b74f38a0058c03a (patch) | |
tree | 5e7f2a26d5d1782d87c596b40f874c6c0b8b8e1a /drivers/mmc/wbsd.c | |
parent | 98ac2162699f7e9880683cb954891817f20b607c (diff) |
mmc: Move host and card drivers to subdirs
Clean up the drivers/mmc directory by moving card and host drivers
into subdirectories.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/wbsd.c')
-rw-r--r-- | drivers/mmc/wbsd.c | 2062 |
1 files changed, 0 insertions, 2062 deletions
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c deleted file mode 100644 index 9f7518b37c33..000000000000 --- a/drivers/mmc/wbsd.c +++ /dev/null @@ -1,2062 +0,0 @@ -/* - * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver - * - * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * - * Warning! - * - * Changes to the FIFO system should be done with extreme care since - * the hardware is full of bugs related to the FIFO. Known issues are: - * - * - FIFO size field in FSR is always zero. - * - * - FIFO interrupts tend not to work as they should. Interrupts are - * triggered only for full/empty events, not for threshold values. - * - * - On APIC systems the FIFO empty interrupt is sometimes lost. - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/dma-mapping.h> -#include <linux/delay.h> -#include <linux/pnp.h> -#include <linux/highmem.h> -#include <linux/mmc/host.h> - -#include <asm/io.h> -#include <asm/dma.h> -#include <asm/scatterlist.h> - -#include "wbsd.h" - -#define DRIVER_NAME "wbsd" - -#define DBG(x...) \ - pr_debug(DRIVER_NAME ": " x) -#define DBGF(f, x...) \ - pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) - -/* - * Device resources - */ - -#ifdef CONFIG_PNP - -static const struct pnp_device_id pnp_dev_table[] = { - { "WEC0517", 0 }, - { "WEC0518", 0 }, - { "", 0 }, -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -#endif /* CONFIG_PNP */ - -static const int config_ports[] = { 0x2E, 0x4E }; -static const int unlock_codes[] = { 0x83, 0x87 }; - -static const int valid_ids[] = { - 0x7112, - }; - -#ifdef CONFIG_PNP -static unsigned int nopnp = 0; -#else -static const unsigned int nopnp = 1; -#endif -static unsigned int io = 0x248; -static unsigned int irq = 6; -static int dma = 2; - -/* - * Basic functions - */ - -static inline void wbsd_unlock_config(struct wbsd_host *host) -{ - BUG_ON(host->config == 0); - - outb(host->unlock_code, host->config); - outb(host->unlock_code, host->config); -} - -static inline void wbsd_lock_config(struct wbsd_host *host) -{ - BUG_ON(host->config == 0); - - outb(LOCK_CODE, host->config); -} - -static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value) -{ - BUG_ON(host->config == 0); - - outb(reg, host->config); - outb(value, host->config + 1); -} - -static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg) -{ - BUG_ON(host->config == 0); - - outb(reg, host->config); - return inb(host->config + 1); -} - -static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value) -{ - outb(index, host->base + WBSD_IDXR); - outb(value, host->base + WBSD_DATAR); -} - -static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index) -{ - outb(index, host->base + WBSD_IDXR); - return inb(host->base + WBSD_DATAR); -} - -/* - * Common routines - */ - -static void wbsd_init_device(struct wbsd_host *host) -{ - u8 setup, ier; - - /* - * Reset chip (SD/MMC part) and fifo. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * Set DAT3 to input - */ - setup &= ~WBSD_DAT3_H; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - host->flags &= ~WBSD_FIGNORE_DETECT; - - /* - * Read back default clock. - */ - host->clk = wbsd_read_index(host, WBSD_IDX_CLK); - - /* - * Power down port. - */ - outb(WBSD_POWER_N, host->base + WBSD_CSR); - - /* - * Set maximum timeout. - */ - wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); - - /* - * Test for card presence - */ - if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) - host->flags |= WBSD_FCARD_PRESENT; - else - host->flags &= ~WBSD_FCARD_PRESENT; - - /* - * Enable interesting interrupts. - */ - ier = 0; - ier |= WBSD_EINT_CARD; - ier |= WBSD_EINT_FIFO_THRE; - ier |= WBSD_EINT_CRC; - ier |= WBSD_EINT_TIMEOUT; - ier |= WBSD_EINT_TC; - - outb(ier, host->base + WBSD_EIR); - - /* - * Clear interrupts. - */ - inb(host->base + WBSD_ISR); -} - -static void wbsd_reset(struct wbsd_host *host) -{ - u8 setup; - - printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc)); - - /* - * Soft reset of chip (SD/MMC part). - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_SOFT_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); -} - -static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq) -{ - unsigned long dmaflags; - - DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); - - if (host->dma >= 0) { - /* - * Release ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - release_dma_lock(dmaflags); - - /* - * Disable DMA on host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - } - - host->mrq = NULL; - - /* - * MMC layer might call back into the driver so first unlock. - */ - spin_unlock(&host->lock); - mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); -} - -/* - * Scatter/gather functions - */ - -static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data) -{ - /* - * Get info. about SG list from data structure. - */ - host->cur_sg = data->sg; - host->num_sg = data->sg_len; - - host->offset = 0; - host->remain = host->cur_sg->length; -} - -static inline int wbsd_next_sg(struct wbsd_host *host) -{ - /* - * Skip to next SG entry. - */ - host->cur_sg++; - host->num_sg--; - - /* - * Any entries left? - */ - if (host->num_sg > 0) { - host->offset = 0; - host->remain = host->cur_sg->length; - } - - return host->num_sg; -} - -static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) -{ - return page_address(host->cur_sg->page) + host->cur_sg->offset; -} - -static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = page_address(sg[i].page) + sg[i].offset; - memcpy(dmabuf, sgbuf, sg[i].length); - dmabuf += sg[i].length; - } -} - -static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = page_address(sg[i].page) + sg[i].offset; - memcpy(sgbuf, dmabuf, sg[i].length); - dmabuf += sg[i].length; - } -} - -/* - * Command handling - */ - -static inline void wbsd_get_short_reply(struct wbsd_host *host, - struct mmc_command *cmd) -{ - /* - * Correct response type? - */ - if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { - cmd->error = MMC_ERR_INVALID; - return; - } - - cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; - cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; -} - -static inline void wbsd_get_long_reply(struct wbsd_host *host, - struct mmc_command *cmd) -{ - int i; - - /* - * Correct response type? - */ - if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { - cmd->error = MMC_ERR_INVALID; - return; - } - - for (i = 0; i < 4; i++) { - cmd->resp[i] = - wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; - } -} - -static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) -{ - int i; - u8 status, isr; - - DBGF("Sending cmd (%x)\n", cmd->opcode); - - /* - * Clear accumulated ISR. The interrupt routine - * will fill this one with events that occur during - * transfer. - */ - host->isr = 0; - - /* - * Send the command (CRC calculated by host). - */ - outb(cmd->opcode, host->base + WBSD_CMDR); - for (i = 3; i >= 0; i--) - outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); - - cmd->error = MMC_ERR_NONE; - - /* - * Wait for the request to complete. - */ - do { - status = wbsd_read_index(host, WBSD_IDX_STATUS); - } while (status & WBSD_CARDTRAFFIC); - - /* - * Do we expect a reply? - */ - if (cmd->flags & MMC_RSP_PRESENT) { - /* - * Read back status. - */ - isr = host->isr; - - /* Card removed? */ - if (isr & WBSD_INT_CARD) - cmd->error = MMC_ERR_TIMEOUT; - /* Timeout? */ - else if (isr & WBSD_INT_TIMEOUT) - cmd->error = MMC_ERR_TIMEOUT; - /* CRC? */ - else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) - cmd->error = MMC_ERR_BADCRC; - /* All ok */ - else { - if (cmd->flags & MMC_RSP_136) - wbsd_get_long_reply(host, cmd); - else - wbsd_get_short_reply(host, cmd); - } - } - - DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error); -} - -/* - * Data functions - */ - -static void wbsd_empty_fifo(struct wbsd_host *host) -{ - struct mmc_data *data = host->mrq->cmd->data; - char *buffer; - int i, fsr, fifo; - - /* - * Handle excessive data. - */ - if (host->num_sg == 0) - return; - - buffer = wbsd_sg_to_buffer(host) + host->offset; - - /* - * Drain the fifo. This has a tendency to loop longer - * than the FIFO length (usually one block). - */ - while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { - /* - * The size field in the FSR is broken so we have to - * do some guessing. - */ - if (fsr & WBSD_FIFO_FULL) - fifo = 16; - else if (fsr & WBSD_FIFO_FUTHRE) - fifo = 8; - else - fifo = 1; - - for (i = 0; i < fifo; i++) { - *buffer = inb(host->base + WBSD_DFR); - buffer++; - host->offset++; - host->remain--; - - data->bytes_xfered++; - - /* - * End of scatter list entry? - */ - if (host->remain == 0) { - /* - * Get next entry. Check if last. - */ - if (!wbsd_next_sg(host)) - return; - - buffer = wbsd_sg_to_buffer(host); - } - } - } - - /* - * This is a very dirty hack to solve a - * hardware problem. The chip doesn't trigger - * FIFO threshold interrupts properly. - */ - if ((data->blocks * data->blksz - data->bytes_xfered) < 16) - tasklet_schedule(&host->fifo_tasklet); -} - -static void wbsd_fill_fifo(struct wbsd_host *host) -{ - struct mmc_data *data = host->mrq->cmd->data; - char *buffer; - int i, fsr, fifo; - - /* - * Check that we aren't being called after the - * entire buffer has been transfered. - */ - if (host->num_sg == 0) - return; - - buffer = wbsd_sg_to_buffer(host) + host->offset; - - /* - * Fill the fifo. This has a tendency to loop longer - * than the FIFO length (usually one block). - */ - while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { - /* - * The size field in the FSR is broken so we have to - * do some guessing. - */ - if (fsr & WBSD_FIFO_EMPTY) - fifo = 0; - else if (fsr & WBSD_FIFO_EMTHRE) - fifo = 8; - else - fifo = 15; - - for (i = 16; i > fifo; i--) { - outb(*buffer, host->base + WBSD_DFR); - buffer++; - host->offset++; - host->remain--; - - data->bytes_xfered++; - - /* - * End of scatter list entry? - */ - if (host->remain == 0) { - /* - * Get next entry. Check if last. - */ - if (!wbsd_next_sg(host)) - return; - - buffer = wbsd_sg_to_buffer(host); - } - } - } - - /* - * The controller stops sending interrupts for - * 'FIFO empty' under certain conditions. So we - * need to be a bit more pro-active. - */ - tasklet_schedule(&host->fifo_tasklet); -} - -static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) -{ - u16 blksize; - u8 setup; - unsigned long dmaflags; - unsigned int size; - - DBGF("blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); - DBGF("tsac %d ms nsac %d clk\n", - data->timeout_ns / 1000000, data->timeout_clks); - - /* - * Calculate size. - */ - size = data->blocks * data->blksz; - - /* - * Check timeout values for overflow. - * (Yes, some cards cause this value to overflow). - */ - if (data->timeout_ns > 127000000) - wbsd_write_index(host, WBSD_IDX_TAAC, 127); - else { - wbsd_write_index(host, WBSD_IDX_TAAC, - data->timeout_ns / 1000000); - } - - if (data->timeout_clks > 255) - wbsd_write_index(host, WBSD_IDX_NSAC, 255); - else - wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); - - /* - * Inform the chip of how large blocks will be - * sent. It needs this to determine when to - * calculate CRC. - * - * Space for CRC must be included in the size. - * Two bytes are needed for each data line. - */ - if (host->bus_width == MMC_BUS_WIDTH_1) { - blksize = data->blksz + 2; - - wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); - wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); - } else if (host->bus_width == MMC_BUS_WIDTH_4) { - blksize = data->blksz + 2 * 4; - - wbsd_write_index(host, WBSD_IDX_PBSMSB, - ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); - wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); - } else { - data->error = MMC_ERR_INVALID; - return; - } - - /* - * Clear the FIFO. This is needed even for DMA - * transfers since the chip still uses the FIFO - * internally. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_FIFO_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * DMA transfer? - */ - if (host->dma >= 0) { - /* - * The buffer for DMA is only 64 kB. - */ - BUG_ON(size > 0x10000); - if (size > 0x10000) { - data->error = MMC_ERR_INVALID; - return; - } - - /* - * Transfer data from the SG list to - * the DMA buffer. - */ - if (data->flags & MMC_DATA_WRITE) - wbsd_sg_to_dma(host, data); - - /* - * Initialise the ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - if (data->flags & MMC_DATA_READ) - set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); - else - set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); - set_dma_addr(host->dma, host->dma_addr); - set_dma_count(host->dma, size); - - enable_dma(host->dma); - release_dma_lock(dmaflags); - - /* - * Enable DMA on the host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); - } else { - /* - * This flag is used to keep printk - * output to a minimum. - */ - host->firsterr = 1; - - /* - * Initialise the SG list. - */ - wbsd_init_sg(host, data); - - /* - * Turn off DMA. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - - /* - * Set up FIFO threshold levels (and fill - * buffer if doing a write). - */ - if (data->flags & MMC_DATA_READ) { - wbsd_write_index(host, WBSD_IDX_FIFOEN, - WBSD_FIFOEN_FULL | 8); - } else { - wbsd_write_index(host, WBSD_IDX_FIFOEN, - WBSD_FIFOEN_EMPTY | 8); - wbsd_fill_fifo(host); - } - } - - data->error = MMC_ERR_NONE; -} - -static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned long dmaflags; - int count; - u8 status; - - WARN_ON(host->mrq == NULL); - - /* - * Send a stop command if needed. - */ - if (data->stop) - wbsd_send_command(host, data->stop); - - /* - * Wait for the controller to leave data - * transfer state. - */ - do { - status = wbsd_read_index(host, WBSD_IDX_STATUS); - } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); - - /* - * DMA transfer? - */ - if (host->dma >= 0) { - /* - * Disable DMA on the host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - - /* - * Turn of ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - count = get_dma_residue(host->dma); - release_dma_lock(dmaflags); - - data->bytes_xfered = host->mrq->data->blocks * - host->mrq->data->blksz - count; - data->bytes_xfered -= data->bytes_xfered % data->blksz; - - /* - * Any leftover data? - */ - if (count) { - printk(KERN_ERR "%s: Incomplete DMA transfer. " - "%d bytes left.\n", - mmc_hostname(host->mmc), count); - - if (data->error == MMC_ERR_NONE) - data->error = MMC_ERR_FAILED; - } else { - /* - * Transfer data from DMA buffer to - * SG list. - */ - if (data->flags & MMC_DATA_READ) - wbsd_dma_to_sg(host, data); - } - - if (data->error != MMC_ERR_NONE) { - if (data->bytes_xfered) - data->bytes_xfered -= data->blksz; - } - } - - DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered); - - wbsd_request_end(host, host->mrq); -} - -/*****************************************************************************\ - * * - * MMC layer callbacks * - * * -\*****************************************************************************/ - -static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct wbsd_host *host = mmc_priv(mmc); - struct mmc_command *cmd; - - /* - * Disable tasklets to avoid a deadlock. - */ - spin_lock_bh(&host->lock); - - BUG_ON(host->mrq != NULL); - - cmd = mrq->cmd; - - host->mrq = mrq; - - /* - * If there is no card in the slot then - * timeout immediatly. - */ - if (!(host->flags & WBSD_FCARD_PRESENT)) { - cmd->error = MMC_ERR_TIMEOUT; - goto done; - } - - /* - * Does the request include data? - */ - if (cmd->data) { - wbsd_prepare_data(host, cmd->data); - - if (cmd->data->error != MMC_ERR_NONE) - goto done; - } - - wbsd_send_command(host, cmd); - - /* - * If this is a data transfer the request - * will be finished after the data has - * transfered. - */ - if (cmd->data && (cmd->error == MMC_ERR_NONE)) { - /* - * The hardware is so delightfully stupid that it has a list - * of "data" commands. If a command isn't on this list, it'll - * just go back to the idle state and won't send any data - * interrupts. - */ - switch (cmd->opcode) { - case 11: - case 17: - case 18: - case 20: - case 24: - case 25: - case 26: - case 27: - case 30: - case 42: - case 56: - break; - - /* ACMDs. We don't keep track of state, so we just treat them - * like any other command. */ - case 51: - break; - - default: -#ifdef CONFIG_MMC_DEBUG - printk(KERN_WARNING "%s: Data command %d is not " - "supported by this controller.\n", - mmc_hostname(host->mmc), cmd->opcode); -#endif - cmd->data->error = MMC_ERR_INVALID; - - if (cmd->data->stop) - wbsd_send_command(host, cmd->data->stop); - - goto done; - }; - - /* - * Dirty fix for hardware bug. - */ - if (host->dma == -1) - tasklet_schedule(&host->fifo_tasklet); - - spin_unlock_bh(&host->lock); - - return; - } - -done: - wbsd_request_end(host, mrq); - - spin_unlock_bh(&host->lock); -} - -static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct wbsd_host *host = mmc_priv(mmc); - u8 clk, setup, pwr; - - spin_lock_bh(&host->lock); - - /* - * Reset the chip on each power off. - * Should clear out any weird states. - */ - if (ios->power_mode == MMC_POWER_OFF) - wbsd_init_device(host); - - if (ios->clock >= 24000000) - clk = WBSD_CLK_24M; - else if (ios->clock >= 16000000) - clk = WBSD_CLK_16M; - else if (ios->clock >= 12000000) - clk = WBSD_CLK_12M; - else - clk = WBSD_CLK_375K; - - /* - * Only write to the clock register when - * there is an actual change. - */ - if (clk != host->clk) { - wbsd_write_index(host, WBSD_IDX_CLK, clk); - host->clk = clk; - } - - /* - * Power up card. - */ - if (ios->power_mode != MMC_POWER_OFF) { - pwr = inb(host->base + WBSD_CSR); - pwr &= ~WBSD_POWER_N; - outb(pwr, host->base + WBSD_CSR); - } - - /* - * MMC cards need to have pin 1 high during init. - * It wreaks havoc with the card detection though so - * that needs to be disabled. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - if (ios->chip_select == MMC_CS_HIGH) { - BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); - setup |= WBSD_DAT3_H; - host->flags |= WBSD_FIGNORE_DETECT; - } else { - if (setup & WBSD_DAT3_H) { - setup &= ~WBSD_DAT3_H; - - /* - * We cannot resume card detection immediatly - * because of capacitance and delays in the chip. - */ - mod_timer(&host->ignore_timer, jiffies + HZ / 100); - } - } - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * Store bus width for later. Will be used when - * setting up the data transfer. - */ - host->bus_width = ios->bus_width; - - spin_unlock_bh(&host->lock); -} - -static int wbsd_get_ro(struct mmc_host *mmc) -{ - struct wbsd_host *host = mmc_priv(mmc); - u8 csr; - - spin_lock_bh(&host->lock); - - csr = inb(host->base + WBSD_CSR); - csr |= WBSD_MSLED; - outb(csr, host->base + WBSD_CSR); - - mdelay(1); - - csr = inb(host->base + WBSD_CSR); - csr &= ~WBSD_MSLED; - outb(csr, host->base + WBSD_CSR); - - spin_unlock_bh(&host->lock); - - return csr & WBSD_WRPT; -} - -static const struct mmc_host_ops wbsd_ops = { - .request = wbsd_request, - .set_ios = wbsd_set_ios, - .get_ro = wbsd_get_ro, -}; - -/*****************************************************************************\ - * * - * Interrupt handling * - * * -\*****************************************************************************/ - -/* - * Helper function to reset detection ignore - */ - -static void wbsd_reset_ignore(unsigned long data) -{ - struct wbsd_host *host = (struct wbsd_host *)data; - - BUG_ON(host == NULL); - - DBG("Resetting card detection ignore\n"); - - spin_lock_bh(&host->lock); - - host->flags &= ~WBSD_FIGNORE_DETECT; - - /* - * Card status might have changed during the - * blackout. - */ - tasklet_schedule(&host->card_tasklet); - - spin_unlock_bh(&host->lock); -} - -/* - * Tasklets - */ - -static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) -{ - WARN_ON(!host->mrq); - if (!host->mrq) - return NULL; - - WARN_ON(!host->mrq->cmd); - if (!host->mrq->cmd) - return NULL; - - WARN_ON(!host->mrq->cmd->data); - if (!host->mrq->cmd->data) - return NULL; - - return host->mrq->cmd->data; -} - -static void wbsd_tasklet_card(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - u8 csr; - int delay = -1; - - spin_lock(&host->lock); - - if (host->flags & WBSD_FIGNORE_DETECT) { - spin_unlock(&host->lock); - return; - } - - csr = inb(host->base + WBSD_CSR); - WARN_ON(csr == 0xff); - - if (csr & WBSD_CARDPRESENT) { - if (!(host->flags & WBSD_FCARD_PRESENT)) { - DBG("Card inserted\n"); - host->flags |= WBSD_FCARD_PRESENT; - - delay = 500; - } - } else if (host->flags & WBSD_FCARD_PRESENT) { - DBG("Card removed\n"); - host->flags &= ~WBSD_FCARD_PRESENT; - - if (host->mrq) { - printk(KERN_ERR "%s: Card removed during transfer!\n", - mmc_hostname(host->mmc)); - wbsd_reset(host); - - host->mrq->cmd->error = MMC_ERR_FAILED; - tasklet_schedule(&host->finish_tasklet); - } - - delay = 0; - } - - /* - * Unlock first since we might get a call back. - */ - - spin_unlock(&host->lock); - - if (delay != -1) - mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); -} - -static void wbsd_tasklet_fifo(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - if (data->flags & MMC_DATA_WRITE) - wbsd_fill_fifo(host); - else - wbsd_empty_fifo(host); - - /* - * Done? - */ - if (host->num_sg == 0) { - wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); - tasklet_schedule(&host->finish_tasklet); - } - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_crc(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - DBGF("CRC error\n"); - - data->error = MMC_ERR_BADCRC; - - tasklet_schedule(&host->finish_tasklet); - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_timeout(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - DBGF("Timeout\n"); - - data->error = MMC_ERR_TIMEOUT; - - tasklet_schedule(&host->finish_tasklet); - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_finish(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - WARN_ON(!host->mrq); - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - wbsd_finish_data(host, data); - -end: - spin_unlock(&host->lock); -} - -/* - * Interrupt handling - */ - -static irqreturn_t wbsd_irq(int irq, void *dev_id) -{ - struct wbsd_host *host = dev_id; - int isr; - - isr = inb(host->base + WBSD_ISR); - - /* - * Was it actually our hardware that caused the interrupt? - */ - if (isr == 0xff || isr == 0x00) - return IRQ_NONE; - - host->isr |= isr; - - /* - * Schedule tasklets as needed. - */ - if (isr & WBSD_INT_CARD) - tasklet_schedule(&host->card_tasklet); - if (isr & WBSD_INT_FIFO_THRE) - tasklet_schedule(&host->fifo_tasklet); - if (isr & WBSD_INT_CRC) - tasklet_hi_schedule(&host->crc_tasklet); - if (isr & WBSD_INT_TIMEOUT) - tasklet_hi_schedule(&host->timeout_tasklet); - if (isr & WBSD_INT_TC) - tasklet_schedule(&host->finish_tasklet); - - return IRQ_HANDLED; -} - -/*****************************************************************************\ - * * - * Device initialisation and shutdown * - * * -\*****************************************************************************/ - -/* - * Allocate/free MMC structure. - */ - -static int __devinit wbsd_alloc_mmc(struct device *dev) -{ - struct mmc_host *mmc; - struct wbsd_host *host; - - /* - * Allocate MMC structure. - */ - mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - host->dma = -1; - - /* - * Set host parameters. - */ - mmc->ops = &wbsd_ops; - mmc->f_min = 375000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; - - spin_lock_init(&host->lock); - - /* - * Set up timers - */ - init_timer(&host->ignore_timer); - host->ignore_timer.data = (unsigned long)host; - host->ignore_timer.function = wbsd_reset_ignore; - - /* - * Maximum number of segments. Worst case is one sector per segment - * so this will be 64kB/512. - */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; - - /* - * Maximum request size. Also limited by 64KiB buffer. - */ - mmc->max_req_size = 65536; - - /* - * Maximum segment size. Could be one segment with the maximum number - * of bytes. - */ - mmc->max_seg_size = mmc->max_req_size; - - /* - * Maximum block size. We have 12 bits (= 4095) but have to subtract - * space for CRC. So the maximum is 4095 - 4*2 = 4087. - */ - mmc->max_blk_size = 4087; - - /* - * Maximum block count. There is no real limit so the maximum - * request size will be the only restriction. - */ - mmc->max_blk_count = mmc->max_req_size; - - dev_set_drvdata(dev, mmc); - - return 0; -} - -static void __devexit wbsd_free_mmc(struct device *dev) -{ - struct mmc_host *mmc; - struct wbsd_host *host; - - mmc = dev_get_drvdata(dev); - if (!mmc) - return; - - host = mmc_priv(mmc); - BUG_ON(host == NULL); - - del_timer_sync(&host->ignore_timer); - - mmc_free_host(mmc); - - dev_set_drvdata(dev, NULL); -} - -/* - * Scan for known chip id:s - */ - -static int __devinit wbsd_scan(struct wbsd_host *host) -{ - int i, j, k; - int id; - - /* - * Iterate through all ports, all codes to - * find hardware that is in our known list. - */ - for (i = 0; i < ARRAY_SIZE(config_ports); i++) { - if (!request_region(config_ports[i], 2, DRIVER_NAME)) - continue; - - for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { - id = 0xFFFF; - - host->config = config_ports[i]; - host->unlock_code = unlock_codes[j]; - - wbsd_unlock_config(host); - - outb(WBSD_CONF_ID_HI, config_ports[i]); - id = inb(config_ports[i] + 1) << 8; - - outb(WBSD_CONF_ID_LO, config_ports[i]); - id |= inb(config_ports[i] + 1); - - wbsd_lock_config(host); - - for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { - if (id == valid_ids[k]) { - host->chip_id = id; - - return 0; - } - } - - if (id != 0xFFFF) { - DBG("Unknown hardware (id %x) found at %x\n", - id, config_ports[i]); - } - } - - release_region(config_ports[i], 2); - } - - host->config = 0; - host->unlock_code = 0; - - return -ENODEV; -} - -/* - * Allocate/free io port ranges - */ - -static int __devinit wbsd_request_region(struct wbsd_host *host, int base) -{ - if (base & 0x7) - return -EINVAL; - - if (!request_region(base, 8, DRIVER_NAME)) - return -EIO; - - host->base = base; - - return 0; -} - -static void __devexit wbsd_release_regions(struct wbsd_host *host) -{ - if (host->base) - release_region(host->base, 8); - - host->base = 0; - - if (host->config) - release_region(host->config, 2); - - host->config = 0; -} - -/* - * Allocate/free DMA port and buffer - */ - -static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) -{ - if (dma < 0) - return; - - if (request_dma(dma, DRIVER_NAME)) - goto err; - - /* - * We need to allocate a special buffer in - * order for ISA to be able to DMA to it. - */ - host->dma_buffer = kmalloc(WBSD_DMA_SIZE, - GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); - if (!host->dma_buffer) - goto free; - - /* - * Translate the address to a physical address. - */ - host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - - /* - * ISA DMA must be aligned on a 64k basis. - */ - if ((host->dma_addr & 0xffff) != 0) - goto kfree; - /* - * ISA cannot access memory above 16 MB. - */ - else if (host->dma_addr >= 0x1000000) - goto kfree; - - host->dma = dma; - - return; - -kfree: - /* - * If we've gotten here then there is some kind of alignment bug - */ - BUG_ON(1); - - dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - host->dma_addr = (dma_addr_t)NULL; - - kfree(host->dma_buffer); - host->dma_buffer = NULL; - -free: - free_dma(dma); - -err: - printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " - "Falling back on FIFO.\n", dma); -} - -static void __devexit wbsd_release_dma(struct wbsd_host *host) -{ - if (host->dma_addr) { - dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - } - kfree(host->dma_buffer); - if (host->dma >= 0) - free_dma(host->dma); - - host->dma = -1; - host->dma_buffer = NULL; - host->dma_addr = (dma_addr_t)NULL; -} - -/* - * Allocate/free IRQ. - */ - -static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) -{ - int ret; - - /* - * Allocate interrupt. - */ - - ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); - if (ret) - return ret; - - host->irq = irq; - - /* - * Set up tasklets. - */ - tasklet_init(&host->card_tasklet, wbsd_tasklet_card, - (unsigned long)host); - tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, - (unsigned long)host); - tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, - (unsigned long)host); - tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, - (unsigned long)host); - tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, - (unsigned long)host); - - return 0; -} - -static void __devexit wbsd_release_irq(struct wbsd_host *host) -{ - if (!host->irq) - return; - - free_irq(host->irq, host); - - host->irq = 0; - - tasklet_kill(&host->card_tasklet); - tasklet_kill(&host->fifo_tasklet); - tasklet_kill(&host->crc_tasklet); - tasklet_kill(&host->timeout_tasklet); - tasklet_kill(&host->finish_tasklet); -} - -/* - * Allocate all resources for the host. - */ - -static int __devinit wbsd_request_resources(struct wbsd_host *host, - int base, int irq, int dma) -{ - int ret; - - /* - * Allocate I/O ports. - */ - ret = wbsd_request_region(host, base); - if (ret) - return ret; - - /* - * Allocate interrupt. - */ - ret = wbsd_request_irq(host, irq); - if (ret) - return ret; - - /* - * Allocate DMA. - */ - wbsd_request_dma(host, dma); - - return 0; -} - -/* - * Release all resources for the host. - */ - -static void __devexit wbsd_release_resources(struct wbsd_host *host) -{ - wbsd_release_dma(host); - wbsd_release_irq(host); - wbsd_release_regions(host); -} - -/* - * Configure the resources the chip should use. - */ - -static void wbsd_chip_config(struct wbsd_host *host) -{ - wbsd_unlock_config(host); - - /* - * Reset the chip. - */ - wbsd_write_config(host, WBSD_CONF_SWRST, 1); - wbsd_write_config(host, WBSD_CONF_SWRST, 0); - - /* - * Select SD/MMC function. - */ - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - - /* - * Set up card detection. - */ - wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); - - /* - * Configure chip - */ - wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); - wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); - - wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); - - if (host->dma >= 0) - wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); - - /* - * Enable and power up chip. - */ - wbsd_write_config(host, WBSD_CONF_ENABLE, 1); - wbsd_write_config(host, WBSD_CONF_POWER, 0x20); - - wbsd_lock_config(host); -} - -/* - * Check that configured resources are correct. - */ - -static int wbsd_chip_validate(struct wbsd_host *host) -{ - int base, irq, dma; - - wbsd_unlock_config(host); - - /* - * Select SD/MMC function. - */ - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - - /* - * Read configuration. - */ - base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; - base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); - - irq = wbsd_read_config(host, WBSD_CONF_IRQ); - - dma = wbsd_read_config(host, WBSD_CONF_DRQ); - - wbsd_lock_config(host); - - /* - * Validate against given configuration. - */ - if (base != host->base) - return 0; - if (irq != host->irq) - return 0; - if ((dma != host->dma) && (host->dma != -1)) - return 0; - - return 1; -} - -/* - * Powers down the SD function - */ - -static void wbsd_chip_poweroff(struct wbsd_host *host) -{ - wbsd_unlock_config(host); - - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - wbsd_write_config(host, WBSD_CONF_ENABLE, 0); - - wbsd_lock_config(host); -} - -/*****************************************************************************\ - * * - * Devices setup and shutdown * - * * -\*****************************************************************************/ - -static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, - int pnp) -{ - struct wbsd_host *host = NULL; - struct mmc_host *mmc = NULL; - int ret; - - ret = wbsd_alloc_mmc(dev); - if (ret) - return ret; - - mmc = dev_get_drvdata(dev); - host = mmc_priv(mmc); - - /* - * Scan for hardware. - */ - ret = wbsd_scan(host); - if (ret) { - if (pnp && (ret == -ENODEV)) { - printk(KERN_WARNING DRIVER_NAME - ": Unable to confirm device presence. You may " - "experience lock-ups.\n"); - } else { - wbsd_free_mmc(dev); - return ret; - } - } - - /* - * Request resources. - */ - ret = wbsd_request_resources(host, base, irq, dma); - if (ret) { - wbsd_release_resources(host); - wbsd_free_mmc(dev); - return ret; - } - - /* - * See if chip needs to be configured. - */ - if (pnp) { - if ((host->config != 0) && !wbsd_chip_validate(host)) { - printk(KERN_WARNING DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); - wbsd_chip_config(host); - } - } else - wbsd_chip_config(host); - - /* - * Power Management stuff. No idea how this works. - * Not tested. - */ -#ifdef CONFIG_PM - if (host->config) { - wbsd_unlock_config(host); - wbsd_write_config(host, WBSD_CONF_PME, 0xA0); - wbsd_lock_config(host); - } -#endif - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - /* - * Reset the chip into a known state. - */ - wbsd_init_device(host); - - mmc_add_host(mmc); - - printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); - if (host->chip_id != 0) - printk(" id %x", (int)host->chip_id); - printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); - if (host->dma >= 0) - printk(" dma %d", (int)host->dma); - else - printk(" FIFO"); - if (pnp) - printk(" PnP"); - printk("\n"); - - return 0; -} - -static void __devexit wbsd_shutdown(struct device *dev, int pnp) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - struct wbsd_host *host; - - if (!mmc) - return; - - host = mmc_priv(mmc); - - mmc_remove_host(mmc); - - /* - * Power down the SD/MMC function. - */ - if (!pnp) - wbsd_chip_poweroff(host); - - wbsd_release_resources(host); - - wbsd_free_mmc(dev); -} - -/* - * Non-PnP - */ - -static int __devinit wbsd_probe(struct platform_device *dev) -{ - /* Use the module parameters for resources */ - return wbsd_init(&dev->dev, io, irq, dma, 0); -} - -static int __devexit wbsd_remove(struct platform_device *dev) -{ - wbsd_shutdown(&dev->dev, 0); - - return 0; -} - -/* - * PnP - */ - -#ifdef CONFIG_PNP - -static int __devinit -wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) -{ - int io, irq, dma; - - /* - * Get resources from PnP layer. - */ - io = pnp_port_start(pnpdev, 0); - irq = pnp_irq(pnpdev, 0); - if (pnp_dma_valid(pnpdev, 0)) - dma = pnp_dma(pnpdev, 0); - else - dma = -1; - - DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); - - return wbsd_init(&pnpdev->dev, io, irq, dma, 1); -} - -static void __devexit wbsd_pnp_remove(struct pnp_dev *dev) -{ - wbsd_shutdown(&dev->dev, 1); -} - -#endif /* CONFIG_PNP */ - -/* - * Power management - */ - -#ifdef CONFIG_PM - -static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) -{ - BUG_ON(host == NULL); - - return mmc_suspend_host(host->mmc, state); -} - -static int wbsd_resume(struct wbsd_host *host) -{ - BUG_ON(host == NULL); - - wbsd_init_device(host); - - return mmc_resume_host(host->mmc); -} - -static int wbsd_platform_suspend(struct platform_device *dev, - pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - struct wbsd_host *host; - int ret; - - if (mmc == NULL) - return 0; - - DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - ret = wbsd_suspend(host, state); - if (ret) - return ret; - - wbsd_chip_poweroff(host); - - return 0; -} - -static int wbsd_platform_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Resuming...\n"); - - host = mmc_priv(mmc); - - wbsd_chip_config(host); - - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - return wbsd_resume(host); -} - -#ifdef CONFIG_PNP - -static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) -{ - struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - return wbsd_suspend(host, state); -} - -static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) -{ - struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Resuming...\n"); - - host = mmc_priv(mmc); - - /* - * See if chip needs to be configured. - */ - if (host->config != 0) { - if (!wbsd_chip_validate(host)) { - printk(KERN_WARNING DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); - wbsd_chip_config(host); - } - } - - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - return wbsd_resume(host); -} - -#endif /* CONFIG_PNP */ - -#else /* CONFIG_PM */ - -#define wbsd_platform_suspend NULL -#define wbsd_platform_resume NULL - -#define wbsd_pnp_suspend NULL -#define wbsd_pnp_resume NULL - -#endif /* CONFIG_PM */ - -static struct platform_device *wbsd_device; - -static struct platform_driver wbsd_driver = { - .probe = wbsd_probe, - .remove = __devexit_p(wbsd_remove), - - .suspend = wbsd_platform_suspend, - .resume = wbsd_platform_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -#ifdef CONFIG_PNP - -static struct pnp_driver wbsd_pnp_driver = { - .name = DRIVER_NAME, - .id_table = pnp_dev_table, - .probe = wbsd_pnp_probe, - .remove = __devexit_p(wbsd_pnp_remove), - - .suspend = wbsd_pnp_suspend, - .resume = wbsd_pnp_resume, -}; - -#endif /* CONFIG_PNP */ - -/* - * Module loading/unloading - */ - -static int __init wbsd_drv_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_NAME - ": Winbond W83L51xD SD/MMC card interface driver\n"); - printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - -#ifdef CONFIG_PNP - - if (!nopnp) { - result = pnp_register_driver(&wbsd_pnp_driver); - if (result < 0) - return result; - } -#endif /* CONFIG_PNP */ - - if (nopnp) { - result = platform_driver_register(&wbsd_driver); - if (result < 0) - return result; - - wbsd_device = platform_device_alloc(DRIVER_NAME, -1); - if (!wbsd_device) { - platform_driver_unregister(&wbsd_driver); - return -ENOMEM; - } - - result = platform_device_add(wbsd_device); - if (result) { - platform_device_put(wbsd_device); - platform_driver_unregister(&wbsd_driver); - return result; - } - } - - return 0; -} - -static void __exit wbsd_drv_exit(void) -{ -#ifdef CONFIG_PNP - - if (!nopnp) - pnp_unregister_driver(&wbsd_pnp_driver); - -#endif /* CONFIG_PNP */ - - if (nopnp) { - platform_device_unregister(wbsd_device); - - platform_driver_unregister(&wbsd_driver); - } - - DBG("unloaded\n"); -} - -module_init(wbsd_drv_init); -module_exit(wbsd_drv_exit); -#ifdef CONFIG_PNP -module_param(nopnp, uint, 0444); -#endif -module_param(io, uint, 0444); -module_param(irq, uint, 0444); -module_param(dma, int, 0444); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); -MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); - -#ifdef CONFIG_PNP -MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); -#endif -MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); -MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); -MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); |