aha1542.c 28.6 KB
Newer Older
1 2
/*
 *  Driver for Adaptec AHA-1542 SCSI host adapters
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5
 *
 *  Copyright (C) 1992  Tommy Thorn
 *  Copyright (C) 1993, 1994, 1995 Eric Youngdale
6
 *  Copyright (C) 2015 Ondrej Zary
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16
 */

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
17 18
#include <linux/isa.h>
#include <linux/pnp.h>
19
#include <linux/slab.h>
Ondrej Zary's avatar
Ondrej Zary committed
20
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
21
#include <asm/dma.h>
Ondrej Zary's avatar
Ondrej Zary committed
22 23
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
Linus Torvalds's avatar
Linus Torvalds committed
24 25 26
#include <scsi/scsi_host.h>
#include "aha1542.h"

27
#define MAXBOARDS 4
Linus Torvalds's avatar
Linus Torvalds committed
28

29 30 31
static bool isapnp = 1;
module_param(isapnp, bool, 0);
MODULE_PARM_DESC(isapnp, "enable PnP support (default=1)");
Linus Torvalds's avatar
Linus Torvalds committed
32

33 34 35
static int io[MAXBOARDS] = { 0x330, 0x334, 0, 0 };
module_param_array(io, int, NULL, 0);
MODULE_PARM_DESC(io, "base IO address of controller (0x130,0x134,0x230,0x234,0x330,0x334, default=0x330,0x334)");
Linus Torvalds's avatar
Linus Torvalds committed
36

37 38 39 40
/* time AHA spends on the AT-bus during data transfer */
static int bus_on[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 11us */
module_param_array(bus_on, int, NULL, 0);
MODULE_PARM_DESC(bus_on, "bus on time [us] (2-15, default=-1 [HW default: 11])");
Linus Torvalds's avatar
Linus Torvalds committed
41

42 43 44 45
/* time AHA spends off the bus (not to monopolize it) during data transfer  */
static int bus_off[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 4us */
module_param_array(bus_off, int, NULL, 0);
MODULE_PARM_DESC(bus_off, "bus off time [us] (1-64, default=-1 [HW default: 4])");
Linus Torvalds's avatar
Linus Torvalds committed
46

47 48 49 50
/* default is jumper selected (J1 on 1542A), factory default = 5 MB/s */
static int dma_speed[MAXBOARDS] = { -1, -1, -1, -1 };
module_param_array(dma_speed, int, NULL, 0);
MODULE_PARM_DESC(dma_speed, "DMA speed [MB/s] (5,6,7,8,10, default=-1 [by jumper])");
Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54 55 56 57 58 59

#define BIOS_TRANSLATION_6432 1	/* Default case these days */
#define BIOS_TRANSLATION_25563 2	/* Big disk case */

struct aha1542_hostdata {
	/* This will effectively start both of them at the first mailbox */
	int bios_translation;	/* Mapping bios uses - for compatibility */
	int aha1542_last_mbi_used;
	int aha1542_last_mbo_used;
Ondrej Zary's avatar
Ondrej Zary committed
60
	struct scsi_cmnd *int_cmds[AHA1542_MAILBOXES];
Linus Torvalds's avatar
Linus Torvalds committed
61 62 63 64
	struct mailbox mb[2 * AHA1542_MAILBOXES];
	struct ccb ccb[AHA1542_MAILBOXES];
};

65 66 67 68
static inline void aha1542_intr_reset(u16 base)
{
	outb(IRST, CONTROL(base));
}
Linus Torvalds's avatar
Linus Torvalds committed
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
static inline bool wait_mask(u16 port, u8 mask, u8 allof, u8 noneof, int timeout)
{
	bool delayed = true;

	if (timeout == 0) {
		timeout = 3000000;
		delayed = false;
	}

	while (1) {
		u8 bits = inb(port) & mask;
		if ((bits & allof) == allof && ((bits & noneof) == 0))
			break;
		if (delayed)
			mdelay(1);
		if (--timeout == 0)
			return false;
	}

	return true;
}
Linus Torvalds's avatar
Linus Torvalds committed
91

92
static int aha1542_outb(unsigned int base, u8 val)
Linus Torvalds's avatar
Linus Torvalds committed
93
{
94 95 96 97 98
	if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
		return 1;
	outb(val, DATA(base));

	return 0;
Ondrej Zary's avatar
Ondrej Zary committed
99 100
}

101
static int aha1542_out(unsigned int base, u8 *buf, int len)
Ondrej Zary's avatar
Ondrej Zary committed
102 103
{
	while (len--) {
Ondrej Zary's avatar
Ondrej Zary committed
104
		if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
Ondrej Zary's avatar
Ondrej Zary committed
105
			return 1;
106
		outb(*buf++, DATA(base));
Ondrej Zary's avatar
Ondrej Zary committed
107
	}
108 109
	if (!wait_mask(INTRFLAGS(base), INTRMASK, HACC, 0, 0))
		return 1;
Ondrej Zary's avatar
Ondrej Zary committed
110

Linus Torvalds's avatar
Linus Torvalds committed
111 112 113 114 115 116
	return 0;
}

/* Only used at boot time, so we do not need to worry about latency as much
   here */

117
static int aha1542_in(unsigned int base, u8 *buf, int len, int timeout)
Linus Torvalds's avatar
Linus Torvalds committed
118 119
{
	while (len--) {
Ondrej Zary's avatar
Ondrej Zary committed
120
		if (!wait_mask(STATUS(base), DF, DF, 0, timeout))
Ondrej Zary's avatar
Ondrej Zary committed
121
			return 1;
122
		*buf++ = inb(DATA(base));
Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
	}
	return 0;
}

static int makecode(unsigned hosterr, unsigned scsierr)
{
	switch (hosterr) {
	case 0x0:
	case 0xa:		/* Linked command complete without error and linked normally */
	case 0xb:		/* Linked command complete without error, interrupt generated */
		hosterr = 0;
		break;

	case 0x11:		/* Selection time out-The initiator selection or target
				   reselection was not complete within the SCSI Time out period */
		hosterr = DID_TIME_OUT;
		break;

	case 0x12:		/* Data overrun/underrun-The target attempted to transfer more data
				   than was allocated by the Data Length field or the sum of the
				   Scatter / Gather Data Length fields. */

	case 0x13:		/* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */

	case 0x15:		/* MBO command was not 00, 01 or 02-The first byte of the CB was
				   invalid. This usually indicates a software failure. */

	case 0x16:		/* Invalid CCB Operation Code-The first byte of the CCB was invalid.
				   This usually indicates a software failure. */

	case 0x17:		/* Linked CCB does not have the same LUN-A subsequent CCB of a set
				   of linked CCB's does not specify the same logical unit number as
				   the first. */
	case 0x18:		/* Invalid Target Direction received from Host-The direction of a
				   Target Mode CCB was invalid. */

	case 0x19:		/* Duplicate CCB Received in Target Mode-More than once CCB was
				   received to service data transfer between the same target LUN
				   and initiator SCSI ID in the same direction. */

	case 0x1a:		/* Invalid CCB or Segment List Parameter-A segment list with a zero
				   length segment or invalid segment list boundaries was received.
				   A CCB parameter was invalid. */
166 167 168
#ifdef DEBUG
		printk("Aha1542: %x %x\n", hosterr, scsierr);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
		hosterr = DID_ERROR;	/* Couldn't find any better */
		break;

	case 0x14:		/* Target bus phase sequence failure-An invalid bus phase or bus
				   phase sequence was requested by the target. The host adapter
				   will generate a SCSI Reset Condition, notifying the host with
				   a SCRD interrupt */
		hosterr = DID_RESET;
		break;
	default:
		printk(KERN_ERR "aha1542: makecode: unknown hoststatus %x\n", hosterr);
		break;
	}
	return scsierr | (hosterr << 16);
}

185
static int aha1542_test_port(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
186
{
187
	u8 inquiry_result[4];
188
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
189 190

	/* Quick and dirty test for presence of the card. */
191
	if (inb(STATUS(sh->io_port)) == 0xff)
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195 196
		return 0;

	/* Reset the adapter. I ought to make a hard reset, but it's not really necessary */

	/* In case some other card was probing here, reset interrupts */
197
	aha1542_intr_reset(sh->io_port);	/* reset interrupts, so they don't block */
Linus Torvalds's avatar
Linus Torvalds committed
198

199
	outb(SRST | IRST /*|SCRST */ , CONTROL(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203

	mdelay(20);		/* Wait a little bit for things to settle down. */

	/* Expect INIT and IDLE, any of the others are bad */
204
	if (!wait_mask(STATUS(sh->io_port), STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0))
Ondrej Zary's avatar
Ondrej Zary committed
205
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
206 207

	/* Shouldn't have generated any interrupts during reset */
208
	if (inb(INTRFLAGS(sh->io_port)) & INTRMASK)
Ondrej Zary's avatar
Ondrej Zary committed
209
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
210 211 212 213

	/* Perform a host adapter inquiry instead so we do not need to set
	   up the mailboxes ahead of time */

214
	aha1542_outb(sh->io_port, CMD_INQUIRY);
Linus Torvalds's avatar
Linus Torvalds committed
215

216
	for (i = 0; i < 4; i++) {
217
		if (!wait_mask(STATUS(sh->io_port), DF, DF, 0, 0))
Ondrej Zary's avatar
Ondrej Zary committed
218
			return 0;
219
		inquiry_result[i] = inb(DATA(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
220 221 222
	}

	/* Reading port should reset DF */
223
	if (inb(STATUS(sh->io_port)) & DF)
Ondrej Zary's avatar
Ondrej Zary committed
224
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
225 226

	/* When HACC, command is completed, and we're though testing */
227
	if (!wait_mask(INTRFLAGS(sh->io_port), HACC, HACC, 0, 0))
Ondrej Zary's avatar
Ondrej Zary committed
228
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
229 230

	/* Clear interrupts */
231
	outb(IRST, CONTROL(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
232

233
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
234 235
}

Ondrej Zary's avatar
Ondrej Zary committed
236
static irqreturn_t aha1542_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
237
{
Ondrej Zary's avatar
Ondrej Zary committed
238
	struct Scsi_Host *sh = dev_id;
239
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
Ondrej Zary's avatar
Ondrej Zary committed
240
	void (*my_done)(struct scsi_cmnd *) = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243
	int errstatus, mbi, mbo, mbistatus;
	int number_serviced;
	unsigned long flags;
Ondrej Zary's avatar
Ondrej Zary committed
244
	struct scsi_cmnd *tmp_cmd;
Linus Torvalds's avatar
Linus Torvalds committed
245
	int flag;
Ondrej Zary's avatar
Ondrej Zary committed
246 247
	struct mailbox *mb = aha1542->mb;
	struct ccb *ccb = aha1542->ccb;
Linus Torvalds's avatar
Linus Torvalds committed
248 249 250

#ifdef DEBUG
	{
251
		flag = inb(INTRFLAGS(sh->io_port));
252
		shost_printk(KERN_DEBUG, sh, "aha1542_intr_handle: ");
Linus Torvalds's avatar
Linus Torvalds committed
253 254 255 256 257 258 259 260 261 262
		if (!(flag & ANYINTR))
			printk("no interrupt?");
		if (flag & MBIF)
			printk("MBIF ");
		if (flag & MBOA)
			printk("MBOF ");
		if (flag & HACC)
			printk("HACC ");
		if (flag & SCRD)
			printk("SCRD ");
263
		printk("status %02x\n", inb(STATUS(sh->io_port)));
Linus Torvalds's avatar
Linus Torvalds committed
264 265 266 267
	};
#endif
	number_serviced = 0;

Ondrej Zary's avatar
Ondrej Zary committed
268 269
	spin_lock_irqsave(sh->host_lock, flags);
	while (1) {
270
		flag = inb(INTRFLAGS(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
271 272 273 274 275 276 277 278 279 280

		/* Check for unusual interrupts.  If any of these happen, we should
		   probably do something special, but for now just printing a message
		   is sufficient.  A SCSI reset detected is something that we really
		   need to deal with in some way. */
		if (flag & ~MBIF) {
			if (flag & MBOA)
				printk("MBOF ");
			if (flag & HACC)
				printk("HACC ");
281
			if (flag & SCRD)
Linus Torvalds's avatar
Linus Torvalds committed
282 283
				printk("SCRD ");
		}
284
		aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
285

Ondrej Zary's avatar
Ondrej Zary committed
286
		mbi = aha1542->aha1542_last_mbi_used + 1;
Linus Torvalds's avatar
Linus Torvalds committed
287 288 289 290 291 292 293 294 295
		if (mbi >= 2 * AHA1542_MAILBOXES)
			mbi = AHA1542_MAILBOXES;

		do {
			if (mb[mbi].status != 0)
				break;
			mbi++;
			if (mbi >= 2 * AHA1542_MAILBOXES)
				mbi = AHA1542_MAILBOXES;
Ondrej Zary's avatar
Ondrej Zary committed
296
		} while (mbi != aha1542->aha1542_last_mbi_used);
Linus Torvalds's avatar
Linus Torvalds committed
297 298

		if (mb[mbi].status == 0) {
Ondrej Zary's avatar
Ondrej Zary committed
299
			spin_unlock_irqrestore(sh->host_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
300
			/* Hmm, no mail.  Must have read it the last time around */
301
			if (!number_serviced)
302
				shost_printk(KERN_WARNING, sh, "interrupt received, but no mail.\n");
Ondrej Zary's avatar
Ondrej Zary committed
303
			return IRQ_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
304 305
		};

306
		mbo = (scsi2int(mb[mbi].ccbptr) - (isa_virt_to_bus(&ccb[0]))) / sizeof(struct ccb);
Linus Torvalds's avatar
Linus Torvalds committed
307 308
		mbistatus = mb[mbi].status;
		mb[mbi].status = 0;
Ondrej Zary's avatar
Ondrej Zary committed
309
		aha1542->aha1542_last_mbi_used = mbi;
Linus Torvalds's avatar
Linus Torvalds committed
310 311

#ifdef DEBUG
312 313 314
		if (ccb[mbo].tarstat | ccb[mbo].hastat)
			shost_printk(KERN_DEBUG, sh, "aha1542_command: returning %x (status %d)\n",
			       ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
Linus Torvalds's avatar
Linus Torvalds committed
315 316 317 318 319 320
#endif

		if (mbistatus == 3)
			continue;	/* Aborted command not found */

#ifdef DEBUG
321
		shost_printk(KERN_DEBUG, sh, "...done %d %d\n", mbo, mbi);
Linus Torvalds's avatar
Linus Torvalds committed
322 323
#endif

Ondrej Zary's avatar
Ondrej Zary committed
324
		tmp_cmd = aha1542->int_cmds[mbo];
Linus Torvalds's avatar
Linus Torvalds committed
325

Ondrej Zary's avatar
Ondrej Zary committed
326
		if (!tmp_cmd || !tmp_cmd->scsi_done) {
Ondrej Zary's avatar
Ondrej Zary committed
327
			spin_unlock_irqrestore(sh->host_lock, flags);
328 329
			shost_printk(KERN_WARNING, sh, "Unexpected interrupt\n");
			shost_printk(KERN_WARNING, sh, "tarstat=%x, hastat=%x idlun=%x ccb#=%d\n", ccb[mbo].tarstat,
Linus Torvalds's avatar
Linus Torvalds committed
330
			       ccb[mbo].hastat, ccb[mbo].idlun, mbo);
Ondrej Zary's avatar
Ondrej Zary committed
331
			return IRQ_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
332
		}
Ondrej Zary's avatar
Ondrej Zary committed
333 334 335
		my_done = tmp_cmd->scsi_done;
		kfree(tmp_cmd->host_scribble);
		tmp_cmd->host_scribble = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339
		/* Fetch the sense data, and tuck it away, in the required slot.  The
		   Adaptec automatically fetches it, and there is no guarantee that
		   we will still have it in the cdb when we come back */
		if (ccb[mbo].tarstat == 2)
Ondrej Zary's avatar
Ondrej Zary committed
340
			memcpy(tmp_cmd->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
341
			       SCSI_SENSE_BUFFERSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344 345 346 347 348 349 350 351 352 353 354


		/* is there mail :-) */

		/* more error checking left out here */
		if (mbistatus != 1)
			/* This is surely wrong, but I don't know what's right */
			errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
		else
			errstatus = 0;

#ifdef DEBUG
		if (errstatus)
355
			shost_printk(KERN_DEBUG, sh, "(aha1542 error:%x %x %x) ", errstatus,
Linus Torvalds's avatar
Linus Torvalds committed
356
			       ccb[mbo].hastat, ccb[mbo].tarstat);
357 358
		if (ccb[mbo].tarstat == 2)
			print_hex_dump_bytes("sense: ", DUMP_PREFIX_NONE, &ccb[mbo].cdb[ccb[mbo].cdblen], 12);
359 360 361
		if (errstatus)
			printk("aha1542_intr_handle: returning %6x\n", errstatus);
#endif
Ondrej Zary's avatar
Ondrej Zary committed
362 363
		tmp_cmd->result = errstatus;
		aha1542->int_cmds[mbo] = NULL;	/* This effectively frees up the mailbox slot, as
Ondrej Zary's avatar
Ondrej Zary committed
364
						   far as queuecommand is concerned */
Ondrej Zary's avatar
Ondrej Zary committed
365
		my_done(tmp_cmd);
Linus Torvalds's avatar
Linus Torvalds committed
366 367 368 369
		number_serviced++;
	};
}

Ondrej Zary's avatar
Ondrej Zary committed
370
static int aha1542_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
371
{
372
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
373
	u8 direction;
Ondrej Zary's avatar
Ondrej Zary committed
374 375
	u8 target = cmd->device->id;
	u8 lun = cmd->device->lun;
Linus Torvalds's avatar
Linus Torvalds committed
376
	unsigned long flags;
Ondrej Zary's avatar
Ondrej Zary committed
377
	int bufflen = scsi_bufflen(cmd);
378
	int mbo, sg_count;
Ondrej Zary's avatar
Ondrej Zary committed
379 380
	struct mailbox *mb = aha1542->mb;
	struct ccb *ccb = aha1542->ccb;
381
	struct chain *cptr;
Linus Torvalds's avatar
Linus Torvalds committed
382

Ondrej Zary's avatar
Ondrej Zary committed
383
	if (*cmd->cmnd == REQUEST_SENSE) {
Linus Torvalds's avatar
Linus Torvalds committed
384
		/* Don't do the command - we have the sense data already */
Ondrej Zary's avatar
Ondrej Zary committed
385
		cmd->result = 0;
Ondrej Zary's avatar
Ondrej Zary committed
386
		cmd->scsi_done(cmd);
Linus Torvalds's avatar
Linus Torvalds committed
387 388 389
		return 0;
	}
#ifdef DEBUG
390 391 392 393 394 395 396 397 398 399
	{
		int i = -1;
		if (*cmd->cmnd == READ_10 || *cmd->cmnd == WRITE_10)
			i = xscsi2int(cmd->cmnd + 2);
		else if (*cmd->cmnd == READ_6 || *cmd->cmnd == WRITE_6)
			i = scsi2int(cmd->cmnd + 2);
		shost_printk(KERN_DEBUG, sh, "aha1542_queuecommand: dev %d cmd %02x pos %d len %d",
						target, *cmd->cmnd, i, bufflen);
		print_hex_dump_bytes("command: ", DUMP_PREFIX_NONE, cmd->cmnd, cmd->cmd_len);
	}
Linus Torvalds's avatar
Linus Torvalds committed
400
#endif
401 402 403 404 405 406 407
	if (bufflen) {	/* allocate memory before taking host_lock */
		sg_count = scsi_sg_count(cmd);
		cptr = kmalloc(sizeof(*cptr) * sg_count, GFP_KERNEL | GFP_DMA);
		if (!cptr)
			return SCSI_MLQUEUE_HOST_BUSY;
	}

Linus Torvalds's avatar
Linus Torvalds committed
408 409 410
	/* Use the outgoing mailboxes in a round-robin fashion, because this
	   is how the host adapter will scan for them */

Ondrej Zary's avatar
Ondrej Zary committed
411
	spin_lock_irqsave(sh->host_lock, flags);
Ondrej Zary's avatar
Ondrej Zary committed
412
	mbo = aha1542->aha1542_last_mbo_used + 1;
Linus Torvalds's avatar
Linus Torvalds committed
413 414 415 416
	if (mbo >= AHA1542_MAILBOXES)
		mbo = 0;

	do {
Ondrej Zary's avatar
Ondrej Zary committed
417
		if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
418 419 420 421
			break;
		mbo++;
		if (mbo >= AHA1542_MAILBOXES)
			mbo = 0;
Ondrej Zary's avatar
Ondrej Zary committed
422
	} while (mbo != aha1542->aha1542_last_mbo_used);
Linus Torvalds's avatar
Linus Torvalds committed
423

Ondrej Zary's avatar
Ondrej Zary committed
424
	if (mb[mbo].status || aha1542->int_cmds[mbo])
Linus Torvalds's avatar
Linus Torvalds committed
425 426
		panic("Unable to find empty mailbox for aha1542.\n");

Ondrej Zary's avatar
Ondrej Zary committed
427
	aha1542->int_cmds[mbo] = cmd;	/* This will effectively prevent someone else from
Ondrej Zary's avatar
Ondrej Zary committed
428
					   screwing with this cdb. */
Linus Torvalds's avatar
Linus Torvalds committed
429

Ondrej Zary's avatar
Ondrej Zary committed
430
	aha1542->aha1542_last_mbo_used = mbo;
Linus Torvalds's avatar
Linus Torvalds committed
431 432

#ifdef DEBUG
Ondrej Zary's avatar
Ondrej Zary committed
433
	shost_printk(KERN_DEBUG, sh, "Sending command (%d %p)...", mbo, cmd->scsi_done);
Linus Torvalds's avatar
Linus Torvalds committed
434 435
#endif

436
	any2scsi(mb[mbo].ccbptr, isa_virt_to_bus(&ccb[mbo]));	/* This gets trashed for some reason */
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439

	memset(&ccb[mbo], 0, sizeof(struct ccb));

Ondrej Zary's avatar
Ondrej Zary committed
440
	ccb[mbo].cdblen = cmd->cmd_len;
Linus Torvalds's avatar
Linus Torvalds committed
441 442

	direction = 0;
Ondrej Zary's avatar
Ondrej Zary committed
443
	if (*cmd->cmnd == READ_10 || *cmd->cmnd == READ_6)
Linus Torvalds's avatar
Linus Torvalds committed
444
		direction = 8;
Ondrej Zary's avatar
Ondrej Zary committed
445
	else if (*cmd->cmnd == WRITE_10 || *cmd->cmnd == WRITE_6)
Linus Torvalds's avatar
Linus Torvalds committed
446 447
		direction = 16;

Ondrej Zary's avatar
Ondrej Zary committed
448
	memcpy(ccb[mbo].cdb, cmd->cmnd, ccb[mbo].cdblen);
Linus Torvalds's avatar
Linus Torvalds committed
449

450
	if (bufflen) {
451
		struct scatterlist *sg;
452
		int i;
453

Linus Torvalds's avatar
Linus Torvalds committed
454
		ccb[mbo].op = 2;	/* SCSI Initiator Command  w/scatter-gather */
455
		cmd->host_scribble = (void *)cptr;
Ondrej Zary's avatar
Ondrej Zary committed
456
		scsi_for_each_sg(cmd, sg, sg_count, i) {
457 458
			any2scsi(cptr[i].dataptr, isa_page_to_bus(sg_page(sg))
								+ sg->offset);
459
			any2scsi(cptr[i].datalen, sg->length);
Linus Torvalds's avatar
Linus Torvalds committed
460
		};
461
		any2scsi(ccb[mbo].datalen, sg_count * sizeof(struct chain));
462
		any2scsi(ccb[mbo].dataptr, isa_virt_to_bus(cptr));
Linus Torvalds's avatar
Linus Torvalds committed
463
#ifdef DEBUG
464 465
		shost_printk(KERN_DEBUG, sh, "cptr %p: ", cptr);
		print_hex_dump_bytes("cptr: ", DUMP_PREFIX_NONE, cptr, 18);
Linus Torvalds's avatar
Linus Torvalds committed
466 467 468
#endif
	} else {
		ccb[mbo].op = 0;	/* SCSI Initiator Command */
Ondrej Zary's avatar
Ondrej Zary committed
469
		cmd->host_scribble = NULL;
470 471
		any2scsi(ccb[mbo].datalen, 0);
		any2scsi(ccb[mbo].dataptr, 0);
Linus Torvalds's avatar
Linus Torvalds committed
472 473 474 475 476 477 478
	};
	ccb[mbo].idlun = (target & 7) << 5 | direction | (lun & 7);	/*SCSI Target Id */
	ccb[mbo].rsalen = 16;
	ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
	ccb[mbo].commlinkid = 0;

#ifdef DEBUG
479
	print_hex_dump_bytes("sending: ", DUMP_PREFIX_NONE, &ccb[mbo], sizeof(ccb[mbo]) - 10);
Ondrej Zary's avatar
Ondrej Zary committed
480
	printk("aha1542_queuecommand: now waiting for interrupt ");
Linus Torvalds's avatar
Linus Torvalds committed
481
#endif
Ondrej Zary's avatar
Ondrej Zary committed
482 483 484
	mb[mbo].status = 1;
	aha1542_outb(cmd->device->host->io_port, CMD_START_SCSI);
	spin_unlock_irqrestore(sh->host_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
485 486 487 488 489

	return 0;
}

/* Initialize mailboxes */
490
static void setup_mailboxes(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
491
{
492
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
Linus Torvalds's avatar
Linus Torvalds committed
493
	int i;
Ondrej Zary's avatar
Ondrej Zary committed
494 495
	struct mailbox *mb = aha1542->mb;
	struct ccb *ccb = aha1542->ccb;
Linus Torvalds's avatar
Linus Torvalds committed
496

497
	u8 mb_cmd[5] = { CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
Linus Torvalds's avatar
Linus Torvalds committed
498 499 500

	for (i = 0; i < AHA1542_MAILBOXES; i++) {
		mb[i].status = mb[AHA1542_MAILBOXES + i].status = 0;
501
		any2scsi(mb[i].ccbptr, isa_virt_to_bus(&ccb[i]));
Linus Torvalds's avatar
Linus Torvalds committed
502
	};
503
	aha1542_intr_reset(sh->io_port);	/* reset interrupts, so they don't block */
504
	any2scsi((mb_cmd + 2), isa_virt_to_bus(mb));
505
	if (aha1542_out(sh->io_port, mb_cmd, 5))
506
		shost_printk(KERN_ERR, sh, "failed setting up mailboxes\n");
507
	aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
508 509
}

510
static int aha1542_getconfig(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
511
{
512
	u8 inquiry_result[3];
Linus Torvalds's avatar
Linus Torvalds committed
513
	int i;
514
	i = inb(STATUS(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
515
	if (i & DF) {
516
		i = inb(DATA(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
517
	};
518 519 520
	aha1542_outb(sh->io_port, CMD_RETCONF);
	aha1542_in(sh->io_port, inquiry_result, 3, 0);
	if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
521
		shost_printk(KERN_ERR, sh, "error querying board settings\n");
522
	aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
523 524
	switch (inquiry_result[0]) {
	case 0x80:
525
		sh->dma_channel = 7;
Linus Torvalds's avatar
Linus Torvalds committed
526 527
		break;
	case 0x40:
528
		sh->dma_channel = 6;
Linus Torvalds's avatar
Linus Torvalds committed
529 530
		break;
	case 0x20:
531
		sh->dma_channel = 5;
Linus Torvalds's avatar
Linus Torvalds committed
532 533
		break;
	case 0x01:
534
		sh->dma_channel = 0;
Linus Torvalds's avatar
Linus Torvalds committed
535 536 537 538
		break;
	case 0:
		/* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
		   Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
539
		sh->dma_channel = 0xFF;
Linus Torvalds's avatar
Linus Torvalds committed
540 541
		break;
	default:
542
		shost_printk(KERN_ERR, sh, "Unable to determine DMA channel.\n");
Linus Torvalds's avatar
Linus Torvalds committed
543 544 545 546
		return -1;
	};
	switch (inquiry_result[1]) {
	case 0x40:
547
		sh->irq = 15;
Linus Torvalds's avatar
Linus Torvalds committed
548 549
		break;
	case 0x20:
550
		sh->irq = 14;
Linus Torvalds's avatar
Linus Torvalds committed
551 552
		break;
	case 0x8:
553
		sh->irq = 12;
Linus Torvalds's avatar
Linus Torvalds committed
554 555
		break;
	case 0x4:
556
		sh->irq = 11;
Linus Torvalds's avatar
Linus Torvalds committed
557 558
		break;
	case 0x2:
559
		sh->irq = 10;
Linus Torvalds's avatar
Linus Torvalds committed
560 561
		break;
	case 0x1:
562
		sh->irq = 9;
Linus Torvalds's avatar
Linus Torvalds committed
563 564
		break;
	default:
565
		shost_printk(KERN_ERR, sh, "Unable to determine IRQ level.\n");
Linus Torvalds's avatar
Linus Torvalds committed
566 567
		return -1;
	};
568
	sh->this_id = inquiry_result[2] & 7;
Linus Torvalds's avatar
Linus Torvalds committed
569 570 571 572 573 574
	return 0;
}

/* This function should only be called for 1542C boards - we can detect
   the special firmware settings and unlock the board */

575
static int aha1542_mbenable(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
576
{
577 578
	static u8 mbenable_cmd[3];
	static u8 mbenable_result[2];
Linus Torvalds's avatar
Linus Torvalds committed
579 580 581 582
	int retval;

	retval = BIOS_TRANSLATION_6432;

583 584
	aha1542_outb(sh->io_port, CMD_EXTBIOS);
	if (aha1542_in(sh->io_port, mbenable_result, 2, 100))
Linus Torvalds's avatar
Linus Torvalds committed
585
		return retval;
586
	if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 100))
587
		goto fail;
588
	aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
589 590 591 592 593 594 595 596 597

	if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
		mbenable_cmd[0] = CMD_MBENABLE;
		mbenable_cmd[1] = 0;
		mbenable_cmd[2] = mbenable_result[1];

		if ((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03))
			retval = BIOS_TRANSLATION_25563;

598
		if (aha1542_out(sh->io_port, mbenable_cmd, 3))
599
			goto fail;
Linus Torvalds's avatar
Linus Torvalds committed
600 601 602
	};
	while (0) {
fail:
603
		shost_printk(KERN_ERR, sh, "Mailbox init failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
604
	}
605
	aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
606 607 608 609
	return retval;
}

/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
610
static int aha1542_query(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
611
{
612
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
613
	u8 inquiry_result[4];
Linus Torvalds's avatar
Linus Torvalds committed
614
	int i;
615
	i = inb(STATUS(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
616
	if (i & DF) {
617
		i = inb(DATA(sh->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
618
	};
619 620 621
	aha1542_outb(sh->io_port, CMD_INQUIRY);
	aha1542_in(sh->io_port, inquiry_result, 4, 0);
	if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
622
		shost_printk(KERN_ERR, sh, "error querying card type\n");
623
	aha1542_intr_reset(sh->io_port);
Linus Torvalds's avatar
Linus Torvalds committed
624

625
	aha1542->bios_translation = BIOS_TRANSLATION_6432;	/* Default case */
Linus Torvalds's avatar
Linus Torvalds committed
626 627 628 629 630 631 632 633

	/* For an AHA1740 series board, we ignore the board since there is a
	   hardware bug which can lead to wrong blocks being returned if the board
	   is operating in the 1542 emulation mode.  Since there is an extended mode
	   driver, we simply ignore the board and let the 1740 driver pick it up.
	 */

	if (inquiry_result[0] == 0x43) {
634
		shost_printk(KERN_INFO, sh, "Emulation mode not supported for AHA-1740 hardware, use aha1740 driver instead.\n");
Linus Torvalds's avatar
Linus Torvalds committed
635 636 637 638 639 640
		return 1;
	};

	/* Always call this - boards that do not support extended bios translation
	   will ignore the command, and we will set the proper default */

641
	aha1542->bios_translation = aha1542_mbenable(sh);
Linus Torvalds's avatar
Linus Torvalds committed
642 643 644 645

	return 0;
}

646
static u8 dma_speed_hw(int dma_speed)
Linus Torvalds's avatar
Linus Torvalds committed
647
{
648 649 650 651 652 653 654 655 656 657 658
	switch (dma_speed) {
	case 5:
		return 0x00;
	case 6:
		return 0x04;
	case 7:
		return 0x01;
	case 8:
		return 0x02;
	case 10:
		return 0x03;
Linus Torvalds's avatar
Linus Torvalds committed
659 660
	}

661
	return 0xff;	/* invalid */
Linus Torvalds's avatar
Linus Torvalds committed
662 663
}

664
/* Set the Bus on/off-times as not to ruin floppy performance */
665
static void aha1542_set_bus_times(struct Scsi_Host *sh, int bus_on, int bus_off, int dma_speed)
Linus Torvalds's avatar
Linus Torvalds committed
666
{
667 668
	if (bus_on > 0) {
		u8 oncmd[] = { CMD_BUSON_TIME, clamp(bus_on, 2, 15) };
Linus Torvalds's avatar
Linus Torvalds committed
669

670 671
		aha1542_intr_reset(sh->io_port);
		if (aha1542_out(sh->io_port, oncmd, 2))
672 673
			goto fail;
	}
Linus Torvalds's avatar
Linus Torvalds committed
674

675 676
	if (bus_off > 0) {
		u8 offcmd[] = { CMD_BUSOFF_TIME, clamp(bus_off, 1, 64) };
Linus Torvalds's avatar
Linus Torvalds committed
677

678 679
		aha1542_intr_reset(sh->io_port);
		if (aha1542_out(sh->io_port, offcmd, 2))
680 681
			goto fail;
	}
Linus Torvalds's avatar
Linus Torvalds committed
682

683 684
	if (dma_speed_hw(dma_speed) != 0xff) {
		u8 dmacmd[] = { CMD_DMASPEED, dma_speed_hw(dma_speed) };
685

686 687
		aha1542_intr_reset(sh->io_port);
		if (aha1542_out(sh->io_port, dmacmd, 2))
688 689
			goto fail;
	}
690
	aha1542_intr_reset(sh->io_port);
691 692
	return;
fail:
693
	shost_printk(KERN_ERR, sh, "setting bus on/off-time failed\n");
694
	aha1542_intr_reset(sh->io_port);
695 696
}

Linus Torvalds's avatar
Linus Torvalds committed
697
/* return non-zero on detection */
698
static struct Scsi_Host *aha1542_hw_init(struct scsi_host_template *tpnt, struct device *pdev, int indx)
Linus Torvalds's avatar
Linus Torvalds committed
699
{
700
	unsigned int base_io = io[indx];
701
	struct Scsi_Host *sh;
Ondrej Zary's avatar
Ondrej Zary committed
702
	struct aha1542_hostdata *aha1542;
703
	char dma_info[] = "no DMA";
Linus Torvalds's avatar
Linus Torvalds committed
704

Ondrej Zary's avatar
Ondrej Zary committed
705 706
	if (base_io == 0)
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
707

Ondrej Zary's avatar
Ondrej Zary committed
708 709
	if (!request_region(base_io, AHA1542_REGION_SIZE, "aha1542"))
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
710

711 712
	sh = scsi_host_alloc(tpnt, sizeof(struct aha1542_hostdata));
	if (!sh)
Ondrej Zary's avatar
Ondrej Zary committed
713
		goto release;
714
	aha1542 = shost_priv(sh);
715

716 717 718 719 720 721 722
	sh->unique_id = base_io;
	sh->io_port = base_io;
	sh->n_io_port = AHA1542_REGION_SIZE;
	aha1542->aha1542_last_mbi_used = 2 * AHA1542_MAILBOXES - 1;
	aha1542->aha1542_last_mbo_used = AHA1542_MAILBOXES - 1;

	if (!aha1542_test_port(sh))
Ondrej Zary's avatar
Ondrej Zary committed
723
		goto unregister;
Linus Torvalds's avatar
Linus Torvalds committed
724

725
	aha1542_set_bus_times(sh, bus_on[indx], bus_off[indx], dma_speed[indx]);
726
	if (aha1542_query(sh))
Ondrej Zary's avatar
Ondrej Zary committed
727
		goto unregister;
728
	if (aha1542_getconfig(sh) == -1)
Ondrej Zary's avatar
Ondrej Zary committed
729
		goto unregister;
Linus Torvalds's avatar
Linus Torvalds committed
730

731
	if (sh->dma_channel != 0xFF)
732 733 734
		snprintf(dma_info, sizeof(dma_info), "DMA %d", sh->dma_channel);
	shost_printk(KERN_INFO, sh, "Adaptec AHA-1542 (SCSI-ID %d) at IO 0x%x, IRQ %d, %s\n",
				sh->this_id, base_io, sh->irq, dma_info);
Ondrej Zary's avatar
Ondrej Zary committed
735
	if (aha1542->bios_translation == BIOS_TRANSLATION_25563)
736
		shost_printk(KERN_INFO, sh, "Using extended bios translation\n");
Linus Torvalds's avatar
Linus Torvalds committed
737

738
	setup_mailboxes(sh);
Linus Torvalds's avatar
Linus Torvalds committed
739

Ondrej Zary's avatar
Ondrej Zary committed
740
	if (request_irq(sh->irq, aha1542_interrupt, 0, "aha1542", sh)) {
741
		shost_printk(KERN_ERR, sh, "Unable to allocate IRQ.\n");
Ondrej Zary's avatar
Ondrej Zary committed
742 743
		goto unregister;
	}
744 745
	if (sh->dma_channel != 0xFF) {
		if (request_dma(sh->dma_channel, "aha1542")) {
746
			shost_printk(KERN_ERR, sh, "Unable to allocate DMA channel.\n");
Ondrej Zary's avatar
Ondrej Zary committed
747 748
			goto free_irq;
		}
749 750 751
		if (sh->dma_channel == 0 || sh->dma_channel >= 5) {
			set_dma_mode(sh->dma_channel, DMA_MODE_CASCADE);
			enable_dma(sh->dma_channel);
Ondrej Zary's avatar
Ondrej Zary committed
752 753
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
754

755
	if (scsi_add_host(sh, pdev))
Ondrej Zary's avatar
Ondrej Zary committed
756
		goto free_dma;
Linus Torvalds's avatar
Linus Torvalds committed
757

758
	scsi_scan_host(sh);
Linus Torvalds's avatar
Linus Torvalds committed
759

760
	return sh;
Ondrej Zary's avatar
Ondrej Zary committed
761
free_dma:
762 763
	if (sh->dma_channel != 0xff)
		free_dma(sh->dma_channel);
Ondrej Zary's avatar
Ondrej Zary committed
764
free_irq:
765
	free_irq(sh->irq, sh);
Ondrej Zary's avatar
Ondrej Zary committed
766
unregister:
767
	scsi_host_put(sh);
Ondrej Zary's avatar
Ondrej Zary committed
768 769
release:
	release_region(base_io, AHA1542_REGION_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
770

771
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
772 773
}

774
static int aha1542_release(struct Scsi_Host *sh)
Linus Torvalds's avatar
Linus Torvalds committed
775
{
776 777 778 779 780 781 782 783
	scsi_remove_host(sh);
	if (sh->dma_channel != 0xff)
		free_dma(sh->dma_channel);
	if (sh->irq)
		free_irq(sh->irq, sh);
	if (sh->io_port && sh->n_io_port)
		release_region(sh->io_port, sh->n_io_port);
	scsi_host_put(sh);
Linus Torvalds's avatar
Linus Torvalds committed
784 785 786 787 788 789 790 791
	return 0;
}


/*
 * This is a device reset.  This is handled by sending a special command
 * to the device.
 */
Ondrej Zary's avatar
Ondrej Zary committed
792
static int aha1542_dev_reset(struct scsi_cmnd *cmd)
Linus Torvalds's avatar
Linus Torvalds committed
793
{
Ondrej Zary's avatar
Ondrej Zary committed
794 795
	struct Scsi_Host *sh = cmd->device->host;
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
Linus Torvalds's avatar
Linus Torvalds committed
796
	unsigned long flags;
Ondrej Zary's avatar
Ondrej Zary committed
797
	struct mailbox *mb = aha1542->mb;
Ondrej Zary's avatar
Ondrej Zary committed
798 799
	u8 target = cmd->device->id;
	u8 lun = cmd->device->lun;
Linus Torvalds's avatar
Linus Torvalds committed
800
	int mbo;
Ondrej Zary's avatar
Ondrej Zary committed
801
	struct ccb *ccb = aha1542->ccb;
Linus Torvalds's avatar
Linus Torvalds committed
802

Ondrej Zary's avatar
Ondrej Zary committed
803
	spin_lock_irqsave(sh->host_lock, flags);
Ondrej Zary's avatar
Ondrej Zary committed
804
	mbo = aha1542->aha1542_last_mbo_used + 1;
Linus Torvalds's avatar
Linus Torvalds committed
805 806 807 808
	if (mbo >= AHA1542_MAILBOXES)
		mbo = 0;

	do {
Ondrej Zary's avatar
Ondrej Zary committed
809
		if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
810 811 812 813
			break;
		mbo++;
		if (mbo >= AHA1542_MAILBOXES)
			mbo = 0;
Ondrej Zary's avatar
Ondrej Zary committed
814
	} while (mbo != aha1542->aha1542_last_mbo_used);
Linus Torvalds's avatar
Linus Torvalds committed
815

Ondrej Zary's avatar
Ondrej Zary committed
816
	if (mb[mbo].status || aha1542->int_cmds[mbo])
Linus Torvalds's avatar
Linus Torvalds committed
817 818
		panic("Unable to find empty mailbox for aha1542.\n");

Ondrej Zary's avatar
Ondrej Zary committed
819
	aha1542->int_cmds[mbo] = cmd;	/* This will effectively
Ondrej Zary's avatar
Ondrej Zary committed
820 821
					   prevent someone else from
					   screwing with this cdb. */
Linus Torvalds's avatar
Linus Torvalds committed
822

Ondrej Zary's avatar
Ondrej Zary committed
823
	aha1542->aha1542_last_mbo_used = mbo;
Linus Torvalds's avatar
Linus Torvalds committed
824

825
	any2scsi(mb[mbo].ccbptr, isa_virt_to_bus(&ccb[mbo]));	/* This gets trashed for some reason */
Linus Torvalds's avatar
Linus Torvalds committed
826 827 828 829 830 831 832 833 834 835 836 837 838 839

	memset(&ccb[mbo], 0, sizeof(struct ccb));

	ccb[mbo].op = 0x81;	/* BUS DEVICE RESET */

	ccb[mbo].idlun = (target & 7) << 5 | (lun & 7);		/*SCSI Target Id */

	ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
	ccb[mbo].commlinkid = 0;

	/* 
	 * Now tell the 1542 to flush all pending commands for this 
	 * target 
	 */
Ondrej Zary's avatar
Ondrej Zary committed
840 841
	aha1542_outb(sh->io_port, CMD_START_SCSI);
	spin_unlock_irqrestore(sh->host_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
842

Ondrej Zary's avatar
Ondrej Zary committed
843
	scmd_printk(KERN_WARNING, cmd,
844
		"Trying device reset for target\n");
Linus Torvalds's avatar
Linus Torvalds committed
845 846 847 848

	return SUCCESS;
}

Ondrej Zary's avatar
Ondrej Zary committed
849
static int aha1542_reset(struct scsi_cmnd *cmd, u8 reset_cmd)
Linus Torvalds's avatar
Linus Torvalds committed
850
{
Ondrej Zary's avatar
Ondrej Zary committed
851 852 853
	struct Scsi_Host *sh = cmd->device->host;
	struct aha1542_hostdata *aha1542 = shost_priv(sh);
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
854 855
	int i;

Ondrej Zary's avatar
Ondrej Zary committed
856
	spin_lock_irqsave(sh->host_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
857 858 859 860 861 862
	/* 
	 * This does a scsi reset for all devices on the bus.
	 * In principle, we could also reset the 1542 - should
	 * we do this?  Try this first, and we can add that later
	 * if it turns out to be useful.
	 */
Ondrej Zary's avatar
Ondrej Zary committed
863
	outb(reset_cmd, CONTROL(cmd->device->host->io_port));
Linus Torvalds's avatar
Linus Torvalds committed
864

Ondrej Zary's avatar
Ondrej Zary committed
865
	if (!wait_mask(STATUS(cmd->device->host->io_port),
Ondrej Zary's avatar
Ondrej Zary committed
866
	     STATMASK, IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0)) {
Ondrej Zary's avatar
Ondrej Zary committed
867
		spin_unlock_irqrestore(sh->host_lock, flags);
Ondrej Zary's avatar
Ondrej Zary committed
868 869
		return FAILED;
	}
Ondrej Zary's avatar
Ondrej Zary committed
870

871 872 873 874 875
	/*
	 * We need to do this too before the 1542 can interact with
	 * us again after host reset.
	 */
	if (reset_cmd & HRST)
876
		setup_mailboxes(cmd->device->host);
Ondrej Zary's avatar
Ondrej Zary committed
877

Linus Torvalds's avatar
Linus Torvalds committed
878 879 880 881 882 883
	/*
	 * Now try to pick up the pieces.  For all pending commands,
	 * free any internal data structures, and basically clear things
	 * out.  We do not try and restart any commands or anything - 
	 * the strategy handler takes care of that crap.
	 */
884
	shost_printk(KERN_WARNING, cmd->device->host, "Sent BUS RESET to scsi host %d\n", cmd->device->host->host_no);
Linus Torvalds's avatar
Linus Torvalds committed
885 886

	for (i = 0; i < AHA1542_MAILBOXES; i++) {
Ondrej Zary's avatar
Ondrej Zary committed
887 888 889
		if (aha1542->int_cmds[i] != NULL) {
			struct scsi_cmnd *tmp_cmd;
			tmp_cmd = aha1542->int_cmds[i];
Linus Torvalds's avatar
Linus Torvalds committed
890

Ondrej Zary's avatar
Ondrej Zary committed
891
			if (tmp_cmd->device->soft_reset) {
Linus Torvalds's avatar
Linus Torvalds committed
892 893 894 895 896 897 898 899
				/*
				 * If this device implements the soft reset option,
				 * then it is still holding onto the command, and
				 * may yet complete it.  In this case, we don't
				 * flush the data.
				 */
				continue;
			}
Ondrej Zary's avatar
Ondrej Zary committed
900 901 902
			kfree(tmp_cmd->host_scribble);
			tmp_cmd->host_scribble = NULL;
			aha1542->int_cmds[i] = NULL;
Ondrej Zary's avatar
Ondrej Zary committed
903
			aha1542->mb[i].status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
904 905 906
		}
	}

Ondrej Zary's avatar
Ondrej Zary committed
907
	spin_unlock_irqrestore(sh->host_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
908 909 910
	return SUCCESS;
}

Ondrej Zary's avatar
Ondrej Zary committed
911
static int aha1542_bus_reset(struct scsi_cmnd *cmd)
Linus Torvalds's avatar
Linus Torvalds committed
912
{
Ondrej Zary's avatar
Ondrej Zary committed
913
	return aha1542_reset(cmd, SCRST);
914
}
Linus Torvalds's avatar
Linus Torvalds committed
915

Ondrej Zary's avatar
Ondrej Zary committed
916
static int aha1542_host_reset(struct scsi_cmnd *cmd)
917
{
Ondrej Zary's avatar
Ondrej Zary committed
918
	return aha1542_reset(cmd, HRST | SCRST);
Linus Torvalds's avatar
Linus Torvalds committed
919 920 921
}

static int aha1542_biosparam(struct scsi_device *sdev,
922
		struct block_device *bdev, sector_t capacity, int geom[])
Linus Torvalds's avatar
Linus Torvalds committed
923
{
Ondrej Zary's avatar
Ondrej Zary committed
924
	struct aha1542_hostdata *aha1542 = shost_priv(sdev->host);
Linus Torvalds's avatar
Linus Torvalds committed
925

926 927
	if (capacity >= 0x200000 &&
			aha1542->bios_translation == BIOS_TRANSLATION_25563) {
Linus Torvalds's avatar
Linus Torvalds committed
928
		/* Please verify that this is the same as what DOS returns */
929 930
		geom[0] = 255;	/* heads */
		geom[1] = 63;	/* sectors */
Linus Torvalds's avatar
Linus Torvalds committed
931
	} else {
932 933
		geom[0] = 64;	/* heads */
		geom[1] = 32;	/* sectors */
Linus Torvalds's avatar
Linus Torvalds committed
934
	}
935
	geom[2] = sector_div(capacity, geom[0] * geom[1]);	/* cylinders */
Linus Torvalds's avatar
Linus Torvalds committed
936 937 938 939 940

	return 0;
}
MODULE_LICENSE("GPL");

941
static struct scsi_host_template driver_template = {
942
	.module			= THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
943 944 945 946 947 948 949 950 951
	.proc_name		= "aha1542",
	.name			= "Adaptec 1542",
	.queuecommand		= aha1542_queuecommand,
	.eh_device_reset_handler= aha1542_dev_reset,
	.eh_bus_reset_handler	= aha1542_bus_reset,
	.eh_host_reset_handler	= aha1542_host_reset,
	.bios_param		= aha1542_biosparam,
	.can_queue		= AHA1542_MAILBOXES, 
	.this_id		= 7,
952
	.sg_tablesize		= 16,
Linus Torvalds's avatar
Linus Torvalds committed
953 954 955
	.unchecked_isa_dma	= 1, 
	.use_clustering		= ENABLE_CLUSTERING,
};
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996

static int aha1542_isa_match(struct device *pdev, unsigned int ndev)
{
	struct Scsi_Host *sh = aha1542_hw_init(&driver_template, pdev, ndev);

	if (!sh)
		return 0;

	dev_set_drvdata(pdev, sh);
	return 1;
}

static int aha1542_isa_remove(struct device *pdev,
				    unsigned int ndev)
{
	aha1542_release(dev_get_drvdata(pdev));
	dev_set_drvdata(pdev, NULL);
	return 0;
}

static struct isa_driver aha1542_isa_driver = {
	.match		= aha1542_isa_match,
	.remove		= aha1542_isa_remove,
	.driver		= {
		.name	= "aha1542"
	},
};
static int isa_registered;

#ifdef CONFIG_PNP
static struct pnp_device_id aha1542_pnp_ids[] = {
	{ .id = "ADP1542" },
	{ .id = "" }
};
MODULE_DEVICE_TABLE(pnp, aha1542_pnp_ids);

static int aha1542_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
{
	int indx;
	struct Scsi_Host *sh;

997 998
	for (indx = 0; indx < ARRAY_SIZE(io); indx++) {
		if (io[indx])
999 1000 1001 1002 1003
			continue;

		if (pnp_activate_dev(pdev) < 0)
			continue;

1004
		io[indx] = pnp_port_start(pdev, 0);
1005 1006 1007 1008

		/* The card can be queried for its DMA, we have
		   the DMA set up that is enough */

1009
		dev_info(&pdev->dev, "ISAPnP found an AHA1535 at I/O 0x%03X", io[indx]);