g_NCR5380.c 19.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Generic Generic NCR5380 driver
 *	
 * Copyright 1993, Drew Eckhardt
 *	Visionary Computing
 *	(Unix and Linux consulting and custom programming)
 *	drew@colorado.edu
 *      +1 (303) 440-4894
 *
 * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
 *    K.Lentin@cs.monash.edu.au
 *
 * NCR53C400A extensions (c) 1996, Ingmar Baumgart
 *    ingmar@gonzo.schwaben.de
 *
 * DTC3181E extensions (c) 1997, Ronald van Cuijlenborg
 * ronald.van.cuijlenborg@tip.nl or nutty@dds.nl
 *
 * Added ISAPNP support for DTC436 adapters,
 * Thomas Sailer, sailer@ife.ee.ethz.ch
 *
22
 * See Documentation/scsi/g_NCR5380.txt for more info.
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25 26
 */

#include <asm/io.h>
#include <linux/blkdev.h>
27
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34 35
#include <scsi/scsi_host.h>
#include "g_NCR5380.h"
#include "NCR5380.h"
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/isapnp.h>
#include <linux/interrupt.h>

36 37 38 39 40 41 42
static int ncr_irq;
static int ncr_dma;
static int ncr_addr;
static int ncr_5380;
static int ncr_53c400;
static int ncr_53c400a;
static int dtc_3181e;
43
static int hp_c2502;
Linus Torvalds's avatar
Linus Torvalds committed
44 45

static struct override {
46
	NCR5380_map_type NCR5380_map_name;
Linus Torvalds's avatar
Linus Torvalds committed
47 48 49 50 51 52 53 54 55 56
	int irq;
	int dma;
	int board;		/* Use NCR53c400, Ricoh, etc. extensions ? */
} overrides
#ifdef GENERIC_NCR5380_OVERRIDE
[] __initdata = GENERIC_NCR5380_OVERRIDE;
#else
[1] __initdata = { { 0,},};
#endif

57
#define NO_OVERRIDES ARRAY_SIZE(overrides)
Linus Torvalds's avatar
Linus Torvalds committed
58

59
#ifndef MODULE
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

/**
 *	internal_setup		-	handle lilo command string override
 *	@board:	BOARD_* identifier for the board
 *	@str: unused
 *	@ints: numeric parameters
 *
 * 	Do LILO command line initialization of the overrides array. Display
 *	errors when needed
 *
 *	Locks: none
 */

static void __init internal_setup(int board, char *str, int *ints)
{
75
	static int commandline_current;
Linus Torvalds's avatar
Linus Torvalds committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	switch (board) {
	case BOARD_NCR5380:
		if (ints[0] != 2 && ints[0] != 3) {
			printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
			return;
		}
		break;
	case BOARD_NCR53C400:
		if (ints[0] != 2) {
			printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
			return;
		}
		break;
	case BOARD_NCR53C400A:
		if (ints[0] != 2) {
			printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
			return;
		}
		break;
	case BOARD_DTC3181E:
		if (ints[0] != 2) {
			printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
			return;
		}
		break;
	}

	if (commandline_current < NO_OVERRIDES) {
		overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1];
		overrides[commandline_current].irq = ints[2];
		if (ints[0] == 3)
			overrides[commandline_current].dma = ints[3];
		else
			overrides[commandline_current].dma = DMA_NONE;
		overrides[commandline_current].board = board;
		++commandline_current;
	}
}


/**
 * 	do_NCR53C80_setup		-	set up entry point
 *	@str: unused
 *
 *	Setup function invoked at boot to parse the ncr5380= command
 *	line.
 */

static int __init do_NCR5380_setup(char *str)
{
	int ints[10];

128
	get_options(str, ARRAY_SIZE(ints), ints);
Linus Torvalds's avatar
Linus Torvalds committed
129 130 131 132 133 134 135
	internal_setup(BOARD_NCR5380, str, ints);
	return 1;
}

/**
 * 	do_NCR53C400_setup		-	set up entry point
 *	@str: unused
136
 *	@ints: integer parameters from kernel setup code
Linus Torvalds's avatar
Linus Torvalds committed
137 138 139 140 141 142 143 144 145
 *
 *	Setup function invoked at boot to parse the ncr53c400= command
 *	line.
 */

static int __init do_NCR53C400_setup(char *str)
{
	int ints[10];

146
	get_options(str, ARRAY_SIZE(ints), ints);
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149 150 151 152 153
	internal_setup(BOARD_NCR53C400, str, ints);
	return 1;
}

/**
 * 	do_NCR53C400A_setup	-	set up entry point
 *	@str: unused
154
 *	@ints: integer parameters from kernel setup code
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157 158 159 160 161 162 163
 *
 *	Setup function invoked at boot to parse the ncr53c400a= command
 *	line.
 */

static int __init do_NCR53C400A_setup(char *str)
{
	int ints[10];

164
	get_options(str, ARRAY_SIZE(ints), ints);
Linus Torvalds's avatar
Linus Torvalds committed
165 166 167 168 169 170 171
	internal_setup(BOARD_NCR53C400A, str, ints);
	return 1;
}

/**
 * 	do_DTC3181E_setup	-	set up entry point
 *	@str: unused
172
 *	@ints: integer parameters from kernel setup code
Linus Torvalds's avatar
Linus Torvalds committed
173 174 175 176 177 178 179 180 181
 *
 *	Setup function invoked at boot to parse the dtc3181e= command
 *	line.
 */

static int __init do_DTC3181E_setup(char *str)
{
	int ints[10];

182
	get_options(str, ARRAY_SIZE(ints), ints);
Linus Torvalds's avatar
Linus Torvalds committed
183 184 185 186 187 188
	internal_setup(BOARD_DTC3181E, str, ints);
	return 1;
}

#endif

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
#ifndef SCSI_G_NCR5380_MEM
/*
 * Configure I/O address of 53C400A or DTC436 by writing magic numbers
 * to ports 0x779 and 0x379.
 */
static void magic_configure(int idx, u8 irq, u8 magic[])
{
	u8 cfg = 0;

	outb(magic[0], 0x779);
	outb(magic[1], 0x379);
	outb(magic[2], 0x379);
	outb(magic[3], 0x379);
	outb(magic[4], 0x379);

	/* allowed IRQs for HP C2502 */
	if (irq != 2 && irq != 3 && irq != 4 && irq != 5 && irq != 7)
		irq = 0;
	if (idx >= 0 && idx <= 7)
		cfg = 0x80 | idx | (irq << 4);
	outb(cfg, 0x379);
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
213 214 215 216 217 218 219 220 221 222 223
/**
 * 	generic_NCR5380_detect	-	look for NCR5380 controllers
 *	@tpnt: the scsi template
 *
 *	Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E
 *	and DTC436(ISAPnP) controllers. If overrides have been set we use
 *	them.
 *
 *	Locks: none
 */

224
static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
Linus Torvalds's avatar
Linus Torvalds committed
225
{
226
	static int current_override;
227
	int count;
Linus Torvalds's avatar
Linus Torvalds committed
228
	unsigned int *ports;
229
	u8 *magic = NULL;
230 231
#ifndef SCSI_G_NCR5380_MEM
	int i;
232
	int port_idx = -1;
233
	unsigned long region_size;
234
#endif
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237 238 239 240
	static unsigned int __initdata ncr_53c400a_ports[] = {
		0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
	};
	static unsigned int __initdata dtc_3181e_ports[] = {
		0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
	};
241 242 243 244 245 246
	static u8 ncr_53c400a_magic[] __initdata = {	/* 53C400A & DTC436 */
		0x59, 0xb9, 0xc5, 0xae, 0xa6
	};
	static u8 hp_c2502_magic[] __initdata = {	/* HP C2502 */
		0x0f, 0x22, 0xf0, 0x20, 0x80
	};
247
	int flags;
Linus Torvalds's avatar
Linus Torvalds committed
248
	struct Scsi_Host *instance;
249
	struct NCR5380_hostdata *hostdata;
250
#ifdef SCSI_G_NCR5380_MEM
251 252
	unsigned long base;
	void __iomem *iomem;
253
	resource_size_t iomem_size;
254
#endif
Linus Torvalds's avatar
Linus Torvalds committed
255

256
	if (ncr_irq)
Linus Torvalds's avatar
Linus Torvalds committed
257
		overrides[0].irq = ncr_irq;
258
	if (ncr_dma)
Linus Torvalds's avatar
Linus Torvalds committed
259
		overrides[0].dma = ncr_dma;
260
	if (ncr_addr)
Linus Torvalds's avatar
Linus Torvalds committed
261
		overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr;
262
	if (ncr_5380)
Linus Torvalds's avatar
Linus Torvalds committed
263
		overrides[0].board = BOARD_NCR5380;
264
	else if (ncr_53c400)
Linus Torvalds's avatar
Linus Torvalds committed
265
		overrides[0].board = BOARD_NCR53C400;
266
	else if (ncr_53c400a)
Linus Torvalds's avatar
Linus Torvalds committed
267
		overrides[0].board = BOARD_NCR53C400A;
268
	else if (dtc_3181e)
Linus Torvalds's avatar
Linus Torvalds committed
269
		overrides[0].board = BOARD_DTC3181E;
270 271
	else if (hp_c2502)
		overrides[0].board = BOARD_HP_C2502;
272
#ifndef SCSI_G_NCR5380_MEM
Linus Torvalds's avatar
Linus Torvalds committed
273 274 275 276 277 278
	if (!current_override && isapnp_present()) {
		struct pnp_dev *dev = NULL;
		count = 0;
		while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
			if (count >= NO_OVERRIDES)
				break;
279
			if (pnp_device_attach(dev) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293
				continue;
			if (pnp_activate_dev(dev) < 0) {
				printk(KERN_ERR "dtc436e probe: activate failed\n");
				pnp_device_detach(dev);
				continue;
			}
			if (!pnp_port_valid(dev, 0)) {
				printk(KERN_ERR "dtc436e probe: no valid port\n");
				pnp_device_detach(dev);
				continue;
			}
			if (pnp_irq_valid(dev, 0))
				overrides[count].irq = pnp_irq(dev, 0);
			else
Finn Thain's avatar
Finn Thain committed
294
				overrides[count].irq = NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
295 296 297 298 299 300 301 302 303
			if (pnp_dma_valid(dev, 0))
				overrides[count].dma = pnp_dma(dev, 0);
			else
				overrides[count].dma = DMA_NONE;
			overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0);
			overrides[count].board = BOARD_DTC3181E;
			count++;
		}
	}
304
#endif
Linus Torvalds's avatar
Linus Torvalds committed
305 306 307 308 309 310

	for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
		if (!(overrides[current_override].NCR5380_map_name))
			continue;

		ports = NULL;
311
		flags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
312 313
		switch (overrides[current_override].board) {
		case BOARD_NCR5380:
314
			flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
Linus Torvalds's avatar
Linus Torvalds committed
315 316 317
			break;
		case BOARD_NCR53C400A:
			ports = ncr_53c400a_ports;
318 319 320 321 322
			magic = ncr_53c400a_magic;
			break;
		case BOARD_HP_C2502:
			ports = ncr_53c400a_ports;
			magic = hp_c2502_magic;
Linus Torvalds's avatar
Linus Torvalds committed
323 324 325
			break;
		case BOARD_DTC3181E:
			ports = dtc_3181e_ports;
326
			magic = ncr_53c400a_magic;
Linus Torvalds's avatar
Linus Torvalds committed
327 328 329
			break;
		}

330
#ifndef SCSI_G_NCR5380_MEM
331
		if (ports && magic) {
Linus Torvalds's avatar
Linus Torvalds committed
332 333 334
			/* wakeup sequence for the NCR53C400A and DTC3181E */

			/* Disable the adapter and look for a free io port */
335
			magic_configure(-1, 0, magic);
Linus Torvalds's avatar
Linus Torvalds committed
336

337 338
			region_size = 16;

Linus Torvalds's avatar
Linus Torvalds committed
339 340
			if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
				for (i = 0; ports[i]; i++) {
341
					if (!request_region(ports[i], region_size, "ncr53c80"))
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344
						continue;
					if (overrides[current_override].NCR5380_map_name == ports[i])
						break;
345
					release_region(ports[i], region_size);
Linus Torvalds's avatar
Linus Torvalds committed
346 347
			} else
				for (i = 0; ports[i]; i++) {
348
					if (!request_region(ports[i], region_size, "ncr53c80"))
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351
						continue;
					if (inb(ports[i]) == 0xff)
						break;
352
					release_region(ports[i], region_size);
Linus Torvalds's avatar
Linus Torvalds committed
353 354 355
				}
			if (ports[i]) {
				/* At this point we have our region reserved */
356
				magic_configure(i, 0, magic); /* no IRQ yet */
Linus Torvalds's avatar
Linus Torvalds committed
357 358 359
				outb(0xc0, ports[i] + 9);
				if (inb(ports[i] + 9) != 0x80)
					continue;
360 361
				overrides[current_override].NCR5380_map_name = ports[i];
				port_idx = i;
Linus Torvalds's avatar
Linus Torvalds committed
362 363 364 365 366 367
			} else
				continue;
		}
		else
		{
			/* Not a 53C400A style setup - just grab */
368 369 370
			region_size = 8;
			if (!request_region(overrides[current_override].NCR5380_map_name,
			                    region_size, "ncr5380"))
Linus Torvalds's avatar
Linus Torvalds committed
371 372 373
				continue;
		}
#else
374
		base = overrides[current_override].NCR5380_map_name;
375 376
		iomem_size = NCR53C400_region_size;
		if (!request_mem_region(base, iomem_size, "ncr5380"))
377
			continue;
378
		iomem = ioremap(base, iomem_size);
379
		if (!iomem) {
380
			release_mem_region(base, iomem_size);
Linus Torvalds's avatar
Linus Torvalds committed
381
			continue;
382
		}
Linus Torvalds's avatar
Linus Torvalds committed
383 384
#endif
		instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
385 386
		if (instance == NULL)
			goto out_release;
387
		hostdata = shost_priv(instance);
Linus Torvalds's avatar
Linus Torvalds committed
388

389
#ifndef SCSI_G_NCR5380_MEM
390
		instance->io_port = overrides[current_override].NCR5380_map_name;
Linus Torvalds's avatar
Linus Torvalds committed
391
		instance->n_io_port = region_size;
392
		hostdata->io_width = 1; /* 8-bit PDMA by default */
393 394 395 396 397

		/*
		 * On NCR53C400 boards, NCR5380 registers are mapped 8 past
		 * the base address.
		 */
398 399
		switch (overrides[current_override].board) {
		case BOARD_NCR53C400:
400
			instance->io_port += 8;
401 402 403
			hostdata->c400_ctl_status = 0;
			hostdata->c400_blk_cnt = 1;
			hostdata->c400_host_buf = 4;
404
			break;
405 406 407
		case BOARD_DTC3181E:
			hostdata->io_width = 2;	/* 16-bit PDMA */
			/* fall through */
408
		case BOARD_NCR53C400A:
409
		case BOARD_HP_C2502:
410 411 412 413
			hostdata->c400_ctl_status = 9;
			hostdata->c400_blk_cnt = 10;
			hostdata->c400_host_buf = 8;
			break;
414
		}
415
#else
416
		instance->base = overrides[current_override].NCR5380_map_name;
417
		hostdata->iomem = iomem;
418
		hostdata->iomem_size = iomem_size;
419 420
		switch (overrides[current_override].board) {
		case BOARD_NCR53C400:
421 422 423
			hostdata->c400_ctl_status = 0x100;
			hostdata->c400_blk_cnt = 0x101;
			hostdata->c400_host_buf = 0x104;
424
			break;
425
		case BOARD_DTC3181E:
426
		case BOARD_NCR53C400A:
427
		case BOARD_HP_C2502:
428 429
			pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
			goto out_unregister;
430
		}
Linus Torvalds's avatar
Linus Torvalds committed
431 432
#endif

433
		if (NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP))
434
			goto out_unregister;
Linus Torvalds's avatar
Linus Torvalds committed
435

436 437
		switch (overrides[current_override].board) {
		case BOARD_NCR53C400:
438
		case BOARD_DTC3181E:
439
		case BOARD_NCR53C400A:
440
		case BOARD_HP_C2502:
441
			NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
442
		}
443

444 445
		NCR5380_maybe_reset_bus(instance);

Linus Torvalds's avatar
Linus Torvalds committed
446 447 448 449 450
		if (overrides[current_override].irq != IRQ_AUTO)
			instance->irq = overrides[current_override].irq;
		else
			instance->irq = NCR5380_probe_irq(instance, 0xffff);

Finn Thain's avatar
Finn Thain committed
451 452 453 454
		/* Compatibility with documented NCR5380 kernel parameters */
		if (instance->irq == 255)
			instance->irq = NO_IRQ;

455 456 457 458 459 460
		if (instance->irq != NO_IRQ) {
#ifndef SCSI_G_NCR5380_MEM
			/* set IRQ for HP C2502 */
			if (overrides[current_override].board == BOARD_HP_C2502)
				magic_configure(port_idx, instance->irq, magic);
#endif
461
			if (request_irq(instance->irq, generic_NCR5380_intr,
462
					0, "NCR5380", instance)) {
Linus Torvalds's avatar
Linus Torvalds committed
463
				printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
Finn Thain's avatar
Finn Thain committed
464
				instance->irq = NO_IRQ;
Linus Torvalds's avatar
Linus Torvalds committed
465
			}
466
		}
Linus Torvalds's avatar
Linus Torvalds committed
467

Finn Thain's avatar
Finn Thain committed
468
		if (instance->irq == NO_IRQ) {
Linus Torvalds's avatar
Linus Torvalds committed
469 470 471 472 473 474 475 476
			printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
			printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
		}

		++current_override;
		++count;
	}
	return count;
477 478 479 480 481 482 483 484

out_unregister:
	scsi_unregister(instance);
out_release:
#ifndef SCSI_G_NCR5380_MEM
	release_region(overrides[current_override].NCR5380_map_name, region_size);
#else
	iounmap(iomem);
485
	release_mem_region(base, iomem_size);
486 487
#endif
	return count;
Linus Torvalds's avatar
Linus Torvalds committed
488 489 490 491 492 493 494 495 496 497 498
}

/**
 *	generic_NCR5380_release_resources	-	free resources
 *	@instance: host adapter to clean up 
 *
 *	Free the generic interface resources from this adapter.
 *
 *	Locks: none
 */
 
499
static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
Linus Torvalds's avatar
Linus Torvalds committed
500
{
Finn Thain's avatar
Finn Thain committed
501
	if (instance->irq != NO_IRQ)
502
		free_irq(instance->irq, instance);
Linus Torvalds's avatar
Linus Torvalds committed
503
	NCR5380_exit(instance);
504
#ifndef SCSI_G_NCR5380_MEM
505
	release_region(instance->io_port, instance->n_io_port);
Linus Torvalds's avatar
Linus Torvalds committed
506
#else
507 508
	{
		struct NCR5380_hostdata *hostdata = shost_priv(instance);
Linus Torvalds's avatar
Linus Torvalds committed
509

510 511 512 513
		iounmap(hostdata->iomem);
		release_mem_region(instance->base, hostdata->iomem_size);
	}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
514 515 516 517
	return 0;
}

/**
Finn Thain's avatar
Finn Thain committed
518
 *	generic_NCR5380_pread - pseudo DMA read
Linus Torvalds's avatar
Linus Torvalds committed
519 520 521 522
 *	@instance: adapter to read from
 *	@dst: buffer to read into
 *	@len: buffer length
 *
Lucas De Marchi's avatar
Lucas De Marchi committed
523
 *	Perform a pseudo DMA mode read from an NCR53C400 or equivalent
Linus Torvalds's avatar
Linus Torvalds committed
524 525 526
 *	controller
 */
 
Finn Thain's avatar
Finn Thain committed
527 528
static inline int generic_NCR5380_pread(struct Scsi_Host *instance,
                                        unsigned char *dst, int len)
Linus Torvalds's avatar
Linus Torvalds committed
529
{
530
	struct NCR5380_hostdata *hostdata = shost_priv(instance);
Linus Torvalds's avatar
Linus Torvalds committed
531 532 533
	int blocks = len / 128;
	int start = 0;

534 535
	NCR5380_write(hostdata->c400_ctl_status, CSR_BASE | CSR_TRANS_DIR);
	NCR5380_write(hostdata->c400_blk_cnt, blocks);
Linus Torvalds's avatar
Linus Torvalds committed
536
	while (1) {
537
		if (NCR5380_read(hostdata->c400_blk_cnt) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
538
			break;
539
		if (NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ) {
Linus Torvalds's avatar
Linus Torvalds committed
540 541 542
			printk(KERN_ERR "53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
			return -1;
		}
543 544
		while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY)
			; /* FIXME - no timeout */
Linus Torvalds's avatar
Linus Torvalds committed
545

546
#ifndef SCSI_G_NCR5380_MEM
547 548 549 550 551
		if (hostdata->io_width == 2)
			insw(instance->io_port + hostdata->c400_host_buf,
							dst + start, 64);
		else
			insb(instance->io_port + hostdata->c400_host_buf,
552
							dst + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
553
#else
554
		/* implies SCSI_G_NCR5380_MEM */
555 556
		memcpy_fromio(dst + start,
		              hostdata->iomem + NCR53C400_host_buffer, 128);
Linus Torvalds's avatar
Linus Torvalds committed
557 558 559 560 561 562
#endif
		start += 128;
		blocks--;
	}

	if (blocks) {
563 564
		while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY)
			; /* FIXME - no timeout */
Linus Torvalds's avatar
Linus Torvalds committed
565

566
#ifndef SCSI_G_NCR5380_MEM
567 568 569 570 571
		if (hostdata->io_width == 2)
			insw(instance->io_port + hostdata->c400_host_buf,
							dst + start, 64);
		else
			insb(instance->io_port + hostdata->c400_host_buf,
572
							dst + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
573
#else
574
		/* implies SCSI_G_NCR5380_MEM */
575 576
		memcpy_fromio(dst + start,
		              hostdata->iomem + NCR53C400_host_buffer, 128);
Linus Torvalds's avatar
Linus Torvalds committed
577 578 579 580 581
#endif
		start += 128;
		blocks--;
	}

582
	if (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ))
Linus Torvalds's avatar
Linus Torvalds committed
583 584
		printk("53C400r: no 53C80 gated irq after transfer");

585 586
	/* wait for 53C80 registers to be available */
	while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG))
Linus Torvalds's avatar
Linus Torvalds committed
587
		;
588

Linus Torvalds's avatar
Linus Torvalds committed
589 590 591 592 593 594 595
	if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
		printk(KERN_ERR "53C400r: no end dma signal\n");
		
	return 0;
}

/**
Finn Thain's avatar
Finn Thain committed
596
 *	generic_NCR5380_pwrite - pseudo DMA write
Linus Torvalds's avatar
Linus Torvalds committed
597 598 599 600
 *	@instance: adapter to read from
 *	@dst: buffer to read into
 *	@len: buffer length
 *
Lucas De Marchi's avatar
Lucas De Marchi committed
601
 *	Perform a pseudo DMA mode read from an NCR53C400 or equivalent
Linus Torvalds's avatar
Linus Torvalds committed
602 603 604
 *	controller
 */

Finn Thain's avatar
Finn Thain committed
605 606
static inline int generic_NCR5380_pwrite(struct Scsi_Host *instance,
                                         unsigned char *src, int len)
Linus Torvalds's avatar
Linus Torvalds committed
607
{
608
	struct NCR5380_hostdata *hostdata = shost_priv(instance);
Linus Torvalds's avatar
Linus Torvalds committed
609 610 611
	int blocks = len / 128;
	int start = 0;

612 613
	NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
	NCR5380_write(hostdata->c400_blk_cnt, blocks);
Linus Torvalds's avatar
Linus Torvalds committed
614
	while (1) {
615
		if (NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ) {
Linus Torvalds's avatar
Linus Torvalds committed
616 617 618 619
			printk(KERN_ERR "53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
			return -1;
		}

620
		if (NCR5380_read(hostdata->c400_blk_cnt) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
621
			break;
622
		while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY)
Linus Torvalds's avatar
Linus Torvalds committed
623
			; // FIXME - timeout
624
#ifndef SCSI_G_NCR5380_MEM
625 626 627 628 629
		if (hostdata->io_width == 2)
			outsw(instance->io_port + hostdata->c400_host_buf,
							src + start, 64);
		else
			outsb(instance->io_port + hostdata->c400_host_buf,
630
							src + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
631
#else
632
		/* implies SCSI_G_NCR5380_MEM */
633 634
		memcpy_toio(hostdata->iomem + NCR53C400_host_buffer,
		            src + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
635 636 637 638 639
#endif
		start += 128;
		blocks--;
	}
	if (blocks) {
640
		while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY)
Linus Torvalds's avatar
Linus Torvalds committed
641 642
			; // FIXME - no timeout

643
#ifndef SCSI_G_NCR5380_MEM
644 645 646 647 648
		if (hostdata->io_width == 2)
			outsw(instance->io_port + hostdata->c400_host_buf,
							src + start, 64);
		else
			outsb(instance->io_port + hostdata->c400_host_buf,
649
							src + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
650
#else
651
		/* implies SCSI_G_NCR5380_MEM */
652 653
		memcpy_toio(hostdata->iomem + NCR53C400_host_buffer,
		            src + start, 128);
Linus Torvalds's avatar
Linus Torvalds committed
654 655 656 657 658
#endif
		start += 128;
		blocks--;
	}

659 660
	/* wait for 53C80 registers to be available */
	while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)) {
661 662 663
		udelay(4); /* DTC436 chip hangs without this */
		/* FIXME - no timeout */
	}
Linus Torvalds's avatar
Linus Torvalds committed
664 665 666 667

	if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
		printk(KERN_ERR "53C400w: no end dma signal\n");
	}
668

Linus Torvalds's avatar
Linus Torvalds committed
669 670 671 672
	while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
		; 	// TIMEOUT
	return 0;
}
673

674 675
static int generic_NCR5380_dma_xfer_len(struct Scsi_Host *instance,
                                        struct scsi_cmnd *cmd)
676
{
677
	struct NCR5380_hostdata *hostdata = shost_priv(instance);
678 679
	int transfersize = cmd->transfersize;

680 681 682
	if (hostdata->flags & FLAG_NO_PSEUDO_DMA)
		return 0;

683 684 685 686 687 688 689
	/* Limit transfers to 32K, for xx400 & xx406
	 * pseudoDMA that transfers in 128 bytes blocks.
	 */
	if (transfersize > 32 * 1024 && cmd->SCp.this_residual &&
	    !(cmd->SCp.this_residual % transfersize))
		transfersize = 32 * 1024;

690 691 692 693
	/* 53C400 datasheet: non-modulo-128-byte transfers should use PIO */
	if (transfersize % 128)
		transfersize = 0;

694 695 696
	return transfersize;
}

Linus Torvalds's avatar
Linus Torvalds committed
697 698 699 700 701 702
/*
 *	Include the NCR5380 core code that we build our driver around	
 */
 
#include "NCR5380.c"

703
static struct scsi_host_template driver_template = {
704 705 706 707 708 709
	.proc_name		= DRV_MODULE_NAME,
	.name			= "Generic NCR5380/NCR53C400 SCSI",
	.detect			= generic_NCR5380_detect,
	.release		= generic_NCR5380_release_resources,
	.info			= generic_NCR5380_info,
	.queuecommand		= generic_NCR5380_queue_command,
Linus Torvalds's avatar
Linus Torvalds committed
710 711
	.eh_abort_handler	= generic_NCR5380_abort,
	.eh_bus_reset_handler	= generic_NCR5380_bus_reset,
712 713 714 715 716
	.can_queue		= 16,
	.this_id		= 7,
	.sg_tablesize		= SG_ALL,
	.cmd_per_lun		= 2,
	.use_clustering		= DISABLE_CLUSTERING,
717
	.cmd_size		= NCR5380_CMD_SIZE,
Finn Thain's avatar
Finn Thain committed
718
	.max_sectors		= 128,
Linus Torvalds's avatar
Linus Torvalds committed
719
};
720

Linus Torvalds's avatar
Linus Torvalds committed
721 722 723 724 725 726 727 728 729
#include "scsi_module.c"

module_param(ncr_irq, int, 0);
module_param(ncr_dma, int, 0);
module_param(ncr_addr, int, 0);
module_param(ncr_5380, int, 0);
module_param(ncr_53c400, int, 0);
module_param(ncr_53c400a, int, 0);
module_param(dtc_3181e, int, 0);
730
module_param(hp_c2502, int, 0);
Linus Torvalds's avatar
Linus Torvalds committed
731 732
MODULE_LICENSE("GPL");

733
#if !defined(SCSI_G_NCR5380_MEM) && defined(MODULE)
734
static struct isapnp_device_id id_table[] = {
Linus Torvalds's avatar
Linus Torvalds committed
735 736 737 738 739 740 741 742
	{
	 ISAPNP_ANY_ID, ISAPNP_ANY_ID,
	 ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e),
	 0},
	{0}
};

MODULE_DEVICE_TABLE(isapnp, id_table);
743
#endif
Linus Torvalds's avatar
Linus Torvalds committed
744 745 746 747 748

__setup("ncr5380=", do_NCR5380_setup);
__setup("ncr53c400=", do_NCR53C400_setup);
__setup("ncr53c400a=", do_NCR53C400A_setup);
__setup("dtc3181e=", do_DTC3181E_setup);