| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*+M************************************************************************* | 
 | 2 |  * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 1997 Perceptive Solutions, Inc. | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify | 
 | 7 |  * it under the terms of the GNU General Public License as published by | 
 | 8 |  * the Free Software Foundation; either version 2, or (at your option) | 
 | 9 |  * any later version. | 
 | 10 |  * | 
 | 11 |  * This program is distributed in the hope that it will be useful, | 
 | 12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 14 |  * GNU General Public License for more details. | 
 | 15 |  * | 
 | 16 |  * You should have received a copy of the GNU General Public License | 
 | 17 |  * along with this program; see the file COPYING.  If not, write to | 
 | 18 |  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | 
 | 19 |  * | 
 | 20 |  * | 
 | 21 |  *	File Name:		psi240i.c | 
 | 22 |  * | 
 | 23 |  *	Description:	SCSI driver for the PSI240I EIDE interface card. | 
 | 24 |  * | 
 | 25 |  *-M*************************************************************************/ | 
 | 26 |  | 
 | 27 | #include <linux/module.h> | 
 | 28 |  | 
 | 29 | #include <linux/blkdev.h> | 
 | 30 | #include <linux/kernel.h> | 
 | 31 | #include <linux/types.h> | 
 | 32 | #include <linux/string.h> | 
 | 33 | #include <linux/ioport.h> | 
 | 34 | #include <linux/delay.h> | 
 | 35 | #include <linux/interrupt.h> | 
 | 36 | #include <linux/proc_fs.h> | 
 | 37 | #include <linux/spinlock.h> | 
 | 38 | #include <linux/stat.h> | 
 | 39 |  | 
 | 40 | #include <asm/dma.h> | 
 | 41 | #include <asm/system.h> | 
 | 42 | #include <asm/io.h> | 
 | 43 | #include "scsi.h" | 
 | 44 | #include <scsi/scsi_host.h> | 
 | 45 |  | 
 | 46 | #include "psi240i.h" | 
 | 47 | #include "psi_chip.h" | 
 | 48 |  | 
 | 49 | //#define DEBUG 1 | 
 | 50 |  | 
 | 51 | #ifdef DEBUG | 
 | 52 | #define DEB(x) x | 
 | 53 | #else | 
 | 54 | #define DEB(x) | 
 | 55 | #endif | 
 | 56 |  | 
 | 57 | #define MAXBOARDS 6	/* Increase this and the sizes of the arrays below, if you need more. */ | 
 | 58 |  | 
 | 59 | #define	PORT_DATA				0 | 
 | 60 | #define	PORT_ERROR				1 | 
 | 61 | #define	PORT_SECTOR_COUNT		2 | 
 | 62 | #define	PORT_LBA_0				3 | 
 | 63 | #define	PORT_LBA_8				4 | 
 | 64 | #define	PORT_LBA_16				5 | 
 | 65 | #define	PORT_LBA_24				6 | 
 | 66 | #define	PORT_STAT_CMD			7 | 
 | 67 | #define	PORT_SEL_FAIL			8 | 
 | 68 | #define	PORT_IRQ_STATUS			9 | 
 | 69 | #define	PORT_ADDRESS			10 | 
 | 70 | #define	PORT_FAIL				11 | 
 | 71 | #define	PORT_ALT_STAT		   	12 | 
 | 72 |  | 
 | 73 | typedef struct | 
 | 74 | 	{ | 
 | 75 | 	UCHAR		   	device;				// device code | 
 | 76 | 	UCHAR			byte6;				// device select register image | 
 | 77 | 	UCHAR			spigot;				// spigot number | 
 | 78 | 	UCHAR			expectingIRQ;		// flag for expecting and interrupt | 
 | 79 | 	USHORT			sectors;			// number of sectors per track | 
 | 80 | 	USHORT			heads;				// number of heads | 
 | 81 | 	USHORT			cylinders;			// number of cylinders for this device | 
 | 82 | 	USHORT			spareword;			// placeholder | 
 | 83 | 	ULONG			blocks;				// number of blocks on device | 
 | 84 | 	}	OUR_DEVICE, *POUR_DEVICE; | 
 | 85 |  | 
 | 86 | typedef struct | 
 | 87 | 	{ | 
 | 88 | 	USHORT		 ports[13]; | 
 | 89 | 	OUR_DEVICE	 device[8]; | 
| Henrik Kretzschmar | c543a37 | 2006-10-10 14:41:45 -0700 | [diff] [blame] | 90 | 	struct scsi_cmnd *pSCmnd; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 | 	IDE_STRUCT	 ide; | 
 | 92 | 	ULONG		 startSector; | 
 | 93 | 	USHORT		 sectorCount; | 
| Henrik Kretzschmar | c543a37 | 2006-10-10 14:41:45 -0700 | [diff] [blame] | 94 | 	struct scsi_cmnd *SCpnt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | 	VOID		*buffer; | 
 | 96 | 	USHORT		 expectingIRQ; | 
 | 97 | 	}	ADAPTER240I, *PADAPTER240I; | 
 | 98 |  | 
 | 99 | #define HOSTDATA(host) ((PADAPTER240I)&host->hostdata) | 
 | 100 |  | 
 | 101 | static struct	Scsi_Host *PsiHost[6] = {NULL,};  /* One for each IRQ level (10-15) */ | 
 | 102 | static			IDENTIFY_DATA	identifyData; | 
 | 103 | static			SETUP			ChipSetup; | 
 | 104 |  | 
 | 105 | static	USHORT	portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5}; | 
 | 106 |  | 
 | 107 | /**************************************************************** | 
 | 108 |  *	Name:	WriteData	:LOCAL | 
 | 109 |  * | 
 | 110 |  *	Description:	Write data to device. | 
 | 111 |  * | 
 | 112 |  *	Parameters:		padapter - Pointer adapter data structure. | 
 | 113 |  * | 
 | 114 |  *	Returns:		TRUE if drive does not assert DRQ in time. | 
 | 115 |  * | 
 | 116 |  ****************************************************************/ | 
 | 117 | static int WriteData (PADAPTER240I padapter) | 
 | 118 | 	{ | 
 | 119 | 	ULONG	timer; | 
 | 120 | 	USHORT *pports = padapter->ports; | 
 | 121 |  | 
 | 122 | 	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value | 
 | 123 | 	do  { | 
 | 124 | 		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) | 
 | 125 | 			{ | 
 | 126 | 			outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256); | 
 | 127 | 			return 0; | 
 | 128 | 			} | 
 | 129 | 		}	while ( time_after(timer, jiffies) );									// test for timeout | 
 | 130 |  | 
 | 131 | 	padapter->ide.ide.ides.cmd = 0;									// null out the command byte | 
 | 132 | 	return 1; | 
 | 133 | 	} | 
 | 134 | /**************************************************************** | 
 | 135 |  *	Name:	IdeCmd	:LOCAL | 
 | 136 |  * | 
 | 137 |  *	Description:	Process a queued command from the SCSI manager. | 
 | 138 |  * | 
 | 139 |  *	Parameters:		padapter - Pointer adapter data structure. | 
 | 140 |  * | 
 | 141 |  *	Returns:		Zero if no error or status register contents on error. | 
 | 142 |  * | 
 | 143 |  ****************************************************************/ | 
 | 144 | static UCHAR IdeCmd (PADAPTER240I padapter) | 
 | 145 | 	{ | 
 | 146 | 	ULONG	timer; | 
 | 147 | 	USHORT *pports = padapter->ports; | 
 | 148 | 	UCHAR	status; | 
 | 149 |  | 
 | 150 | 	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]);	// select the spigot | 
 | 151 | 	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive | 
 | 152 | 	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value | 
 | 153 | 	do  { | 
 | 154 | 		status = inb_p (padapter->ports[PORT_STAT_CMD]); | 
 | 155 | 		if ( status & IDE_STATUS_DRDY ) | 
 | 156 | 			{ | 
 | 157 | 			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); | 
 | 158 | 			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); | 
 | 159 | 			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); | 
 | 160 | 			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); | 
 | 161 | 			padapter->expectingIRQ = 1; | 
 | 162 | 			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); | 
 | 163 |  | 
 | 164 | 			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) | 
 | 165 | 				return (WriteData (padapter)); | 
 | 166 |  | 
 | 167 | 			return 0; | 
 | 168 | 			} | 
 | 169 | 		}	while ( time_after(timer, jiffies) );									// test for timeout | 
 | 170 |  | 
 | 171 | 	padapter->ide.ide.ides.cmd = 0;									// null out the command byte | 
 | 172 | 	return status; | 
 | 173 | 	} | 
 | 174 | /**************************************************************** | 
 | 175 |  *	Name:	SetupTransfer	:LOCAL | 
 | 176 |  * | 
 | 177 |  *	Description:	Setup a data transfer command. | 
 | 178 |  * | 
 | 179 |  *	Parameters:		padapter - Pointer adapter data structure. | 
 | 180 |  *					drive	 - Drive/head register upper nibble only. | 
 | 181 |  * | 
 | 182 |  *	Returns:		TRUE if no data to transfer. | 
 | 183 |  * | 
 | 184 |  ****************************************************************/ | 
 | 185 | static int SetupTransfer (PADAPTER240I padapter, UCHAR drive) | 
 | 186 | 	{ | 
 | 187 | 	if ( padapter->sectorCount ) | 
 | 188 | 		{ | 
 | 189 | 		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; | 
 | 190 | 		padapter->ide.ide.ide[6] |= drive; | 
 | 191 | 		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; | 
 | 192 | 		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer | 
 | 193 | 		padapter->startSector += padapter->ide.ide.ides.sectors; | 
 | 194 | 		return 0; | 
 | 195 | 		} | 
 | 196 | 	else | 
 | 197 | 		{ | 
 | 198 | 		padapter->ide.ide.ides.cmd = 0;								// null out the command byte | 
 | 199 | 		padapter->SCpnt = NULL; | 
 | 200 | 		return 1; | 
 | 201 | 		} | 
 | 202 | 	} | 
 | 203 | /**************************************************************** | 
 | 204 |  *	Name:	DecodeError	:LOCAL | 
 | 205 |  * | 
 | 206 |  *	Description:	Decode and process device errors. | 
 | 207 |  * | 
 | 208 |  *	Parameters:		pshost - Pointer to host data block. | 
 | 209 |  *					status - Status register code. | 
 | 210 |  * | 
 | 211 |  *	Returns:		The driver status code. | 
 | 212 |  * | 
 | 213 |  ****************************************************************/ | 
 | 214 | static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) | 
 | 215 | 	{ | 
 | 216 | 	PADAPTER240I	padapter = HOSTDATA(pshost); | 
 | 217 | 	UCHAR			error; | 
 | 218 |  | 
 | 219 | 	padapter->expectingIRQ = 0; | 
 | 220 | 	padapter->SCpnt = NULL; | 
 | 221 | 	if ( status & IDE_STATUS_WRITE_FAULT ) | 
 | 222 | 		{ | 
 | 223 | 		return DID_PARITY << 16; | 
 | 224 | 		} | 
 | 225 | 	if ( status & IDE_STATUS_BUSY ) | 
 | 226 | 		return DID_BUS_BUSY << 16; | 
 | 227 |  | 
 | 228 | 	error = inb_p (padapter->ports[PORT_ERROR]); | 
 | 229 | 	DEB(printk ("\npsi240i error register: %x", error)); | 
 | 230 | 	switch ( error ) | 
 | 231 | 		{ | 
 | 232 | 		case IDE_ERROR_AMNF: | 
 | 233 | 		case IDE_ERROR_TKONF: | 
 | 234 | 		case IDE_ERROR_ABRT: | 
 | 235 | 		case IDE_ERROR_IDFN: | 
 | 236 | 		case IDE_ERROR_UNC: | 
 | 237 | 		case IDE_ERROR_BBK: | 
 | 238 | 		default: | 
 | 239 | 			return DID_ERROR << 16; | 
 | 240 | 		} | 
 | 241 | 	return DID_ERROR << 16; | 
 | 242 | 	} | 
 | 243 | /**************************************************************** | 
 | 244 |  *	Name:	Irq_Handler	:LOCAL | 
 | 245 |  * | 
 | 246 |  *	Description:	Interrupt handler. | 
 | 247 |  * | 
 | 248 |  *	Parameters:		irq		- Hardware IRQ number. | 
 | 249 |  *					dev_id	- | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 |  * | 
 | 251 |  *	Returns:		TRUE if drive is not ready in time. | 
 | 252 |  * | 
 | 253 |  ****************************************************************/ | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 254 | static void Irq_Handler (int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | 	{ | 
| Henrik Kretzschmar | c543a37 | 2006-10-10 14:41:45 -0700 | [diff] [blame] | 256 | 	struct Scsi_Host *shost;	// Pointer to host data block | 
 | 257 | 	PADAPTER240I padapter;		// Pointer to adapter control structure | 
 | 258 | 	USHORT *pports;			// I/O port array | 
 | 259 | 	struct scsi_cmnd *SCpnt; | 
 | 260 | 	UCHAR status; | 
 | 261 | 	int z; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 262 |  | 
 | 263 | 	DEB(printk ("\npsi240i received interrupt\n")); | 
 | 264 |  | 
 | 265 | 	shost = PsiHost[irq - 10]; | 
 | 266 | 	if ( !shost ) | 
 | 267 | 		panic ("Splunge!"); | 
 | 268 |  | 
 | 269 | 	padapter = HOSTDATA(shost); | 
 | 270 | 	pports = padapter->ports; | 
 | 271 | 	SCpnt = padapter->SCpnt; | 
 | 272 |  | 
 | 273 | 	if ( !padapter->expectingIRQ ) | 
 | 274 | 		{ | 
 | 275 | 		DEB(printk ("\npsi240i Unsolicited interrupt\n")); | 
 | 276 | 		return; | 
 | 277 | 		} | 
 | 278 | 	padapter->expectingIRQ = 0; | 
 | 279 |  | 
 | 280 | 	status = inb_p (padapter->ports[PORT_STAT_CMD]);			// read the device status | 
 | 281 | 	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) | 
 | 282 | 		goto irqerror; | 
 | 283 |  | 
 | 284 | 	DEB(printk ("\npsi240i processing interrupt")); | 
 | 285 | 	switch ( padapter->ide.ide.ides.cmd )							// decide how to handle the interrupt | 
 | 286 | 		{ | 
 | 287 | 		case IDE_CMD_READ_MULTIPLE: | 
 | 288 | 			if ( status & IDE_STATUS_DRQ ) | 
 | 289 | 				{ | 
 | 290 | 				insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256); | 
 | 291 | 				padapter->buffer += padapter->ide.ide.ides.sectors * 512; | 
 | 292 | 				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) | 
 | 293 | 					{ | 
 | 294 | 					SCpnt->result = DID_OK << 16; | 
 | 295 | 					padapter->SCpnt = NULL; | 
 | 296 | 					SCpnt->scsi_done (SCpnt); | 
 | 297 | 					return; | 
 | 298 | 					} | 
 | 299 | 				if ( !(status = IdeCmd (padapter)) ) | 
 | 300 | 					return; | 
 | 301 | 				} | 
 | 302 | 			break; | 
 | 303 |  | 
 | 304 | 		case IDE_CMD_WRITE_MULTIPLE: | 
 | 305 | 			padapter->buffer += padapter->ide.ide.ides.sectors * 512; | 
 | 306 | 			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) | 
 | 307 | 				{ | 
 | 308 | 				SCpnt->result = DID_OK << 16; | 
 | 309 | 				padapter->SCpnt = NULL; | 
 | 310 | 				SCpnt->scsi_done (SCpnt); | 
 | 311 | 				return; | 
 | 312 | 				} | 
 | 313 | 			if ( !(status = IdeCmd (padapter)) ) | 
 | 314 | 				return; | 
 | 315 | 			break; | 
 | 316 |  | 
 | 317 | 		case IDE_COMMAND_IDENTIFY: | 
 | 318 | 			{ | 
 | 319 | 			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer; | 
 | 320 |  | 
 | 321 | 			if ( status & IDE_STATUS_DRQ ) | 
 | 322 | 				{ | 
 | 323 | 				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); | 
 | 324 |  | 
 | 325 | 				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure. | 
 | 326 | 				pinquiryData->DeviceType = 0; | 
 | 327 | 				pinquiryData->Versions = 2; | 
 | 328 | 				pinquiryData->AdditionalLength = 35 - 4; | 
 | 329 |  | 
 | 330 | 				// Fill in vendor identification fields. | 
| Adrian Bunk | 05052f7 | 2006-11-08 19:56:37 -0800 | [diff] [blame] | 331 | 				for ( z = 0;  z < 8;  z += 2 ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | 					{ | 
 | 333 | 					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1]; | 
 | 334 | 					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; | 
 | 335 | 					} | 
 | 336 |  | 
 | 337 | 				// Initialize unused portion of product id. | 
 | 338 | 				for ( z = 0;  z < 4;  z++ ) | 
 | 339 | 					pinquiryData->ProductId[12 + z] = ' '; | 
 | 340 |  | 
 | 341 | 				// Move firmware revision from IDENTIFY data to | 
 | 342 | 				// product revision in INQUIRY data. | 
 | 343 | 				for ( z = 0;  z < 4;  z += 2 ) | 
 | 344 | 					{ | 
 | 345 | 					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; | 
 | 346 | 					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; | 
 | 347 | 					} | 
 | 348 |  | 
 | 349 | 				SCpnt->result = DID_OK << 16; | 
 | 350 | 				padapter->SCpnt = NULL; | 
 | 351 | 				SCpnt->scsi_done (SCpnt); | 
 | 352 | 				return; | 
 | 353 | 				} | 
 | 354 | 			break; | 
 | 355 | 			} | 
 | 356 |  | 
 | 357 | 		default: | 
 | 358 | 			SCpnt->result = DID_OK << 16; | 
 | 359 | 			padapter->SCpnt = NULL; | 
 | 360 | 			SCpnt->scsi_done (SCpnt); | 
 | 361 | 			return; | 
 | 362 | 		} | 
 | 363 |  | 
 | 364 | irqerror:; | 
 | 365 | 	DEB(printk ("\npsi240i error  Device Status: %X\n", status)); | 
 | 366 | 	SCpnt->result = DecodeError (shost, status); | 
 | 367 | 	SCpnt->scsi_done (SCpnt); | 
 | 368 | 	} | 
 | 369 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 370 | static irqreturn_t do_Irq_Handler (int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 371 | { | 
 | 372 | 	unsigned long flags; | 
 | 373 | 	struct Scsi_Host *dev = dev_id; | 
 | 374 | 	 | 
 | 375 | 	spin_lock_irqsave(dev->host_lock, flags); | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 376 | 	Irq_Handler(irq, dev_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 377 | 	spin_unlock_irqrestore(dev->host_lock, flags); | 
 | 378 | 	return IRQ_HANDLED; | 
 | 379 | } | 
 | 380 |  | 
 | 381 | /**************************************************************** | 
 | 382 |  *	Name:	Psi240i_QueueCommand | 
 | 383 |  * | 
 | 384 |  *	Description:	Process a queued command from the SCSI manager. | 
 | 385 |  * | 
 | 386 |  *	Parameters:		SCpnt - Pointer to SCSI command structure. | 
 | 387 |  *					done  - Pointer to done function to call. | 
 | 388 |  * | 
 | 389 |  *	Returns:		Status code. | 
 | 390 |  * | 
 | 391 |  ****************************************************************/ | 
| Henrik Kretzschmar | c543a37 | 2006-10-10 14:41:45 -0700 | [diff] [blame] | 392 | static int Psi240i_QueueCommand(struct scsi_cmnd *SCpnt, | 
 | 393 | 				void (*done)(struct scsi_cmnd *)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 394 | 	{ | 
| Henrik Kretzschmar | c543a37 | 2006-10-10 14:41:45 -0700 | [diff] [blame] | 395 | 	UCHAR *cdb = (UCHAR *)SCpnt->cmnd; | 
 | 396 | 	// Pointer to SCSI CDB | 
 | 397 | 	PADAPTER240I padapter = HOSTDATA (SCpnt->device->host); | 
 | 398 | 	// Pointer to adapter control structure | 
 | 399 | 	POUR_DEVICE pdev = &padapter->device [SCpnt->device->id]; | 
 | 400 | 	// Pointer to device information | 
 | 401 | 	UCHAR rc; | 
 | 402 | 	// command return code | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 |  | 
 | 404 | 	SCpnt->scsi_done = done; | 
 | 405 | 	padapter->ide.ide.ides.spigot = pdev->spigot; | 
 | 406 | 	padapter->buffer = SCpnt->request_buffer; | 
 | 407 | 	if (done) | 
 | 408 | 		{ | 
 | 409 | 		if ( !pdev->device ) | 
 | 410 | 			{ | 
 | 411 | 			SCpnt->result = DID_BAD_TARGET << 16; | 
 | 412 | 			done (SCpnt); | 
 | 413 | 			return 0; | 
 | 414 | 			} | 
 | 415 | 		} | 
 | 416 | 	else | 
 | 417 | 		{ | 
 | 418 | 		printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb); | 
 | 419 | 		return 0; | 
 | 420 | 		} | 
 | 421 |  | 
 | 422 | 	switch ( *cdb ) | 
 | 423 | 		{ | 
 | 424 | 		case SCSIOP_INQUIRY:   					// inquiry CDB | 
 | 425 | 			{ | 
 | 426 | 			padapter->ide.ide.ide[6] = pdev->byte6; | 
 | 427 | 			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY; | 
 | 428 | 			break; | 
 | 429 | 			} | 
 | 430 |  | 
 | 431 | 		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB | 
 | 432 | 			SCpnt->result = DID_OK << 16; | 
 | 433 | 			done (SCpnt); | 
 | 434 | 			return 0; | 
 | 435 |  | 
 | 436 | 		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB | 
 | 437 | 			{ | 
 | 438 | 			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer; | 
 | 439 |  | 
 | 440 | 			pdata->blksiz = 0x20000; | 
 | 441 | 			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks); | 
 | 442 | 			SCpnt->result = DID_OK << 16; | 
 | 443 | 			done (SCpnt); | 
 | 444 | 			return 0; | 
 | 445 | 			} | 
 | 446 |  | 
 | 447 | 		case SCSIOP_VERIFY:						// verify CDB | 
 | 448 | 			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]); | 
 | 449 | 			padapter->ide.ide.ide[6] |= pdev->byte6; | 
 | 450 | 			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8)); | 
 | 451 | 			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY; | 
 | 452 | 			break; | 
 | 453 |  | 
 | 454 | 		case SCSIOP_READ:						// read10 CDB | 
 | 455 | 			padapter->startSector = XSCSI2LONG (&cdb[2]); | 
 | 456 | 			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); | 
 | 457 | 			SetupTransfer (padapter, pdev->byte6); | 
 | 458 | 			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; | 
 | 459 | 			break; | 
 | 460 |  | 
 | 461 | 		case SCSIOP_READ6:						// read6  CDB | 
 | 462 | 			padapter->startSector = SCSI2LONG (&cdb[1]); | 
 | 463 | 			padapter->sectorCount = cdb[4]; | 
 | 464 | 			SetupTransfer (padapter, pdev->byte6); | 
 | 465 | 			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; | 
 | 466 | 			break; | 
 | 467 |  | 
 | 468 | 		case SCSIOP_WRITE:						// write10 CDB | 
 | 469 | 			padapter->startSector = XSCSI2LONG (&cdb[2]); | 
 | 470 | 			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); | 
 | 471 | 			SetupTransfer (padapter, pdev->byte6); | 
 | 472 | 			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; | 
 | 473 | 			break; | 
 | 474 | 		case SCSIOP_WRITE6:						// write6  CDB | 
 | 475 | 			padapter->startSector = SCSI2LONG (&cdb[1]); | 
 | 476 | 			padapter->sectorCount = cdb[4]; | 
 | 477 | 			SetupTransfer (padapter, pdev->byte6); | 
 | 478 | 			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; | 
 | 479 | 			break; | 
 | 480 |  | 
 | 481 | 		default: | 
 | 482 | 			DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb)); | 
 | 483 | 			SCpnt->result = DID_ERROR << 16; | 
 | 484 | 			done (SCpnt); | 
 | 485 | 			return 0; | 
 | 486 | 		} | 
 | 487 |  | 
 | 488 | 	padapter->SCpnt = SCpnt;  									// Save this command data | 
 | 489 |  | 
 | 490 | 	rc = IdeCmd (padapter); | 
 | 491 | 	if ( rc ) | 
 | 492 | 		{ | 
 | 493 | 		padapter->expectingIRQ = 0; | 
 | 494 | 		DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd)); | 
 | 495 | 		SCpnt->result = DID_ERROR << 16; | 
 | 496 | 		done (SCpnt); | 
 | 497 | 		return 0; | 
 | 498 | 		} | 
 | 499 | 	DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd)); | 
 | 500 | 	return 0; | 
 | 501 | 	} | 
 | 502 |  | 
 | 503 | /*************************************************************************** | 
 | 504 |  *	Name:			ReadChipMemory | 
 | 505 |  * | 
 | 506 |  *	Description:	Read information from controller memory. | 
 | 507 |  * | 
 | 508 |  *	Parameters:		psetup	- Pointer to memory image of setup information. | 
 | 509 |  *					base	- base address of memory. | 
 | 510 |  *					length	- lenght of data space in bytes. | 
 | 511 |  *					port	- I/O address of data port. | 
 | 512 |  * | 
 | 513 |  *	Returns:		Nothing. | 
 | 514 |  * | 
 | 515 |  **************************************************************************/ | 
 | 516 | static void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port) | 
 | 517 | 	{ | 
 | 518 | 	USHORT	z, zz; | 
 | 519 | 	UCHAR	*pd = (UCHAR *)pdata; | 
 | 520 | 	outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup data port | 
 | 521 | 	zz = 0; | 
 | 522 | 	while ( zz < length ) | 
 | 523 | 		{ | 
 | 524 | 		outw_p (base, port + REG_ADDRESS);				// setup address | 
 | 525 |  | 
 | 526 | 		for ( z = 0;  z < 8;  z++ ) | 
 | 527 | 			{ | 
 | 528 | 			if ( (zz + z) < length ) | 
 | 529 | 			*pd++ = inb_p (port + z);	// read data byte | 
 | 530 | 			} | 
 | 531 | 		zz += 8; | 
 | 532 | 		base += 8; | 
 | 533 | 		} | 
 | 534 | 	} | 
 | 535 | /**************************************************************** | 
 | 536 |  *	Name:	Psi240i_Detect | 
 | 537 |  * | 
 | 538 |  *	Description:	Detect and initialize our boards. | 
 | 539 |  * | 
 | 540 |  *	Parameters:		tpnt - Pointer to SCSI host template structure. | 
 | 541 |  * | 
 | 542 |  *	Returns:		Number of adapters found. | 
 | 543 |  * | 
 | 544 |  ****************************************************************/ | 
| Christoph Hellwig | d0be4a7d | 2005-10-31 18:31:40 +0100 | [diff] [blame] | 545 | static int Psi240i_Detect (struct scsi_host_template *tpnt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 546 | 	{ | 
 | 547 | 	int					board; | 
 | 548 | 	int					count = 0; | 
 | 549 | 	int					unit; | 
 | 550 | 	int					z; | 
 | 551 | 	USHORT				port, port_range = 16; | 
 | 552 | 	CHIP_CONFIG_N		chipConfig; | 
 | 553 | 	CHIP_DEVICE_N		chipDevice[8]; | 
 | 554 | 	struct Scsi_Host   *pshost; | 
 | 555 |  | 
 | 556 | 	for ( board = 0;  board < MAXBOARDS;  board++ )					// scan for I/O ports | 
 | 557 | 		{ | 
 | 558 | 		pshost = NULL; | 
 | 559 | 		port = portAddr[board];								// get base address to test | 
 | 560 | 		if ( !request_region (port, port_range, "psi240i") ) | 
 | 561 | 			continue; | 
 | 562 | 		if ( inb_p (port + REG_FAIL) != CHIP_ID )			// do the first test for likley hood that it is us | 
 | 563 | 			goto host_init_failure; | 
 | 564 | 		outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup EEPROM/RAM access | 
 | 565 | 		outw (0, port + REG_ADDRESS);						// setup EEPROM address zero | 
 | 566 | 		if ( inb_p (port) != 0x55 )							// test 1st byte | 
 | 567 | 			goto host_init_failure;									//   nope | 
 | 568 | 		if ( inb_p (port + 1) != 0xAA )						// test 2nd byte | 
 | 569 | 			goto host_init_failure;								//   nope | 
 | 570 |  | 
 | 571 | 		// at this point our board is found and can be accessed.  Now we need to initialize | 
 | 572 | 		// our informatation and register with the kernel. | 
 | 573 |  | 
 | 574 |  | 
 | 575 | 		ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port); | 
 | 576 | 		ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port); | 
 | 577 | 		ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port); | 
 | 578 |  | 
 | 579 | 		if ( !chipConfig.numDrives )						// if no devices on this board | 
 | 580 | 			goto host_init_failure; | 
 | 581 |  | 
 | 582 | 		pshost = scsi_register (tpnt, sizeof(ADAPTER240I)); | 
 | 583 | 		if(pshost == NULL) | 
 | 584 | 			goto host_init_failure;	 | 
 | 585 |  | 
 | 586 | 		PsiHost[chipConfig.irq - 10] = pshost; | 
 | 587 | 		pshost->unique_id = port; | 
 | 588 | 		pshost->io_port = port; | 
 | 589 | 		pshost->n_io_port = 16;  /* Number of bytes of I/O space used */ | 
 | 590 | 		pshost->irq = chipConfig.irq; | 
 | 591 |  | 
 | 592 | 		for ( z = 0;  z < 11;  z++ )						// build regester address array | 
 | 593 | 			HOSTDATA(pshost)->ports[z] = port + z; | 
 | 594 | 		HOSTDATA(pshost)->ports[11] = port + REG_FAIL; | 
 | 595 | 		HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT; | 
 | 596 | 		DEB (printk ("\nPorts =")); | 
 | 597 | 		DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]);); | 
 | 598 |  | 
 | 599 | 		for ( z = 0;  z < chipConfig.numDrives;  ++z ) | 
 | 600 | 			{ | 
 | 601 | 			unit = chipDevice[z].channel & 0x0F; | 
 | 602 | 			HOSTDATA(pshost)->device[unit].device	 = ChipSetup.setupDevice[unit].device; | 
 | 603 | 			HOSTDATA(pshost)->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0); | 
 | 604 | 			HOSTDATA(pshost)->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1)); | 
 | 605 | 			HOSTDATA(pshost)->device[unit].sectors	 = ChipSetup.setupDevice[unit].sectors; | 
 | 606 | 			HOSTDATA(pshost)->device[unit].heads	 = ChipSetup.setupDevice[unit].heads; | 
 | 607 | 			HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders; | 
 | 608 | 			HOSTDATA(pshost)->device[unit].blocks	 = ChipSetup.setupDevice[unit].blocks; | 
 | 609 | 			DEB (printk ("\nHOSTDATA->device    = %X", HOSTDATA(pshost)->device[unit].device)); | 
 | 610 | 			DEB (printk ("\n          byte6     = %X", HOSTDATA(pshost)->device[unit].byte6)); | 
 | 611 | 			DEB (printk ("\n          spigot    = %X", HOSTDATA(pshost)->device[unit].spigot)); | 
 | 612 | 			DEB (printk ("\n          sectors   = %X", HOSTDATA(pshost)->device[unit].sectors)); | 
 | 613 | 			DEB (printk ("\n          heads     = %X", HOSTDATA(pshost)->device[unit].heads)); | 
 | 614 | 			DEB (printk ("\n          cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders)); | 
 | 615 | 			DEB (printk ("\n          blocks    = %lX", HOSTDATA(pshost)->device[unit].blocks)); | 
 | 616 | 			} | 
 | 617 |  | 
 | 618 | 		if ( request_irq (chipConfig.irq, do_Irq_Handler, 0, "psi240i", pshost) == 0 )  | 
 | 619 | 			{ | 
 | 620 | 			printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x  IRQ = %d\n", port, chipConfig.irq); | 
 | 621 | 		        printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); | 
 | 622 | 		        count++; | 
 | 623 | 		        continue; | 
 | 624 | 			} | 
 | 625 |  | 
 | 626 | 		printk ("Unable to allocate IRQ for PSI-240I controller.\n"); | 
 | 627 |             | 
 | 628 | host_init_failure: | 
 | 629 | 		 | 
 | 630 | 		release_region (port, port_range); | 
 | 631 | 		if (pshost) | 
 | 632 | 			scsi_unregister (pshost); | 
 | 633 |  | 
 | 634 | 		} | 
 | 635 | 	return count; | 
 | 636 | 	} | 
 | 637 |  | 
 | 638 | static int Psi240i_Release(struct Scsi_Host *shost) | 
 | 639 | { | 
 | 640 | 	if (shost->irq) | 
 | 641 | 		free_irq(shost->irq, NULL); | 
 | 642 | 	if (shost->io_port && shost->n_io_port) | 
 | 643 | 		release_region(shost->io_port, shost->n_io_port); | 
 | 644 | 	scsi_unregister(shost); | 
 | 645 | 	return 0; | 
 | 646 | } | 
 | 647 |  | 
 | 648 | /**************************************************************** | 
 | 649 |  *	Name:	Psi240i_BiosParam | 
 | 650 |  * | 
 | 651 |  *	Description:	Process the biosparam request from the SCSI manager to | 
 | 652 |  *					return C/H/S data. | 
 | 653 |  * | 
 | 654 |  *	Parameters:		disk - Pointer to SCSI disk structure. | 
 | 655 |  *					dev	 - Major/minor number from kernel. | 
 | 656 |  *					geom - Pointer to integer array to place geometry data. | 
 | 657 |  * | 
 | 658 |  *	Returns:		zero. | 
 | 659 |  * | 
 | 660 |  ****************************************************************/ | 
 | 661 | static int Psi240i_BiosParam (struct scsi_device *sdev, struct block_device *dev, | 
 | 662 | 		sector_t capacity, int geom[]) | 
 | 663 | 	{ | 
 | 664 | 	POUR_DEVICE	pdev; | 
 | 665 |  | 
| Jeff Garzik | 422c0d6 | 2005-10-24 18:05:09 -0400 | [diff] [blame] | 666 | 	pdev = &(HOSTDATA(sdev->host)->device[sdev_id(sdev)]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 |  | 
 | 668 | 	geom[0] = pdev->heads; | 
 | 669 | 	geom[1] = pdev->sectors; | 
 | 670 | 	geom[2] = pdev->cylinders; | 
 | 671 | 	return 0; | 
 | 672 | 	} | 
 | 673 |  | 
 | 674 | MODULE_LICENSE("GPL"); | 
 | 675 |  | 
| Christoph Hellwig | d0be4a7d | 2005-10-31 18:31:40 +0100 | [diff] [blame] | 676 | static struct scsi_host_template driver_template = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 677 | 	.proc_name		= "psi240i",  | 
 | 678 | 	.name			= "PSI-240I EIDE Disk Controller", | 
 | 679 | 	.detect			= Psi240i_Detect, | 
 | 680 | 	.release		= Psi240i_Release, | 
 | 681 | 	.queuecommand		= Psi240i_QueueCommand, | 
 | 682 | 	.bios_param	  	= Psi240i_BiosParam, | 
 | 683 | 	.can_queue	  	= 1, | 
 | 684 | 	.this_id	  	= -1, | 
 | 685 | 	.sg_tablesize	  	= SG_NONE, | 
 | 686 | 	.cmd_per_lun	  	= 1,  | 
 | 687 | 	.use_clustering		= DISABLE_CLUSTERING, | 
 | 688 | }; | 
 | 689 | #include "scsi_module.c" |