wbsd.c 41.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6
/*
 *  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
7 8 9
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *
 * 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>
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"
45
#define DRIVER_VERSION "1.6"
L
Linus Torvalds 已提交
46 47

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*
 * Common routines
 */

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	/*
	 * 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 已提交
195
static void wbsd_reset(struct wbsd_host *host)
L
Linus Torvalds 已提交
196 197
{
	u8 setup;
198

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

L
Linus Torvalds 已提交
201 202 203 204 205 206 207 208
	/*
	 * 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 已提交
209
static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
L
Linus Torvalds 已提交
210 211
{
	unsigned long dmaflags;
212

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

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

L
Linus Torvalds 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243
	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 已提交
244
static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
245 246 247 248 249 250 251 252 253 254 255
{
	/*
	 * 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 已提交
256
static inline int wbsd_next_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
257 258 259 260 261 262 263 264 265 266
{
	/*
	 * Skip to next SG entry.
	 */
	host->cur_sg++;
	host->num_sg--;

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
299 300 301 302 303
	/*
	 * 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 已提交
304
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
305 306 307 308 309 310 311
		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;
312

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
344 345 346 347 348
	/*
	 * 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 已提交
349
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
350 351 352 353 354 355 356
		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;
357

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

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

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

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

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

/*
 * Command handling
 */
380

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

P
Pierre Ossman 已提交
392 393 394 395 396
	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 已提交
397 398
}

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

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

P
Pierre Ossman 已提交
412
	for (i = 0; i < 4; i++) {
L
Linus Torvalds 已提交
413 414 415 416 417 418 419 420 421 422 423
		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 已提交
424
static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
L
Linus Torvalds 已提交
425 426 427
{
	int i;
	u8 status, isr;
428

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

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

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

L
Linus Torvalds 已提交
447 448 449 450 451 452 453 454 455 456
	/*
	 * 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 已提交
457
	if (cmd->flags & MMC_RSP_PRESENT) {
L
Linus Torvalds 已提交
458 459 460 461
		/*
		 * Read back status.
		 */
		isr = host->isr;
462

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

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

/*
 * Data functions
 */

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

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

L
Linus Torvalds 已提交
500 501 502 503 504 505
	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 已提交
506
	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
L
Linus Torvalds 已提交
507 508 509
		/*
		 * The size field in the FSR is broken so we have to
		 * do some guessing.
510
		 */
L
Linus Torvalds 已提交
511 512 513 514 515 516
		if (fsr & WBSD_FIFO_FULL)
			fifo = 16;
		else if (fsr & WBSD_FIFO_FUTHRE)
			fifo = 8;
		else
			fifo = 1;
517

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

			data->bytes_xfered++;
525

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

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

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

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

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

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

L
Linus Torvalds 已提交
562 563 564 565 566 567 568 569 570 571 572
	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 已提交
573
static void wbsd_fill_fifo(struct wbsd_host *host)
L
Linus Torvalds 已提交
574
{
P
Pierre Ossman 已提交
575 576
	struct mmc_data *data = host->mrq->cmd->data;
	char *buffer;
L
Linus Torvalds 已提交
577
	int i, fsr, fifo;
578

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	BUG_ON(host->mrq != NULL);

	cmd = mrq->cmd;

	host->mrq = mrq;
884

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

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

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

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

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

		spin_unlock_bh(&host->lock);

		return;
	}
922

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

	spin_unlock_bh(&host->lock);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	data->error = MMC_ERR_BADCRC;
1179

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

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

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

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

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

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

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

	data->error = MMC_ERR_TIMEOUT;
1203

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

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

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

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

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

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

	wbsd_finish_data(host, data);
1226 1227

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

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

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

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

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

		data->error = MMC_ERR_BADCRC;
1247

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

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

/*
 * Interrupt handling
 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1374
	del_timer_sync(&host->ignore_timer);
1375

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

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

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

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

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

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

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

			wbsd_unlock_config(host);
1405

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

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

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

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

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

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

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

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

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

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

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

1449
	host->base = base;
1450

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
1507
	return;
1508

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

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

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

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

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

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

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

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

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

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

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

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

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

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

P
Pierre Ossman 已提交
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
	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 已提交
1603
static int __devinit wbsd_request_resources(struct wbsd_host *host,
P
Pierre Ossman 已提交
1604 1605 1606
	int base, int irq, int dma)
{
	int ret;
1607

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

	host = mmc_priv(mmc);
1845

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

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

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

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

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

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

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

	return 0;
}

/*
 * PnP
 */

#ifdef CONFIG_PNP

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

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

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

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

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

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

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

#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1914

P
Pierre Ossman 已提交
1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
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 已提交
1931 1932
static int wbsd_platform_suspend(struct platform_device *dev,
				 pm_message_t state)
L
Linus Torvalds 已提交
1933
{
1934
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1935 1936 1937
	struct wbsd_host *host;
	int ret;

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

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

	host = mmc_priv(mmc);

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

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

	return 0;
}

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

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

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

	host = mmc_priv(mmc);

	wbsd_chip_config(host);

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

P
Pierre Ossman 已提交
1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
	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 已提交
1985

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

	host = mmc_priv(mmc);

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

P
Pierre Ossman 已提交
1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007
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 已提交
2008 2009
	if (host->config != 0) {
		if (!wbsd_chip_validate(host)) {
P
Pierre Ossman 已提交
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027
			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 已提交
2028 2029
#else /* CONFIG_PM */

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

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

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

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

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

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

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

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

#endif /* CONFIG_PNP */

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

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

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

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

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

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

		result = platform_device_add(wbsd_device);
P
Pierre Ossman 已提交
2099
		if (result) {
2100 2101 2102 2103
			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

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

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

	DBG("unloaded\n");
}

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

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

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