| 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" |