wbsd.c 40.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
 *
 *  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>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
29
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
30
#include <linux/interrupt.h>
P
Pierre Ossman 已提交
31
#include <linux/dma-mapping.h>
L
Linus Torvalds 已提交
32
#include <linux/delay.h>
P
Pierre Ossman 已提交
33
#include <linux/pnp.h>
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41 42 43 44
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>

#include <asm/io.h>
#include <asm/dma.h>
#include <asm/scatterlist.h>

#include "wbsd.h"

#define DRIVER_NAME "wbsd"
P
Pierre Ossman 已提交
45
#define DRIVER_VERSION "1.5"
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55 56

#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
	printk(KERN_DEBUG DRIVER_NAME ": " x)
#define DBGF(f, x...) \
	printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__ , ##x)
#else
#define DBG(x...)	do { } while (0)
#define DBGF(x...)	do { } while (0)
#endif

P
Pierre Ossman 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
/*
 * 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 */

A
Adrian Bunk 已提交
73 74 75 76 77 78 79
static const int config_ports[] = { 0x2E, 0x4E };
static const int unlock_codes[] = { 0x83, 0x87 };

static const int valid_ids[] = {
	0x7112,
	};

P
Pierre Ossman 已提交
80 81 82 83 84 85 86 87 88
#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;

L
Linus Torvalds 已提交
89 90 91 92 93 94
/*
 * Basic functions
 */

static inline void wbsd_unlock_config(struct wbsd_host* host)
{
P
Pierre Ossman 已提交
95
	BUG_ON(host->config == 0);
96

L
Linus Torvalds 已提交
97 98 99 100 101 102
	outb(host->unlock_code, host->config);
	outb(host->unlock_code, host->config);
}

static inline void wbsd_lock_config(struct wbsd_host* host)
{
P
Pierre Ossman 已提交
103
	BUG_ON(host->config == 0);
104

L
Linus Torvalds 已提交
105 106 107 108 109
	outb(LOCK_CODE, host->config);
}

static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value)
{
P
Pierre Ossman 已提交
110
	BUG_ON(host->config == 0);
111

L
Linus Torvalds 已提交
112 113 114 115 116 117
	outb(reg, host->config);
	outb(value, host->config + 1);
}

static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg)
{
P
Pierre Ossman 已提交
118
	BUG_ON(host->config == 0);
119

L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	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;
143

L
Linus Torvalds 已提交
144 145 146 147 148 149
	/*
	 * 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);
150

P
Pierre Ossman 已提交
151 152 153 154 155 156
	/*
	 * Set DAT3 to input
	 */
	setup &= ~WBSD_DAT3_H;
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
	host->flags &= ~WBSD_FIGNORE_DETECT;
157

L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166
	/*
	 * Read back default clock.
	 */
	host->clk = wbsd_read_index(host, WBSD_IDX_CLK);

	/*
	 * Power down port.
	 */
	outb(WBSD_POWER_N, host->base + WBSD_CSR);
167

L
Linus Torvalds 已提交
168 169 170 171
	/*
	 * Set maximum timeout.
	 */
	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
172

P
Pierre Ossman 已提交
173 174 175 176 177 178 179
	/*
	 * Test for card presence
	 */
	if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
		host->flags |= WBSD_FCARD_PRESENT;
	else
		host->flags &= ~WBSD_FCARD_PRESENT;
180

L
Linus Torvalds 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
	/*
	 * Enable interesting interrupts.
	 */
	ier = 0;
	ier |= WBSD_EINT_CARD;
	ier |= WBSD_EINT_FIFO_THRE;
	ier |= WBSD_EINT_CCRC;
	ier |= WBSD_EINT_TIMEOUT;
	ier |= WBSD_EINT_CRC;
	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;
203

204
	printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
205

L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213 214 215 216
	/*
	 * 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;
217

L
Linus Torvalds 已提交
218
	DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
219

L
Linus Torvalds 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	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);
	}
235

L
Linus Torvalds 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	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;
	  }
278

L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	return host->num_sg;
}

static inline char* wbsd_kmap_sg(struct wbsd_host* host)
{
	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->mapped_sg, KM_BIO_SRC_IRQ);
}

static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
{
	unsigned int len, i, size;
	struct scatterlist* sg;
	char* dmabuf = host->dma_buffer;
	char* sgbuf;
300

L
Linus Torvalds 已提交
301
	size = host->size;
302

L
Linus Torvalds 已提交
303 304
	sg = data->sg;
	len = data->sg_len;
305

L
Linus Torvalds 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319
	/*
	 * Just loop through all entries. Size might not
	 * be the entire list though so make sure that
	 * we do not transfer too much.
	 */
	for (i = 0;i < len;i++)
	{
		sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
		if (size < sg[i].length)
			memcpy(dmabuf, sgbuf, size);
		else
			memcpy(dmabuf, sgbuf, sg[i].length);
		kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
		dmabuf += sg[i].length;
320

L
Linus Torvalds 已提交
321 322 323 324
		if (size < sg[i].length)
			size = 0;
		else
			size -= sg[i].length;
325

L
Linus Torvalds 已提交
326 327 328
		if (size == 0)
			break;
	}
329

L
Linus Torvalds 已提交
330 331 332 333
	/*
	 * Check that we didn't get a request to transfer
	 * more data than can fit into the SG list.
	 */
334

L
Linus Torvalds 已提交
335
	BUG_ON(size != 0);
336

L
Linus Torvalds 已提交
337 338 339 340 341 342 343 344 345
	host->size -= size;
}

static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data)
{
	unsigned int len, i, size;
	struct scatterlist* sg;
	char* dmabuf = host->dma_buffer;
	char* sgbuf;
346

L
Linus Torvalds 已提交
347
	size = host->size;
348

L
Linus Torvalds 已提交
349 350
	sg = data->sg;
	len = data->sg_len;
351

L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364 365
	/*
	 * Just loop through all entries. Size might not
	 * be the entire list though so make sure that
	 * we do not transfer too much.
	 */
	for (i = 0;i < len;i++)
	{
		sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
		if (size < sg[i].length)
			memcpy(sgbuf, dmabuf, size);
		else
			memcpy(sgbuf, dmabuf, sg[i].length);
		kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
		dmabuf += sg[i].length;
366

L
Linus Torvalds 已提交
367 368 369 370
		if (size < sg[i].length)
			size = 0;
		else
			size -= sg[i].length;
371

L
Linus Torvalds 已提交
372 373 374
		if (size == 0)
			break;
	}
375

L
Linus Torvalds 已提交
376 377 378 379
	/*
	 * Check that we didn't get a request to transfer
	 * more data than can fit into the SG list.
	 */
380

L
Linus Torvalds 已提交
381
	BUG_ON(size != 0);
382

L
Linus Torvalds 已提交
383 384 385 386 387 388
	host->size -= size;
}

/*
 * Command handling
 */
389

L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397 398 399 400
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;
	}
401

L
Linus Torvalds 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
	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;
418

L
Linus Torvalds 已提交
419 420 421 422 423 424 425 426
	/*
	 * Correct response type?
	 */
	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG)
	{
		cmd->error = MMC_ERR_INVALID;
		return;
	}
427

L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
	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;
445

L
Linus Torvalds 已提交
446 447 448 449 450 451 452 453
	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;
454

L
Linus Torvalds 已提交
455 456 457 458 459 460
	/*
	 * 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);
461

L
Linus Torvalds 已提交
462
	cmd->error = MMC_ERR_NONE;
463

L
Linus Torvalds 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
	/*
	 * 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_MASK) != MMC_RSP_NONE)
	{
		/*
		 * Read back status.
		 */
		isr = host->isr;
480

L
Linus Torvalds 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
		/* 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_MASK) == MMC_RSP_SHORT)
				wbsd_get_short_reply(host, cmd);
			else
				wbsd_get_long_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;
512

L
Linus Torvalds 已提交
513 514 515 516 517
	/*
	 * Handle excessive data.
	 */
	if (data->bytes_xfered == host->size)
		return;
518

L
Linus Torvalds 已提交
519 520 521 522 523 524 525 526 527 528 529
	buffer = wbsd_kmap_sg(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.
530
		 */
L
Linus Torvalds 已提交
531 532 533 534 535 536
		if (fsr & WBSD_FIFO_FULL)
			fifo = 16;
		else if (fsr & WBSD_FIFO_FUTHRE)
			fifo = 8;
		else
			fifo = 1;
537

L
Linus Torvalds 已提交
538 539 540 541 542 543 544 545
		for (i = 0;i < fifo;i++)
		{
			*buffer = inb(host->base + WBSD_DFR);
			buffer++;
			host->offset++;
			host->remain--;

			data->bytes_xfered++;
546

L
Linus Torvalds 已提交
547 548 549 550 551
			/*
			 * Transfer done?
			 */
			if (data->bytes_xfered == host->size)
			{
552
				wbsd_kunmap_sg(host);
L
Linus Torvalds 已提交
553 554
				return;
			}
555

L
Linus Torvalds 已提交
556 557 558 559 560 561
			/*
			 * End of scatter list entry?
			 */
			if (host->remain == 0)
			{
				wbsd_kunmap_sg(host);
562

L
Linus Torvalds 已提交
563 564 565 566 567 568 569 570 571 572 573 574
				/*
				 * Get next entry. Check if last.
				 */
				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);
575

L
Linus Torvalds 已提交
576
					host->size = data->bytes_xfered;
577

L
Linus Torvalds 已提交
578 579
					return;
				}
580

L
Linus Torvalds 已提交
581 582 583 584
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
585

L
Linus Torvalds 已提交
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
	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;
602

L
Linus Torvalds 已提交
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
	/*
	 * Check that we aren't being called after the
	 * entire buffer has been transfered.
	 */
	if (data->bytes_xfered == host->size)
		return;

	buffer = wbsd_kmap_sg(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.
621
		 */
L
Linus Torvalds 已提交
622 623 624 625 626 627 628 629 630 631 632 633 634
		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--;
635

L
Linus Torvalds 已提交
636
			data->bytes_xfered++;
637

L
Linus Torvalds 已提交
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
			/*
			 * Transfer done?
			 */
			if (data->bytes_xfered == host->size)
			{
				wbsd_kunmap_sg(host);
				return;
			}

			/*
			 * End of scatter list entry?
			 */
			if (host->remain == 0)
			{
				wbsd_kunmap_sg(host);
653

L
Linus Torvalds 已提交
654 655 656 657 658 659 660 661 662 663 664 665
				/*
				 * Get next entry. Check if last.
				 */
				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);
666

L
Linus Torvalds 已提交
667
					host->size = data->bytes_xfered;
668

L
Linus Torvalds 已提交
669 670
					return;
				}
671

L
Linus Torvalds 已提交
672 673 674 675
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
676

L
Linus Torvalds 已提交
677
	wbsd_kunmap_sg(host);
678

P
Pierre Ossman 已提交
679 680 681 682 683 684
	/*
	 * 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);
L
Linus Torvalds 已提交
685 686 687 688 689 690 691 692 693 694 695 696
}

static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
{
	u16 blksize;
	u8 setup;
	unsigned long dmaflags;

	DBGF("blksz %04x blks %04x flags %08x\n",
		1 << data->blksz_bits, data->blocks, data->flags);
	DBGF("tsac %d ms nsac %d clk\n",
		data->timeout_ns / 1000000, data->timeout_clks);
697

L
Linus Torvalds 已提交
698 699 700 701 702 703 704 705 706 707 708 709 710
	/*
	 * Calculate size.
	 */
	host->size = data->blocks << data->blksz_bits;

	/*
	 * 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);
711

L
Linus Torvalds 已提交
712 713 714 715
	if (data->timeout_clks > 255)
		wbsd_write_index(host, WBSD_IDX_NSAC, 255);
	else
		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
716

L
Linus Torvalds 已提交
717 718 719 720 721 722
	/*
	 * 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.
723
	 * Two bytes are needed for each data line.
L
Linus Torvalds 已提交
724
	 */
725 726 727 728 729 730 731 732 733 734
	if (host->bus_width == MMC_BUS_WIDTH_1)
	{
		blksize = (1 << data->blksz_bits) + 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 = (1 << data->blksz_bits) + 2 * 4;
735

736 737 738 739 740 741 742 743 744
		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;
	}
L
Linus Torvalds 已提交
745 746 747 748 749 750 751 752 753

	/*
	 * 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);
754

L
Linus Torvalds 已提交
755 756 757 758
	/*
	 * DMA transfer?
	 */
	if (host->dma >= 0)
759
	{
L
Linus Torvalds 已提交
760 761 762 763 764 765 766 767 768
		/*
		 * The buffer for DMA is only 64 kB.
		 */
		BUG_ON(host->size > 0x10000);
		if (host->size > 0x10000)
		{
			data->error = MMC_ERR_INVALID;
			return;
		}
769

L
Linus Torvalds 已提交
770 771 772 773 774 775
		/*
		 * Transfer data from the SG list to
		 * the DMA buffer.
		 */
		if (data->flags & MMC_DATA_WRITE)
			wbsd_sg_to_dma(host, data);
776

L
Linus Torvalds 已提交
777 778
		/*
		 * Initialise the ISA DMA controller.
779
		 */
L
Linus Torvalds 已提交
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
		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, host->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;
805

L
Linus Torvalds 已提交
806 807 808 809
		/*
		 * Initialise the SG list.
		 */
		wbsd_init_sg(host, data);
810

L
Linus Torvalds 已提交
811 812 813 814
		/*
		 * Turn off DMA.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
815

L
Linus Torvalds 已提交
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
		/*
		 * 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);
		}
831 832
	}

L
Linus Torvalds 已提交
833 834 835 836 837 838 839 840
	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;
841

L
Linus Torvalds 已提交
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
	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));
858

L
Linus Torvalds 已提交
859 860 861 862 863 864 865 866 867
	/*
	 * DMA transfer?
	 */
	if (host->dma >= 0)
	{
		/*
		 * Disable DMA on the host.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
868

L
Linus Torvalds 已提交
869 870 871 872 873 874 875 876
		/*
		 * 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);
877

L
Linus Torvalds 已提交
878 879 880 881 882
		/*
		 * Any leftover data?
		 */
		if (count)
		{
883 884 885
			printk(KERN_ERR "%s: Incomplete DMA transfer. "
				"%d bytes left.\n",
				mmc_hostname(host->mmc), count);
886

L
Linus Torvalds 已提交
887 888 889 890 891 892 893 894 895 896
			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);
897

L
Linus Torvalds 已提交
898 899 900
			data->bytes_xfered = host->size;
		}
	}
901

L
Linus Torvalds 已提交
902
	DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
903

L
Linus Torvalds 已提交
904 905 906
	wbsd_request_end(host, host->mrq);
}

P
Pierre Ossman 已提交
907 908 909 910 911
/*****************************************************************************\
 *                                                                           *
 * MMC layer callbacks                                                       *
 *                                                                           *
\*****************************************************************************/
L
Linus Torvalds 已提交
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927

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;
928

L
Linus Torvalds 已提交
929 930 931 932
	/*
	 * If there is no card in the slot then
	 * timeout immediatly.
	 */
P
Pierre Ossman 已提交
933
	if (!(host->flags & WBSD_FCARD_PRESENT))
L
Linus Torvalds 已提交
934 935 936 937 938 939 940 941 942 943 944
	{
		cmd->error = MMC_ERR_TIMEOUT;
		goto done;
	}

	/*
	 * Does the request include data?
	 */
	if (cmd->data)
	{
		wbsd_prepare_data(host, cmd->data);
945

L
Linus Torvalds 已提交
946 947 948
		if (cmd->data->error != MMC_ERR_NONE)
			goto done;
	}
949

L
Linus Torvalds 已提交
950 951 952 953 954 955
	wbsd_send_command(host, cmd);

	/*
	 * If this is a data transfer the request
	 * will be finished after the data has
	 * transfered.
956
	 */
L
Linus Torvalds 已提交
957 958 959 960 961 962 963 964 965 966 967 968
	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;
	}
969

L
Linus Torvalds 已提交
970 971 972 973 974 975 976 977 978 979
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;
980

981 982 983
	DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n",
	     ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select,
	     ios->vdd, ios->bus_width);
L
Linus Torvalds 已提交
984 985 986 987 988 989 990 991 992

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

L
Linus Torvalds 已提交
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
	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;
	}

P
Pierre Ossman 已提交
1013 1014 1015
	/*
	 * Power up card.
	 */
L
Linus Torvalds 已提交
1016 1017 1018 1019 1020 1021 1022
	if (ios->power_mode != MMC_POWER_OFF)
	{
		pwr = inb(host->base + WBSD_CSR);
		pwr &= ~WBSD_POWER_N;
		outb(pwr, host->base + WBSD_CSR);
	}

P
Pierre Ossman 已提交
1023 1024 1025
	/*
	 * MMC cards need to have pin 1 high during init.
	 * It wreaks havoc with the card detection though so
1026
	 * that needs to be disabled.
P
Pierre Ossman 已提交
1027 1028
	 */
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
1029
	if (ios->chip_select == MMC_CS_HIGH)
P
Pierre Ossman 已提交
1030
	{
1031
		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
P
Pierre Ossman 已提交
1032 1033 1034 1035 1036
		setup |= WBSD_DAT3_H;
		host->flags |= WBSD_FIGNORE_DETECT;
	}
	else
	{
P
Pierre Ossman 已提交
1037 1038 1039
		if (setup & WBSD_DAT3_H)
		{
			setup &= ~WBSD_DAT3_H;
1040

P
Pierre Ossman 已提交
1041 1042 1043 1044 1045 1046
			/*
			 * We cannot resume card detection immediatly
			 * because of capacitance and delays in the chip.
			 */
			mod_timer(&host->ignore_timer, jiffies + HZ/100);
		}
P
Pierre Ossman 已提交
1047 1048
	}
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
1049

1050 1051 1052 1053 1054 1055
	/*
	 * Store bus width for later. Will be used when
	 * setting up the data transfer.
	 */
	host->bus_width = ios->bus_width;

L
Linus Torvalds 已提交
1056 1057 1058
	spin_unlock_bh(&host->lock);
}

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
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;
}

P
Pierre Ossman 已提交
1081 1082 1083
static struct mmc_host_ops wbsd_ops = {
	.request	= wbsd_request,
	.set_ios	= wbsd_set_ios,
1084
	.get_ro		= wbsd_get_ro,
P
Pierre Ossman 已提交
1085 1086 1087 1088 1089 1090 1091 1092
};

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

1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
/*
 * 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);
}

L
Linus Torvalds 已提交
1118 1119 1120 1121
/*
 * Tasklets
 */

1122
static inline struct mmc_data* wbsd_get_data(struct wbsd_host* host)
L
Linus Torvalds 已提交
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
{
	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;
1135

L
Linus Torvalds 已提交
1136 1137 1138 1139 1140 1141 1142
	return host->mrq->cmd->data;
}

static void wbsd_tasklet_card(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	u8 csr;
1143
	int delay = -1;
1144

L
Linus Torvalds 已提交
1145
	spin_lock(&host->lock);
1146

P
Pierre Ossman 已提交
1147 1148 1149 1150 1151
	if (host->flags & WBSD_FIGNORE_DETECT)
	{
		spin_unlock(&host->lock);
		return;
	}
1152

L
Linus Torvalds 已提交
1153 1154
	csr = inb(host->base + WBSD_CSR);
	WARN_ON(csr == 0xff);
1155

L
Linus Torvalds 已提交
1156
	if (csr & WBSD_CARDPRESENT)
P
Pierre Ossman 已提交
1157 1158 1159 1160 1161
	{
		if (!(host->flags & WBSD_FCARD_PRESENT))
		{
			DBG("Card inserted\n");
			host->flags |= WBSD_FCARD_PRESENT;
1162

1163
			delay = 500;
P
Pierre Ossman 已提交
1164 1165 1166
		}
	}
	else if (host->flags & WBSD_FCARD_PRESENT)
L
Linus Torvalds 已提交
1167 1168
	{
		DBG("Card removed\n");
P
Pierre Ossman 已提交
1169
		host->flags &= ~WBSD_FCARD_PRESENT;
1170

L
Linus Torvalds 已提交
1171 1172
		if (host->mrq)
		{
1173 1174
			printk(KERN_ERR "%s: Card removed during transfer!\n",
				mmc_hostname(host->mmc));
L
Linus Torvalds 已提交
1175
			wbsd_reset(host);
1176

L
Linus Torvalds 已提交
1177 1178 1179
			host->mrq->cmd->error = MMC_ERR_FAILED;
			tasklet_schedule(&host->finish_tasklet);
		}
1180

1181
		delay = 0;
1182
	}
1183 1184 1185 1186 1187 1188 1189 1190 1191

	/*
	 * 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));
L
Linus Torvalds 已提交
1192 1193 1194 1195 1196 1197
}

static void wbsd_tasklet_fifo(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct mmc_data* data;
1198

L
Linus Torvalds 已提交
1199
	spin_lock(&host->lock);
1200

L
Linus Torvalds 已提交
1201 1202
	if (!host->mrq)
		goto end;
1203

L
Linus Torvalds 已提交
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
	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->size == data->bytes_xfered)
	{
		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
		tasklet_schedule(&host->finish_tasklet);
	}

1222
end:
L
Linus Torvalds 已提交
1223 1224 1225 1226 1227 1228 1229
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_crc(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct mmc_data* data;
1230

L
Linus Torvalds 已提交
1231
	spin_lock(&host->lock);
1232

L
Linus Torvalds 已提交
1233 1234
	if (!host->mrq)
		goto end;
1235

L
Linus Torvalds 已提交
1236 1237 1238
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1239

L
Linus Torvalds 已提交
1240 1241 1242
	DBGF("CRC error\n");

	data->error = MMC_ERR_BADCRC;
1243

L
Linus Torvalds 已提交
1244 1245
	tasklet_schedule(&host->finish_tasklet);

1246
end:
L
Linus Torvalds 已提交
1247 1248 1249 1250 1251 1252 1253
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_timeout(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct mmc_data* data;
1254

L
Linus Torvalds 已提交
1255
	spin_lock(&host->lock);
1256

L
Linus Torvalds 已提交
1257 1258
	if (!host->mrq)
		goto end;
1259

L
Linus Torvalds 已提交
1260 1261 1262
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1263

L
Linus Torvalds 已提交
1264 1265 1266
	DBGF("Timeout\n");

	data->error = MMC_ERR_TIMEOUT;
1267

L
Linus Torvalds 已提交
1268 1269
	tasklet_schedule(&host->finish_tasklet);

1270
end:
L
Linus Torvalds 已提交
1271 1272 1273 1274 1275 1276 1277
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_finish(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct mmc_data* data;
1278

L
Linus Torvalds 已提交
1279
	spin_lock(&host->lock);
1280

L
Linus Torvalds 已提交
1281 1282 1283
	WARN_ON(!host->mrq);
	if (!host->mrq)
		goto end;
1284

L
Linus Torvalds 已提交
1285 1286 1287 1288 1289
	data = wbsd_get_data(host);
	if (!data)
		goto end;

	wbsd_finish_data(host, data);
1290 1291

end:
L
Linus Torvalds 已提交
1292 1293 1294 1295 1296 1297 1298
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_block(unsigned long param)
{
	struct wbsd_host* host = (struct wbsd_host*)param;
	struct mmc_data* data;
1299

L
Linus Torvalds 已提交
1300 1301 1302 1303 1304 1305 1306 1307
	spin_lock(&host->lock);

	if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
		WBSD_CRC_OK)
	{
		data = wbsd_get_data(host);
		if (!data)
			goto end;
1308

L
Linus Torvalds 已提交
1309 1310 1311
		DBGF("CRC error\n");

		data->error = MMC_ERR_BADCRC;
1312

L
Linus Torvalds 已提交
1313 1314 1315
		tasklet_schedule(&host->finish_tasklet);
	}

1316
end:
L
Linus Torvalds 已提交
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
	spin_unlock(&host->lock);
}

/*
 * Interrupt handling
 */

static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
{
	struct wbsd_host* host = dev_id;
	int isr;
1328

L
Linus Torvalds 已提交
1329 1330 1331 1332 1333 1334 1335
	isr = inb(host->base + WBSD_ISR);

	/*
	 * Was it actually our hardware that caused the interrupt?
	 */
	if (isr == 0xff || isr == 0x00)
		return IRQ_NONE;
1336

L
Linus Torvalds 已提交
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
	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_BUSYEND)
		tasklet_hi_schedule(&host->block_tasklet);
	if (isr & WBSD_INT_TC)
		tasklet_schedule(&host->finish_tasklet);
1354

L
Linus Torvalds 已提交
1355 1356 1357
	return IRQ_HANDLED;
}

P
Pierre Ossman 已提交
1358 1359 1360 1361 1362 1363
/*****************************************************************************\
 *                                                                           *
 * Device initialisation and shutdown                                        *
 *                                                                           *
\*****************************************************************************/

L
Linus Torvalds 已提交
1364
/*
P
Pierre Ossman 已提交
1365
 * Allocate/free MMC structure.
L
Linus Torvalds 已提交
1366 1367
 */

P
Pierre Ossman 已提交
1368 1369 1370 1371
static int __devinit wbsd_alloc_mmc(struct device* dev)
{
	struct mmc_host* mmc;
	struct wbsd_host* host;
1372

P
Pierre Ossman 已提交
1373 1374 1375 1376 1377 1378
	/*
	 * Allocate MMC structure.
	 */
	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
	if (!mmc)
		return -ENOMEM;
1379

P
Pierre Ossman 已提交
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
	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;
1392
	mmc->caps = MMC_CAP_4_BIT_DATA;
1393

P
Pierre Ossman 已提交
1394
	spin_lock_init(&host->lock);
1395

1396
	/*
1397
	 * Set up timers
1398
	 */
1399 1400 1401
	init_timer(&host->ignore_timer);
	host->ignore_timer.data = (unsigned long)host;
	host->ignore_timer.function = wbsd_reset_ignore;
1402

P
Pierre Ossman 已提交
1403 1404 1405 1406 1407 1408
	/*
	 * 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;
1409

P
Pierre Ossman 已提交
1410 1411 1412 1413 1414
	/*
	 * Maximum number of sectors in one transfer. Also limited by 64kB
	 * buffer.
	 */
	mmc->max_sectors = 128;
1415

P
Pierre Ossman 已提交
1416 1417 1418 1419 1420
	/*
	 * Maximum segment size. Could be one segment with the maximum number
	 * of segments.
	 */
	mmc->max_seg_size = mmc->max_sectors * 512;
1421

P
Pierre Ossman 已提交
1422
	dev_set_drvdata(dev, mmc);
1423

P
Pierre Ossman 已提交
1424 1425 1426 1427 1428 1429
	return 0;
}

static void __devexit wbsd_free_mmc(struct device* dev)
{
	struct mmc_host* mmc;
1430
	struct wbsd_host* host;
1431

P
Pierre Ossman 已提交
1432 1433 1434
	mmc = dev_get_drvdata(dev);
	if (!mmc)
		return;
1435

1436 1437
	host = mmc_priv(mmc);
	BUG_ON(host == NULL);
1438

1439
	del_timer_sync(&host->ignore_timer);
1440

P
Pierre Ossman 已提交
1441
	mmc_free_host(mmc);
1442

P
Pierre Ossman 已提交
1443 1444 1445 1446 1447 1448 1449 1450
	dev_set_drvdata(dev, NULL);
}

/*
 * Scan for known chip id:s
 */

static int __devinit wbsd_scan(struct wbsd_host* host)
L
Linus Torvalds 已提交
1451 1452 1453
{
	int i, j, k;
	int id;
1454

L
Linus Torvalds 已提交
1455 1456 1457 1458
	/*
	 * Iterate through all ports, all codes to
	 * find hardware that is in our known list.
	 */
1459
	for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
L
Linus Torvalds 已提交
1460 1461
		if (!request_region(config_ports[i], 2, DRIVER_NAME))
			continue;
1462

1463
		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
L
Linus Torvalds 已提交
1464
			id = 0xFFFF;
1465

P
Pierre Ossman 已提交
1466 1467 1468 1469
			host->config = config_ports[i];
			host->unlock_code = unlock_codes[j];

			wbsd_unlock_config(host);
1470

L
Linus Torvalds 已提交
1471 1472 1473 1474 1475
			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);
1476

P
Pierre Ossman 已提交
1477 1478
			wbsd_lock_config(host);

1479
			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
L
Linus Torvalds 已提交
1480
				if (id == valid_ids[k])
1481
				{
L
Linus Torvalds 已提交
1482
					host->chip_id = id;
1483

L
Linus Torvalds 已提交
1484 1485 1486
					return 0;
				}
			}
1487

L
Linus Torvalds 已提交
1488 1489 1490 1491 1492 1493
			if (id != 0xFFFF)
			{
				DBG("Unknown hardware (id %x) found at %x\n",
					id, config_ports[i]);
			}
		}
1494

L
Linus Torvalds 已提交
1495 1496
		release_region(config_ports[i], 2);
	}
1497

P
Pierre Ossman 已提交
1498 1499 1500
	host->config = 0;
	host->unlock_code = 0;

L
Linus Torvalds 已提交
1501 1502 1503
	return -ENODEV;
}

P
Pierre Ossman 已提交
1504 1505 1506 1507 1508
/*
 * Allocate/free io port ranges
 */

static int __devinit wbsd_request_region(struct wbsd_host* host, int base)
L
Linus Torvalds 已提交
1509 1510 1511
{
	if (io & 0x7)
		return -EINVAL;
1512

P
Pierre Ossman 已提交
1513
	if (!request_region(base, 8, DRIVER_NAME))
L
Linus Torvalds 已提交
1514
		return -EIO;
1515

L
Linus Torvalds 已提交
1516
	host->base = io;
1517

L
Linus Torvalds 已提交
1518 1519 1520
	return 0;
}

P
Pierre Ossman 已提交
1521
static void __devexit wbsd_release_regions(struct wbsd_host* host)
L
Linus Torvalds 已提交
1522 1523 1524
{
	if (host->base)
		release_region(host->base, 8);
1525

P
Pierre Ossman 已提交
1526
	host->base = 0;
L
Linus Torvalds 已提交
1527 1528 1529

	if (host->config)
		release_region(host->config, 2);
1530

P
Pierre Ossman 已提交
1531
	host->config = 0;
L
Linus Torvalds 已提交
1532 1533
}

P
Pierre Ossman 已提交
1534 1535 1536 1537 1538
/*
 * Allocate/free DMA port and buffer
 */

static void __devinit wbsd_request_dma(struct wbsd_host* host, int dma)
L
Linus Torvalds 已提交
1539 1540 1541
{
	if (dma < 0)
		return;
1542

L
Linus Torvalds 已提交
1543 1544
	if (request_dma(dma, DRIVER_NAME))
		goto err;
1545

L
Linus Torvalds 已提交
1546 1547 1548 1549
	/*
	 * We need to allocate a special buffer in
	 * order for ISA to be able to DMA to it.
	 */
P
Pierre Ossman 已提交
1550
	host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
L
Linus Torvalds 已提交
1551 1552 1553 1554 1555 1556 1557
		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
	if (!host->dma_buffer)
		goto free;

	/*
	 * Translate the address to a physical address.
	 */
P
Pierre Ossman 已提交
1558 1559
	host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer,
		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
1560

L
Linus Torvalds 已提交
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
	/*
	 * 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;
1573

L
Linus Torvalds 已提交
1574
	return;
1575

L
Linus Torvalds 已提交
1576 1577 1578 1579 1580
kfree:
	/*
	 * If we've gotten here then there is some kind of alignment bug
	 */
	BUG_ON(1);
1581

P
Pierre Ossman 已提交
1582 1583 1584
	dma_unmap_single(host->mmc->dev, host->dma_addr, WBSD_DMA_SIZE,
		DMA_BIDIRECTIONAL);
	host->dma_addr = (dma_addr_t)NULL;
1585

L
Linus Torvalds 已提交
1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596
	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);
}

P
Pierre Ossman 已提交
1597 1598 1599 1600 1601
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);
1602
	kfree(host->dma_buffer);
P
Pierre Ossman 已提交
1603 1604
	if (host->dma >= 0)
		free_dma(host->dma);
1605

P
Pierre Ossman 已提交
1606 1607 1608 1609
	host->dma = -1;
	host->dma_buffer = NULL;
	host->dma_addr = (dma_addr_t)NULL;
}
L
Linus Torvalds 已提交
1610 1611

/*
P
Pierre Ossman 已提交
1612
 * Allocate/free IRQ.
L
Linus Torvalds 已提交
1613 1614
 */

P
Pierre Ossman 已提交
1615
static int __devinit wbsd_request_irq(struct wbsd_host* host, int irq)
L
Linus Torvalds 已提交
1616 1617
{
	int ret;
1618

L
Linus Torvalds 已提交
1619
	/*
P
Pierre Ossman 已提交
1620
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1621
	 */
P
Pierre Ossman 已提交
1622 1623 1624 1625

	ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
	if (ret)
		return ret;
1626

P
Pierre Ossman 已提交
1627 1628
	host->irq = irq;

L
Linus Torvalds 已提交
1629
	/*
P
Pierre Ossman 已提交
1630
	 * Set up tasklets.
L
Linus Torvalds 已提交
1631
	 */
P
Pierre Ossman 已提交
1632 1633 1634 1635 1636 1637
	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);
1638

P
Pierre Ossman 已提交
1639 1640
	return 0;
}
L
Linus Torvalds 已提交
1641

P
Pierre Ossman 已提交
1642 1643 1644 1645
static void __devexit wbsd_release_irq(struct wbsd_host* host)
{
	if (!host->irq)
		return;
L
Linus Torvalds 已提交
1646

P
Pierre Ossman 已提交
1647
	free_irq(host->irq, host);
1648

P
Pierre Ossman 已提交
1649
	host->irq = 0;
1650

P
Pierre Ossman 已提交
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666
	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;
1667

L
Linus Torvalds 已提交
1668 1669 1670
	/*
	 * Allocate I/O ports.
	 */
P
Pierre Ossman 已提交
1671
	ret = wbsd_request_region(host, base);
L
Linus Torvalds 已提交
1672
	if (ret)
P
Pierre Ossman 已提交
1673
		return ret;
L
Linus Torvalds 已提交
1674 1675

	/*
P
Pierre Ossman 已提交
1676
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1677
	 */
P
Pierre Ossman 已提交
1678 1679 1680 1681 1682 1683 1684 1685
	ret = wbsd_request_irq(host, irq);
	if (ret)
		return ret;

	/*
	 * Allocate DMA.
	 */
	wbsd_request_dma(host, dma);
1686

P
Pierre Ossman 已提交
1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
	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.
 */

P
Pierre Ossman 已提交
1705
static void wbsd_chip_config(struct wbsd_host* host)
P
Pierre Ossman 已提交
1706
{
P
Pierre Ossman 已提交
1707 1708
	wbsd_unlock_config(host);

P
Pierre Ossman 已提交
1709 1710
	/*
	 * Reset the chip.
1711
	 */
P
Pierre Ossman 已提交
1712 1713
	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
L
Linus Torvalds 已提交
1714 1715 1716 1717 1718

	/*
	 * Select SD/MMC function.
	 */
	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1719

L
Linus Torvalds 已提交
1720 1721 1722
	/*
	 * Set up card detection.
	 */
P
Pierre Ossman 已提交
1723
	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
1724

L
Linus Torvalds 已提交
1725
	/*
P
Pierre Ossman 已提交
1726
	 * Configure chip
L
Linus Torvalds 已提交
1727 1728 1729
	 */
	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
1730

P
Pierre Ossman 已提交
1731
	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
1732

P
Pierre Ossman 已提交
1733 1734
	if (host->dma >= 0)
		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
1735

L
Linus Torvalds 已提交
1736
	/*
P
Pierre Ossman 已提交
1737
	 * Enable and power up chip.
L
Linus Torvalds 已提交
1738
	 */
P
Pierre Ossman 已提交
1739 1740
	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
P
Pierre Ossman 已提交
1741 1742

	wbsd_lock_config(host);
P
Pierre Ossman 已提交
1743 1744 1745 1746 1747
}

/*
 * Check that configured resources are correct.
 */
1748

P
Pierre Ossman 已提交
1749
static int wbsd_chip_validate(struct wbsd_host* host)
P
Pierre Ossman 已提交
1750 1751
{
	int base, irq, dma;
1752

P
Pierre Ossman 已提交
1753 1754
	wbsd_unlock_config(host);

L
Linus Torvalds 已提交
1755
	/*
P
Pierre Ossman 已提交
1756
	 * Select SD/MMC function.
L
Linus Torvalds 已提交
1757
	 */
P
Pierre Ossman 已提交
1758
	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1759

L
Linus Torvalds 已提交
1760
	/*
P
Pierre Ossman 已提交
1761
	 * Read configuration.
L
Linus Torvalds 已提交
1762
	 */
P
Pierre Ossman 已提交
1763 1764
	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
1765

P
Pierre Ossman 已提交
1766
	irq = wbsd_read_config(host, WBSD_CONF_IRQ);
1767

P
Pierre Ossman 已提交
1768
	dma = wbsd_read_config(host, WBSD_CONF_DRQ);
1769

P
Pierre Ossman 已提交
1770 1771
	wbsd_lock_config(host);

L
Linus Torvalds 已提交
1772
	/*
P
Pierre Ossman 已提交
1773
	 * Validate against given configuration.
L
Linus Torvalds 已提交
1774
	 */
P
Pierre Ossman 已提交
1775 1776 1777 1778 1779 1780
	if (base != host->base)
		return 0;
	if (irq != host->irq)
		return 0;
	if ((dma != host->dma) && (host->dma != -1))
		return 0;
1781

P
Pierre Ossman 已提交
1782 1783 1784
	return 1;
}

P
Pierre Ossman 已提交
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798
/*
 * 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);
}

P
Pierre Ossman 已提交
1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
/*****************************************************************************\
 *                                                                           *
 * 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;
1811

P
Pierre Ossman 已提交
1812 1813 1814
	ret = wbsd_alloc_mmc(dev);
	if (ret)
		return ret;
1815

P
Pierre Ossman 已提交
1816 1817
	mmc = dev_get_drvdata(dev);
	host = mmc_priv(mmc);
1818

L
Linus Torvalds 已提交
1819
	/*
P
Pierre Ossman 已提交
1820
	 * Scan for hardware.
L
Linus Torvalds 已提交
1821
	 */
P
Pierre Ossman 已提交
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836
	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;
		}
	}
1837

L
Linus Torvalds 已提交
1838
	/*
P
Pierre Ossman 已提交
1839
	 * Request resources.
L
Linus Torvalds 已提交
1840
	 */
P
Pierre Ossman 已提交
1841 1842 1843 1844 1845 1846 1847
	ret = wbsd_request_resources(host, io, irq, dma);
	if (ret)
	{
		wbsd_release_resources(host);
		wbsd_free_mmc(dev);
		return ret;
	}
1848

L
Linus Torvalds 已提交
1849
	/*
P
Pierre Ossman 已提交
1850
	 * See if chip needs to be configured.
L
Linus Torvalds 已提交
1851
	 */
P
Pierre Ossman 已提交
1852
	if (pnp)
P
Pierre Ossman 已提交
1853
	{
P
Pierre Ossman 已提交
1854
		if ((host->config != 0) && !wbsd_chip_validate(host))
P
Pierre Ossman 已提交
1855 1856 1857 1858 1859 1860 1861 1862 1863 1864
		{
			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);
1865

L
Linus Torvalds 已提交
1866 1867 1868 1869 1870
	/*
	 * Power Management stuff. No idea how this works.
	 * Not tested.
	 */
#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1871
	if (host->config)
P
Pierre Ossman 已提交
1872 1873
	{
		wbsd_unlock_config(host);
P
Pierre Ossman 已提交
1874
		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
P
Pierre Ossman 已提交
1875 1876
		wbsd_lock_config(host);
	}
L
Linus Torvalds 已提交
1877
#endif
P
Pierre Ossman 已提交
1878 1879 1880 1881
	/*
	 * Allow device to initialise itself properly.
	 */
	mdelay(5);
L
Linus Torvalds 已提交
1882 1883 1884 1885 1886

	/*
	 * Reset the chip into a known state.
	 */
	wbsd_init_device(host);
1887

L
Linus Torvalds 已提交
1888 1889
	mmc_add_host(mmc);

R
Russell King 已提交
1890
	printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
P
Pierre Ossman 已提交
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900
	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");
L
Linus Torvalds 已提交
1901 1902 1903 1904

	return 0;
}

P
Pierre Ossman 已提交
1905
static void __devexit wbsd_shutdown(struct device* dev, int pnp)
L
Linus Torvalds 已提交
1906 1907 1908
{
	struct mmc_host* mmc = dev_get_drvdata(dev);
	struct wbsd_host* host;
1909

L
Linus Torvalds 已提交
1910
	if (!mmc)
P
Pierre Ossman 已提交
1911
		return;
L
Linus Torvalds 已提交
1912 1913

	host = mmc_priv(mmc);
1914

L
Linus Torvalds 已提交
1915 1916
	mmc_remove_host(mmc);

P
Pierre Ossman 已提交
1917 1918 1919
	/*
	 * Power down the SD/MMC function.
	 */
P
Pierre Ossman 已提交
1920
	if (!pnp)
P
Pierre Ossman 已提交
1921
		wbsd_chip_poweroff(host);
1922

P
Pierre Ossman 已提交
1923
	wbsd_release_resources(host);
1924

P
Pierre Ossman 已提交
1925 1926
	wbsd_free_mmc(dev);
}
L
Linus Torvalds 已提交
1927

P
Pierre Ossman 已提交
1928 1929 1930 1931
/*
 * Non-PnP
 */

1932
static int __devinit wbsd_probe(struct platform_device* dev)
P
Pierre Ossman 已提交
1933
{
1934
	return wbsd_init(&dev->dev, io, irq, dma, 0);
P
Pierre Ossman 已提交
1935 1936
}

1937
static int __devexit wbsd_remove(struct platform_device* dev)
P
Pierre Ossman 已提交
1938
{
1939
	wbsd_shutdown(&dev->dev, 0);
P
Pierre Ossman 已提交
1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953

	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;
1954

P
Pierre Ossman 已提交
1955 1956 1957 1958 1959 1960 1961 1962 1963
	/*
	 * 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;
1964

P
Pierre Ossman 已提交
1965
	DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
1966

P
Pierre Ossman 已提交
1967 1968
	return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
}
L
Linus Torvalds 已提交
1969

P
Pierre Ossman 已提交
1970 1971 1972
static void __devexit wbsd_pnp_remove(struct pnp_dev * dev)
{
	wbsd_shutdown(&dev->dev, 1);
L
Linus Torvalds 已提交
1973 1974
}

P
Pierre Ossman 已提交
1975 1976
#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
1977 1978 1979 1980 1981
/*
 * Power management
 */

#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1982

1983
static int wbsd_suspend(struct platform_device *dev, pm_message_t state)
L
Linus Torvalds 已提交
1984
{
1985
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
	struct wbsd_host *host;
	int ret;

	if (!mmc)
		return 0;

	DBG("Suspending...\n");

	ret = mmc_suspend_host(mmc, state);
	if (!ret)
		return ret;

	host = mmc_priv(mmc);

	wbsd_chip_poweroff(host);
L
Linus Torvalds 已提交
2001 2002 2003 2004

	return 0;
}

2005
static int wbsd_resume(struct platform_device *dev)
L
Linus Torvalds 已提交
2006
{
2007
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
2008
	struct wbsd_host *host;
L
Linus Torvalds 已提交
2009

P
Pierre Ossman 已提交
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
	if (!mmc)
		return 0;

	DBG("Resuming...\n");

	host = mmc_priv(mmc);

	wbsd_chip_config(host);

	/*
	 * Allow device to initialise itself properly.
	 */
	mdelay(5);

	wbsd_init_device(host);

	return mmc_resume_host(mmc);
L
Linus Torvalds 已提交
2027
}
P
Pierre Ossman 已提交
2028 2029 2030

#else /* CONFIG_PM */

L
Linus Torvalds 已提交
2031 2032
#define wbsd_suspend NULL
#define wbsd_resume NULL
P
Pierre Ossman 已提交
2033 2034

#endif /* CONFIG_PM */
L
Linus Torvalds 已提交
2035

P
Pierre Ossman 已提交
2036
static struct platform_device *wbsd_device;
L
Linus Torvalds 已提交
2037

2038
static struct platform_driver wbsd_driver = {
L
Linus Torvalds 已提交
2039
	.probe		= wbsd_probe,
P
Pierre Ossman 已提交
2040
	.remove		= __devexit_p(wbsd_remove),
2041

L
Linus Torvalds 已提交
2042 2043
	.suspend	= wbsd_suspend,
	.resume		= wbsd_resume,
2044 2045 2046
	.driver		= {
		.name	= DRIVER_NAME,
	},
L
Linus Torvalds 已提交
2047 2048
};

P
Pierre Ossman 已提交
2049 2050 2051 2052 2053 2054
#ifdef CONFIG_PNP

static struct pnp_driver wbsd_pnp_driver = {
	.name		= DRIVER_NAME,
	.id_table	= pnp_dev_table,
	.probe		= wbsd_pnp_probe,
P
Pierre Ossman 已提交
2055
	.remove		= __devexit_p(wbsd_pnp_remove),
P
Pierre Ossman 已提交
2056 2057 2058 2059
};

#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
2060 2061 2062 2063 2064 2065 2066
/*
 * Module loading/unloading
 */

static int __init wbsd_drv_init(void)
{
	int result;
2067

L
Linus Torvalds 已提交
2068 2069 2070 2071 2072
	printk(KERN_INFO DRIVER_NAME
		": Winbond W83L51xD SD/MMC card interface driver, "
		DRIVER_VERSION "\n");
	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");

P
Pierre Ossman 已提交
2073 2074 2075 2076 2077 2078 2079 2080 2081
#ifdef CONFIG_PNP

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

2082 2083
#endif /* CONFIG_PNP */

P
Pierre Ossman 已提交
2084 2085
	if (nopnp)
	{
2086
		result = platform_driver_register(&wbsd_driver);
P
Pierre Ossman 已提交
2087 2088 2089
		if (result < 0)
			return result;

2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
		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;
		}
P
Pierre Ossman 已提交
2104
	}
L
Linus Torvalds 已提交
2105 2106 2107 2108 2109 2110

	return 0;
}

static void __exit wbsd_drv_exit(void)
{
P
Pierre Ossman 已提交
2111 2112 2113 2114
#ifdef CONFIG_PNP

	if (!nopnp)
		pnp_unregister_driver(&wbsd_pnp_driver);
2115 2116

#endif /* CONFIG_PNP */
P
Pierre Ossman 已提交
2117 2118 2119 2120

	if (nopnp)
	{
		platform_device_unregister(wbsd_device);
2121

2122
		platform_driver_unregister(&wbsd_driver);
P
Pierre Ossman 已提交
2123
	}
L
Linus Torvalds 已提交
2124 2125 2126 2127 2128 2129

	DBG("unloaded\n");
}

module_init(wbsd_drv_init);
module_exit(wbsd_drv_exit);
P
Pierre Ossman 已提交
2130 2131 2132
#ifdef CONFIG_PNP
module_param(nopnp, uint, 0444);
#endif
L
Linus Torvalds 已提交
2133 2134 2135 2136 2137
module_param(io, uint, 0444);
module_param(irq, uint, 0444);
module_param(dma, int, 0444);

MODULE_LICENSE("GPL");
P
Pierre Ossman 已提交
2138
MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
L
Linus Torvalds 已提交
2139 2140 2141
MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
MODULE_VERSION(DRIVER_VERSION);

P
Pierre Ossman 已提交
2142 2143 2144
#ifdef CONFIG_PNP
MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
#endif
L
Linus Torvalds 已提交
2145 2146 2147
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)");