Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85bcc130 authored by Pierre Ossman's avatar Pierre Ossman Committed by Russell King
Browse files

[PATCH] MMC: wbsd update

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).
parent 88d7bd8c
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -51,7 +51,7 @@ config MMC_PXA


config MMC_WBSD
config MMC_WBSD
	tristate "Winbond W83L51xD SD/MMC Card Interface support"
	tristate "Winbond W83L51xD SD/MMC Card Interface support"
	depends on MMC && ISA && ISA_DMA_API
	depends on MMC && ISA_DMA_API
	help
	help
	  This selects the Winbond(R) W83L51xD Secure digital and
	  This selects the Winbond(R) W83L51xD Secure digital and
          Multimedia card Interface.
          Multimedia card Interface.
+531 −185
Original line number Original line Diff line number Diff line
@@ -28,7 +28,9 @@
#include <linux/ioport.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/pnp.h>
#include <linux/highmem.h>
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/protocol.h>
@@ -40,7 +42,7 @@
#include "wbsd.h"
#include "wbsd.h"


#define DRIVER_NAME "wbsd"
#define DRIVER_NAME "wbsd"
#define DRIVER_VERSION "1.1"
#define DRIVER_VERSION "1.2"


#ifdef CONFIG_MMC_DEBUG
#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
#define DBG(x...) \
@@ -52,10 +54,6 @@
#define DBGF(x...)	do { } while (0)
#define DBGF(x...)	do { } while (0)
#endif
#endif


static unsigned int io = 0x248;
static unsigned int irq = 6;
static int dma = 2;

#ifdef CONFIG_MMC_DEBUG
#ifdef CONFIG_MMC_DEBUG
void DBG_REG(int reg, u8 value)
void DBG_REG(int reg, u8 value)
{
{
@@ -78,29 +76,62 @@ void DBG_REG(int reg, u8 value)
#define DBG_REG(r, v) do {}  while (0)
#define DBG_REG(r, v) do {}  while (0)
#endif
#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
 * Basic functions
 */
 */


static inline void wbsd_unlock_config(struct wbsd_host* host)
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);
	outb(host->unlock_code, host->config);
	outb(host->unlock_code, host->config);
}
}


static inline void wbsd_lock_config(struct wbsd_host* host)
static inline void wbsd_lock_config(struct wbsd_host* host)
{
{
	BUG_ON(host->config == 0);
	
	outb(LOCK_CODE, host->config);
	outb(LOCK_CODE, host->config);
}
}


static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value)
static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value)
{
{
	BUG_ON(host->config == 0);
	
	outb(reg, host->config);
	outb(reg, host->config);
	outb(value, host->config + 1);
	outb(value, host->config + 1);
}
}


static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg)
static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg)
{
{
	BUG_ON(host->config == 0);
	
	outb(reg, host->config);
	outb(reg, host->config);
	return inb(host->config + 1);
	return inb(host->config + 1);
}
}
@@ -132,6 +163,13 @@ static void wbsd_init_device(struct wbsd_host* host)
	setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
	setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
	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.
	 * Read back default clock.
	 */
	 */
@@ -147,6 +185,14 @@ static void wbsd_init_device(struct wbsd_host* host)
	 */
	 */
	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
	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.
	 * Enable interesting interrupts.
	 */
	 */
@@ -407,8 +453,6 @@ static inline void wbsd_get_long_reply(struct wbsd_host* host,
	}
	}
}
}


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)
static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
{
{
	int i;
	int i;
@@ -646,6 +690,13 @@ static void wbsd_fill_fifo(struct wbsd_host* host)
	}
	}
	
	
	wbsd_kunmap_sg(host);
	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)
static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
@@ -850,9 +901,11 @@ static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
	wbsd_request_end(host, host->mrq);
	wbsd_request_end(host, host->mrq);
}
}


/*
/*****************************************************************************\
 * MMC Callbacks
 *                                                                           *
 */
 * MMC layer callbacks                                                       *
 *                                                                           *
\*****************************************************************************/


static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
{
{
@@ -874,7 +927,7 @@ static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
	 * If there is no card in the slot then
	 * If there is no card in the slot then
	 * timeout immediatly.
	 * timeout immediatly.
	 */
	 */
	if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))
	if (!(host->flags & WBSD_FCARD_PRESENT))
	{
	{
		cmd->error = MMC_ERR_TIMEOUT;
		cmd->error = MMC_ERR_TIMEOUT;
		goto done;
		goto done;
@@ -953,33 +1006,50 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
		host->clk = clk;
		host->clk = clk;
	}
	}


	if (ios->power_mode != MMC_POWER_OFF)
	{
	/*
	/*
	 * Power up card.
	 * Power up card.
	 */
	 */
	if (ios->power_mode != MMC_POWER_OFF)
	{
		pwr = inb(host->base + WBSD_CSR);
		pwr = inb(host->base + WBSD_CSR);
		pwr &= ~WBSD_POWER_N;
		pwr &= ~WBSD_POWER_N;
		outb(pwr, host->base + WBSD_CSR);
		outb(pwr, host->base + WBSD_CSR);
	}


	/*
	/*
		 * This behaviour is stolen from the
	 * MMC cards need to have pin 1 high during init.
		 * Windows driver. Don't know why, but
	 * Init time corresponds rather nicely with the bus mode.
		 * it is needed.
	 * It wreaks havoc with the card detection though so
	 * that needs to be disabed.
	 */
	 */
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
		if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
	if ((ios->power_mode == MMC_POWER_ON) &&
		(ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
	{
		setup |= WBSD_DAT3_H;
		setup |= WBSD_DAT3_H;
		host->flags |= WBSD_FIGNORE_DETECT;
	}
	else
	else
	{
		setup &= ~WBSD_DAT3_H;
		setup &= ~WBSD_DAT3_H;
		wbsd_write_index(host, WBSD_IDX_SETUP, setup);
		host->flags &= ~WBSD_FIGNORE_DETECT;

		mdelay(1);
	}
	}
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
	
	
	spin_unlock_bh(&host->lock);
	spin_unlock_bh(&host->lock);
}
}


static struct mmc_host_ops wbsd_ops = {
	.request	= wbsd_request,
	.set_ios	= wbsd_set_ios,
};

/*****************************************************************************\
 *                                                                           *
 * Interrupt handling                                                        *
 *                                                                           *
\*****************************************************************************/

/*
/*
 * Tasklets
 * Tasklets
 */
 */
@@ -1005,17 +1075,33 @@ static void wbsd_tasklet_card(unsigned long param)
{
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct wbsd_host* host = (struct wbsd_host*)param;
	u8 csr;
	u8 csr;
	int change = 0;
	
	
	spin_lock(&host->lock);
	spin_lock(&host->lock);
	
	
	if (host->flags & WBSD_FIGNORE_DETECT)
	{
		spin_unlock(&host->lock);
		return;
	}
	
	csr = inb(host->base + WBSD_CSR);
	csr = inb(host->base + WBSD_CSR);
	WARN_ON(csr == 0xff);
	WARN_ON(csr == 0xff);
	
	
	if (csr & WBSD_CARDPRESENT)
	if (csr & WBSD_CARDPRESENT)
	{
		if (!(host->flags & WBSD_FCARD_PRESENT))
		{
			DBG("Card inserted\n");
			DBG("Card inserted\n");
	else
			host->flags |= WBSD_FCARD_PRESENT;
			change = 1;
		}
	}
	else if (host->flags & WBSD_FCARD_PRESENT)
	{
	{
		DBG("Card removed\n");
		DBG("Card removed\n");
		host->flags &= ~WBSD_FCARD_PRESENT;
		change = 1;
		
		
		if (host->mrq)
		if (host->mrq)
		{
		{
@@ -1033,6 +1119,7 @@ static void wbsd_tasklet_card(unsigned long param)
	 */
	 */
	spin_unlock(&host->lock);
	spin_unlock(&host->lock);


	if (change)
		mmc_detect_change(host->mmc);
		mmc_detect_change(host->mmc);
}
}


@@ -1200,11 +1287,85 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
	return IRQ_HANDLED;
	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;
	
	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);
}

/*
/*
 * Support functions for probe
 * Scan for known chip id:s
 */
 */


static int wbsd_scan(struct wbsd_host* host)
static int __devinit wbsd_scan(struct wbsd_host* host)
{
{
	int i, j, k;
	int i, j, k;
	int id;
	int id;
@@ -1258,12 +1419,16 @@ static int wbsd_scan(struct wbsd_host* host)
	return -ENODEV;
	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)
	if (io & 0x7)
		return -EINVAL;
		return -EINVAL;
	
	
	if (!request_region(io, 8, DRIVER_NAME))
	if (!request_region(base, 8, DRIVER_NAME))
		return -EIO;
		return -EIO;
	
	
	host->base = io;
	host->base = io;
@@ -1271,19 +1436,25 @@ static int wbsd_request_regions(struct wbsd_host* host)
	return 0;
	return 0;
}
}


static void wbsd_release_regions(struct wbsd_host* host)
static void __devexit wbsd_release_regions(struct wbsd_host* host)
{
{
	if (host->base)
	if (host->base)
		release_region(host->base, 8);
		release_region(host->base, 8);
	
	
	host->base = 0;

	if (host->config)
	if (host->config)
		release_region(host->config, 2);
		release_region(host->config, 2);
	
	host->config = 0;
}
}


static void wbsd_init_dma(struct wbsd_host* host)
/*
{
 * Allocate/free DMA port and buffer
	host->dma = -1;
 */


static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
{
	if (dma < 0)
	if (dma < 0)
		return;
		return;
	
	
@@ -1294,7 +1465,7 @@ static void wbsd_init_dma(struct wbsd_host* host)
	 * We need to allocate a special buffer in
	 * We need to allocate a special buffer in
	 * order for ISA to be able to DMA to it.
	 * 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);
		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
	if (!host->dma_buffer)
	if (!host->dma_buffer)
		goto free;
		goto free;
@@ -1302,7 +1473,8 @@ static void wbsd_init_dma(struct wbsd_host* host)
	/*
	/*
	 * Translate the address to a physical address.
	 * 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.
	 * ISA DMA must be aligned on a 64k basis.
@@ -1325,6 +1497,10 @@ kfree:
	 */
	 */
	BUG_ON(1);
	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);
	kfree(host->dma_buffer);
	host->dma_buffer = NULL;
	host->dma_buffer = NULL;


@@ -1336,60 +1512,122 @@ err:
		"Falling back on FIFO.\n", dma);
		"Falling back on FIFO.\n", dma);
}
}


static struct mmc_host_ops wbsd_ops = {
static void __devexit wbsd_release_dma(struct wbsd_host* host)
	.request	= wbsd_request,
{
	.set_ios	= wbsd_set_ios,
	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;
	int ret;
	
	
	/*
	/*
	 * Allocate MMC structure.
	 * Allocate interrupt.
	 */
	 */
	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
	if (!mmc)
		return -ENOMEM;


	host = mmc_priv(mmc);
	ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
	host->mmc = mmc;
	if (ret)
		return ret;
	
	host->irq = irq;


	/*
	/*
	 * Scan for hardware.
	 * Set up tasklets.
	 */
	 */
	ret = wbsd_scan(host);
	tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host);
	if (ret)
	tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host);
		goto freemmc;
	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;
}

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);
}


/*
/*
	 * Reset the chip.
 * Allocate all resources for the host.
 */
 */
	wbsd_write_config(host, WBSD_CONF_SWRST, 1);

	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
static int __devinit wbsd_request_resources(struct wbsd_host* host,
	int base, int irq, int dma)
{
	int ret;
	
	
	/*
	/*
	 * Allocate I/O ports.
	 * Allocate I/O ports.
	 */
	 */
	ret = wbsd_request_regions(host);
	ret = wbsd_request_region(host, base);
	if (ret)
	if (ret)
		goto release;
		return ret;


	/*
	/*
	 * Set host parameters.
	 * Allocate interrupt.
	 */
	 */
	mmc->ops = &wbsd_ops;
	ret = wbsd_request_irq(host, irq);
	mmc->f_min = 375000;
	if (ret)
	mmc->f_max = 24000000;
		return ret;
	mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;


	spin_lock_init(&host->lock);
	/*
	 * 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 __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.
	 * Select SD/MMC function.
@@ -1399,132 +1637,178 @@ static int wbsd_probe(struct device* dev)
	/*
	/*
	 * Set up card detection.
	 * 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_HI, host->base >> 8);
	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
	
	
	/*
	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
	 * Allocate interrupt.
	 */
	ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
	if (ret)
		goto release;
	
	
	host->irq = irq;
	if (host->dma >= 0)
		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
	
	
	/*
	/*
	 * Set up tasklets.
	 * Enable and power up chip.
	 */
	 */
	tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host);
	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
	tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host);
	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
	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);


/*
/*
	 * Configure interrupt.
 * Check that configured resources are correct.
 */
 */
	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
 
 
	/*
static int __devinit wbsd_chip_validate(struct wbsd_host* host)
	 * Allocate DMA.
{
	 */
	int base, irq, dma;
	wbsd_init_dma(host);
	
	
	/*
	/*
	 * If all went well, then configure DMA.
	 * Select SD/MMC function.
	 */
	 */
	if (host->dma >= 0)
	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
	
	
	/*
	/*
	 * Maximum number of segments. Worst case is one sector per segment
	 * Read configuration.
	 * so this will be 64kB/512.
	 */
	 */
	mmc->max_hw_segs = 128;
	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
	mmc->max_phys_segs = 128;
	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);
	
	
	/*
	/*
	 * Maximum number of sectors in one transfer. Also limited by 64kB
	 * Validate against given configuration.
	 * buffer.
	 */
	 */
	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
	 * Scan for hardware.
	 * of segments.
	 */
	 */
	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.
	 * Power Management stuff. No idea how this works.
	 * Not tested.
	 * Not tested.
	 */
	 */
#ifdef CONFIG_PM
#ifdef CONFIG_PM
	if (host->config)
		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
#endif
#endif
	/*
	 * Allow device to initialise itself properly.
	 */
	mdelay(5);


	/*
	/*
	 * Reset the chip into a known state.
	 * Reset the chip into a known state.
	 */
	 */
	wbsd_init_device(host);
	wbsd_init_device(host);
	
	
	dev_set_drvdata(dev, mmc);
	
	/*
	 * Add host to MMC layer.
	 */
	mmc_add_host(mmc);
	mmc_add_host(mmc);


	printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",
	printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
		mmc->host_name, (int)host->chip_id, (int)host->base,
	if (host->chip_id != 0)
		(int)host->irq, (int)host->dma);
		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;
	return 0;

release:
	wbsd_release_regions(host);

freemmc:
	mmc_free_host(mmc);

	return ret;
}
}


/*
static void __devexit wbsd_shutdown(struct device* dev, int pnp)
 * Device remove
 */

static int wbsd_remove(struct device* dev)
{
{
	struct mmc_host* mmc = dev_get_drvdata(dev);
	struct mmc_host* mmc = dev_get_drvdata(dev);
	struct wbsd_host* host;
	struct wbsd_host* host;
	
	
	if (!mmc)
	if (!mmc)
		return 0;
		return;


	host = mmc_priv(mmc);
	host = mmc_priv(mmc);
	
	
	/*
	 * Unregister host with MMC layer.
	 */
	mmc_remove_host(mmc);
	mmc_remove_host(mmc);


	if (!pnp)
	{
		/*
		/*
		 * Power down the SD/MMC function.
		 * Power down the SD/MMC function.
		 */
		 */
@@ -1532,32 +1816,62 @@ static int wbsd_remove(struct device* dev)
		wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
		wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
		wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
		wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
		wbsd_lock_config(host);
		wbsd_lock_config(host);
	}
	
	wbsd_release_resources(host);
	
	wbsd_free_mmc(dev);
}


/*
/*
	 * Free resources.
 * Non-PnP
 */
 */
	if (host->dma_buffer)
		kfree(host->dma_buffer);


	if (host->dma >= 0)
static int __devinit wbsd_probe(struct device* dev)
		free_dma(host->dma);
{
	return wbsd_init(dev, io, irq, dma, 0);
}


	free_irq(host->irq, host);
static int __devexit wbsd_remove(struct device* dev)
{
	wbsd_shutdown(dev, 0);


	tasklet_kill(&host->card_tasklet);
	return 0;
	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);


	wbsd_release_regions(host);
/*
 * PnP
 */


	mmc_free_host(mmc);
#ifdef CONFIG_PNP


	return 0;
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
 * Power management
 */
 */
@@ -1581,17 +1895,7 @@ static int wbsd_resume(struct device *dev, u32 level)
#define wbsd_resume NULL
#define wbsd_resume NULL
#endif
#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 = {
static struct device_driver wbsd_driver = {
	.name		= DRIVER_NAME,
	.name		= DRIVER_NAME,
@@ -1603,6 +1907,17 @@ static struct device_driver wbsd_driver = {
	.resume		= wbsd_resume,
	.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
 * Module loading/unloading
 */
 */
@@ -1616,28 +1931,56 @@ static int __init wbsd_drv_init(void)
		DRIVER_VERSION "\n");
		DRIVER_VERSION "\n");
	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");


	result = driver_register(&wbsd_driver);
#ifdef CONFIG_PNP

	if (!nopnp)
	{
		result = pnp_register_driver(&wbsd_pnp_driver);
		if (result < 0)
		if (result < 0)
			return result;
			return result;
	}


	result = platform_device_register(&wbsd_device);
#endif /* CONFIG_PNP */	
	
	if (nopnp)
	{
		result = driver_register(&wbsd_driver);
		if (result < 0)
		if (result < 0)
			return result;
			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;
	return 0;
}
}


static void __exit wbsd_drv_exit(void)
static void __exit wbsd_drv_exit(void)
{
{
	platform_device_unregister(&wbsd_device);
#ifdef CONFIG_PNP

	if (!nopnp)
		pnp_unregister_driver(&wbsd_pnp_driver);
	
#endif /* CONFIG_PNP */	

	if (nopnp)
	{
		platform_device_unregister(wbsd_device);
	
	
		driver_unregister(&wbsd_driver);
		driver_unregister(&wbsd_driver);
	}


	DBG("unloaded\n");
	DBG("unloaded\n");
}
}


module_init(wbsd_drv_init);
module_init(wbsd_drv_init);
module_exit(wbsd_drv_exit);
module_exit(wbsd_drv_exit);
#ifdef CONFIG_PNP
module_param(nopnp, uint, 0444);
#endif
module_param(io, uint, 0444);
module_param(io, uint, 0444);
module_param(irq, uint, 0444);
module_param(irq, uint, 0444);
module_param(dma, int, 0444);
module_param(dma, int, 0444);
@@ -1646,6 +1989,9 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
MODULE_VERSION(DRIVER_VERSION);
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(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(irq, "IRQ to allocate. (default 6)");
MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
+12 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,12 @@ const int valid_ids[] = {


#define DEVICE_SD		0x03
#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_CMDR		0x00
#define WBSD_DFR		0x01
#define WBSD_DFR		0x01
#define WBSD_EIR		0x02
#define WBSD_EIR		0x02
@@ -133,6 +139,7 @@ const int valid_ids[] = {
#define WBSD_CRC_OK		0x05 /* S010E (00101) */
#define WBSD_CRC_OK		0x05 /* S010E (00101) */
#define WBSD_CRC_FAIL		0x0B /* S101E (01011) */
#define WBSD_CRC_FAIL		0x0B /* S101E (01011) */


#define WBSD_DMA_SIZE		65536


struct wbsd_host
struct wbsd_host
{
{
@@ -140,6 +147,11 @@ struct wbsd_host
	
	
	spinlock_t		lock;		/* Mutex */
	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 */
	struct mmc_request*	mrq;		/* Current request */
	
	
	u8			isr;		/* Accumulated ISR */
	u8			isr;		/* Accumulated ISR */