wbsd.c 41.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
/*
 *  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/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
28
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
29
#include <linux/interrupt.h>
P
Pierre Ossman 已提交
30
#include <linux/dma-mapping.h>
L
Linus Torvalds 已提交
31
#include <linux/delay.h>
P
Pierre Ossman 已提交
32
#include <linux/pnp.h>
L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40 41 42 43
#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"
44
#define DRIVER_VERSION "1.6"
L
Linus Torvalds 已提交
45 46

#define DBG(x...) \
47
	pr_debug(DRIVER_NAME ": " x)
L
Linus Torvalds 已提交
48
#define DBGF(f, x...) \
49
	pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
L
Linus Torvalds 已提交
50

P
Pierre Ossman 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*
 * 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 已提交
67 68 69 70 71 72 73
static const int config_ports[] = { 0x2E, 0x4E };
static const int unlock_codes[] = { 0x83, 0x87 };

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

P
Pierre Ossman 已提交
74 75 76 77 78 79 80 81 82
#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 已提交
83 84 85 86
/*
 * Basic functions
 */

P
Pierre Ossman 已提交
87
static inline void wbsd_unlock_config(struct wbsd_host *host)
L
Linus Torvalds 已提交
88
{
P
Pierre Ossman 已提交
89
	BUG_ON(host->config == 0);
90

L
Linus Torvalds 已提交
91 92 93 94
	outb(host->unlock_code, host->config);
	outb(host->unlock_code, host->config);
}

P
Pierre Ossman 已提交
95
static inline void wbsd_lock_config(struct wbsd_host *host)
L
Linus Torvalds 已提交
96
{
P
Pierre Ossman 已提交
97
	BUG_ON(host->config == 0);
98

L
Linus Torvalds 已提交
99 100 101
	outb(LOCK_CODE, host->config);
}

P
Pierre Ossman 已提交
102
static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
L
Linus Torvalds 已提交
103
{
P
Pierre Ossman 已提交
104
	BUG_ON(host->config == 0);
105

L
Linus Torvalds 已提交
106 107 108 109
	outb(reg, host->config);
	outb(value, host->config + 1);
}

P
Pierre Ossman 已提交
110
static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
L
Linus Torvalds 已提交
111
{
P
Pierre Ossman 已提交
112
	BUG_ON(host->config == 0);
113

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

P
Pierre Ossman 已提交
118
static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
L
Linus Torvalds 已提交
119 120 121 122 123
{
	outb(index, host->base + WBSD_IDXR);
	outb(value, host->base + WBSD_DATAR);
}

P
Pierre Ossman 已提交
124
static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133
{
	outb(index, host->base + WBSD_IDXR);
	return inb(host->base + WBSD_DATAR);
}

/*
 * Common routines
 */

P
Pierre Ossman 已提交
134
static void wbsd_init_device(struct wbsd_host *host)
L
Linus Torvalds 已提交
135 136
{
	u8 setup, ier;
137

L
Linus Torvalds 已提交
138 139 140 141 142 143
	/*
	 * 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);
144

P
Pierre Ossman 已提交
145 146 147 148 149 150
	/*
	 * Set DAT3 to input
	 */
	setup &= ~WBSD_DAT3_H;
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
	host->flags &= ~WBSD_FIGNORE_DETECT;
151

L
Linus Torvalds 已提交
152 153 154 155 156 157 158 159 160
	/*
	 * Read back default clock.
	 */
	host->clk = wbsd_read_index(host, WBSD_IDX_CLK);

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

L
Linus Torvalds 已提交
162 163 164 165
	/*
	 * Set maximum timeout.
	 */
	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
166

P
Pierre Ossman 已提交
167 168 169 170 171 172 173
	/*
	 * Test for card presence
	 */
	if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
		host->flags |= WBSD_FCARD_PRESENT;
	else
		host->flags &= ~WBSD_FCARD_PRESENT;
174

L
Linus Torvalds 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	/*
	 * 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);
}

P
Pierre Ossman 已提交
194
static void wbsd_reset(struct wbsd_host *host)
L
Linus Torvalds 已提交
195 196
{
	u8 setup;
197

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

L
Linus Torvalds 已提交
200 201 202 203 204 205 206 207
	/*
	 * 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);
}

P
Pierre Ossman 已提交
208
static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
L
Linus Torvalds 已提交
209 210
{
	unsigned long dmaflags;
211

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

P
Pierre Ossman 已提交
214
	if (host->dma >= 0) {
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227
		/*
		 * 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);
	}
228

L
Linus Torvalds 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242
	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
 */

P
Pierre Ossman 已提交
243
static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251 252 253 254
{
	/*
	 * 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;
}

P
Pierre Ossman 已提交
255
static inline int wbsd_next_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265
{
	/*
	 * Skip to next SG entry.
	 */
	host->cur_sg++;
	host->num_sg--;

	/*
	 * Any entries left?
	 */
P
Pierre Ossman 已提交
266 267 268 269
	if (host->num_sg > 0) {
		host->offset = 0;
		host->remain = host->cur_sg->length;
	}
270

L
Linus Torvalds 已提交
271 272 273
	return host->num_sg;
}

P
Pierre Ossman 已提交
274
static inline char *wbsd_kmap_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
275 276 277 278 279 280
{
	host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
		host->cur_sg->offset;
	return host->mapped_sg;
}

P
Pierre Ossman 已提交
281
static inline void wbsd_kunmap_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
282 283 284 285
{
	kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
}

P
Pierre Ossman 已提交
286
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
287 288
{
	unsigned int len, i, size;
P
Pierre Ossman 已提交
289 290 291
	struct scatterlist *sg;
	char *dmabuf = host->dma_buffer;
	char *sgbuf;
292

L
Linus Torvalds 已提交
293
	size = host->size;
294

L
Linus Torvalds 已提交
295 296
	sg = data->sg;
	len = data->sg_len;
297

L
Linus Torvalds 已提交
298 299 300 301 302
	/*
	 * Just loop through all entries. Size might not
	 * be the entire list though so make sure that
	 * we do not transfer too much.
	 */
P
Pierre Ossman 已提交
303
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
304 305 306 307 308 309 310
		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;
311

L
Linus Torvalds 已提交
312 313 314 315
		if (size < sg[i].length)
			size = 0;
		else
			size -= sg[i].length;
316

L
Linus Torvalds 已提交
317 318 319
		if (size == 0)
			break;
	}
320

L
Linus Torvalds 已提交
321 322 323 324
	/*
	 * Check that we didn't get a request to transfer
	 * more data than can fit into the SG list.
	 */
325

L
Linus Torvalds 已提交
326
	BUG_ON(size != 0);
327

L
Linus Torvalds 已提交
328 329 330
	host->size -= size;
}

P
Pierre Ossman 已提交
331
static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
332 333
{
	unsigned int len, i, size;
P
Pierre Ossman 已提交
334 335 336
	struct scatterlist *sg;
	char *dmabuf = host->dma_buffer;
	char *sgbuf;
337

L
Linus Torvalds 已提交
338
	size = host->size;
339

L
Linus Torvalds 已提交
340 341
	sg = data->sg;
	len = data->sg_len;
342

L
Linus Torvalds 已提交
343 344 345 346 347
	/*
	 * Just loop through all entries. Size might not
	 * be the entire list though so make sure that
	 * we do not transfer too much.
	 */
P
Pierre Ossman 已提交
348
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
349 350 351 352 353 354 355
		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;
356

L
Linus Torvalds 已提交
357 358 359 360
		if (size < sg[i].length)
			size = 0;
		else
			size -= sg[i].length;
361

L
Linus Torvalds 已提交
362 363 364
		if (size == 0)
			break;
	}
365

L
Linus Torvalds 已提交
366 367 368 369
	/*
	 * Check that we didn't get a request to transfer
	 * more data than can fit into the SG list.
	 */
370

L
Linus Torvalds 已提交
371
	BUG_ON(size != 0);
372

L
Linus Torvalds 已提交
373 374 375 376 377 378
	host->size -= size;
}

/*
 * Command handling
 */
379

P
Pierre Ossman 已提交
380 381
static inline void wbsd_get_short_reply(struct wbsd_host *host,
					struct mmc_command *cmd)
L
Linus Torvalds 已提交
382 383 384 385
{
	/*
	 * Correct response type?
	 */
P
Pierre Ossman 已提交
386
	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
L
Linus Torvalds 已提交
387 388 389
		cmd->error = MMC_ERR_INVALID;
		return;
	}
390

P
Pierre Ossman 已提交
391 392 393 394 395
	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;
L
Linus Torvalds 已提交
396 397
}

P
Pierre Ossman 已提交
398 399
static inline void wbsd_get_long_reply(struct wbsd_host *host,
	struct mmc_command *cmd)
L
Linus Torvalds 已提交
400 401
{
	int i;
402

L
Linus Torvalds 已提交
403 404 405
	/*
	 * Correct response type?
	 */
P
Pierre Ossman 已提交
406
	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
L
Linus Torvalds 已提交
407 408 409
		cmd->error = MMC_ERR_INVALID;
		return;
	}
410

P
Pierre Ossman 已提交
411
	for (i = 0; i < 4; i++) {
L
Linus Torvalds 已提交
412 413 414 415 416 417 418 419 420 421 422
		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;
	}
}

P
Pierre Ossman 已提交
423
static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
L
Linus Torvalds 已提交
424 425 426
{
	int i;
	u8 status, isr;
427

L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435
	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;
436

L
Linus Torvalds 已提交
437 438 439 440
	/*
	 * Send the command (CRC calculated by host).
	 */
	outb(cmd->opcode, host->base + WBSD_CMDR);
P
Pierre Ossman 已提交
441
	for (i = 3; i >= 0; i--)
L
Linus Torvalds 已提交
442
		outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
443

L
Linus Torvalds 已提交
444
	cmd->error = MMC_ERR_NONE;
445

L
Linus Torvalds 已提交
446 447 448 449 450 451 452 453 454 455
	/*
	 * Wait for the request to complete.
	 */
	do {
		status = wbsd_read_index(host, WBSD_IDX_STATUS);
	} while (status & WBSD_CARDTRAFFIC);

	/*
	 * Do we expect a reply?
	 */
R
Russell King 已提交
456
	if (cmd->flags & MMC_RSP_PRESENT) {
L
Linus Torvalds 已提交
457 458 459 460
		/*
		 * Read back status.
		 */
		isr = host->isr;
461

L
Linus Torvalds 已提交
462 463 464 465 466 467 468 469 470 471
		/* 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 */
P
Pierre Ossman 已提交
472
		else {
R
Russell King 已提交
473
			if (cmd->flags & MMC_RSP_136)
L
Linus Torvalds 已提交
474
				wbsd_get_long_reply(host, cmd);
R
Russell King 已提交
475 476
			else
				wbsd_get_short_reply(host, cmd);
L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485 486
		}
	}

	DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
}

/*
 * Data functions
 */

P
Pierre Ossman 已提交
487
static void wbsd_empty_fifo(struct wbsd_host *host)
L
Linus Torvalds 已提交
488
{
P
Pierre Ossman 已提交
489 490
	struct mmc_data *data = host->mrq->cmd->data;
	char *buffer;
L
Linus Torvalds 已提交
491
	int i, fsr, fifo;
492

L
Linus Torvalds 已提交
493 494 495 496 497
	/*
	 * Handle excessive data.
	 */
	if (data->bytes_xfered == host->size)
		return;
498

L
Linus Torvalds 已提交
499 500 501 502 503 504
	buffer = wbsd_kmap_sg(host) + host->offset;

	/*
	 * Drain the fifo. This has a tendency to loop longer
	 * than the FIFO length (usually one block).
	 */
P
Pierre Ossman 已提交
505
	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
L
Linus Torvalds 已提交
506 507 508
		/*
		 * The size field in the FSR is broken so we have to
		 * do some guessing.
509
		 */
L
Linus Torvalds 已提交
510 511 512 513 514 515
		if (fsr & WBSD_FIFO_FULL)
			fifo = 16;
		else if (fsr & WBSD_FIFO_FUTHRE)
			fifo = 8;
		else
			fifo = 1;
516

P
Pierre Ossman 已提交
517
		for (i = 0; i < fifo; i++) {
L
Linus Torvalds 已提交
518 519 520 521 522 523
			*buffer = inb(host->base + WBSD_DFR);
			buffer++;
			host->offset++;
			host->remain--;

			data->bytes_xfered++;
524

L
Linus Torvalds 已提交
525 526 527
			/*
			 * Transfer done?
			 */
P
Pierre Ossman 已提交
528
			if (data->bytes_xfered == host->size) {
529
				wbsd_kunmap_sg(host);
L
Linus Torvalds 已提交
530 531
				return;
			}
532

L
Linus Torvalds 已提交
533 534 535
			/*
			 * End of scatter list entry?
			 */
P
Pierre Ossman 已提交
536
			if (host->remain == 0) {
L
Linus Torvalds 已提交
537
				wbsd_kunmap_sg(host);
538

L
Linus Torvalds 已提交
539 540 541
				/*
				 * Get next entry. Check if last.
				 */
P
Pierre Ossman 已提交
542
				if (!wbsd_next_sg(host)) {
L
Linus Torvalds 已提交
543 544 545 546 547 548 549
					/*
					 * 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);
550

L
Linus Torvalds 已提交
551
					host->size = data->bytes_xfered;
552

L
Linus Torvalds 已提交
553 554
					return;
				}
555

L
Linus Torvalds 已提交
556 557 558 559
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
560

L
Linus Torvalds 已提交
561 562 563 564 565 566 567 568 569 570 571
	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);
}

P
Pierre Ossman 已提交
572
static void wbsd_fill_fifo(struct wbsd_host *host)
L
Linus Torvalds 已提交
573
{
P
Pierre Ossman 已提交
574 575
	struct mmc_data *data = host->mrq->cmd->data;
	char *buffer;
L
Linus Torvalds 已提交
576
	int i, fsr, fifo;
577

L
Linus Torvalds 已提交
578 579 580 581 582 583 584 585 586 587 588 589 590
	/*
	 * 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).
	 */
P
Pierre Ossman 已提交
591
	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
L
Linus Torvalds 已提交
592 593 594
		/*
		 * The size field in the FSR is broken so we have to
		 * do some guessing.
595
		 */
L
Linus Torvalds 已提交
596 597 598 599 600 601 602
		if (fsr & WBSD_FIFO_EMPTY)
			fifo = 0;
		else if (fsr & WBSD_FIFO_EMTHRE)
			fifo = 8;
		else
			fifo = 15;

P
Pierre Ossman 已提交
603
		for (i = 16; i > fifo; i--) {
L
Linus Torvalds 已提交
604 605 606 607
			outb(*buffer, host->base + WBSD_DFR);
			buffer++;
			host->offset++;
			host->remain--;
608

L
Linus Torvalds 已提交
609
			data->bytes_xfered++;
610

L
Linus Torvalds 已提交
611 612 613
			/*
			 * Transfer done?
			 */
P
Pierre Ossman 已提交
614
			if (data->bytes_xfered == host->size) {
L
Linus Torvalds 已提交
615 616 617 618 619 620 621
				wbsd_kunmap_sg(host);
				return;
			}

			/*
			 * End of scatter list entry?
			 */
P
Pierre Ossman 已提交
622
			if (host->remain == 0) {
L
Linus Torvalds 已提交
623
				wbsd_kunmap_sg(host);
624

L
Linus Torvalds 已提交
625 626 627
				/*
				 * Get next entry. Check if last.
				 */
P
Pierre Ossman 已提交
628
				if (!wbsd_next_sg(host)) {
L
Linus Torvalds 已提交
629 630 631 632 633 634 635
					/*
					 * 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);
636

L
Linus Torvalds 已提交
637
					host->size = data->bytes_xfered;
638

L
Linus Torvalds 已提交
639 640
					return;
				}
641

L
Linus Torvalds 已提交
642 643 644 645
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
646

L
Linus Torvalds 已提交
647
	wbsd_kunmap_sg(host);
648

P
Pierre Ossman 已提交
649 650 651 652 653 654
	/*
	 * 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 已提交
655 656
}

P
Pierre Ossman 已提交
657
static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
658 659 660 661 662 663
{
	u16 blksize;
	u8 setup;
	unsigned long dmaflags;

	DBGF("blksz %04x blks %04x flags %08x\n",
664
		data->blksz, data->blocks, data->flags);
L
Linus Torvalds 已提交
665 666
	DBGF("tsac %d ms nsac %d clk\n",
		data->timeout_ns / 1000000, data->timeout_clks);
667

L
Linus Torvalds 已提交
668 669 670
	/*
	 * Calculate size.
	 */
671
	host->size = data->blocks * data->blksz;
L
Linus Torvalds 已提交
672 673 674 675 676 677 678

	/*
	 * 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);
P
Pierre Ossman 已提交
679 680 681 682
	else {
		wbsd_write_index(host, WBSD_IDX_TAAC,
			data->timeout_ns / 1000000);
	}
683

L
Linus Torvalds 已提交
684 685 686 687
	if (data->timeout_clks > 255)
		wbsd_write_index(host, WBSD_IDX_NSAC, 255);
	else
		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
688

L
Linus Torvalds 已提交
689 690 691 692 693 694
	/*
	 * 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.
695
	 * Two bytes are needed for each data line.
L
Linus Torvalds 已提交
696
	 */
P
Pierre Ossman 已提交
697
	if (host->bus_width == MMC_BUS_WIDTH_1) {
698
		blksize = data->blksz + 2;
699 700 701

		wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
P
Pierre Ossman 已提交
702
	} else if (host->bus_width == MMC_BUS_WIDTH_4) {
703
		blksize = data->blksz + 2 * 4;
704

P
Pierre Ossman 已提交
705 706
		wbsd_write_index(host, WBSD_IDX_PBSMSB,
			((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
707
		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
P
Pierre Ossman 已提交
708
	} else {
709 710 711
		data->error = MMC_ERR_INVALID;
		return;
	}
L
Linus Torvalds 已提交
712 713 714 715 716 717 718 719 720

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

L
Linus Torvalds 已提交
722 723 724
	/*
	 * DMA transfer?
	 */
P
Pierre Ossman 已提交
725
	if (host->dma >= 0) {
L
Linus Torvalds 已提交
726 727 728 729
		/*
		 * The buffer for DMA is only 64 kB.
		 */
		BUG_ON(host->size > 0x10000);
P
Pierre Ossman 已提交
730
		if (host->size > 0x10000) {
L
Linus Torvalds 已提交
731 732 733
			data->error = MMC_ERR_INVALID;
			return;
		}
734

L
Linus Torvalds 已提交
735 736 737 738 739 740
		/*
		 * Transfer data from the SG list to
		 * the DMA buffer.
		 */
		if (data->flags & MMC_DATA_WRITE)
			wbsd_sg_to_dma(host, data);
741

L
Linus Torvalds 已提交
742 743
		/*
		 * Initialise the ISA DMA controller.
744
		 */
L
Linus Torvalds 已提交
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
		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);
P
Pierre Ossman 已提交
762
	} else {
L
Linus Torvalds 已提交
763 764 765 766 767
		/*
		 * This flag is used to keep printk
		 * output to a minimum.
		 */
		host->firsterr = 1;
768

L
Linus Torvalds 已提交
769 770 771 772
		/*
		 * Initialise the SG list.
		 */
		wbsd_init_sg(host, data);
773

L
Linus Torvalds 已提交
774 775 776 777
		/*
		 * Turn off DMA.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
778

L
Linus Torvalds 已提交
779 780 781 782
		/*
		 * Set up FIFO threshold levels (and fill
		 * buffer if doing a write).
		 */
P
Pierre Ossman 已提交
783
		if (data->flags & MMC_DATA_READ) {
L
Linus Torvalds 已提交
784 785
			wbsd_write_index(host, WBSD_IDX_FIFOEN,
				WBSD_FIFOEN_FULL | 8);
P
Pierre Ossman 已提交
786
		} else {
L
Linus Torvalds 已提交
787 788 789 790
			wbsd_write_index(host, WBSD_IDX_FIFOEN,
				WBSD_FIFOEN_EMPTY | 8);
			wbsd_fill_fifo(host);
		}
791 792
	}

L
Linus Torvalds 已提交
793 794 795
	data->error = MMC_ERR_NONE;
}

P
Pierre Ossman 已提交
796
static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
797 798 799 800
{
	unsigned long dmaflags;
	int count;
	u8 status;
801

L
Linus Torvalds 已提交
802 803 804 805 806 807 808 809 810 811 812 813
	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.
	 */
P
Pierre Ossman 已提交
814
	do {
L
Linus Torvalds 已提交
815 816
		status = wbsd_read_index(host, WBSD_IDX_STATUS);
	} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
817

L
Linus Torvalds 已提交
818 819 820
	/*
	 * DMA transfer?
	 */
P
Pierre Ossman 已提交
821
	if (host->dma >= 0) {
L
Linus Torvalds 已提交
822 823 824 825
		/*
		 * Disable DMA on the host.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
826

L
Linus Torvalds 已提交
827 828 829 830 831 832 833 834
		/*
		 * 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);
835

L
Linus Torvalds 已提交
836 837 838
		/*
		 * Any leftover data?
		 */
P
Pierre Ossman 已提交
839
		if (count) {
840 841 842
			printk(KERN_ERR "%s: Incomplete DMA transfer. "
				"%d bytes left.\n",
				mmc_hostname(host->mmc), count);
843

L
Linus Torvalds 已提交
844
			data->error = MMC_ERR_FAILED;
P
Pierre Ossman 已提交
845
		} else {
L
Linus Torvalds 已提交
846 847 848 849 850 851
			/*
			 * Transfer data from DMA buffer to
			 * SG list.
			 */
			if (data->flags & MMC_DATA_READ)
				wbsd_dma_to_sg(host, data);
852

L
Linus Torvalds 已提交
853 854 855
			data->bytes_xfered = host->size;
		}
	}
856

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

L
Linus Torvalds 已提交
859 860 861
	wbsd_request_end(host, host->mrq);
}

P
Pierre Ossman 已提交
862 863 864 865 866
/*****************************************************************************\
 *                                                                           *
 * MMC layer callbacks                                                       *
 *                                                                           *
\*****************************************************************************/
L
Linus Torvalds 已提交
867

P
Pierre Ossman 已提交
868
static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
L
Linus Torvalds 已提交
869
{
P
Pierre Ossman 已提交
870 871
	struct wbsd_host *host = mmc_priv(mmc);
	struct mmc_command *cmd;
L
Linus Torvalds 已提交
872 873 874 875 876 877 878 879 880 881 882

	/*
	 * Disable tasklets to avoid a deadlock.
	 */
	spin_lock_bh(&host->lock);

	BUG_ON(host->mrq != NULL);

	cmd = mrq->cmd;

	host->mrq = mrq;
883

L
Linus Torvalds 已提交
884 885 886 887
	/*
	 * If there is no card in the slot then
	 * timeout immediatly.
	 */
P
Pierre Ossman 已提交
888
	if (!(host->flags & WBSD_FCARD_PRESENT)) {
L
Linus Torvalds 已提交
889 890 891 892 893 894 895
		cmd->error = MMC_ERR_TIMEOUT;
		goto done;
	}

	/*
	 * Does the request include data?
	 */
P
Pierre Ossman 已提交
896
	if (cmd->data) {
L
Linus Torvalds 已提交
897
		wbsd_prepare_data(host, cmd->data);
898

L
Linus Torvalds 已提交
899 900 901
		if (cmd->data->error != MMC_ERR_NONE)
			goto done;
	}
902

L
Linus Torvalds 已提交
903 904 905 906 907 908
	wbsd_send_command(host, cmd);

	/*
	 * If this is a data transfer the request
	 * will be finished after the data has
	 * transfered.
909
	 */
P
Pierre Ossman 已提交
910
	if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
L
Linus Torvalds 已提交
911 912 913 914 915 916 917 918 919 920
		/*
		 * Dirty fix for hardware bug.
		 */
		if (host->dma == -1)
			tasklet_schedule(&host->fifo_tasklet);

		spin_unlock_bh(&host->lock);

		return;
	}
921

L
Linus Torvalds 已提交
922 923 924 925 926 927
done:
	wbsd_request_end(host, mrq);

	spin_unlock_bh(&host->lock);
}

P
Pierre Ossman 已提交
928
static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
L
Linus Torvalds 已提交
929
{
P
Pierre Ossman 已提交
930
	struct wbsd_host *host = mmc_priv(mmc);
L
Linus Torvalds 已提交
931
	u8 clk, setup, pwr;
932

L
Linus Torvalds 已提交
933 934 935 936 937 938 939 940
	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);
941

L
Linus Torvalds 已提交
942 943 944 945 946 947 948 949 950 951 952 953 954
	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.
	 */
P
Pierre Ossman 已提交
955
	if (clk != host->clk) {
L
Linus Torvalds 已提交
956 957 958 959
		wbsd_write_index(host, WBSD_IDX_CLK, clk);
		host->clk = clk;
	}

P
Pierre Ossman 已提交
960 961 962
	/*
	 * Power up card.
	 */
P
Pierre Ossman 已提交
963
	if (ios->power_mode != MMC_POWER_OFF) {
L
Linus Torvalds 已提交
964 965 966 967 968
		pwr = inb(host->base + WBSD_CSR);
		pwr &= ~WBSD_POWER_N;
		outb(pwr, host->base + WBSD_CSR);
	}

P
Pierre Ossman 已提交
969 970 971
	/*
	 * MMC cards need to have pin 1 high during init.
	 * It wreaks havoc with the card detection though so
972
	 * that needs to be disabled.
P
Pierre Ossman 已提交
973 974
	 */
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
P
Pierre Ossman 已提交
975
	if (ios->chip_select == MMC_CS_HIGH) {
976
		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
P
Pierre Ossman 已提交
977 978
		setup |= WBSD_DAT3_H;
		host->flags |= WBSD_FIGNORE_DETECT;
P
Pierre Ossman 已提交
979 980
	} else {
		if (setup & WBSD_DAT3_H) {
P
Pierre Ossman 已提交
981
			setup &= ~WBSD_DAT3_H;
982

P
Pierre Ossman 已提交
983 984 985 986
			/*
			 * We cannot resume card detection immediatly
			 * because of capacitance and delays in the chip.
			 */
P
Pierre Ossman 已提交
987
			mod_timer(&host->ignore_timer, jiffies + HZ / 100);
P
Pierre Ossman 已提交
988
		}
P
Pierre Ossman 已提交
989 990
	}
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
991

992 993 994 995 996 997
	/*
	 * Store bus width for later. Will be used when
	 * setting up the data transfer.
	 */
	host->bus_width = ios->bus_width;

L
Linus Torvalds 已提交
998 999 1000
	spin_unlock_bh(&host->lock);
}

P
Pierre Ossman 已提交
1001
static int wbsd_get_ro(struct mmc_host *mmc)
1002
{
P
Pierre Ossman 已提交
1003
	struct wbsd_host *host = mmc_priv(mmc);
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
	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 已提交
1023 1024 1025
static struct mmc_host_ops wbsd_ops = {
	.request	= wbsd_request,
	.set_ios	= wbsd_set_ios,
1026
	.get_ro		= wbsd_get_ro,
P
Pierre Ossman 已提交
1027 1028 1029 1030 1031 1032 1033 1034
};

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

1035 1036 1037 1038 1039 1040
/*
 * Helper function to reset detection ignore
 */

static void wbsd_reset_ignore(unsigned long data)
{
P
Pierre Ossman 已提交
1041
	struct wbsd_host *host = (struct wbsd_host *)data;
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059

	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 已提交
1060 1061 1062 1063
/*
 * Tasklets
 */

P
Pierre Ossman 已提交
1064
static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
L
Linus Torvalds 已提交
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
{
	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;
1077

L
Linus Torvalds 已提交
1078 1079 1080 1081 1082
	return host->mrq->cmd->data;
}

static void wbsd_tasklet_card(unsigned long param)
{
P
Pierre Ossman 已提交
1083
	struct wbsd_host *host = (struct wbsd_host *)param;
L
Linus Torvalds 已提交
1084
	u8 csr;
1085
	int delay = -1;
1086

L
Linus Torvalds 已提交
1087
	spin_lock(&host->lock);
1088

P
Pierre Ossman 已提交
1089
	if (host->flags & WBSD_FIGNORE_DETECT) {
P
Pierre Ossman 已提交
1090 1091 1092
		spin_unlock(&host->lock);
		return;
	}
1093

L
Linus Torvalds 已提交
1094 1095
	csr = inb(host->base + WBSD_CSR);
	WARN_ON(csr == 0xff);
1096

P
Pierre Ossman 已提交
1097 1098
	if (csr & WBSD_CARDPRESENT) {
		if (!(host->flags & WBSD_FCARD_PRESENT)) {
P
Pierre Ossman 已提交
1099 1100
			DBG("Card inserted\n");
			host->flags |= WBSD_FCARD_PRESENT;
1101

1102
			delay = 500;
P
Pierre Ossman 已提交
1103
		}
P
Pierre Ossman 已提交
1104
	} else if (host->flags & WBSD_FCARD_PRESENT) {
L
Linus Torvalds 已提交
1105
		DBG("Card removed\n");
P
Pierre Ossman 已提交
1106
		host->flags &= ~WBSD_FCARD_PRESENT;
1107

P
Pierre Ossman 已提交
1108
		if (host->mrq) {
1109 1110
			printk(KERN_ERR "%s: Card removed during transfer!\n",
				mmc_hostname(host->mmc));
L
Linus Torvalds 已提交
1111
			wbsd_reset(host);
1112

L
Linus Torvalds 已提交
1113 1114 1115
			host->mrq->cmd->error = MMC_ERR_FAILED;
			tasklet_schedule(&host->finish_tasklet);
		}
1116

1117
		delay = 0;
1118
	}
1119 1120 1121 1122 1123 1124 1125 1126 1127

	/*
	 * 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 已提交
1128 1129 1130 1131
}

static void wbsd_tasklet_fifo(unsigned long param)
{
P
Pierre Ossman 已提交
1132 1133
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1134

L
Linus Torvalds 已提交
1135
	spin_lock(&host->lock);
1136

L
Linus Torvalds 已提交
1137 1138
	if (!host->mrq)
		goto end;
1139

L
Linus Torvalds 已提交
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
	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?
	 */
P
Pierre Ossman 已提交
1152
	if (host->size == data->bytes_xfered) {
L
Linus Torvalds 已提交
1153 1154 1155 1156
		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
		tasklet_schedule(&host->finish_tasklet);
	}

1157
end:
L
Linus Torvalds 已提交
1158 1159 1160 1161 1162
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_crc(unsigned long param)
{
P
Pierre Ossman 已提交
1163 1164
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1165

L
Linus Torvalds 已提交
1166
	spin_lock(&host->lock);
1167

L
Linus Torvalds 已提交
1168 1169
	if (!host->mrq)
		goto end;
1170

L
Linus Torvalds 已提交
1171 1172 1173
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1174

L
Linus Torvalds 已提交
1175 1176 1177
	DBGF("CRC error\n");

	data->error = MMC_ERR_BADCRC;
1178

L
Linus Torvalds 已提交
1179 1180
	tasklet_schedule(&host->finish_tasklet);

1181
end:
L
Linus Torvalds 已提交
1182 1183 1184 1185 1186
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_timeout(unsigned long param)
{
P
Pierre Ossman 已提交
1187 1188
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1189

L
Linus Torvalds 已提交
1190
	spin_lock(&host->lock);
1191

L
Linus Torvalds 已提交
1192 1193
	if (!host->mrq)
		goto end;
1194

L
Linus Torvalds 已提交
1195 1196 1197
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1198

L
Linus Torvalds 已提交
1199 1200 1201
	DBGF("Timeout\n");

	data->error = MMC_ERR_TIMEOUT;
1202

L
Linus Torvalds 已提交
1203 1204
	tasklet_schedule(&host->finish_tasklet);

1205
end:
L
Linus Torvalds 已提交
1206 1207 1208 1209 1210
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_finish(unsigned long param)
{
P
Pierre Ossman 已提交
1211 1212
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1213

L
Linus Torvalds 已提交
1214
	spin_lock(&host->lock);
1215

L
Linus Torvalds 已提交
1216 1217 1218
	WARN_ON(!host->mrq);
	if (!host->mrq)
		goto end;
1219

L
Linus Torvalds 已提交
1220 1221 1222 1223 1224
	data = wbsd_get_data(host);
	if (!data)
		goto end;

	wbsd_finish_data(host, data);
1225 1226

end:
L
Linus Torvalds 已提交
1227 1228 1229 1230 1231
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_block(unsigned long param)
{
P
Pierre Ossman 已提交
1232 1233
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1234

L
Linus Torvalds 已提交
1235 1236 1237
	spin_lock(&host->lock);

	if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
P
Pierre Ossman 已提交
1238
		WBSD_CRC_OK) {
L
Linus Torvalds 已提交
1239 1240 1241
		data = wbsd_get_data(host);
		if (!data)
			goto end;
1242

L
Linus Torvalds 已提交
1243 1244 1245
		DBGF("CRC error\n");

		data->error = MMC_ERR_BADCRC;
1246

L
Linus Torvalds 已提交
1247 1248 1249
		tasklet_schedule(&host->finish_tasklet);
	}

1250
end:
L
Linus Torvalds 已提交
1251 1252 1253 1254 1255 1256 1257 1258 1259
	spin_unlock(&host->lock);
}

/*
 * Interrupt handling
 */

static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
{
P
Pierre Ossman 已提交
1260
	struct wbsd_host *host = dev_id;
L
Linus Torvalds 已提交
1261
	int isr;
1262

L
Linus Torvalds 已提交
1263 1264 1265 1266 1267 1268 1269
	isr = inb(host->base + WBSD_ISR);

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

L
Linus Torvalds 已提交
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
	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);
1288

L
Linus Torvalds 已提交
1289 1290 1291
	return IRQ_HANDLED;
}

P
Pierre Ossman 已提交
1292 1293 1294 1295 1296 1297
/*****************************************************************************\
 *                                                                           *
 * Device initialisation and shutdown                                        *
 *                                                                           *
\*****************************************************************************/

L
Linus Torvalds 已提交
1298
/*
P
Pierre Ossman 已提交
1299
 * Allocate/free MMC structure.
L
Linus Torvalds 已提交
1300 1301
 */

P
Pierre Ossman 已提交
1302
static int __devinit wbsd_alloc_mmc(struct device *dev)
P
Pierre Ossman 已提交
1303
{
P
Pierre Ossman 已提交
1304 1305
	struct mmc_host *mmc;
	struct wbsd_host *host;
1306

P
Pierre Ossman 已提交
1307 1308 1309 1310 1311 1312
	/*
	 * Allocate MMC structure.
	 */
	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
	if (!mmc)
		return -ENOMEM;
1313

P
Pierre Ossman 已提交
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
	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;
P
Pierre Ossman 已提交
1325
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
1326
	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
1327

P
Pierre Ossman 已提交
1328
	spin_lock_init(&host->lock);
1329

1330
	/*
1331
	 * Set up timers
1332
	 */
1333 1334 1335
	init_timer(&host->ignore_timer);
	host->ignore_timer.data = (unsigned long)host;
	host->ignore_timer.function = wbsd_reset_ignore;
1336

P
Pierre Ossman 已提交
1337 1338 1339 1340 1341 1342
	/*
	 * 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;
1343

P
Pierre Ossman 已提交
1344 1345 1346 1347 1348
	/*
	 * Maximum number of sectors in one transfer. Also limited by 64kB
	 * buffer.
	 */
	mmc->max_sectors = 128;
1349

P
Pierre Ossman 已提交
1350 1351 1352 1353 1354
	/*
	 * Maximum segment size. Could be one segment with the maximum number
	 * of segments.
	 */
	mmc->max_seg_size = mmc->max_sectors * 512;
1355

P
Pierre Ossman 已提交
1356
	dev_set_drvdata(dev, mmc);
1357

P
Pierre Ossman 已提交
1358 1359 1360
	return 0;
}

P
Pierre Ossman 已提交
1361
static void __devexit wbsd_free_mmc(struct device *dev)
P
Pierre Ossman 已提交
1362
{
P
Pierre Ossman 已提交
1363 1364
	struct mmc_host *mmc;
	struct wbsd_host *host;
1365

P
Pierre Ossman 已提交
1366 1367 1368
	mmc = dev_get_drvdata(dev);
	if (!mmc)
		return;
1369

1370 1371
	host = mmc_priv(mmc);
	BUG_ON(host == NULL);
1372

1373
	del_timer_sync(&host->ignore_timer);
1374

P
Pierre Ossman 已提交
1375
	mmc_free_host(mmc);
1376

P
Pierre Ossman 已提交
1377 1378 1379 1380 1381 1382 1383
	dev_set_drvdata(dev, NULL);
}

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

P
Pierre Ossman 已提交
1384
static int __devinit wbsd_scan(struct wbsd_host *host)
L
Linus Torvalds 已提交
1385 1386 1387
{
	int i, j, k;
	int id;
1388

L
Linus Torvalds 已提交
1389 1390 1391 1392
	/*
	 * Iterate through all ports, all codes to
	 * find hardware that is in our known list.
	 */
1393
	for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
L
Linus Torvalds 已提交
1394 1395
		if (!request_region(config_ports[i], 2, DRIVER_NAME))
			continue;
1396

1397
		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
L
Linus Torvalds 已提交
1398
			id = 0xFFFF;
1399

P
Pierre Ossman 已提交
1400 1401 1402 1403
			host->config = config_ports[i];
			host->unlock_code = unlock_codes[j];

			wbsd_unlock_config(host);
1404

L
Linus Torvalds 已提交
1405 1406 1407 1408 1409
			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);
1410

P
Pierre Ossman 已提交
1411 1412
			wbsd_lock_config(host);

1413
			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
P
Pierre Ossman 已提交
1414
				if (id == valid_ids[k]) {
L
Linus Torvalds 已提交
1415
					host->chip_id = id;
1416

L
Linus Torvalds 已提交
1417 1418 1419
					return 0;
				}
			}
1420

P
Pierre Ossman 已提交
1421
			if (id != 0xFFFF) {
L
Linus Torvalds 已提交
1422 1423 1424 1425
				DBG("Unknown hardware (id %x) found at %x\n",
					id, config_ports[i]);
			}
		}
1426

L
Linus Torvalds 已提交
1427 1428
		release_region(config_ports[i], 2);
	}
1429

P
Pierre Ossman 已提交
1430 1431 1432
	host->config = 0;
	host->unlock_code = 0;

L
Linus Torvalds 已提交
1433 1434 1435
	return -ENODEV;
}

P
Pierre Ossman 已提交
1436 1437 1438 1439
/*
 * Allocate/free io port ranges
 */

P
Pierre Ossman 已提交
1440
static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
L
Linus Torvalds 已提交
1441
{
1442
	if (base & 0x7)
L
Linus Torvalds 已提交
1443
		return -EINVAL;
1444

P
Pierre Ossman 已提交
1445
	if (!request_region(base, 8, DRIVER_NAME))
L
Linus Torvalds 已提交
1446
		return -EIO;
1447

1448
	host->base = base;
1449

L
Linus Torvalds 已提交
1450 1451 1452
	return 0;
}

P
Pierre Ossman 已提交
1453
static void __devexit wbsd_release_regions(struct wbsd_host *host)
L
Linus Torvalds 已提交
1454 1455 1456
{
	if (host->base)
		release_region(host->base, 8);
1457

P
Pierre Ossman 已提交
1458
	host->base = 0;
L
Linus Torvalds 已提交
1459 1460 1461

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

P
Pierre Ossman 已提交
1463
	host->config = 0;
L
Linus Torvalds 已提交
1464 1465
}

P
Pierre Ossman 已提交
1466 1467 1468 1469
/*
 * Allocate/free DMA port and buffer
 */

P
Pierre Ossman 已提交
1470
static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
L
Linus Torvalds 已提交
1471 1472 1473
{
	if (dma < 0)
		return;
1474

L
Linus Torvalds 已提交
1475 1476
	if (request_dma(dma, DRIVER_NAME))
		goto err;
1477

L
Linus Torvalds 已提交
1478 1479 1480 1481
	/*
	 * We need to allocate a special buffer in
	 * order for ISA to be able to DMA to it.
	 */
P
Pierre Ossman 已提交
1482
	host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
L
Linus Torvalds 已提交
1483 1484 1485 1486 1487 1488 1489
		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
	if (!host->dma_buffer)
		goto free;

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

L
Linus Torvalds 已提交
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
	/*
	 * 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;
1505

L
Linus Torvalds 已提交
1506
	return;
1507

L
Linus Torvalds 已提交
1508 1509 1510 1511 1512
kfree:
	/*
	 * If we've gotten here then there is some kind of alignment bug
	 */
	BUG_ON(1);
1513

P
Pierre Ossman 已提交
1514 1515
	dma_unmap_single(host->mmc->dev, host->dma_addr,
		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
P
Pierre Ossman 已提交
1516
	host->dma_addr = (dma_addr_t)NULL;
1517

L
Linus Torvalds 已提交
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528
	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 已提交
1529
static void __devexit wbsd_release_dma(struct wbsd_host *host)
P
Pierre Ossman 已提交
1530
{
P
Pierre Ossman 已提交
1531 1532 1533 1534
	if (host->dma_addr) {
		dma_unmap_single(host->mmc->dev, host->dma_addr,
			WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
	}
1535
	kfree(host->dma_buffer);
P
Pierre Ossman 已提交
1536 1537
	if (host->dma >= 0)
		free_dma(host->dma);
1538

P
Pierre Ossman 已提交
1539 1540 1541 1542
	host->dma = -1;
	host->dma_buffer = NULL;
	host->dma_addr = (dma_addr_t)NULL;
}
L
Linus Torvalds 已提交
1543 1544

/*
P
Pierre Ossman 已提交
1545
 * Allocate/free IRQ.
L
Linus Torvalds 已提交
1546 1547
 */

P
Pierre Ossman 已提交
1548
static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
L
Linus Torvalds 已提交
1549 1550
{
	int ret;
1551

L
Linus Torvalds 已提交
1552
	/*
P
Pierre Ossman 已提交
1553
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1554
	 */
P
Pierre Ossman 已提交
1555

1556
	ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
P
Pierre Ossman 已提交
1557 1558
	if (ret)
		return ret;
1559

P
Pierre Ossman 已提交
1560 1561
	host->irq = irq;

L
Linus Torvalds 已提交
1562
	/*
P
Pierre Ossman 已提交
1563
	 * Set up tasklets.
L
Linus Torvalds 已提交
1564
	 */
P
Pierre Ossman 已提交
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
	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);
1577

P
Pierre Ossman 已提交
1578 1579
	return 0;
}
L
Linus Torvalds 已提交
1580

P
Pierre Ossman 已提交
1581
static void __devexit wbsd_release_irq(struct wbsd_host *host)
P
Pierre Ossman 已提交
1582 1583 1584
{
	if (!host->irq)
		return;
L
Linus Torvalds 已提交
1585

P
Pierre Ossman 已提交
1586
	free_irq(host->irq, host);
1587

P
Pierre Ossman 已提交
1588
	host->irq = 0;
1589

P
Pierre Ossman 已提交
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601
	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.
 */

P
Pierre Ossman 已提交
1602
static int __devinit wbsd_request_resources(struct wbsd_host *host,
P
Pierre Ossman 已提交
1603 1604 1605
	int base, int irq, int dma)
{
	int ret;
1606

L
Linus Torvalds 已提交
1607 1608 1609
	/*
	 * Allocate I/O ports.
	 */
P
Pierre Ossman 已提交
1610
	ret = wbsd_request_region(host, base);
L
Linus Torvalds 已提交
1611
	if (ret)
P
Pierre Ossman 已提交
1612
		return ret;
L
Linus Torvalds 已提交
1613 1614

	/*
P
Pierre Ossman 已提交
1615
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1616
	 */
P
Pierre Ossman 已提交
1617 1618 1619 1620 1621 1622 1623 1624
	ret = wbsd_request_irq(host, irq);
	if (ret)
		return ret;

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

P
Pierre Ossman 已提交
1626 1627 1628 1629 1630 1631 1632
	return 0;
}

/*
 * Release all resources for the host.
 */

P
Pierre Ossman 已提交
1633
static void __devexit wbsd_release_resources(struct wbsd_host *host)
P
Pierre Ossman 已提交
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643
{
	wbsd_release_dma(host);
	wbsd_release_irq(host);
	wbsd_release_regions(host);
}

/*
 * Configure the resources the chip should use.
 */

P
Pierre Ossman 已提交
1644
static void wbsd_chip_config(struct wbsd_host *host)
P
Pierre Ossman 已提交
1645
{
P
Pierre Ossman 已提交
1646 1647
	wbsd_unlock_config(host);

P
Pierre Ossman 已提交
1648 1649
	/*
	 * Reset the chip.
1650
	 */
P
Pierre Ossman 已提交
1651 1652
	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
L
Linus Torvalds 已提交
1653 1654 1655 1656 1657

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

L
Linus Torvalds 已提交
1659 1660 1661
	/*
	 * Set up card detection.
	 */
P
Pierre Ossman 已提交
1662
	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
1663

L
Linus Torvalds 已提交
1664
	/*
P
Pierre Ossman 已提交
1665
	 * Configure chip
L
Linus Torvalds 已提交
1666 1667 1668
	 */
	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
1669

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

P
Pierre Ossman 已提交
1672 1673
	if (host->dma >= 0)
		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
1674

L
Linus Torvalds 已提交
1675
	/*
P
Pierre Ossman 已提交
1676
	 * Enable and power up chip.
L
Linus Torvalds 已提交
1677
	 */
P
Pierre Ossman 已提交
1678 1679
	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
P
Pierre Ossman 已提交
1680 1681

	wbsd_lock_config(host);
P
Pierre Ossman 已提交
1682 1683 1684 1685 1686
}

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

P
Pierre Ossman 已提交
1688
static int wbsd_chip_validate(struct wbsd_host *host)
P
Pierre Ossman 已提交
1689 1690
{
	int base, irq, dma;
1691

P
Pierre Ossman 已提交
1692 1693
	wbsd_unlock_config(host);

L
Linus Torvalds 已提交
1694
	/*
P
Pierre Ossman 已提交
1695
	 * Select SD/MMC function.
L
Linus Torvalds 已提交
1696
	 */
P
Pierre Ossman 已提交
1697
	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1698

L
Linus Torvalds 已提交
1699
	/*
P
Pierre Ossman 已提交
1700
	 * Read configuration.
L
Linus Torvalds 已提交
1701
	 */
P
Pierre Ossman 已提交
1702 1703
	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
1704

P
Pierre Ossman 已提交
1705
	irq = wbsd_read_config(host, WBSD_CONF_IRQ);
1706

P
Pierre Ossman 已提交
1707
	dma = wbsd_read_config(host, WBSD_CONF_DRQ);
1708

P
Pierre Ossman 已提交
1709 1710
	wbsd_lock_config(host);

L
Linus Torvalds 已提交
1711
	/*
P
Pierre Ossman 已提交
1712
	 * Validate against given configuration.
L
Linus Torvalds 已提交
1713
	 */
P
Pierre Ossman 已提交
1714 1715 1716 1717 1718 1719
	if (base != host->base)
		return 0;
	if (irq != host->irq)
		return 0;
	if ((dma != host->dma) && (host->dma != -1))
		return 0;
1720

P
Pierre Ossman 已提交
1721 1722 1723
	return 1;
}

P
Pierre Ossman 已提交
1724 1725 1726 1727
/*
 * Powers down the SD function
 */

P
Pierre Ossman 已提交
1728
static void wbsd_chip_poweroff(struct wbsd_host *host)
P
Pierre Ossman 已提交
1729 1730 1731 1732 1733 1734 1735 1736 1737
{
	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 已提交
1738 1739 1740 1741 1742 1743
/*****************************************************************************\
 *                                                                           *
 * Devices setup and shutdown                                                *
 *                                                                           *
\*****************************************************************************/

P
Pierre Ossman 已提交
1744
static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
P
Pierre Ossman 已提交
1745 1746
	int pnp)
{
P
Pierre Ossman 已提交
1747 1748
	struct wbsd_host *host = NULL;
	struct mmc_host *mmc = NULL;
P
Pierre Ossman 已提交
1749
	int ret;
1750

P
Pierre Ossman 已提交
1751 1752 1753
	ret = wbsd_alloc_mmc(dev);
	if (ret)
		return ret;
1754

P
Pierre Ossman 已提交
1755 1756
	mmc = dev_get_drvdata(dev);
	host = mmc_priv(mmc);
1757

L
Linus Torvalds 已提交
1758
	/*
P
Pierre Ossman 已提交
1759
	 * Scan for hardware.
L
Linus Torvalds 已提交
1760
	 */
P
Pierre Ossman 已提交
1761
	ret = wbsd_scan(host);
P
Pierre Ossman 已提交
1762 1763
	if (ret) {
		if (pnp && (ret == -ENODEV)) {
P
Pierre Ossman 已提交
1764 1765 1766
			printk(KERN_WARNING DRIVER_NAME
				": Unable to confirm device presence. You may "
				"experience lock-ups.\n");
P
Pierre Ossman 已提交
1767
		} else {
P
Pierre Ossman 已提交
1768 1769 1770 1771
			wbsd_free_mmc(dev);
			return ret;
		}
	}
1772

L
Linus Torvalds 已提交
1773
	/*
P
Pierre Ossman 已提交
1774
	 * Request resources.
L
Linus Torvalds 已提交
1775
	 */
1776
	ret = wbsd_request_resources(host, base, irq, dma);
P
Pierre Ossman 已提交
1777
	if (ret) {
P
Pierre Ossman 已提交
1778 1779 1780 1781
		wbsd_release_resources(host);
		wbsd_free_mmc(dev);
		return ret;
	}
1782

L
Linus Torvalds 已提交
1783
	/*
P
Pierre Ossman 已提交
1784
	 * See if chip needs to be configured.
L
Linus Torvalds 已提交
1785
	 */
P
Pierre Ossman 已提交
1786 1787
	if (pnp) {
		if ((host->config != 0) && !wbsd_chip_validate(host)) {
P
Pierre Ossman 已提交
1788 1789 1790 1791 1792 1793
			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);
		}
P
Pierre Ossman 已提交
1794
	} else
P
Pierre Ossman 已提交
1795
		wbsd_chip_config(host);
1796

L
Linus Torvalds 已提交
1797 1798 1799 1800 1801
	/*
	 * Power Management stuff. No idea how this works.
	 * Not tested.
	 */
#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1802
	if (host->config) {
P
Pierre Ossman 已提交
1803
		wbsd_unlock_config(host);
P
Pierre Ossman 已提交
1804
		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
P
Pierre Ossman 已提交
1805 1806
		wbsd_lock_config(host);
	}
L
Linus Torvalds 已提交
1807
#endif
P
Pierre Ossman 已提交
1808 1809 1810 1811
	/*
	 * Allow device to initialise itself properly.
	 */
	mdelay(5);
L
Linus Torvalds 已提交
1812 1813 1814 1815 1816

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

L
Linus Torvalds 已提交
1818 1819
	mmc_add_host(mmc);

R
Russell King 已提交
1820
	printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
P
Pierre Ossman 已提交
1821 1822 1823 1824 1825 1826 1827 1828 1829 1830
	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 已提交
1831 1832 1833 1834

	return 0;
}

P
Pierre Ossman 已提交
1835
static void __devexit wbsd_shutdown(struct device *dev, int pnp)
L
Linus Torvalds 已提交
1836
{
P
Pierre Ossman 已提交
1837 1838
	struct mmc_host *mmc = dev_get_drvdata(dev);
	struct wbsd_host *host;
1839

L
Linus Torvalds 已提交
1840
	if (!mmc)
P
Pierre Ossman 已提交
1841
		return;
L
Linus Torvalds 已提交
1842 1843

	host = mmc_priv(mmc);
1844

L
Linus Torvalds 已提交
1845 1846
	mmc_remove_host(mmc);

P
Pierre Ossman 已提交
1847 1848 1849
	/*
	 * Power down the SD/MMC function.
	 */
P
Pierre Ossman 已提交
1850
	if (!pnp)
P
Pierre Ossman 已提交
1851
		wbsd_chip_poweroff(host);
1852

P
Pierre Ossman 已提交
1853
	wbsd_release_resources(host);
1854

P
Pierre Ossman 已提交
1855 1856
	wbsd_free_mmc(dev);
}
L
Linus Torvalds 已提交
1857

P
Pierre Ossman 已提交
1858 1859 1860 1861
/*
 * Non-PnP
 */

P
Pierre Ossman 已提交
1862
static int __devinit wbsd_probe(struct platform_device *dev)
P
Pierre Ossman 已提交
1863
{
1864
	/* Use the module parameters for resources */
1865
	return wbsd_init(&dev->dev, io, irq, dma, 0);
P
Pierre Ossman 已提交
1866 1867
}

P
Pierre Ossman 已提交
1868
static int __devexit wbsd_remove(struct platform_device *dev)
P
Pierre Ossman 已提交
1869
{
1870
	wbsd_shutdown(&dev->dev, 0);
P
Pierre Ossman 已提交
1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881

	return 0;
}

/*
 * PnP
 */

#ifdef CONFIG_PNP

static int __devinit
P
Pierre Ossman 已提交
1882
wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
P
Pierre Ossman 已提交
1883 1884
{
	int io, irq, dma;
1885

P
Pierre Ossman 已提交
1886 1887 1888 1889 1890 1891 1892 1893 1894
	/*
	 * 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;
1895

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

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

P
Pierre Ossman 已提交
1901
static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
P
Pierre Ossman 已提交
1902 1903
{
	wbsd_shutdown(&dev->dev, 1);
L
Linus Torvalds 已提交
1904 1905
}

P
Pierre Ossman 已提交
1906 1907
#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
1908 1909 1910 1911 1912
/*
 * Power management
 */

#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1913

P
Pierre Ossman 已提交
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929
static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
{
	BUG_ON(host == NULL);

	return mmc_suspend_host(host->mmc, state);
}

static int wbsd_resume(struct wbsd_host *host)
{
	BUG_ON(host == NULL);

	wbsd_init_device(host);

	return mmc_resume_host(host->mmc);
}

P
Pierre Ossman 已提交
1930 1931
static int wbsd_platform_suspend(struct platform_device *dev,
				 pm_message_t state)
L
Linus Torvalds 已提交
1932
{
1933
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1934 1935 1936
	struct wbsd_host *host;
	int ret;

P
Pierre Ossman 已提交
1937
	if (mmc == NULL)
P
Pierre Ossman 已提交
1938 1939
		return 0;

P
Pierre Ossman 已提交
1940
	DBGF("Suspending...\n");
P
Pierre Ossman 已提交
1941 1942 1943

	host = mmc_priv(mmc);

P
Pierre Ossman 已提交
1944 1945 1946 1947
	ret = wbsd_suspend(host, state);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
1948
	wbsd_chip_poweroff(host);
L
Linus Torvalds 已提交
1949 1950 1951 1952

	return 0;
}

P
Pierre Ossman 已提交
1953
static int wbsd_platform_resume(struct platform_device *dev)
L
Linus Torvalds 已提交
1954
{
1955
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1956
	struct wbsd_host *host;
L
Linus Torvalds 已提交
1957

P
Pierre Ossman 已提交
1958
	if (mmc == NULL)
P
Pierre Ossman 已提交
1959 1960
		return 0;

P
Pierre Ossman 已提交
1961
	DBGF("Resuming...\n");
P
Pierre Ossman 已提交
1962 1963 1964 1965 1966 1967 1968 1969 1970 1971

	host = mmc_priv(mmc);

	wbsd_chip_config(host);

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

P
Pierre Ossman 已提交
1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983
	return wbsd_resume(host);
}

#ifdef CONFIG_PNP

static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
{
	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
	struct wbsd_host *host;

	if (mmc == NULL)
		return 0;
P
Pierre Ossman 已提交
1984

P
Pierre Ossman 已提交
1985 1986 1987 1988 1989
	DBGF("Suspending...\n");

	host = mmc_priv(mmc);

	return wbsd_suspend(host, state);
L
Linus Torvalds 已提交
1990
}
P
Pierre Ossman 已提交
1991

P
Pierre Ossman 已提交
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006
static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
{
	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
	struct wbsd_host *host;

	if (mmc == NULL)
		return 0;

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

	host = mmc_priv(mmc);

	/*
	 * See if chip needs to be configured.
	 */
P
Pierre Ossman 已提交
2007 2008
	if (host->config != 0) {
		if (!wbsd_chip_validate(host)) {
P
Pierre Ossman 已提交
2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
			printk(KERN_WARNING DRIVER_NAME
				": PnP active but chip not configured! "
				"You probably have a buggy BIOS. "
				"Configuring chip manually.\n");
			wbsd_chip_config(host);
		}
	}

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

	return wbsd_resume(host);
}

#endif /* CONFIG_PNP */

P
Pierre Ossman 已提交
2027 2028
#else /* CONFIG_PM */

P
Pierre Ossman 已提交
2029 2030 2031 2032 2033
#define wbsd_platform_suspend NULL
#define wbsd_platform_resume NULL

#define wbsd_pnp_suspend NULL
#define wbsd_pnp_resume NULL
P
Pierre Ossman 已提交
2034 2035

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

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

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

P
Pierre Ossman 已提交
2043 2044
	.suspend	= wbsd_platform_suspend,
	.resume		= wbsd_platform_resume,
2045 2046 2047
	.driver		= {
		.name	= DRIVER_NAME,
	},
L
Linus Torvalds 已提交
2048 2049
};

P
Pierre Ossman 已提交
2050 2051 2052 2053 2054 2055
#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 已提交
2056
	.remove		= __devexit_p(wbsd_pnp_remove),
P
Pierre Ossman 已提交
2057 2058 2059

	.suspend	= wbsd_pnp_suspend,
	.resume		= wbsd_pnp_resume,
P
Pierre Ossman 已提交
2060 2061 2062 2063
};

#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
2064 2065 2066 2067 2068 2069 2070
/*
 * Module loading/unloading
 */

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

L
Linus Torvalds 已提交
2072 2073 2074 2075 2076
	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 已提交
2077 2078
#ifdef CONFIG_PNP

P
Pierre Ossman 已提交
2079
	if (!nopnp) {
P
Pierre Ossman 已提交
2080 2081 2082 2083
		result = pnp_register_driver(&wbsd_pnp_driver);
		if (result < 0)
			return result;
	}
2084 2085
#endif /* CONFIG_PNP */

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

2091
		wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
P
Pierre Ossman 已提交
2092
		if (!wbsd_device) {
2093 2094 2095 2096 2097
			platform_driver_unregister(&wbsd_driver);
			return -ENOMEM;
		}

		result = platform_device_add(wbsd_device);
P
Pierre Ossman 已提交
2098
		if (result) {
2099 2100 2101 2102
			platform_device_put(wbsd_device);
			platform_driver_unregister(&wbsd_driver);
			return result;
		}
P
Pierre Ossman 已提交
2103
	}
L
Linus Torvalds 已提交
2104 2105 2106 2107 2108 2109

	return 0;
}

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

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

#endif /* CONFIG_PNP */
P
Pierre Ossman 已提交
2116

P
Pierre Ossman 已提交
2117
	if (nopnp) {
P
Pierre Ossman 已提交
2118
		platform_device_unregister(wbsd_device);
2119

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

	DBG("unloaded\n");
}

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

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

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