* [PATCH] wbsd update
@ 2005-01-19 10:41 Pierre Ossman
2005-01-21 11:13 ` Russell King
0 siblings, 1 reply; 5+ messages in thread
From: Pierre Ossman @ 2005-01-19 10:41 UTC (permalink / raw)
To: LKML, Russell King
[-- Attachment #1: Type: text/plain, Size: 332 bytes --]
This patch includes the latest changes to the wbsd driver.
Changelog:
* Proper usage of kunmap.
* Comment about hw bugs.
* Waits for data transfers to finish properly.
* Added module version info.
* FIFO bug fix for small reads.
* Optimised FIFO loop.
* DMA demand mode.
* IRQ race condition when sending commands fixed.
/Pierre
[-- Attachment #2: wbsd-2.6.10-5.patch --]
[-- Type: text/x-patch, Size: 11881 bytes --]
Index: linux/drivers/mmc/wbsd.c
===================================================================
--- linux/drivers/mmc/wbsd.c (revision 105)
+++ linux/drivers/mmc/wbsd.c (working copy)
@@ -1,11 +1,24 @@
/*
- * linux/drivers/mmc/wbsd.c
+ * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
*
- * Copyright (C) 2004 Pierre Ossman, All Rights Reserved.
+ * Copyright (C) 2004-2005 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 version 2 as
* published by the Free Software Foundation.
+ *
+ *
+ * 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/config.h>
@@ -27,7 +40,7 @@
#include "wbsd.h"
#define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.0"
+#define DRIVER_VERSION "1.1"
#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
@@ -239,13 +252,14 @@
static inline char* wbsd_kmap_sg(struct wbsd_host* host)
{
- return kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
+ host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
host->cur_sg->offset;
+ return host->mapped_sg;
}
static inline void wbsd_kunmap_sg(struct wbsd_host* host)
{
- kunmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ);
+ kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
}
static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
@@ -272,7 +286,7 @@
memcpy(dmabuf, sgbuf, size);
else
memcpy(dmabuf, sgbuf, sg[i].length);
- kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
dmabuf += sg[i].length;
if (size < sg[i].length)
@@ -318,7 +332,7 @@
memcpy(sgbuf, dmabuf, size);
else
memcpy(sgbuf, dmabuf, sg[i].length);
- kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
dmabuf += sg[i].length;
if (size < sg[i].length)
@@ -400,16 +414,16 @@
static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
{
int i;
- u8 status, eir, isr;
+ u8 status, isr;
DBGF("Sending cmd (%x)\n", cmd->opcode);
/*
- * Disable interrupts as the interrupt routine
- * will destroy the contents of ISR.
+ * Clear accumulated ISR. The interrupt routine
+ * will fill this one with events that occur during
+ * transfer.
*/
- eir = inb(host->base + WBSD_EIR);
- outb(0, host->base + WBSD_EIR);
+ host->isr = 0;
/*
* Send the command (CRC calculated by host).
@@ -435,7 +449,7 @@
/*
* Read back status.
*/
- isr = inb(host->base + WBSD_ISR);
+ isr = host->isr;
/* Card removed? */
if (isr & WBSD_INT_CARD)
@@ -456,17 +470,6 @@
}
}
- /*
- * Restore interrupt mask to previous value.
- */
- outb(eir, host->base + WBSD_EIR);
-
- /*
- * Call the interrupt routine to jump start
- * interrupts.
- */
- wbsd_irq(0, host, NULL);
-
DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
}
@@ -478,6 +481,7 @@
{
struct mmc_data* data = host->mrq->cmd->data;
char* buffer;
+ int i, fsr, fifo;
/*
* Handle excessive data.
@@ -491,60 +495,83 @@
* Drain the fifo. This has a tendency to loop longer
* than the FIFO length (usually one block).
*/
- while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_EMPTY))
+ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY))
{
- *buffer = inb(host->base + WBSD_DFR);
- buffer++;
- host->offset++;
- host->remain--;
-
- data->bytes_xfered++;
-
/*
- * Transfer done?
- */
- if (data->bytes_xfered == host->size)
- {
- wbsd_kunmap_sg(host);
- return;
- }
+ * 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;
- /*
- * End of scatter list entry?
- */
- if (host->remain == 0)
+ for (i = 0;i < fifo;i++)
{
- wbsd_kunmap_sg(host);
+ *buffer = inb(host->base + WBSD_DFR);
+ buffer++;
+ host->offset++;
+ host->remain--;
+
+ data->bytes_xfered++;
/*
- * Get next entry. Check if last.
+ * Transfer done?
*/
- if (!wbsd_next_sg(host))
+ if (data->bytes_xfered == host->size)
{
+ wbsd_kunmap_sg(host);
+ return;
+ }
+
+ /*
+ * End of scatter list entry?
+ */
+ if (host->remain == 0)
+ {
+ wbsd_kunmap_sg(host);
+
/*
- * We should never reach this point.
- * It means that we're trying to
- * transfer more blocks than can fit
- * into the scatter list.
+ * Get next entry. Check if last.
*/
- BUG_ON(1);
+ if (!wbsd_next_sg(host))
+ {
+ /*
+ * We should never reach this point.
+ * It means that we're trying to
+ * transfer more blocks than can fit
+ * into the scatter list.
+ */
+ BUG_ON(1);
+
+ host->size = data->bytes_xfered;
+
+ return;
+ }
- host->size = data->bytes_xfered;
-
- return;
+ buffer = wbsd_kmap_sg(host);
}
-
- buffer = wbsd_kmap_sg(host);
}
}
wbsd_kunmap_sg(host);
+
+ /*
+ * This is a very dirty hack to solve a
+ * hardware problem. The chip doesn't trigger
+ * FIFO threshold interrupts properly.
+ */
+ if ((host->size - 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
@@ -559,50 +586,64 @@
* Fill the fifo. This has a tendency to loop longer
* than the FIFO length (usually one block).
*/
- while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_FULL))
+ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL))
{
- outb(*buffer, host->base + WBSD_DFR);
- buffer++;
- host->offset++;
- host->remain--;
-
- data->bytes_xfered++;
-
/*
- * Transfer done?
- */
- if (data->bytes_xfered == host->size)
- {
- wbsd_kunmap_sg(host);
- return;
- }
+ * 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;
- /*
- * End of scatter list entry?
- */
- if (host->remain == 0)
+ for (i = 16;i > fifo;i--)
{
- wbsd_kunmap_sg(host);
+ outb(*buffer, host->base + WBSD_DFR);
+ buffer++;
+ host->offset++;
+ host->remain--;
+ data->bytes_xfered++;
+
/*
- * Get next entry. Check if last.
+ * Transfer done?
*/
- if (!wbsd_next_sg(host))
+ if (data->bytes_xfered == host->size)
{
+ wbsd_kunmap_sg(host);
+ return;
+ }
+
+ /*
+ * End of scatter list entry?
+ */
+ if (host->remain == 0)
+ {
+ wbsd_kunmap_sg(host);
+
/*
- * We should never reach this point.
- * It means that we're trying to
- * transfer more blocks than can fit
- * into the scatter list.
+ * Get next entry. Check if last.
*/
- BUG_ON(1);
+ if (!wbsd_next_sg(host))
+ {
+ /*
+ * We should never reach this point.
+ * It means that we're trying to
+ * transfer more blocks than can fit
+ * into the scatter list.
+ */
+ BUG_ON(1);
+
+ host->size = data->bytes_xfered;
+
+ return;
+ }
- host->size = data->bytes_xfered;
-
- return;
+ buffer = wbsd_kmap_sg(host);
}
-
- buffer = wbsd_kmap_sg(host);
}
}
@@ -689,9 +730,9 @@
disable_dma(host->dma);
clear_dma_ff(host->dma);
if (data->flags & MMC_DATA_READ)
- set_dma_mode(host->dma, DMA_MODE_READ);
+ set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
else
- set_dma_mode(host->dma, DMA_MODE_WRITE);
+ set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
set_dma_addr(host->dma, host->dma_addr);
set_dma_count(host->dma, host->size);
@@ -701,8 +742,7 @@
/*
* Enable DMA on the host.
*/
- wbsd_write_index(host, WBSD_IDX_DMA,
- WBSD_DMA_SINGLE | WBSD_DMA_ENABLE);
+ wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
}
else
{
@@ -746,6 +786,7 @@
{
unsigned long dmaflags;
int count;
+ u8 status;
WARN_ON(host->mrq == NULL);
@@ -754,6 +795,15 @@
*/
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?
@@ -851,7 +901,13 @@
* transfered.
*/
if (cmd->data && (cmd->error == MMC_ERR_NONE))
- {
+ {
+ /*
+ * Dirty fix for hardware bug.
+ */
+ if (host->dma == -1)
+ tasklet_schedule(&host->fifo_tasklet);
+
spin_unlock_bh(&host->lock);
return;
@@ -1021,7 +1077,6 @@
spin_lock(&host->lock);
- WARN_ON(!host->mrq);
if (!host->mrq)
goto end;
@@ -1046,7 +1101,6 @@
spin_lock(&host->lock);
- WARN_ON(!host->mrq);
if (!host->mrq)
goto end;
@@ -1126,6 +1180,8 @@
*/
if (isr == 0xff || isr == 0x00)
return IRQ_NONE;
+
+ host->isr |= isr;
/*
* Schedule tasklets as needed.
@@ -1133,7 +1189,7 @@
if (isr & WBSD_INT_CARD)
tasklet_schedule(&host->card_tasklet);
if (isr & WBSD_INT_FIFO_THRE)
- tasklet_hi_schedule(&host->fifo_tasklet);
+ tasklet_schedule(&host->fifo_tasklet);
if (isr & WBSD_INT_CRC)
tasklet_hi_schedule(&host->crc_tasklet);
if (isr & WBSD_INT_TIMEOUT)
@@ -1392,8 +1448,8 @@
* Maximum number of segments. Worst case is one sector per segment
* so this will be 64kB/512.
*/
- mmc->max_hw_segs = NR_SG;
- mmc->max_phys_segs = NR_SG;
+ mmc->max_hw_segs = 128;
+ mmc->max_phys_segs = 128;
/*
* Maximum number of sectors in one transfer. Also limited by 64kB
@@ -1590,6 +1646,7 @@
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
+MODULE_VERSION(DRIVER_VERSION);
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)");
Index: linux/drivers/mmc/wbsd.h
===================================================================
--- linux/drivers/mmc/wbsd.h (revision 105)
+++ linux/drivers/mmc/wbsd.h (working copy)
@@ -1,7 +1,7 @@
/*
- * linux/drivers/mmc/wbsd.h
+ * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
*
- * Copyright (C) 2004 Pierre Ossman, All Rights Reserved.
+ * Copyright (C) 2004-2005 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 version 2 as
@@ -119,6 +119,8 @@
#define WBSD_FIFOEN_FULL 0x10
#define WBSD_FIFO_THREMASK 0x0F
+#define WBSD_BLOCK_READ 0x80
+#define WBSD_BLOCK_WRITE 0x40
#define WBSD_BUSY 0x20
#define WBSD_CARDTRAFFIC 0x04
#define WBSD_SENDCMD 0x02
@@ -132,9 +134,6 @@
#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
-/* 64kB / 512 */
-#define NR_SG 128
-
struct wbsd_host
{
struct mmc_host* mmc; /* MMC structure */
@@ -143,9 +142,11 @@
struct mmc_request* mrq; /* Current request */
- struct scatterlist sg[NR_SG]; /* SG list */
+ u8 isr; /* Accumulated ISR */
+
struct scatterlist* cur_sg; /* Current SG entry */
unsigned int num_sg; /* Number of entries left */
+ void* mapped_sg; /* vaddr of mapped sg */
unsigned int offset; /* Offset into current entry */
unsigned int remain; /* Data left in curren entry */
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH] wbsd update
@ 2005-03-22 21:29 Pierre Ossman
2005-03-24 15:50 ` Russell King
0 siblings, 1 reply; 5+ messages in thread
From: Pierre Ossman @ 2005-03-22 21:29 UTC (permalink / raw)
To: LKML, Russell King
[-- Attachment #1: Type: text/plain, Size: 255 bytes --]
Updates to the wbsd driver.
* Fix to handle DAT3 card detection.
* Fixed bug which could cause large writes to stall in FIFO mode.
* Plug 'n Play support. In most cases you need ACPI PNP for this to work.
* Uses generic DMA API (ISA dependency removed).
[-- Attachment #2: wbsd-2.6.11-2.patch --]
[-- Type: text/plain, Size: 25014 bytes --]
Index: linux/drivers/mmc/wbsd.c
===================================================================
--- linux/drivers/mmc/wbsd.c (revision 132)
+++ linux/drivers/mmc/wbsd.c (working copy)
@@ -28,7 +28,9 @@
#include <linux/ioport.h>
#include <linux/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 <linux/mmc/protocol.h>
@@ -40,7 +42,7 @@
#include "wbsd.h"
#define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.1"
+#define DRIVER_VERSION "1.2"
#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
@@ -52,10 +54,6 @@
#define DBGF(x...) do { } while (0)
#endif
-static unsigned int io = 0x248;
-static unsigned int irq = 6;
-static int dma = 2;
-
#ifdef CONFIG_MMC_DEBUG
void DBG_REG(int reg, u8 value)
{
@@ -79,28 +77,61 @@
#endif
/*
+ * 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 */
+
+#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);
}
@@ -133,6 +164,13 @@
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);
@@ -148,6 +186,14 @@
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;
@@ -407,8 +453,6 @@
}
}
-static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs);
-
static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
{
int i;
@@ -646,6 +690,13 @@
}
wbsd_kunmap_sg(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)
@@ -850,9 +901,11 @@
wbsd_request_end(host, host->mrq);
}
-/*
- * MMC Callbacks
- */
+/*****************************************************************************\
+ * *
+ * MMC layer callbacks *
+ * *
+\*****************************************************************************/
static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
{
@@ -874,7 +927,7 @@
* If there is no card in the slot then
* timeout immediatly.
*/
- if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))
+ if (!(host->flags & WBSD_FCARD_PRESENT))
{
cmd->error = MMC_ERR_TIMEOUT;
goto done;
@@ -953,33 +1006,50 @@
host->clk = clk;
}
+ /*
+ * Power up card.
+ */
if (ios->power_mode != MMC_POWER_OFF)
{
- /*
- * Power up card.
- */
pwr = inb(host->base + WBSD_CSR);
pwr &= ~WBSD_POWER_N;
outb(pwr, host->base + WBSD_CSR);
+ }
- /*
- * This behaviour is stolen from the
- * Windows driver. Don't know why, but
- * it is needed.
- */
- setup = wbsd_read_index(host, WBSD_IDX_SETUP);
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- setup |= WBSD_DAT3_H;
- else
- setup &= ~WBSD_DAT3_H;
- wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-
- mdelay(1);
+ /*
+ * MMC cards need to have pin 1 high during init.
+ * Init time corresponds rather nicely with the bus mode.
+ * It wreaks havoc with the card detection though so
+ * that needs to be disabed.
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ if ((ios->power_mode == MMC_POWER_ON) &&
+ (ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
+ {
+ setup |= WBSD_DAT3_H;
+ host->flags |= WBSD_FIGNORE_DETECT;
}
-
+ else
+ {
+ setup &= ~WBSD_DAT3_H;
+ host->flags &= ~WBSD_FIGNORE_DETECT;
+ }
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
spin_unlock_bh(&host->lock);
}
+static struct mmc_host_ops wbsd_ops = {
+ .request = wbsd_request,
+ .set_ios = wbsd_set_ios,
+};
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
/*
* Tasklets
*/
@@ -1005,17 +1075,33 @@
{
struct wbsd_host* host = (struct wbsd_host*)param;
u8 csr;
+ int change = 0;
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)
- DBG("Card inserted\n");
- else
{
+ if (!(host->flags & WBSD_FCARD_PRESENT))
+ {
+ DBG("Card inserted\n");
+ host->flags |= WBSD_FCARD_PRESENT;
+ change = 1;
+ }
+ }
+ else if (host->flags & WBSD_FCARD_PRESENT)
+ {
DBG("Card removed\n");
+ host->flags &= ~WBSD_FCARD_PRESENT;
+ change = 1;
if (host->mrq)
{
@@ -1033,7 +1119,8 @@
*/
spin_unlock(&host->lock);
- mmc_detect_change(host->mmc);
+ if (change)
+ mmc_detect_change(host->mmc);
}
static void wbsd_tasklet_fifo(unsigned long param)
@@ -1200,12 +1287,86 @@
return IRQ_HANDLED;
}
+/*****************************************************************************\
+ * *
+ * Device initialisation and shutdown *
+ * *
+\*****************************************************************************/
+
/*
- * Support functions for probe
+ * Allocate/free MMC structure.
*/
-static int wbsd_scan(struct wbsd_host* host)
+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;
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * 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 number of sectors in one transfer. Also limited by 64kB
+ * buffer.
+ */
+ mmc->max_sectors = 128;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of segments.
+ */
+ mmc->max_seg_size = mmc->max_sectors * 512;
+
+ dev_set_drvdata(dev, mmc);
+
+ return 0;
+}
+
+static void __devexit wbsd_free_mmc(struct device* dev)
+{
+ struct mmc_host* mmc;
+
+ mmc = dev_get_drvdata(dev);
+ if (!mmc)
+ return;
+
+ 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;
@@ -1258,12 +1419,16 @@
return -ENODEV;
}
-static int wbsd_request_regions(struct wbsd_host* host)
+/*
+ * Allocate/free io port ranges
+ */
+
+static int __devinit wbsd_request_region(struct wbsd_host* host, int base)
{
if (io & 0x7)
return -EINVAL;
- if (!request_region(io, 8, DRIVER_NAME))
+ if (!request_region(base, 8, DRIVER_NAME))
return -EIO;
host->base = io;
@@ -1271,19 +1436,25 @@
return 0;
}
-static void wbsd_release_regions(struct wbsd_host* host)
+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;
}
-static void wbsd_init_dma(struct wbsd_host* host)
+/*
+ * Allocate/free DMA port and buffer
+ */
+
+static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
{
- host->dma = -1;
-
if (dma < 0)
return;
@@ -1294,7 +1465,7 @@
* We need to allocate a special buffer in
* order for ISA to be able to DMA to it.
*/
- host->dma_buffer = kmalloc(65536,
+ host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
if (!host->dma_buffer)
goto free;
@@ -1302,7 +1473,8 @@
/*
* Translate the address to a physical address.
*/
- host->dma_addr = isa_virt_to_bus(host->dma_buffer);
+ host->dma_addr = dma_map_single(host->dev, host->dma_buffer,
+ WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
/*
* ISA DMA must be aligned on a 64k basis.
@@ -1325,6 +1497,10 @@
*/
BUG_ON(1);
+ dma_unmap_single(host->dev, host->dma_addr, WBSD_DMA_SIZE,
+ DMA_BIDIRECTIONAL);
+ host->dma_addr = (dma_addr_t)NULL;
+
kfree(host->dma_buffer);
host->dma_buffer = NULL;
@@ -1336,62 +1512,124 @@
"Falling back on FIFO.\n", dma);
}
-static struct mmc_host_ops wbsd_ops = {
- .request = wbsd_request,
- .set_ios = wbsd_set_ios,
-};
+static void __devexit wbsd_release_dma(struct wbsd_host* host)
+{
+ if (host->dma_addr)
+ dma_unmap_single(host->dev, host->dma_addr, WBSD_DMA_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (host->dma_buffer)
+ 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;
+}
/*
- * Device probe
+ * Allocate/free IRQ.
*/
-static int wbsd_probe(struct device* dev)
+static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq)
{
- struct wbsd_host* host = NULL;
- struct mmc_host* mmc = NULL;
int ret;
/*
- * Allocate MMC structure.
+ * Allocate interrupt.
*/
- mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
- if (!mmc)
- return -ENOMEM;
+
+ ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
+ if (ret)
+ return ret;
- host = mmc_priv(mmc);
- host->mmc = mmc;
-
+ host->irq = irq;
+
/*
- * Scan for hardware.
+ * Set up tasklets.
*/
- ret = wbsd_scan(host);
- if (ret)
- goto freemmc;
+ 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);
+ tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
+
+ return 0;
+}
- /*
- * Reset the chip.
- */
- wbsd_write_config(host, WBSD_CONF_SWRST, 1);
- wbsd_write_config(host, WBSD_CONF_SWRST, 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);
+ tasklet_kill(&host->block_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_regions(host);
+ ret = wbsd_request_region(host, base);
if (ret)
- goto release;
+ return ret;
/*
- * Set host parameters.
+ * Allocate interrupt.
*/
- mmc->ops = &wbsd_ops;
- mmc->f_min = 375000;
- mmc->f_max = 24000000;
- mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ ret = wbsd_request_irq(host, irq);
+ if (ret)
+ return ret;
+
+ /*
+ * Allocate DMA.
+ */
+ wbsd_request_dma(host, dma);
- spin_lock_init(&host->lock);
+ 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 __devinit wbsd_chip_config(struct wbsd_host* 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);
@@ -1399,165 +1637,241 @@
/*
* Set up card detection.
*/
- wbsd_write_config(host, WBSD_CONF_PINS, 0x02);
+ wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
/*
- * Configure I/O port.
+ * Configure chip
*/
wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
-
- /*
- * Allocate interrupt.
- */
- ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
- if (ret)
- goto release;
- host->irq = irq;
+ wbsd_write_config(host, WBSD_CONF_IRQ, host->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);
- tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
+ if (host->dma >= 0)
+ wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
/*
- * Configure interrupt.
+ * Enable and power up chip.
*/
- wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+ wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+ wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+}
+
+/*
+ * Check that configured resources are correct.
+ */
+
+static int __devinit wbsd_chip_validate(struct wbsd_host* host)
+{
+ int base, irq, dma;
/*
- * Allocate DMA.
+ * Select SD/MMC function.
*/
- wbsd_init_dma(host);
+ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
/*
- * If all went well, then configure DMA.
+ * Read configuration.
*/
- if (host->dma >= 0)
- wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+ base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
+ base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
- /*
- * 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;
+ irq = wbsd_read_config(host, WBSD_CONF_IRQ);
+ dma = wbsd_read_config(host, WBSD_CONF_DRQ);
+
/*
- * Maximum number of sectors in one transfer. Also limited by 64kB
- * buffer.
+ * Validate against given configuration.
*/
- mmc->max_sectors = 128;
+ if (base != host->base)
+ return 0;
+ if (irq != host->irq)
+ return 0;
+ if ((dma != host->dma) && (host->dma != -1))
+ return 0;
+ return 1;
+}
+
+/*****************************************************************************\
+ * *
+ * 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);
+
/*
- * Maximum segment size. Could be one segment with the maximum number
- * of segments.
+ * Scan for hardware.
*/
- mmc->max_seg_size = mmc->max_sectors * 512;
+ 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;
+ }
+ }
/*
- * Enable chip.
+ * Request resources.
*/
- wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+ ret = wbsd_request_resources(host, io, irq, dma);
+ if (ret)
+ {
+ wbsd_release_resources(host);
+ wbsd_free_mmc(dev);
+ return ret;
+ }
/*
- * Power up chip.
+ * See if chip needs to be configured.
*/
- wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+ if (pnp && (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);
+ }
+ }
+ else
+ wbsd_chip_config(host);
/*
* Power Management stuff. No idea how this works.
* Not tested.
*/
#ifdef CONFIG_PM
- wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+ if (host->config)
+ wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
#endif
+ /*
+ * Allow device to initialise itself properly.
+ */
+ mdelay(5);
/*
* Reset the chip into a known state.
*/
wbsd_init_device(host);
- dev_set_drvdata(dev, mmc);
-
- /*
- * Add host to MMC layer.
- */
mmc_add_host(mmc);
- printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",
- mmc->host_name, (int)host->chip_id, (int)host->base,
- (int)host->irq, (int)host->dma);
+ printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
+ 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;
-
-release:
- wbsd_release_regions(host);
-
-freemmc:
- mmc_free_host(mmc);
-
- return ret;
}
-/*
- * Device remove
- */
-
-static int wbsd_remove(struct device* dev)
+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 0;
+ return;
host = mmc_priv(mmc);
- /*
- * Unregister host with MMC layer.
- */
mmc_remove_host(mmc);
- /*
- * Power down the SD/MMC function.
- */
- 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);
+ if (!pnp)
+ {
+ /*
+ * Power down the SD/MMC function.
+ */
+ 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);
+ }
- /*
- * Free resources.
- */
- if (host->dma_buffer)
- kfree(host->dma_buffer);
+ wbsd_release_resources(host);
- if (host->dma >= 0)
- free_dma(host->dma);
+ wbsd_free_mmc(dev);
+}
- free_irq(host->irq, host);
+/*
+ * Non-PnP
+ */
+
+static int __devinit wbsd_probe(struct device* dev)
+{
+ return wbsd_init(dev, io, irq, dma, 0);
+}
+
+static int __devexit wbsd_remove(struct device* dev)
+{
+ wbsd_shutdown(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;
- 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);
- tasklet_kill(&host->block_tasklet);
+ /*
+ * 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;
- wbsd_release_regions(host);
+ DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
- mmc_free_host(mmc);
+ return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
+}
- return 0;
+static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
+{
+ wbsd_shutdown(&dev->dev, 1);
}
+#endif /* CONFIG_PNP */
+
/*
* Power management
*/
@@ -1581,7 +1895,7 @@
#define wbsd_resume NULL
#endif
-static void wbsd_release(struct device *dev)
+static void wbsd_release_dev(struct device *dev)
{
}
@@ -1589,7 +1903,7 @@
.name = DRIVER_NAME,
.id = -1,
.dev = {
- .release = wbsd_release,
+ .release = wbsd_release_dev,
},
};
@@ -1603,6 +1917,17 @@
.resume = wbsd_resume,
};
+#ifdef CONFIG_PNP
+
+static struct pnp_driver wbsd_pnp_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pnp_dev_table,
+ .probe = wbsd_pnp_probe,
+ .remove = wbsd_pnp_remove,
+};
+
+#endif /* CONFIG_PNP */
+
/*
* Module loading/unloading
*/
@@ -1615,29 +1940,56 @@
": Winbond W83L51xD SD/MMC card interface driver, "
DRIVER_VERSION "\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 */
- result = driver_register(&wbsd_driver);
- if (result < 0)
- return result;
+ if (nopnp)
+ {
+ result = driver_register(&wbsd_driver);
+ if (result < 0)
+ return result;
- result = platform_device_register(&wbsd_device);
- if (result < 0)
- return result;
+ result = platform_device_register(&wbsd_device);
+ if (result < 0)
+ return result;
+ }
return 0;
}
static void __exit wbsd_drv_exit(void)
{
- platform_device_unregister(&wbsd_device);
+#ifdef CONFIG_PNP
+
+ if (!nopnp)
+ pnp_unregister_driver(&wbsd_pnp_driver);
- driver_unregister(&wbsd_driver);
+#endif /* CONFIG_PNP */
+ if (nopnp)
+ {
+ platform_device_unregister(&wbsd_device);
+
+ 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);
@@ -1646,6 +1998,9 @@
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
MODULE_VERSION(DRIVER_VERSION);
+#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)");
Index: linux/drivers/mmc/wbsd.h
===================================================================
--- linux/drivers/mmc/wbsd.h (revision 132)
+++ linux/drivers/mmc/wbsd.h (working copy)
@@ -35,6 +35,12 @@
#define DEVICE_SD 0x03
+#define WBSD_PINS_DAT3_HI 0x20
+#define WBSD_PINS_DAT3_OUT 0x10
+#define WBSD_PINS_GP11_HI 0x04
+#define WBSD_PINS_DETECT_GP11 0x02
+#define WBSD_PINS_DETECT_DAT3 0x01
+
#define WBSD_CMDR 0x00
#define WBSD_DFR 0x01
#define WBSD_EIR 0x02
@@ -133,13 +139,20 @@
#define WBSD_CRC_OK 0x05 /* S010E (00101) */
#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
+#define WBSD_DMA_SIZE 65536
struct wbsd_host
{
+ struct device* dev;
struct mmc_host* mmc; /* MMC structure */
spinlock_t lock; /* Mutex */
+ int flags; /* Driver states */
+
+#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */
+#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */
+
struct mmc_request* mrq; /* Current request */
u8 isr; /* Accumulated ISR */
Index: linux/drivers/mmc/Kconfig
===================================================================
--- linux/drivers/mmc/Kconfig (revision 132)
+++ linux/drivers/mmc/Kconfig (working copy)
@@ -51,7 +77,7 @@
config MMC_WBSD
tristate "Winbond W83L51xD SD/MMC Card Interface support"
- depends on MMC && ISA
+ depends on MMC
help
This selects the Winbond(R) W83L51xD Secure digital and
Multimedia card Interface.
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] wbsd update
2005-03-22 21:29 Pierre Ossman
@ 2005-03-24 15:50 ` Russell King
2005-04-01 7:36 ` Pierre Ossman
0 siblings, 1 reply; 5+ messages in thread
From: Russell King @ 2005-03-24 15:50 UTC (permalink / raw)
To: Pierre Ossman; +Cc: LKML
On Tue, Mar 22, 2005 at 10:29:35PM +0100, Pierre Ossman wrote:
> @@ -1581,7 +1895,7 @@
> #define wbsd_resume NULL
> #endif
>
> -static void wbsd_release(struct device *dev)
> +static void wbsd_release_dev(struct device *dev)
> {
> }
>
> @@ -1589,7 +1903,7 @@
> .name = DRIVER_NAME,
> .id = -1,
> .dev = {
> - .release = wbsd_release,
> + .release = wbsd_release_dev,
> },
> };
>
Please use platform_device_register_simple() instead of providing a buggy
release function.
> @@ -133,13 +139,20 @@
> #define WBSD_CRC_OK 0x05 /* S010E (00101) */
> #define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
>
> +#define WBSD_DMA_SIZE 65536
>
> struct wbsd_host
> {
> + struct device* dev;
You might like to consider using mmc->dev instead of duplicating it
here.
--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of: 2.6 Serial core
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] wbsd update
2005-03-24 15:50 ` Russell King
@ 2005-04-01 7:36 ` Pierre Ossman
0 siblings, 0 replies; 5+ messages in thread
From: Pierre Ossman @ 2005-04-01 7:36 UTC (permalink / raw)
To: Russell King; +Cc: LKML
[-- Attachment #1: Type: text/plain, Size: 335 bytes --]
Russell King wrote:
>Please use platform_device_register_simple() instead of providing a buggy
>release function.
>
>
>
>You might like to consider using mmc->dev instead of duplicating it
>here.
>
>
>
Sorry about the delay. It's been a rather busy week. Included is the
same patch with the two issues above fixed.
Rgds
Pierre
[-- Attachment #2: wbsd-2.6.11-3.patch --]
[-- Type: text/plain, Size: 25118 bytes --]
Index: linux/drivers/mmc/wbsd.c
===================================================================
--- linux/drivers/mmc/wbsd.c (revision 132)
+++ linux/drivers/mmc/wbsd.c (working copy)
@@ -28,7 +28,9 @@
#include <linux/ioport.h>
#include <linux/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 <linux/mmc/protocol.h>
@@ -40,7 +42,7 @@
#include "wbsd.h"
#define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.1"
+#define DRIVER_VERSION "1.2"
#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
@@ -52,10 +54,6 @@
#define DBGF(x...) do { } while (0)
#endif
-static unsigned int io = 0x248;
-static unsigned int irq = 6;
-static int dma = 2;
-
#ifdef CONFIG_MMC_DEBUG
void DBG_REG(int reg, u8 value)
{
@@ -79,28 +77,61 @@
#endif
/*
+ * 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 */
+
+#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);
}
@@ -133,6 +164,13 @@
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);
@@ -148,6 +186,14 @@
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;
@@ -407,8 +453,6 @@
}
}
-static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs);
-
static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
{
int i;
@@ -646,6 +690,13 @@
}
wbsd_kunmap_sg(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)
@@ -850,9 +901,11 @@
wbsd_request_end(host, host->mrq);
}
-/*
- * MMC Callbacks
- */
+/*****************************************************************************\
+ * *
+ * MMC layer callbacks *
+ * *
+\*****************************************************************************/
static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
{
@@ -874,7 +927,7 @@
* If there is no card in the slot then
* timeout immediatly.
*/
- if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))
+ if (!(host->flags & WBSD_FCARD_PRESENT))
{
cmd->error = MMC_ERR_TIMEOUT;
goto done;
@@ -953,33 +1006,50 @@
host->clk = clk;
}
+ /*
+ * Power up card.
+ */
if (ios->power_mode != MMC_POWER_OFF)
{
- /*
- * Power up card.
- */
pwr = inb(host->base + WBSD_CSR);
pwr &= ~WBSD_POWER_N;
outb(pwr, host->base + WBSD_CSR);
+ }
- /*
- * This behaviour is stolen from the
- * Windows driver. Don't know why, but
- * it is needed.
- */
- setup = wbsd_read_index(host, WBSD_IDX_SETUP);
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- setup |= WBSD_DAT3_H;
- else
- setup &= ~WBSD_DAT3_H;
- wbsd_write_index(host, WBSD_IDX_SETUP, setup);
-
- mdelay(1);
+ /*
+ * MMC cards need to have pin 1 high during init.
+ * Init time corresponds rather nicely with the bus mode.
+ * It wreaks havoc with the card detection though so
+ * that needs to be disabed.
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ if ((ios->power_mode == MMC_POWER_ON) &&
+ (ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
+ {
+ setup |= WBSD_DAT3_H;
+ host->flags |= WBSD_FIGNORE_DETECT;
}
-
+ else
+ {
+ setup &= ~WBSD_DAT3_H;
+ host->flags &= ~WBSD_FIGNORE_DETECT;
+ }
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
spin_unlock_bh(&host->lock);
}
+static struct mmc_host_ops wbsd_ops = {
+ .request = wbsd_request,
+ .set_ios = wbsd_set_ios,
+};
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
/*
* Tasklets
*/
@@ -1005,17 +1075,33 @@
{
struct wbsd_host* host = (struct wbsd_host*)param;
u8 csr;
+ int change = 0;
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)
- DBG("Card inserted\n");
- else
{
+ if (!(host->flags & WBSD_FCARD_PRESENT))
+ {
+ DBG("Card inserted\n");
+ host->flags |= WBSD_FCARD_PRESENT;
+ change = 1;
+ }
+ }
+ else if (host->flags & WBSD_FCARD_PRESENT)
+ {
DBG("Card removed\n");
+ host->flags &= ~WBSD_FCARD_PRESENT;
+ change = 1;
if (host->mrq)
{
@@ -1033,7 +1119,8 @@
*/
spin_unlock(&host->lock);
- mmc_detect_change(host->mmc);
+ if (change)
+ mmc_detect_change(host->mmc);
}
static void wbsd_tasklet_fifo(unsigned long param)
@@ -1200,12 +1287,86 @@
return IRQ_HANDLED;
}
+/*****************************************************************************\
+ * *
+ * Device initialisation and shutdown *
+ * *
+\*****************************************************************************/
+
/*
- * Support functions for probe
+ * Allocate/free MMC structure.
*/
-static int wbsd_scan(struct wbsd_host* host)
+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;
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * 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 number of sectors in one transfer. Also limited by 64kB
+ * buffer.
+ */
+ mmc->max_sectors = 128;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of segments.
+ */
+ mmc->max_seg_size = mmc->max_sectors * 512;
+
+ dev_set_drvdata(dev, mmc);
+
+ return 0;
+}
+
+static void __devexit wbsd_free_mmc(struct device* dev)
+{
+ struct mmc_host* mmc;
+
+ mmc = dev_get_drvdata(dev);
+ if (!mmc)
+ return;
+
+ 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;
@@ -1258,12 +1419,16 @@
return -ENODEV;
}
-static int wbsd_request_regions(struct wbsd_host* host)
+/*
+ * Allocate/free io port ranges
+ */
+
+static int __devinit wbsd_request_region(struct wbsd_host* host, int base)
{
if (io & 0x7)
return -EINVAL;
- if (!request_region(io, 8, DRIVER_NAME))
+ if (!request_region(base, 8, DRIVER_NAME))
return -EIO;
host->base = io;
@@ -1271,19 +1436,25 @@
return 0;
}
-static void wbsd_release_regions(struct wbsd_host* host)
+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;
}
-static void wbsd_init_dma(struct wbsd_host* host)
+/*
+ * Allocate/free DMA port and buffer
+ */
+
+static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
{
- host->dma = -1;
-
if (dma < 0)
return;
@@ -1294,7 +1465,7 @@
* We need to allocate a special buffer in
* order for ISA to be able to DMA to it.
*/
- host->dma_buffer = kmalloc(65536,
+ host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
if (!host->dma_buffer)
goto free;
@@ -1302,7 +1473,8 @@
/*
* Translate the address to a physical address.
*/
- host->dma_addr = isa_virt_to_bus(host->dma_buffer);
+ host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer,
+ WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
/*
* ISA DMA must be aligned on a 64k basis.
@@ -1325,6 +1497,10 @@
*/
BUG_ON(1);
+ dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
+ DMA_BIDIRECTIONAL);
+ host->dma_addr = (dma_addr_t)NULL;
+
kfree(host->dma_buffer);
host->dma_buffer = NULL;
@@ -1336,62 +1512,124 @@
"Falling back on FIFO.\n", dma);
}
-static struct mmc_host_ops wbsd_ops = {
- .request = wbsd_request,
- .set_ios = wbsd_set_ios,
-};
+static void __devexit wbsd_release_dma(struct wbsd_host* host)
+{
+ if (host->dma_addr)
+ dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (host->dma_buffer)
+ 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;
+}
/*
- * Device probe
+ * Allocate/free IRQ.
*/
-static int wbsd_probe(struct device* dev)
+static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq)
{
- struct wbsd_host* host = NULL;
- struct mmc_host* mmc = NULL;
int ret;
/*
- * Allocate MMC structure.
+ * Allocate interrupt.
*/
- mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
- if (!mmc)
- return -ENOMEM;
+
+ ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
+ if (ret)
+ return ret;
- host = mmc_priv(mmc);
- host->mmc = mmc;
-
+ host->irq = irq;
+
/*
- * Scan for hardware.
+ * Set up tasklets.
*/
- ret = wbsd_scan(host);
- if (ret)
- goto freemmc;
+ 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);
+ tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
+
+ return 0;
+}
- /*
- * Reset the chip.
- */
- wbsd_write_config(host, WBSD_CONF_SWRST, 1);
- wbsd_write_config(host, WBSD_CONF_SWRST, 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);
+ tasklet_kill(&host->block_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_regions(host);
+ ret = wbsd_request_region(host, base);
if (ret)
- goto release;
+ return ret;
/*
- * Set host parameters.
+ * Allocate interrupt.
*/
- mmc->ops = &wbsd_ops;
- mmc->f_min = 375000;
- mmc->f_max = 24000000;
- mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ ret = wbsd_request_irq(host, irq);
+ if (ret)
+ return ret;
+
+ /*
+ * Allocate DMA.
+ */
+ wbsd_request_dma(host, dma);
- spin_lock_init(&host->lock);
+ 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 __devinit wbsd_chip_config(struct wbsd_host* 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);
@@ -1399,165 +1637,241 @@
/*
* Set up card detection.
*/
- wbsd_write_config(host, WBSD_CONF_PINS, 0x02);
+ wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
/*
- * Configure I/O port.
+ * Configure chip
*/
wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
-
- /*
- * Allocate interrupt.
- */
- ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
- if (ret)
- goto release;
- host->irq = irq;
+ wbsd_write_config(host, WBSD_CONF_IRQ, host->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);
- tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
+ if (host->dma >= 0)
+ wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
/*
- * Configure interrupt.
+ * Enable and power up chip.
*/
- wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+ wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+ wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+}
+
+/*
+ * Check that configured resources are correct.
+ */
+
+static int __devinit wbsd_chip_validate(struct wbsd_host* host)
+{
+ int base, irq, dma;
/*
- * Allocate DMA.
+ * Select SD/MMC function.
*/
- wbsd_init_dma(host);
+ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
/*
- * If all went well, then configure DMA.
+ * Read configuration.
*/
- if (host->dma >= 0)
- wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+ base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
+ base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
- /*
- * 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;
+ irq = wbsd_read_config(host, WBSD_CONF_IRQ);
+ dma = wbsd_read_config(host, WBSD_CONF_DRQ);
+
/*
- * Maximum number of sectors in one transfer. Also limited by 64kB
- * buffer.
+ * Validate against given configuration.
*/
- mmc->max_sectors = 128;
+ if (base != host->base)
+ return 0;
+ if (irq != host->irq)
+ return 0;
+ if ((dma != host->dma) && (host->dma != -1))
+ return 0;
+ return 1;
+}
+
+/*****************************************************************************\
+ * *
+ * 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);
+
/*
- * Maximum segment size. Could be one segment with the maximum number
- * of segments.
+ * Scan for hardware.
*/
- mmc->max_seg_size = mmc->max_sectors * 512;
+ 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;
+ }
+ }
/*
- * Enable chip.
+ * Request resources.
*/
- wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+ ret = wbsd_request_resources(host, io, irq, dma);
+ if (ret)
+ {
+ wbsd_release_resources(host);
+ wbsd_free_mmc(dev);
+ return ret;
+ }
/*
- * Power up chip.
+ * See if chip needs to be configured.
*/
- wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+ if (pnp && (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);
+ }
+ }
+ else
+ wbsd_chip_config(host);
/*
* Power Management stuff. No idea how this works.
* Not tested.
*/
#ifdef CONFIG_PM
- wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+ if (host->config)
+ wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
#endif
+ /*
+ * Allow device to initialise itself properly.
+ */
+ mdelay(5);
/*
* Reset the chip into a known state.
*/
wbsd_init_device(host);
- dev_set_drvdata(dev, mmc);
-
- /*
- * Add host to MMC layer.
- */
mmc_add_host(mmc);
- printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",
- mmc->host_name, (int)host->chip_id, (int)host->base,
- (int)host->irq, (int)host->dma);
+ printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
+ 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;
-
-release:
- wbsd_release_regions(host);
-
-freemmc:
- mmc_free_host(mmc);
-
- return ret;
}
-/*
- * Device remove
- */
-
-static int wbsd_remove(struct device* dev)
+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 0;
+ return;
host = mmc_priv(mmc);
- /*
- * Unregister host with MMC layer.
- */
mmc_remove_host(mmc);
- /*
- * Power down the SD/MMC function.
- */
- 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);
+ if (!pnp)
+ {
+ /*
+ * Power down the SD/MMC function.
+ */
+ 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);
+ }
- /*
- * Free resources.
- */
- if (host->dma_buffer)
- kfree(host->dma_buffer);
+ wbsd_release_resources(host);
- if (host->dma >= 0)
- free_dma(host->dma);
+ wbsd_free_mmc(dev);
+}
- free_irq(host->irq, host);
+/*
+ * Non-PnP
+ */
+
+static int __devinit wbsd_probe(struct device* dev)
+{
+ return wbsd_init(dev, io, irq, dma, 0);
+}
+
+static int __devexit wbsd_remove(struct device* dev)
+{
+ wbsd_shutdown(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;
- 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);
- tasklet_kill(&host->block_tasklet);
+ /*
+ * 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;
- wbsd_release_regions(host);
+ DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
- mmc_free_host(mmc);
+ return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
+}
- return 0;
+static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
+{
+ wbsd_shutdown(&dev->dev, 1);
}
+#endif /* CONFIG_PNP */
+
/*
* Power management
*/
@@ -1581,18 +1895,8 @@
#define wbsd_resume NULL
#endif
-static void wbsd_release(struct device *dev)
-{
-}
+static struct platform_device *wbsd_device;
-static struct platform_device wbsd_device = {
- .name = DRIVER_NAME,
- .id = -1,
- .dev = {
- .release = wbsd_release,
- },
-};
-
static struct device_driver wbsd_driver = {
.name = DRIVER_NAME,
.bus = &platform_bus_type,
@@ -1603,6 +1907,17 @@
.resume = wbsd_resume,
};
+#ifdef CONFIG_PNP
+
+static struct pnp_driver wbsd_pnp_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pnp_dev_table,
+ .probe = wbsd_pnp_probe,
+ .remove = wbsd_pnp_remove,
+};
+
+#endif /* CONFIG_PNP */
+
/*
* Module loading/unloading
*/
@@ -1615,29 +1930,57 @@
": Winbond W83L51xD SD/MMC card interface driver, "
DRIVER_VERSION "\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 */
- result = driver_register(&wbsd_driver);
- if (result < 0)
- return result;
+ if (nopnp)
+ {
+ result = driver_register(&wbsd_driver);
+ if (result < 0)
+ return result;
- result = platform_device_register(&wbsd_device);
- if (result < 0)
- return result;
+ wbsd_device = platform_device_register_simple(DRIVER_NAME, -1,
+ NULL, 0);
+ if (IS_ERR(wbsd_device))
+ return PTR_ERR(wbsd_device);
+ }
return 0;
}
static void __exit wbsd_drv_exit(void)
{
- platform_device_unregister(&wbsd_device);
+#ifdef CONFIG_PNP
+
+ if (!nopnp)
+ pnp_unregister_driver(&wbsd_pnp_driver);
- driver_unregister(&wbsd_driver);
+#endif /* CONFIG_PNP */
+ if (nopnp)
+ {
+ platform_device_unregister(wbsd_device);
+
+ 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);
@@ -1646,6 +1989,9 @@
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
MODULE_VERSION(DRIVER_VERSION);
+#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)");
Index: linux/drivers/mmc/wbsd.h
===================================================================
--- linux/drivers/mmc/wbsd.h (revision 132)
+++ linux/drivers/mmc/wbsd.h (working copy)
@@ -35,6 +35,12 @@
#define DEVICE_SD 0x03
+#define WBSD_PINS_DAT3_HI 0x20
+#define WBSD_PINS_DAT3_OUT 0x10
+#define WBSD_PINS_GP11_HI 0x04
+#define WBSD_PINS_DETECT_GP11 0x02
+#define WBSD_PINS_DETECT_DAT3 0x01
+
#define WBSD_CMDR 0x00
#define WBSD_DFR 0x01
#define WBSD_EIR 0x02
@@ -133,6 +139,7 @@
#define WBSD_CRC_OK 0x05 /* S010E (00101) */
#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
+#define WBSD_DMA_SIZE 65536
struct wbsd_host
{
@@ -140,6 +147,11 @@
spinlock_t lock; /* Mutex */
+ int flags; /* Driver states */
+
+#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */
+#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */
+
struct mmc_request* mrq; /* Current request */
u8 isr; /* Accumulated ISR */
Index: linux/drivers/mmc/Kconfig
===================================================================
--- linux/drivers/mmc/Kconfig (revision 132)
+++ linux/drivers/mmc/Kconfig (working copy)
@@ -51,7 +77,7 @@
config MMC_WBSD
tristate "Winbond W83L51xD SD/MMC Card Interface support"
- depends on MMC && ISA
+ depends on MMC
help
This selects the Winbond(R) W83L51xD Secure digital and
Multimedia card Interface.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2005-04-01 7:39 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-01-19 10:41 [PATCH] wbsd update Pierre Ossman
2005-01-21 11:13 ` Russell King
-- strict thread matches above, loose matches on Subject: below --
2005-03-22 21:29 Pierre Ossman
2005-03-24 15:50 ` Russell King
2005-04-01 7:36 ` Pierre Ossman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox