wbsd.c 42.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
 *
 *  Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *
 * Warning!
 *
 * Changes to the FIFO system should be done with extreme care since
 * the hardware is full of bugs related to the FIFO. Known issues are:
 *
 * - FIFO size field in FSR is always zero.
 *
 * - FIFO interrupts tend not to work as they should. Interrupts are
 *   triggered only for full/empty events, not for threshold values.
 *
 * - On APIC systems the FIFO empty interrupt is sometimes lost.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/ioport.h>
29
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
30
#include <linux/interrupt.h>
P
Pierre Ossman 已提交
31
#include <linux/dma-mapping.h>
L
Linus Torvalds 已提交
32
#include <linux/delay.h>
P
Pierre Ossman 已提交
33
#include <linux/pnp.h>
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41 42 43 44
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>

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

#include "wbsd.h"

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

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

P
Pierre Ossman 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
/*
 * Device resources
 */

#ifdef CONFIG_PNP

static const struct pnp_device_id pnp_dev_table[] = {
	{ "WEC0517", 0 },
	{ "WEC0518", 0 },
	{ "", 0 },
};

MODULE_DEVICE_TABLE(pnp, pnp_dev_table);

#endif /* CONFIG_PNP */

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

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

P
Pierre Ossman 已提交
80 81 82 83 84 85 86 87 88
#ifdef CONFIG_PNP
static unsigned int nopnp = 0;
#else
static const unsigned int nopnp = 1;
#endif
static unsigned int io = 0x248;
static unsigned int irq = 6;
static int dma = 2;

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
120 121 122 123
	outb(reg, host->config);
	return inb(host->config + 1);
}

P
Pierre Ossman 已提交
124
static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
L
Linus Torvalds 已提交
125 126 127 128 129
{
	outb(index, host->base + WBSD_IDXR);
	outb(value, host->base + WBSD_DATAR);
}

P
Pierre Ossman 已提交
130
static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139
{
	outb(index, host->base + WBSD_IDXR);
	return inb(host->base + WBSD_DATAR);
}

/*
 * Common routines
 */

P
Pierre Ossman 已提交
140
static void wbsd_init_device(struct wbsd_host *host)
L
Linus Torvalds 已提交
141 142
{
	u8 setup, ier;
143

L
Linus Torvalds 已提交
144 145 146 147 148 149
	/*
	 * Reset chip (SD/MMC part) and fifo.
	 */
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
	setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
150

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
206 207 208 209 210 211 212 213
	/*
	 * 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 已提交
214
static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
L
Linus Torvalds 已提交
215 216
{
	unsigned long dmaflags;
217

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

P
Pierre Ossman 已提交
220
	if (host->dma >= 0) {
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233
		/*
		 * 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);
	}
234

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

	/*
	 * Any entries left?
	 */
P
Pierre Ossman 已提交
272 273 274 275
	if (host->num_sg > 0) {
		host->offset = 0;
		host->remain = host->cur_sg->length;
	}
276

L
Linus Torvalds 已提交
277 278 279
	return host->num_sg;
}

P
Pierre Ossman 已提交
280
static inline char *wbsd_kmap_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
281 282 283 284 285 286
{
	host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
		host->cur_sg->offset;
	return host->mapped_sg;
}

P
Pierre Ossman 已提交
287
static inline void wbsd_kunmap_sg(struct wbsd_host *host)
L
Linus Torvalds 已提交
288 289 290 291
{
	kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
}

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

L
Linus Torvalds 已提交
299
	size = host->size;
300

L
Linus Torvalds 已提交
301 302
	sg = data->sg;
	len = data->sg_len;
303

L
Linus Torvalds 已提交
304 305 306 307 308
	/*
	 * 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 已提交
309
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
310 311 312 313 314 315 316
		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;
317

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

L
Linus Torvalds 已提交
323 324 325
		if (size == 0)
			break;
	}
326

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

L
Linus Torvalds 已提交
332
	BUG_ON(size != 0);
333

L
Linus Torvalds 已提交
334 335 336
	host->size -= size;
}

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

L
Linus Torvalds 已提交
344
	size = host->size;
345

L
Linus Torvalds 已提交
346 347
	sg = data->sg;
	len = data->sg_len;
348

L
Linus Torvalds 已提交
349 350 351 352 353
	/*
	 * 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 已提交
354
	for (i = 0; i < len; i++) {
L
Linus Torvalds 已提交
355 356 357 358 359 360 361
		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;
362

L
Linus Torvalds 已提交
363 364 365 366
		if (size < sg[i].length)
			size = 0;
		else
			size -= sg[i].length;
367

L
Linus Torvalds 已提交
368 369 370
		if (size == 0)
			break;
	}
371

L
Linus Torvalds 已提交
372 373 374 375
	/*
	 * Check that we didn't get a request to transfer
	 * more data than can fit into the SG list.
	 */
376

L
Linus Torvalds 已提交
377
	BUG_ON(size != 0);
378

L
Linus Torvalds 已提交
379 380 381 382 383 384
	host->size -= size;
}

/*
 * Command handling
 */
385

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

P
Pierre Ossman 已提交
397 398 399 400 401
	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 已提交
402 403
}

P
Pierre Ossman 已提交
404 405
static inline void wbsd_get_long_reply(struct wbsd_host *host,
	struct mmc_command *cmd)
L
Linus Torvalds 已提交
406 407
{
	int i;
408

L
Linus Torvalds 已提交
409 410 411
	/*
	 * Correct response type?
	 */
P
Pierre Ossman 已提交
412
	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
L
Linus Torvalds 已提交
413 414 415
		cmd->error = MMC_ERR_INVALID;
		return;
	}
416

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

L
Linus Torvalds 已提交
434 435 436 437 438 439 440 441
	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;
442

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

L
Linus Torvalds 已提交
450
	cmd->error = MMC_ERR_NONE;
451

L
Linus Torvalds 已提交
452 453 454 455 456 457 458 459 460 461
	/*
	 * Wait for the request to complete.
	 */
	do {
		status = wbsd_read_index(host, WBSD_IDX_STATUS);
	} while (status & WBSD_CARDTRAFFIC);

	/*
	 * Do we expect a reply?
	 */
P
Pierre Ossman 已提交
462
	if ((cmd->flags & MMC_RSP_MASK) != MMC_RSP_NONE) {
L
Linus Torvalds 已提交
463 464 465 466
		/*
		 * Read back status.
		 */
		isr = host->isr;
467

L
Linus Torvalds 已提交
468 469 470 471 472 473 474 475 476 477
		/* 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 已提交
478
		else {
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486 487 488 489 490 491 492
			if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT)
				wbsd_get_short_reply(host, cmd);
			else
				wbsd_get_long_reply(host, cmd);
		}
	}

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

/*
 * Data functions
 */

P
Pierre Ossman 已提交
493
static void wbsd_empty_fifo(struct wbsd_host *host)
L
Linus Torvalds 已提交
494
{
P
Pierre Ossman 已提交
495 496
	struct mmc_data *data = host->mrq->cmd->data;
	char *buffer;
L
Linus Torvalds 已提交
497
	int i, fsr, fifo;
498

L
Linus Torvalds 已提交
499 500 501 502 503
	/*
	 * Handle excessive data.
	 */
	if (data->bytes_xfered == host->size)
		return;
504

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

P
Pierre Ossman 已提交
523
		for (i = 0; i < fifo; i++) {
L
Linus Torvalds 已提交
524 525 526 527 528 529
			*buffer = inb(host->base + WBSD_DFR);
			buffer++;
			host->offset++;
			host->remain--;

			data->bytes_xfered++;
530

L
Linus Torvalds 已提交
531 532 533
			/*
			 * Transfer done?
			 */
P
Pierre Ossman 已提交
534
			if (data->bytes_xfered == host->size) {
535
				wbsd_kunmap_sg(host);
L
Linus Torvalds 已提交
536 537
				return;
			}
538

L
Linus Torvalds 已提交
539 540 541
			/*
			 * End of scatter list entry?
			 */
P
Pierre Ossman 已提交
542
			if (host->remain == 0) {
L
Linus Torvalds 已提交
543
				wbsd_kunmap_sg(host);
544

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

L
Linus Torvalds 已提交
557
					host->size = data->bytes_xfered;
558

L
Linus Torvalds 已提交
559 560
					return;
				}
561

L
Linus Torvalds 已提交
562 563 564 565
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
566

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

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

P
Pierre Ossman 已提交
609
		for (i = 16; i > fifo; i--) {
L
Linus Torvalds 已提交
610 611 612 613
			outb(*buffer, host->base + WBSD_DFR);
			buffer++;
			host->offset++;
			host->remain--;
614

L
Linus Torvalds 已提交
615
			data->bytes_xfered++;
616

L
Linus Torvalds 已提交
617 618 619
			/*
			 * Transfer done?
			 */
P
Pierre Ossman 已提交
620
			if (data->bytes_xfered == host->size) {
L
Linus Torvalds 已提交
621 622 623 624 625 626 627
				wbsd_kunmap_sg(host);
				return;
			}

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

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

L
Linus Torvalds 已提交
643
					host->size = data->bytes_xfered;
644

L
Linus Torvalds 已提交
645 646
					return;
				}
647

L
Linus Torvalds 已提交
648 649 650 651
				buffer = wbsd_kmap_sg(host);
			}
		}
	}
652

L
Linus Torvalds 已提交
653
	wbsd_kunmap_sg(host);
654

P
Pierre Ossman 已提交
655 656 657 658 659 660
	/*
	 * 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 已提交
661 662
}

P
Pierre Ossman 已提交
663
static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
664 665 666 667 668 669 670 671 672
{
	u16 blksize;
	u8 setup;
	unsigned long dmaflags;

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

L
Linus Torvalds 已提交
674 675 676 677 678 679 680 681 682 683 684
	/*
	 * Calculate size.
	 */
	host->size = data->blocks << data->blksz_bits;

	/*
	 * Check timeout values for overflow.
	 * (Yes, some cards cause this value to overflow).
	 */
	if (data->timeout_ns > 127000000)
		wbsd_write_index(host, WBSD_IDX_TAAC, 127);
P
Pierre Ossman 已提交
685 686 687 688
	else {
		wbsd_write_index(host, WBSD_IDX_TAAC,
			data->timeout_ns / 1000000);
	}
689

L
Linus Torvalds 已提交
690 691 692 693
	if (data->timeout_clks > 255)
		wbsd_write_index(host, WBSD_IDX_NSAC, 255);
	else
		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
694

L
Linus Torvalds 已提交
695 696 697 698 699 700
	/*
	 * 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.
701
	 * Two bytes are needed for each data line.
L
Linus Torvalds 已提交
702
	 */
P
Pierre Ossman 已提交
703
	if (host->bus_width == MMC_BUS_WIDTH_1) {
704 705 706 707
		blksize = (1 << data->blksz_bits) + 2;

		wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
P
Pierre Ossman 已提交
708
	} else if (host->bus_width == MMC_BUS_WIDTH_4) {
709
		blksize = (1 << data->blksz_bits) + 2 * 4;
710

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

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

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

L
Linus Torvalds 已提交
741 742 743 744 745 746
		/*
		 * Transfer data from the SG list to
		 * the DMA buffer.
		 */
		if (data->flags & MMC_DATA_WRITE)
			wbsd_sg_to_dma(host, data);
747

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

L
Linus Torvalds 已提交
775 776 777 778
		/*
		 * Initialise the SG list.
		 */
		wbsd_init_sg(host, data);
779

L
Linus Torvalds 已提交
780 781 782 783
		/*
		 * Turn off DMA.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
784

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

L
Linus Torvalds 已提交
799 800 801
	data->error = MMC_ERR_NONE;
}

P
Pierre Ossman 已提交
802
static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
L
Linus Torvalds 已提交
803 804 805 806
{
	unsigned long dmaflags;
	int count;
	u8 status;
807

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

L
Linus Torvalds 已提交
824 825 826
	/*
	 * DMA transfer?
	 */
P
Pierre Ossman 已提交
827
	if (host->dma >= 0) {
L
Linus Torvalds 已提交
828 829 830 831
		/*
		 * Disable DMA on the host.
		 */
		wbsd_write_index(host, WBSD_IDX_DMA, 0);
832

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

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

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

L
Linus Torvalds 已提交
859 860 861
			data->bytes_xfered = host->size;
		}
	}
862

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

L
Linus Torvalds 已提交
865 866 867
	wbsd_request_end(host, host->mrq);
}

P
Pierre Ossman 已提交
868 869 870 871 872
/*****************************************************************************\
 *                                                                           *
 * MMC layer callbacks                                                       *
 *                                                                           *
\*****************************************************************************/
L
Linus Torvalds 已提交
873

P
Pierre Ossman 已提交
874
static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
L
Linus Torvalds 已提交
875
{
P
Pierre Ossman 已提交
876 877
	struct wbsd_host *host = mmc_priv(mmc);
	struct mmc_command *cmd;
L
Linus Torvalds 已提交
878 879 880 881 882 883 884 885 886 887 888

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

	BUG_ON(host->mrq != NULL);

	cmd = mrq->cmd;

	host->mrq = mrq;
889

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

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

L
Linus Torvalds 已提交
905 906 907
		if (cmd->data->error != MMC_ERR_NONE)
			goto done;
	}
908

L
Linus Torvalds 已提交
909 910 911 912 913 914
	wbsd_send_command(host, cmd);

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

		spin_unlock_bh(&host->lock);

		return;
	}
927

L
Linus Torvalds 已提交
928 929 930 931 932 933
done:
	wbsd_request_end(host, mrq);

	spin_unlock_bh(&host->lock);
}

P
Pierre Ossman 已提交
934
static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
L
Linus Torvalds 已提交
935
{
P
Pierre Ossman 已提交
936
	struct wbsd_host *host = mmc_priv(mmc);
L
Linus Torvalds 已提交
937
	u8 clk, setup, pwr;
938

939
	DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n",
P
Pierre Ossman 已提交
940 941
		ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select,
		ios->vdd, ios->bus_width);
L
Linus Torvalds 已提交
942 943 944 945 946 947 948 949 950

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

L
Linus Torvalds 已提交
952 953 954 955 956 957 958 959 960 961 962 963 964
	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 已提交
965
	if (clk != host->clk) {
L
Linus Torvalds 已提交
966 967 968 969
		wbsd_write_index(host, WBSD_IDX_CLK, clk);
		host->clk = clk;
	}

P
Pierre Ossman 已提交
970 971 972
	/*
	 * Power up card.
	 */
P
Pierre Ossman 已提交
973
	if (ios->power_mode != MMC_POWER_OFF) {
L
Linus Torvalds 已提交
974 975 976 977 978
		pwr = inb(host->base + WBSD_CSR);
		pwr &= ~WBSD_POWER_N;
		outb(pwr, host->base + WBSD_CSR);
	}

P
Pierre Ossman 已提交
979 980 981
	/*
	 * MMC cards need to have pin 1 high during init.
	 * It wreaks havoc with the card detection though so
982
	 * that needs to be disabled.
P
Pierre Ossman 已提交
983 984
	 */
	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
P
Pierre Ossman 已提交
985
	if (ios->chip_select == MMC_CS_HIGH) {
986
		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
P
Pierre Ossman 已提交
987 988
		setup |= WBSD_DAT3_H;
		host->flags |= WBSD_FIGNORE_DETECT;
P
Pierre Ossman 已提交
989 990
	} else {
		if (setup & WBSD_DAT3_H) {
P
Pierre Ossman 已提交
991
			setup &= ~WBSD_DAT3_H;
992

P
Pierre Ossman 已提交
993 994 995 996
			/*
			 * We cannot resume card detection immediatly
			 * because of capacitance and delays in the chip.
			 */
P
Pierre Ossman 已提交
997
			mod_timer(&host->ignore_timer, jiffies + HZ / 100);
P
Pierre Ossman 已提交
998
		}
P
Pierre Ossman 已提交
999 1000
	}
	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
1001

1002 1003 1004 1005 1006 1007
	/*
	 * Store bus width for later. Will be used when
	 * setting up the data transfer.
	 */
	host->bus_width = ios->bus_width;

L
Linus Torvalds 已提交
1008 1009 1010
	spin_unlock_bh(&host->lock);
}

P
Pierre Ossman 已提交
1011
static int wbsd_get_ro(struct mmc_host *mmc)
1012
{
P
Pierre Ossman 已提交
1013
	struct wbsd_host *host = mmc_priv(mmc);
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
	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 已提交
1033 1034 1035
static struct mmc_host_ops wbsd_ops = {
	.request	= wbsd_request,
	.set_ios	= wbsd_set_ios,
1036
	.get_ro		= wbsd_get_ro,
P
Pierre Ossman 已提交
1037 1038 1039 1040 1041 1042 1043 1044
};

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

1045 1046 1047 1048 1049 1050
/*
 * Helper function to reset detection ignore
 */

static void wbsd_reset_ignore(unsigned long data)
{
P
Pierre Ossman 已提交
1051
	struct wbsd_host *host = (struct wbsd_host *)data;
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069

	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 已提交
1070 1071 1072 1073
/*
 * Tasklets
 */

P
Pierre Ossman 已提交
1074
static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
L
Linus Torvalds 已提交
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
{
	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;
1087

L
Linus Torvalds 已提交
1088 1089 1090 1091 1092
	return host->mrq->cmd->data;
}

static void wbsd_tasklet_card(unsigned long param)
{
P
Pierre Ossman 已提交
1093
	struct wbsd_host *host = (struct wbsd_host *)param;
L
Linus Torvalds 已提交
1094
	u8 csr;
1095
	int delay = -1;
1096

L
Linus Torvalds 已提交
1097
	spin_lock(&host->lock);
1098

P
Pierre Ossman 已提交
1099
	if (host->flags & WBSD_FIGNORE_DETECT) {
P
Pierre Ossman 已提交
1100 1101 1102
		spin_unlock(&host->lock);
		return;
	}
1103

L
Linus Torvalds 已提交
1104 1105
	csr = inb(host->base + WBSD_CSR);
	WARN_ON(csr == 0xff);
1106

P
Pierre Ossman 已提交
1107 1108
	if (csr & WBSD_CARDPRESENT) {
		if (!(host->flags & WBSD_FCARD_PRESENT)) {
P
Pierre Ossman 已提交
1109 1110
			DBG("Card inserted\n");
			host->flags |= WBSD_FCARD_PRESENT;
1111

1112
			delay = 500;
P
Pierre Ossman 已提交
1113
		}
P
Pierre Ossman 已提交
1114
	} else if (host->flags & WBSD_FCARD_PRESENT) {
L
Linus Torvalds 已提交
1115
		DBG("Card removed\n");
P
Pierre Ossman 已提交
1116
		host->flags &= ~WBSD_FCARD_PRESENT;
1117

P
Pierre Ossman 已提交
1118
		if (host->mrq) {
1119 1120
			printk(KERN_ERR "%s: Card removed during transfer!\n",
				mmc_hostname(host->mmc));
L
Linus Torvalds 已提交
1121
			wbsd_reset(host);
1122

L
Linus Torvalds 已提交
1123 1124 1125
			host->mrq->cmd->error = MMC_ERR_FAILED;
			tasklet_schedule(&host->finish_tasklet);
		}
1126

1127
		delay = 0;
1128
	}
1129 1130 1131 1132 1133 1134 1135 1136 1137

	/*
	 * 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 已提交
1138 1139 1140 1141
}

static void wbsd_tasklet_fifo(unsigned long param)
{
P
Pierre Ossman 已提交
1142 1143
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1144

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

L
Linus Torvalds 已提交
1147 1148
	if (!host->mrq)
		goto end;
1149

L
Linus Torvalds 已提交
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
	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 已提交
1162
	if (host->size == data->bytes_xfered) {
L
Linus Torvalds 已提交
1163 1164 1165 1166
		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
		tasklet_schedule(&host->finish_tasklet);
	}

1167
end:
L
Linus Torvalds 已提交
1168 1169 1170 1171 1172
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_crc(unsigned long param)
{
P
Pierre Ossman 已提交
1173 1174
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1175

L
Linus Torvalds 已提交
1176
	spin_lock(&host->lock);
1177

L
Linus Torvalds 已提交
1178 1179
	if (!host->mrq)
		goto end;
1180

L
Linus Torvalds 已提交
1181 1182 1183
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1184

L
Linus Torvalds 已提交
1185 1186 1187
	DBGF("CRC error\n");

	data->error = MMC_ERR_BADCRC;
1188

L
Linus Torvalds 已提交
1189 1190
	tasklet_schedule(&host->finish_tasklet);

1191
end:
L
Linus Torvalds 已提交
1192 1193 1194 1195 1196
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_timeout(unsigned long param)
{
P
Pierre Ossman 已提交
1197 1198
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1199

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

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

L
Linus Torvalds 已提交
1205 1206 1207
	data = wbsd_get_data(host);
	if (!data)
		goto end;
1208

L
Linus Torvalds 已提交
1209 1210 1211
	DBGF("Timeout\n");

	data->error = MMC_ERR_TIMEOUT;
1212

L
Linus Torvalds 已提交
1213 1214
	tasklet_schedule(&host->finish_tasklet);

1215
end:
L
Linus Torvalds 已提交
1216 1217 1218 1219 1220
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_finish(unsigned long param)
{
P
Pierre Ossman 已提交
1221 1222
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1223

L
Linus Torvalds 已提交
1224
	spin_lock(&host->lock);
1225

L
Linus Torvalds 已提交
1226 1227 1228
	WARN_ON(!host->mrq);
	if (!host->mrq)
		goto end;
1229

L
Linus Torvalds 已提交
1230 1231 1232 1233 1234
	data = wbsd_get_data(host);
	if (!data)
		goto end;

	wbsd_finish_data(host, data);
1235 1236

end:
L
Linus Torvalds 已提交
1237 1238 1239 1240 1241
	spin_unlock(&host->lock);
}

static void wbsd_tasklet_block(unsigned long param)
{
P
Pierre Ossman 已提交
1242 1243
	struct wbsd_host *host = (struct wbsd_host *)param;
	struct mmc_data *data;
1244

L
Linus Torvalds 已提交
1245 1246 1247
	spin_lock(&host->lock);

	if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
P
Pierre Ossman 已提交
1248
		WBSD_CRC_OK) {
L
Linus Torvalds 已提交
1249 1250 1251
		data = wbsd_get_data(host);
		if (!data)
			goto end;
1252

L
Linus Torvalds 已提交
1253 1254 1255
		DBGF("CRC error\n");

		data->error = MMC_ERR_BADCRC;
1256

L
Linus Torvalds 已提交
1257 1258 1259
		tasklet_schedule(&host->finish_tasklet);
	}

1260
end:
L
Linus Torvalds 已提交
1261 1262 1263 1264 1265 1266 1267 1268 1269
	spin_unlock(&host->lock);
}

/*
 * Interrupt handling
 */

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

L
Linus Torvalds 已提交
1273 1274 1275 1276 1277 1278 1279
	isr = inb(host->base + WBSD_ISR);

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

L
Linus Torvalds 已提交
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
	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);
1298

L
Linus Torvalds 已提交
1299 1300 1301
	return IRQ_HANDLED;
}

P
Pierre Ossman 已提交
1302 1303 1304 1305 1306 1307
/*****************************************************************************\
 *                                                                           *
 * Device initialisation and shutdown                                        *
 *                                                                           *
\*****************************************************************************/

L
Linus Torvalds 已提交
1308
/*
P
Pierre Ossman 已提交
1309
 * Allocate/free MMC structure.
L
Linus Torvalds 已提交
1310 1311
 */

P
Pierre Ossman 已提交
1312
static int __devinit wbsd_alloc_mmc(struct device *dev)
P
Pierre Ossman 已提交
1313
{
P
Pierre Ossman 已提交
1314 1315
	struct mmc_host *mmc;
	struct wbsd_host *host;
1316

P
Pierre Ossman 已提交
1317 1318 1319 1320 1321 1322
	/*
	 * Allocate MMC structure.
	 */
	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
	if (!mmc)
		return -ENOMEM;
1323

P
Pierre Ossman 已提交
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
	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 已提交
1335
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
1336
	mmc->caps = MMC_CAP_4_BIT_DATA;
1337

P
Pierre Ossman 已提交
1338
	spin_lock_init(&host->lock);
1339

1340
	/*
1341
	 * Set up timers
1342
	 */
1343 1344 1345
	init_timer(&host->ignore_timer);
	host->ignore_timer.data = (unsigned long)host;
	host->ignore_timer.function = wbsd_reset_ignore;
1346

P
Pierre Ossman 已提交
1347 1348 1349 1350 1351 1352
	/*
	 * 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;
1353

P
Pierre Ossman 已提交
1354 1355 1356 1357 1358
	/*
	 * Maximum number of sectors in one transfer. Also limited by 64kB
	 * buffer.
	 */
	mmc->max_sectors = 128;
1359

P
Pierre Ossman 已提交
1360 1361 1362 1363 1364
	/*
	 * Maximum segment size. Could be one segment with the maximum number
	 * of segments.
	 */
	mmc->max_seg_size = mmc->max_sectors * 512;
1365

P
Pierre Ossman 已提交
1366
	dev_set_drvdata(dev, mmc);
1367

P
Pierre Ossman 已提交
1368 1369 1370
	return 0;
}

P
Pierre Ossman 已提交
1371
static void __devexit wbsd_free_mmc(struct device *dev)
P
Pierre Ossman 已提交
1372
{
P
Pierre Ossman 已提交
1373 1374
	struct mmc_host *mmc;
	struct wbsd_host *host;
1375

P
Pierre Ossman 已提交
1376 1377 1378
	mmc = dev_get_drvdata(dev);
	if (!mmc)
		return;
1379

1380 1381
	host = mmc_priv(mmc);
	BUG_ON(host == NULL);
1382

1383
	del_timer_sync(&host->ignore_timer);
1384

P
Pierre Ossman 已提交
1385
	mmc_free_host(mmc);
1386

P
Pierre Ossman 已提交
1387 1388 1389 1390 1391 1392 1393
	dev_set_drvdata(dev, NULL);
}

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

P
Pierre Ossman 已提交
1394
static int __devinit wbsd_scan(struct wbsd_host *host)
L
Linus Torvalds 已提交
1395 1396 1397
{
	int i, j, k;
	int id;
1398

L
Linus Torvalds 已提交
1399 1400 1401 1402
	/*
	 * Iterate through all ports, all codes to
	 * find hardware that is in our known list.
	 */
1403
	for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
L
Linus Torvalds 已提交
1404 1405
		if (!request_region(config_ports[i], 2, DRIVER_NAME))
			continue;
1406

1407
		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
L
Linus Torvalds 已提交
1408
			id = 0xFFFF;
1409

P
Pierre Ossman 已提交
1410 1411 1412 1413
			host->config = config_ports[i];
			host->unlock_code = unlock_codes[j];

			wbsd_unlock_config(host);
1414

L
Linus Torvalds 已提交
1415 1416 1417 1418 1419
			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);
1420

P
Pierre Ossman 已提交
1421 1422
			wbsd_lock_config(host);

1423
			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
P
Pierre Ossman 已提交
1424
				if (id == valid_ids[k]) {
L
Linus Torvalds 已提交
1425
					host->chip_id = id;
1426

L
Linus Torvalds 已提交
1427 1428 1429
					return 0;
				}
			}
1430

P
Pierre Ossman 已提交
1431
			if (id != 0xFFFF) {
L
Linus Torvalds 已提交
1432 1433 1434 1435
				DBG("Unknown hardware (id %x) found at %x\n",
					id, config_ports[i]);
			}
		}
1436

L
Linus Torvalds 已提交
1437 1438
		release_region(config_ports[i], 2);
	}
1439

P
Pierre Ossman 已提交
1440 1441 1442
	host->config = 0;
	host->unlock_code = 0;

L
Linus Torvalds 已提交
1443 1444 1445
	return -ENODEV;
}

P
Pierre Ossman 已提交
1446 1447 1448 1449
/*
 * Allocate/free io port ranges
 */

P
Pierre Ossman 已提交
1450
static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
L
Linus Torvalds 已提交
1451 1452 1453
{
	if (io & 0x7)
		return -EINVAL;
1454

P
Pierre Ossman 已提交
1455
	if (!request_region(base, 8, DRIVER_NAME))
L
Linus Torvalds 已提交
1456
		return -EIO;
1457

L
Linus Torvalds 已提交
1458
	host->base = io;
1459

L
Linus Torvalds 已提交
1460 1461 1462
	return 0;
}

P
Pierre Ossman 已提交
1463
static void __devexit wbsd_release_regions(struct wbsd_host *host)
L
Linus Torvalds 已提交
1464 1465 1466
{
	if (host->base)
		release_region(host->base, 8);
1467

P
Pierre Ossman 已提交
1468
	host->base = 0;
L
Linus Torvalds 已提交
1469 1470 1471

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

P
Pierre Ossman 已提交
1473
	host->config = 0;
L
Linus Torvalds 已提交
1474 1475
}

P
Pierre Ossman 已提交
1476 1477 1478 1479
/*
 * Allocate/free DMA port and buffer
 */

P
Pierre Ossman 已提交
1480
static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
L
Linus Torvalds 已提交
1481 1482 1483
{
	if (dma < 0)
		return;
1484

L
Linus Torvalds 已提交
1485 1486
	if (request_dma(dma, DRIVER_NAME))
		goto err;
1487

L
Linus Torvalds 已提交
1488 1489 1490 1491
	/*
	 * We need to allocate a special buffer in
	 * order for ISA to be able to DMA to it.
	 */
P
Pierre Ossman 已提交
1492
	host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
L
Linus Torvalds 已提交
1493 1494 1495 1496 1497 1498 1499
		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
	if (!host->dma_buffer)
		goto free;

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

L
Linus Torvalds 已提交
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514
	/*
	 * 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;
1515

L
Linus Torvalds 已提交
1516
	return;
1517

L
Linus Torvalds 已提交
1518 1519 1520 1521 1522
kfree:
	/*
	 * If we've gotten here then there is some kind of alignment bug
	 */
	BUG_ON(1);
1523

P
Pierre Ossman 已提交
1524 1525
	dma_unmap_single(host->mmc->dev, host->dma_addr,
		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
P
Pierre Ossman 已提交
1526
	host->dma_addr = (dma_addr_t)NULL;
1527

L
Linus Torvalds 已提交
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538
	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 已提交
1539
static void __devexit wbsd_release_dma(struct wbsd_host *host)
P
Pierre Ossman 已提交
1540
{
P
Pierre Ossman 已提交
1541 1542 1543 1544
	if (host->dma_addr) {
		dma_unmap_single(host->mmc->dev, host->dma_addr,
			WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
	}
1545
	kfree(host->dma_buffer);
P
Pierre Ossman 已提交
1546 1547
	if (host->dma >= 0)
		free_dma(host->dma);
1548

P
Pierre Ossman 已提交
1549 1550 1551 1552
	host->dma = -1;
	host->dma_buffer = NULL;
	host->dma_addr = (dma_addr_t)NULL;
}
L
Linus Torvalds 已提交
1553 1554

/*
P
Pierre Ossman 已提交
1555
 * Allocate/free IRQ.
L
Linus Torvalds 已提交
1556 1557
 */

P
Pierre Ossman 已提交
1558
static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
L
Linus Torvalds 已提交
1559 1560
{
	int ret;
1561

L
Linus Torvalds 已提交
1562
	/*
P
Pierre Ossman 已提交
1563
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1564
	 */
P
Pierre Ossman 已提交
1565 1566 1567 1568

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

P
Pierre Ossman 已提交
1570 1571
	host->irq = irq;

L
Linus Torvalds 已提交
1572
	/*
P
Pierre Ossman 已提交
1573
	 * Set up tasklets.
L
Linus Torvalds 已提交
1574
	 */
P
Pierre Ossman 已提交
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
	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);
1587

P
Pierre Ossman 已提交
1588 1589
	return 0;
}
L
Linus Torvalds 已提交
1590

P
Pierre Ossman 已提交
1591
static void __devexit wbsd_release_irq(struct wbsd_host *host)
P
Pierre Ossman 已提交
1592 1593 1594
{
	if (!host->irq)
		return;
L
Linus Torvalds 已提交
1595

P
Pierre Ossman 已提交
1596
	free_irq(host->irq, host);
1597

P
Pierre Ossman 已提交
1598
	host->irq = 0;
1599

P
Pierre Ossman 已提交
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
	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 已提交
1612
static int __devinit wbsd_request_resources(struct wbsd_host *host,
P
Pierre Ossman 已提交
1613 1614 1615
	int base, int irq, int dma)
{
	int ret;
1616

L
Linus Torvalds 已提交
1617 1618 1619
	/*
	 * Allocate I/O ports.
	 */
P
Pierre Ossman 已提交
1620
	ret = wbsd_request_region(host, base);
L
Linus Torvalds 已提交
1621
	if (ret)
P
Pierre Ossman 已提交
1622
		return ret;
L
Linus Torvalds 已提交
1623 1624

	/*
P
Pierre Ossman 已提交
1625
	 * Allocate interrupt.
L
Linus Torvalds 已提交
1626
	 */
P
Pierre Ossman 已提交
1627 1628 1629 1630 1631 1632 1633 1634
	ret = wbsd_request_irq(host, irq);
	if (ret)
		return ret;

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

P
Pierre Ossman 已提交
1636 1637 1638 1639 1640 1641 1642
	return 0;
}

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

P
Pierre Ossman 已提交
1643
static void __devexit wbsd_release_resources(struct wbsd_host *host)
P
Pierre Ossman 已提交
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653
{
	wbsd_release_dma(host);
	wbsd_release_irq(host);
	wbsd_release_regions(host);
}

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

P
Pierre Ossman 已提交
1654
static void wbsd_chip_config(struct wbsd_host *host)
P
Pierre Ossman 已提交
1655
{
P
Pierre Ossman 已提交
1656 1657
	wbsd_unlock_config(host);

P
Pierre Ossman 已提交
1658 1659
	/*
	 * Reset the chip.
1660
	 */
P
Pierre Ossman 已提交
1661 1662
	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
L
Linus Torvalds 已提交
1663 1664 1665 1666 1667

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

L
Linus Torvalds 已提交
1669 1670 1671
	/*
	 * Set up card detection.
	 */
P
Pierre Ossman 已提交
1672
	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
1673

L
Linus Torvalds 已提交
1674
	/*
P
Pierre Ossman 已提交
1675
	 * Configure chip
L
Linus Torvalds 已提交
1676 1677 1678
	 */
	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
1679

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

P
Pierre Ossman 已提交
1682 1683
	if (host->dma >= 0)
		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
1684

L
Linus Torvalds 已提交
1685
	/*
P
Pierre Ossman 已提交
1686
	 * Enable and power up chip.
L
Linus Torvalds 已提交
1687
	 */
P
Pierre Ossman 已提交
1688 1689
	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
P
Pierre Ossman 已提交
1690 1691

	wbsd_lock_config(host);
P
Pierre Ossman 已提交
1692 1693 1694 1695 1696
}

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

P
Pierre Ossman 已提交
1698
static int wbsd_chip_validate(struct wbsd_host *host)
P
Pierre Ossman 已提交
1699 1700
{
	int base, irq, dma;
1701

P
Pierre Ossman 已提交
1702 1703
	wbsd_unlock_config(host);

L
Linus Torvalds 已提交
1704
	/*
P
Pierre Ossman 已提交
1705
	 * Select SD/MMC function.
L
Linus Torvalds 已提交
1706
	 */
P
Pierre Ossman 已提交
1707
	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
1708

L
Linus Torvalds 已提交
1709
	/*
P
Pierre Ossman 已提交
1710
	 * Read configuration.
L
Linus Torvalds 已提交
1711
	 */
P
Pierre Ossman 已提交
1712 1713
	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
1714

P
Pierre Ossman 已提交
1715
	irq = wbsd_read_config(host, WBSD_CONF_IRQ);
1716

P
Pierre Ossman 已提交
1717
	dma = wbsd_read_config(host, WBSD_CONF_DRQ);
1718

P
Pierre Ossman 已提交
1719 1720
	wbsd_lock_config(host);

L
Linus Torvalds 已提交
1721
	/*
P
Pierre Ossman 已提交
1722
	 * Validate against given configuration.
L
Linus Torvalds 已提交
1723
	 */
P
Pierre Ossman 已提交
1724 1725 1726 1727 1728 1729
	if (base != host->base)
		return 0;
	if (irq != host->irq)
		return 0;
	if ((dma != host->dma) && (host->dma != -1))
		return 0;
1730

P
Pierre Ossman 已提交
1731 1732 1733
	return 1;
}

P
Pierre Ossman 已提交
1734 1735 1736 1737
/*
 * Powers down the SD function
 */

P
Pierre Ossman 已提交
1738
static void wbsd_chip_poweroff(struct wbsd_host *host)
P
Pierre Ossman 已提交
1739 1740 1741 1742 1743 1744 1745 1746 1747
{
	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 已提交
1748 1749 1750 1751 1752 1753
/*****************************************************************************\
 *                                                                           *
 * Devices setup and shutdown                                                *
 *                                                                           *
\*****************************************************************************/

P
Pierre Ossman 已提交
1754
static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
P
Pierre Ossman 已提交
1755 1756
	int pnp)
{
P
Pierre Ossman 已提交
1757 1758
	struct wbsd_host *host = NULL;
	struct mmc_host *mmc = NULL;
P
Pierre Ossman 已提交
1759
	int ret;
1760

P
Pierre Ossman 已提交
1761 1762 1763
	ret = wbsd_alloc_mmc(dev);
	if (ret)
		return ret;
1764

P
Pierre Ossman 已提交
1765 1766
	mmc = dev_get_drvdata(dev);
	host = mmc_priv(mmc);
1767

L
Linus Torvalds 已提交
1768
	/*
P
Pierre Ossman 已提交
1769
	 * Scan for hardware.
L
Linus Torvalds 已提交
1770
	 */
P
Pierre Ossman 已提交
1771
	ret = wbsd_scan(host);
P
Pierre Ossman 已提交
1772 1773
	if (ret) {
		if (pnp && (ret == -ENODEV)) {
P
Pierre Ossman 已提交
1774 1775 1776
			printk(KERN_WARNING DRIVER_NAME
				": Unable to confirm device presence. You may "
				"experience lock-ups.\n");
P
Pierre Ossman 已提交
1777
		} else {
P
Pierre Ossman 已提交
1778 1779 1780 1781
			wbsd_free_mmc(dev);
			return ret;
		}
	}
1782

L
Linus Torvalds 已提交
1783
	/*
P
Pierre Ossman 已提交
1784
	 * Request resources.
L
Linus Torvalds 已提交
1785
	 */
P
Pierre Ossman 已提交
1786
	ret = wbsd_request_resources(host, io, irq, dma);
P
Pierre Ossman 已提交
1787
	if (ret) {
P
Pierre Ossman 已提交
1788 1789 1790 1791
		wbsd_release_resources(host);
		wbsd_free_mmc(dev);
		return ret;
	}
1792

L
Linus Torvalds 已提交
1793
	/*
P
Pierre Ossman 已提交
1794
	 * See if chip needs to be configured.
L
Linus Torvalds 已提交
1795
	 */
P
Pierre Ossman 已提交
1796 1797
	if (pnp) {
		if ((host->config != 0) && !wbsd_chip_validate(host)) {
P
Pierre Ossman 已提交
1798 1799 1800 1801 1802 1803
			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 已提交
1804
	} else
P
Pierre Ossman 已提交
1805
		wbsd_chip_config(host);
1806

L
Linus Torvalds 已提交
1807 1808 1809 1810 1811
	/*
	 * Power Management stuff. No idea how this works.
	 * Not tested.
	 */
#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1812
	if (host->config) {
P
Pierre Ossman 已提交
1813
		wbsd_unlock_config(host);
P
Pierre Ossman 已提交
1814
		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
P
Pierre Ossman 已提交
1815 1816
		wbsd_lock_config(host);
	}
L
Linus Torvalds 已提交
1817
#endif
P
Pierre Ossman 已提交
1818 1819 1820 1821
	/*
	 * Allow device to initialise itself properly.
	 */
	mdelay(5);
L
Linus Torvalds 已提交
1822 1823 1824 1825 1826

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

L
Linus Torvalds 已提交
1828 1829
	mmc_add_host(mmc);

R
Russell King 已提交
1830
	printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
P
Pierre Ossman 已提交
1831 1832 1833 1834 1835 1836 1837 1838 1839 1840
	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 已提交
1841 1842 1843 1844

	return 0;
}

P
Pierre Ossman 已提交
1845
static void __devexit wbsd_shutdown(struct device *dev, int pnp)
L
Linus Torvalds 已提交
1846
{
P
Pierre Ossman 已提交
1847 1848
	struct mmc_host *mmc = dev_get_drvdata(dev);
	struct wbsd_host *host;
1849

L
Linus Torvalds 已提交
1850
	if (!mmc)
P
Pierre Ossman 已提交
1851
		return;
L
Linus Torvalds 已提交
1852 1853

	host = mmc_priv(mmc);
1854

L
Linus Torvalds 已提交
1855 1856
	mmc_remove_host(mmc);

P
Pierre Ossman 已提交
1857 1858 1859
	/*
	 * Power down the SD/MMC function.
	 */
P
Pierre Ossman 已提交
1860
	if (!pnp)
P
Pierre Ossman 已提交
1861
		wbsd_chip_poweroff(host);
1862

P
Pierre Ossman 已提交
1863
	wbsd_release_resources(host);
1864

P
Pierre Ossman 已提交
1865 1866
	wbsd_free_mmc(dev);
}
L
Linus Torvalds 已提交
1867

P
Pierre Ossman 已提交
1868 1869 1870 1871
/*
 * Non-PnP
 */

P
Pierre Ossman 已提交
1872
static int __devinit wbsd_probe(struct platform_device *dev)
P
Pierre Ossman 已提交
1873
{
1874
	return wbsd_init(&dev->dev, io, irq, dma, 0);
P
Pierre Ossman 已提交
1875 1876
}

P
Pierre Ossman 已提交
1877
static int __devexit wbsd_remove(struct platform_device *dev)
P
Pierre Ossman 已提交
1878
{
1879
	wbsd_shutdown(&dev->dev, 0);
P
Pierre Ossman 已提交
1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890

	return 0;
}

/*
 * PnP
 */

#ifdef CONFIG_PNP

static int __devinit
P
Pierre Ossman 已提交
1891
wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
P
Pierre Ossman 已提交
1892 1893
{
	int io, irq, dma;
1894

P
Pierre Ossman 已提交
1895 1896 1897 1898 1899 1900 1901 1902 1903
	/*
	 * 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;
1904

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

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

P
Pierre Ossman 已提交
1910
static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
P
Pierre Ossman 已提交
1911 1912
{
	wbsd_shutdown(&dev->dev, 1);
L
Linus Torvalds 已提交
1913 1914
}

P
Pierre Ossman 已提交
1915 1916
#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
1917 1918 1919 1920 1921
/*
 * Power management
 */

#ifdef CONFIG_PM
P
Pierre Ossman 已提交
1922

P
Pierre Ossman 已提交
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938
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 已提交
1939 1940
static int wbsd_platform_suspend(struct platform_device *dev,
				 pm_message_t state)
L
Linus Torvalds 已提交
1941
{
1942
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1943 1944 1945
	struct wbsd_host *host;
	int ret;

P
Pierre Ossman 已提交
1946
	if (mmc == NULL)
P
Pierre Ossman 已提交
1947 1948
		return 0;

P
Pierre Ossman 已提交
1949
	DBGF("Suspending...\n");
P
Pierre Ossman 已提交
1950 1951 1952

	host = mmc_priv(mmc);

P
Pierre Ossman 已提交
1953 1954 1955 1956
	ret = wbsd_suspend(host, state);
	if (ret)
		return ret;

P
Pierre Ossman 已提交
1957
	wbsd_chip_poweroff(host);
L
Linus Torvalds 已提交
1958 1959 1960 1961

	return 0;
}

P
Pierre Ossman 已提交
1962
static int wbsd_platform_resume(struct platform_device *dev)
L
Linus Torvalds 已提交
1963
{
1964
	struct mmc_host *mmc = platform_get_drvdata(dev);
P
Pierre Ossman 已提交
1965
	struct wbsd_host *host;
L
Linus Torvalds 已提交
1966

P
Pierre Ossman 已提交
1967
	if (mmc == NULL)
P
Pierre Ossman 已提交
1968 1969
		return 0;

P
Pierre Ossman 已提交
1970
	DBGF("Resuming...\n");
P
Pierre Ossman 已提交
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980

	host = mmc_priv(mmc);

	wbsd_chip_config(host);

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

P
Pierre Ossman 已提交
1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992
	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 已提交
1993

P
Pierre Ossman 已提交
1994 1995 1996 1997 1998
	DBGF("Suspending...\n");

	host = mmc_priv(mmc);

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

P
Pierre Ossman 已提交
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015
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 已提交
2016 2017
	if (host->config != 0) {
		if (!wbsd_chip_validate(host)) {
P
Pierre Ossman 已提交
2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035
			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 已提交
2036 2037
#else /* CONFIG_PM */

P
Pierre Ossman 已提交
2038 2039 2040 2041 2042
#define wbsd_platform_suspend NULL
#define wbsd_platform_resume NULL

#define wbsd_pnp_suspend NULL
#define wbsd_pnp_resume NULL
P
Pierre Ossman 已提交
2043 2044

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

P
Pierre Ossman 已提交
2046
static struct platform_device *wbsd_device;
L
Linus Torvalds 已提交
2047

2048
static struct platform_driver wbsd_driver = {
L
Linus Torvalds 已提交
2049
	.probe		= wbsd_probe,
P
Pierre Ossman 已提交
2050
	.remove		= __devexit_p(wbsd_remove),
2051

P
Pierre Ossman 已提交
2052 2053
	.suspend	= wbsd_platform_suspend,
	.resume		= wbsd_platform_resume,
2054 2055 2056
	.driver		= {
		.name	= DRIVER_NAME,
	},
L
Linus Torvalds 已提交
2057 2058
};

P
Pierre Ossman 已提交
2059 2060 2061 2062 2063 2064
#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 已提交
2065
	.remove		= __devexit_p(wbsd_pnp_remove),
P
Pierre Ossman 已提交
2066 2067 2068

	.suspend	= wbsd_pnp_suspend,
	.resume		= wbsd_pnp_resume,
P
Pierre Ossman 已提交
2069 2070 2071 2072
};

#endif /* CONFIG_PNP */

L
Linus Torvalds 已提交
2073 2074 2075 2076 2077 2078 2079
/*
 * Module loading/unloading
 */

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

L
Linus Torvalds 已提交
2081 2082 2083 2084 2085
	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 已提交
2086 2087
#ifdef CONFIG_PNP

P
Pierre Ossman 已提交
2088
	if (!nopnp) {
P
Pierre Ossman 已提交
2089 2090 2091 2092
		result = pnp_register_driver(&wbsd_pnp_driver);
		if (result < 0)
			return result;
	}
2093 2094
#endif /* CONFIG_PNP */

P
Pierre Ossman 已提交
2095
	if (nopnp) {
2096
		result = platform_driver_register(&wbsd_driver);
P
Pierre Ossman 已提交
2097 2098 2099
		if (result < 0)
			return result;

2100
		wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
P
Pierre Ossman 已提交
2101
		if (!wbsd_device) {
2102 2103 2104 2105 2106
			platform_driver_unregister(&wbsd_driver);
			return -ENOMEM;
		}

		result = platform_device_add(wbsd_device);
P
Pierre Ossman 已提交
2107
		if (result) {
2108 2109 2110 2111
			platform_device_put(wbsd_device);
			platform_driver_unregister(&wbsd_driver);
			return result;
		}
P
Pierre Ossman 已提交
2112
	}
L
Linus Torvalds 已提交
2113 2114 2115 2116 2117 2118

	return 0;
}

static void __exit wbsd_drv_exit(void)
{
P
Pierre Ossman 已提交
2119 2120 2121 2122
#ifdef CONFIG_PNP

	if (!nopnp)
		pnp_unregister_driver(&wbsd_pnp_driver);
2123 2124

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

P
Pierre Ossman 已提交
2126
	if (nopnp) {
P
Pierre Ossman 已提交
2127
		platform_device_unregister(wbsd_device);
2128

2129
		platform_driver_unregister(&wbsd_driver);
P
Pierre Ossman 已提交
2130
	}
L
Linus Torvalds 已提交
2131 2132 2133 2134 2135 2136

	DBG("unloaded\n");
}

module_init(wbsd_drv_init);
module_exit(wbsd_drv_exit);
P
Pierre Ossman 已提交
2137 2138 2139
#ifdef CONFIG_PNP
module_param(nopnp, uint, 0444);
#endif
L
Linus Torvalds 已提交
2140 2141 2142 2143 2144
module_param(io, uint, 0444);
module_param(irq, uint, 0444);
module_param(dma, int, 0444);

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

P
Pierre Ossman 已提交
2149 2150 2151
#ifdef CONFIG_PNP
MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
#endif
L
Linus Torvalds 已提交
2152 2153 2154
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)");