diff -Nur --exclude '*.orig' linux-2.6.8.1.org/drivers/scsi/iteraid.c linux-2.6.8.1/drivers/scsi/iteraid.c --- linux-2.6.8.1.org/drivers/scsi/iteraid.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8.1/drivers/scsi/iteraid.c 2004-05-11 14:46:12.000000000 +0200 @@ -0,0 +1,5844 @@ +/* + * linux/drivers/scsi/iteraid.c + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * ITE IT8212 RAID controller device driver for Linux. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Revision 0.0 2002/12/05 15:12:12 root + * Empty function bodies; detect() works. + * + * Revision 0.1 2002/12/17 19:21:37 root + * First "dma thing doesn't work" version. + * + * Revision 0.3 2002/12/23 17:12:09 root + * Rewrite the dma routines. Reference the ide-dma.c. + * + * Revision 0.4 2002/12/26 10:19:29 root + * The dma read/write works, using some ways to prove it. But there is a + * problem about the "unknown partition table". The fdisk is ok, but + * after writing the created partitions, it still shows the "unknown + * partition table" and i can't access the created partitions. + * + * Revision 0.5 2003/01/07 21:49:30 root + * The problem of "unknown partition table" has been solved. + * We must "ENABLE CLUSTERING". There is still a another problem about + * the SCATTER/GATHER. + * + * Revision 0.6 2003/01/10 17:45:32 root + * The SCATTER/GATHER problem has been solved. Now verify the read/write + * function and make sure each RAID configurations are workable. If testing + * is OK, then it will be a version 1.0..... + * + * Revision 1.0 2003/01/16 17:45:32 root + * First release version. + * + * FixME 1: + * In RedHat 7.3, if using SG_ALL, the SCSI will timeout. It looks like + * an command is requested but the interrupt is not been asserted. So + * try to add a watchdog timer to monitor the interrupts. But this kind + * of situration will not happen in Mandrake 9.0 and also when using + * SG_NONE in RedHat 7.3. + * + * FixME 2: + * Module load problem in RedHat 7.3. + * + * Fixed: Compile in the graphic mode (GNOME or KDE) will fix the + * module load problem. + * + * Revision 1.1 2003/02/10 10:32:21 root + * Compile in the graphic mode (GNOME or KDE) will fix the + * module load problem. + * + * Revision 1.2 2003/02/18 14:10:35 root + * Fix the interrupt service routine for share irq problem. + * + * ATAPI support ---> schedule is three weeks. (2003/02/28) + * + * Revision 1.3 2003/02/27 + * First relase ATAPI version. But there will be an error if no disc in the + * CD-ROM. Because the commands like TEST_UNIT_READY and READ_CAPACITY will + * get the error response. This situration in WINDOWS will be then send the + * REQUEST SENSE command to the device but in Linux, it will never get + * REQUEST SENSE command. So can we send by ourself??? + * + * 2003/03/05 root + * + * Note 1: + * According to "The Linux SCSI Generic (sg) HOWTO", the device will respond + * with a single byte value call the 'scsi_status'. GOOD is the scsi status + * indicating everything has gone well. The most common other status is + * CHECK CONDITION. In this latter case, the SCSI mid layer issues a REQUEST + * SENSE SCSI command. The response of the REQUEST SENSE is 18 bytes or more + * in length and is called the "sense buffer". It will indicate why the original + * command may not have been executed. It is important to realize that a CHECK + * CONDITION may very in severity from informative (e.g. command needed to be + * retried before succeeding) to fatal (e.g. 'medium error' which often + * indicates it is time to replace the disk). + * + * Note 2: + * When using the ATAPI BIOS, we also do not need to set up the timimg in linux + * driver. But it is necessary to write the timing routine in win system, + * cause it has a s1, s2, s3 mode and devices wake up from these modes need to + * be initialized again and do not pass through the BIOS. + * + * Note 3: + * The 48-bit support and AP for RAID in linux will the next job. + * + * Revision 1.31 2003/03/14 09:40:35 root + * Fix an error when no disc is on the CD-ROM and the audio cd is ready to play. + * + * 2003/04/08 root + * The ioctl code sklection is finished. But there is a problem about + * "Bad address" when copy_from_user() is called. + * + * Fixed: Use kmalloc() and kfree() to allocate the buffer instead of automatic + * variables (use stack). The stack size is limited in kernel space. + * + * Revision 1.32 2003/04/14 18:20:23 root + * Complete the IOCTLs code. + * + * The IOCTLs are listed below + * =========================== + * + * (1) ITE_IOC_GET_IDENTIFY_DATA + * + * Return virtual drive 512 bytes identification data. + * + * (2) ITE_IOC_GET_PHY_DISK_STATUS + * + * Developer can decide to return 4 physical disk information in + * 512 bytes (data structure should be defined) or 512 bytes + * identification data of the physical disk specified by AP. + * + * (3) ITE_IOC_CREATE_DISK_ARRAY + * + * Create a new array and erase (or keep) boot sector. + * + * (4) ITE_IOC_REBUILD_START + * + * AP nees to specify target/source drive, starting LBA and length. + * + * (5) ITE_IOC_GET_REBUILD_STATUS + * + * Return rebuild percentage or last LBA No. + * + * (6) ITE_IOC_RESET_ADAPTER + * + * Reset the controller. + * + * Revision 1.33 2003/04/15 11:10:08 root + * The 48-bit support. + * + * Revision 1.34 2003/04/20 13:20:38 root + * Change some values in iteraid.h, so it will not hang in Red Hat Linux + * and improve the performance. + * + * can_queue: 1 --------------------> can_queue: 124 + * sg_tablesize: SG_NONE -----------> sg_tablesize: 16 + * cmd_per_lun: 128 ----------------> cmd_per_lun: 1 + * use_clustering: ENABLE_CLUSTER --> use_clustering: DISABLE_CLUSTER + * + * 2003/04/25 root + * The code will hang on Gigabyte's motherboard when the sourth bridge is + * sis 962L and 963. + * + * Revision 1.35 2003/04/28 10:06:20 root + * Fixed: Do not enable interrupt again when send each command in + * IdeSendCommand() routine. + * + * 2003/05/20 root + * Linux SCSI error handling code should be changed to new one. + * + * The shortcomings of the existing code. + * + * 1. The old error handling code is an imperfect state machine. It + * would occasionally get stuck in loops whereby the bus would be reset + * over and over again, where the problem would never be resolved and + * control of the machine would never return to the user. + * + * Reference the http://www.andante.org/scsi.html + * + * The kernel after 2.5 or 2.6 will not use the old error handling codes. + * + * In iteraid.h + * + * #define ITERAID \ + * { \ + * proc_name: "it8212", \ + * proc_info: iteraid_proc_info, \ + * . \ + * . \ + * eh_about_handler: iteraid_about_eh, \ --> New added + * eh_device_reset_handler: NULL \ --> New added + * eh_bus_reset_handler: NULL \ --> New added + * eh_host_reset_handler: iteraid_reset_eh \ --> New added + * use_new_eh_code: 0 --> 1 \ + * } + * + * 2003/06/23 root 17:30:41 + * TODO: Error code still use the old method. + * + * Revision 1.36 2003/06/23 19:52:31 root + * Fixed: Use the new error handling code. + * + * Revision 1.40 2003/07/25 10:00:00 root + * Released version 1.40 by Mark Lu. + * + * Revision 1.41 2003/08/06 13:55:17 root + * Added support for clean shutdown notification/feature table. + * + * Revision 1.42 2003/08/21 11:38:57 root + * Problem: When linux was installed onto IT8212 controller with two disks, + * configured as RAID 1 (P0S0), the hot swap will hang the system. + * Solve: Use the AtapiResetController() instead of only IT8212ResetAdapter(). + * + * Revision 1.43 2003/12/24 23:19:07 root + * Fixed: Fixed a compile error at line 5815. Just move up the variable + * rebuild_info of type PRAID_REBUILD_INFO with other variables. + * + * Revision 1.44 2004/03/16 13:12:35 root + * Fixed: (1) The crash problem when using "rmmod" to remove the iteraid module. + * (2) Support two IT8212 cards or chips. + * (3) A bug when accessing the slave disk more than 137G. + * Thanks for Martine Purschke kindly help to find this bug and + * fix it. + * (4) can_queue: 12 --------------------> can_queue: 1 + * (5) Change the Transparent(Bypass) mode initial PCI registers setting. + * (6) Change IDE I/O, control and dma base address from USHORT to ULONG, + * so that the non x86 platform, like MIPS, will load the correct + * address. + * (7) Add GPL license in iteraid.h. + * + * Revision 1.45 2004/05/07 11:07:16 root + * Fixed : (1) 64-bit support. + * (2) In IT8212SetBestTransferMode() there are a number of arrays, + * all of which are defined read/write and assigned on the stack. + * Now put them in a R/O segment, by replacing e.g. "UCHAR + * udmaTiming" with "static const UCHAR udmaTiming". + */ + +#include + +MODULE_AUTHOR ("ITE Tech,Inc."); +MODULE_DESCRIPTION ("ITE IT8212 RAID Controller Linux Driver"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" + +#include "iteraid.h" + +MODULE_LICENSE("GPL"); + +#define MARK_DEBUG_DUMP_MEM 0 /* 1=Enable dump memory content */ +#define MARK_DEBUG_BYPASS_MODE 0 /* 1=Enable use bypass mode */ +#define MARK_DUMP_CREATE_INFO 0 /* 1=Dump raid create info */ +#define MARK_SET_BEST_TRANSFER 0 /* 0=BIOS set best trans mode */ + +#define PRD_BYTES 8 /* PRD table size */ +#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) + +struct Scsi_Host * ite_vhost = 0; /* SCSI virtual host */ +Scsi_Cmnd * it8212_req_last = 0; /* SRB request list */ + +unsigned int NumAdapters = 0; /* Adapters number */ +PITE_ADAPTER ite_adapters[2]; /* How many adapters support */ + +/************************************************************************ + * Notifier blockto get a notify on system shutdown/halt/reboot. + ************************************************************************/ +static int ite_halt(struct notifier_block *nb, ulong event, void *buf); +struct notifier_block ite_notifier = +{ + ite_halt, NULL, 0 +}; + +static struct semaphore mimd_entry_mtx; + +static spinlock_t queue_request_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; + +static int driver_ver = 145; /* Current driver version */ +static int ite_major = 0; /* itedev chardev major number */ + +/************************************************************************ + * The File Operations structure for the ioctl interface of the driver. + ************************************************************************/ +static struct file_operations itedev_fops = +{ + .owner = THIS_MODULE, + .ioctl = itedev_ioctl_entry, + .open = itedev_open, + .release = itedev_close +}; + +#if (MARK_DEBUG_DUMP_MEM) +/************************************************************************ + * Dump buffer. + ************************************************************************/ +static void +HexDump(unsigned char *buf, int length) +{ + + unsigned int i = 0; + unsigned int j = 0; + + printk("\n"); + for (i = 0; i < length; i += 16) + { + printk("%04X ", i); + for (j = i; (j < i + 8) && (j < length); j++) + { + printk(" %02X", buf[j]); + } + if (j == i + 8) printk("-"); + for (j = i + 8; (j < i + 16) && (j < length); j++) + { + printk("%02X ", buf[j]); + } + printk("\n"); + } + +} /* end HexDump */ +#endif + +/************************************************************************ + * This routine maps ATAPI and IDE errors to specific SRB statuses. + ************************************************************************/ +u8 +MapError(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u8 errorByte; + u8 srbStatus; + u8 scsiStatus; + + /* + * Read the error register. + */ + errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]); + printk("MapError: error register is %x\n", errorByte); + + /* + * If this is ATAPI error. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) + { + switch (errorByte >> 4) + { + case SCSI_SENSE_NO_SENSE: + printk("ATAPI: no sense information\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_RECOVERED_ERROR: + printk("ATAPI: recovered error\n"); + scsiStatus = 0; + srbStatus = SRB_STATUS_SUCCESS; + break; + + case SCSI_SENSE_NOT_READY: + printk("ATAPI: device not ready\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_MEDIUM_ERROR: + printk("ATAPI: media error\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_HARDWARE_ERROR: + printk("ATAPI: hardware error\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ILLEGAL_REQUEST: + printk("ATAPI: illegal request\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_UNIT_ATTENTION: + printk("ATAPI: unit attention\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_DATA_PROTECT: + printk("ATAPI: data protect\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_BLANK_CHECK: + printk("ATAPI: blank check\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + case SCSI_SENSE_ABORTED_COMMAND: + printk("ATAPI: command Aborted\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + + default: + printk("ATAPI: invalid sense information\n"); + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + break; + } + } + /* + * If this is IDE error. + */ + else + { + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + + /* + * Save errorByte, to be used by SCSIOP_REQUEST_SENSE. + */ + pChan->ReturningMediaStatus = errorByte; + + if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) + { + printk("IDE: media change\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + } + else if (errorByte & IDE_ERROR_COMMAND_ABORTED) + { + printk("IDE: command abort\n"); + srbStatus = SRB_STATUS_ABORTED; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) + { + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + /* + * pChan->ErrorCount++; + */ + } + else if (errorByte & IDE_ERROR_END_OF_MEDIA) + { + printk("IDE: end of media\n"); + + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED)) + { + /* + * pChan->ErrorCount++; + */ + } + } + else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) + { + printk("IDE: illegal length\n"); + srbStatus = SRB_STATUS_INVALID_REQUEST; + } + else if (errorByte & IDE_ERROR_BAD_BLOCK) + { + printk("IDE: bad block\n"); + + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) + { + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + else if (errorByte & IDE_ERROR_ID_NOT_FOUND) + { + printk("IDE: id not found\n"); + + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + + if (Srb->SenseInfoBuffer) + { + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + /* + * pChan->ErrorCount++; + */ + } + else if (errorByte & IDE_ERROR_MEDIA_CHANGE) + { + printk("IDE: media change\n"); + + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (Srb->SenseInfoBuffer) + { + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xD; + senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + else if (errorByte & IDE_ERROR_DATA_ERROR) + { + printk("IDE: data error\n"); + + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + + if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED)) + { + /* + * pChan->ErrorCount++; + */ + } + + /* + * Build sense buffer. + */ + if (Srb->SenseInfoBuffer) + { + PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; + + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + } + + /* + * Set SCSI status to indicate a check condition. + */ + Srb->ScsiStatus = scsiStatus; + + return srbStatus; + +} /* end MapError */ + +/************************************************************************ + * Just get the higest bit value. + ************************************************************************/ +static u8 +RaidGetHighestBit(u8 Number) +{ + + char bit; + + for (bit = 7; bit >= 0; bit--) + { + if (Number & (1 << bit)) return bit; + } + + return 0xFF; + +} /* end RaidGetHighestBit */ + +/************************************************************************ + * Reset IDE controller or ATAPI device. + ************************************************************************/ +static void +AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan) +{ + + u8 resetResult; + u8 status; + u8 i; + unsigned long dma_base; + SCSI_REQUEST_BLOCK srb; + printk("AtapiResetController enter\n"); + + dma_base = pChan->dma_base; + resetResult = FALSE; + + /* + * Check and see if we are processing an internal srb. + */ + if (pChan->OriginalSrb) + { + pChan->CurrentSrb = pChan->OriginalSrb; + pChan->OriginalSrb = NULL; + } + + /* + * To avoid unexpected interrupts occurs during reset procedure. + * + * 1. Stop bus master operation. + */ + outb(0, dma_base); + + for (i = 0; i < 2; i++) + { + outb((u8)((i << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]); + + /* + * 2. Clear interrupts if there is any. + */ + GetBaseStatus(pChan, status); + + /* + * 3. Disable interrupts. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[ATAPI_CONTROL_OFFSET]); + + /* + * 4. Clear interrupts again. + */ + GetBaseStatus(pChan, status); + } + + /* + * Check if request is in progress. + */ + if (pChan->CurrentSrb) + { + /* + * Complete outstanding request with SRB_STATUS_BUS_RESET. + */ + srb.SrbStatus = SRB_STATUS_BUS_RESET; + + /* + * Clear request tracking fields. + */ + pChan->CurrentSrb = NULL; + pChan->WordsLeft = 0; + pChan->DataBuffer = NULL; + + /* + * Indicate ready for next request. + */ + TaskDone(pChan, &srb); + } + + /* + * Clear expecting interrupt flag. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->RDP = FALSE; + + resetResult = IT8212ResetAdapter(pAdap); + + /* + * Set transfer modes after resetting the adapter. + */ + + /* + * Reenable interrupts. + */ + for (i = 0; i < 4; i++) + { + outb((u8)(((i & 1) << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]); + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[ATAPI_CONTROL_OFFSET]); + } + + printk("AtapiResetController exit\n"); + +} /* end AtapiResetController */ + +/************************************************************************ + * IDE start read/write transfer. + ************************************************************************/ +void +IdeStartTransfer +( + PChannel pChan, + PSCSI_REQUEST_BLOCK Srb, + u32 startingSector, + u32 SectorNumber +) +{ + + u8 DiskId; + u8 drvSelect; + u8 bmClearStat; + unsigned long dma_base; + dprintk("IdeStartTransfer enter\n"); + + DiskId = (u8) Srb->TargetId; + dma_base = pChan->dma_base; + + /* + * 48-bit support. + */ + if ((startingSector + SectorNumber) > 0x0FFFFFFF) + { + /* + * Select drive and set LBA mode. + */ + outb((u8) (((DiskId & 0x1) << 4) | 0xA0 | 0x40), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Sector count register. + */ + outb((u8) (SectorNumber >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); + + /* + * LBA low register. + */ + outb((u8) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + + /* + * LBA mid register. + */ + outb((u8) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); + + /* + * LBA high register. + */ + outb((u8) 0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb((u8) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Start the IDE read/write DMA command. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + outb(IDE_COMMAND_READ_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + { + outb(IDE_COMMAND_WRITE_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + } + /* + * 28-bit addressing. + */ + else + { + /* + * Select drive and set LBA mode. + */ + drvSelect = (u8) (startingSector >> 24); + drvSelect = drvSelect | (((u8) DiskId & 0x1) << 4) | 0x40 | 0xA0; + + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + outb((u8) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8)(startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8)(startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Start the IDE read/write DMA command. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + outb(IDE_COMMAND_READ_DMA, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + { + outb(IDE_COMMAND_WRITE_DMA, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Setup PRD table physical address. + */ + outl(pChan->dmatable_dma, dma_base + 4); + + /* + * Read Bus Master status. + */ + bmClearStat = inb(dma_base + 2); + if (Srb->TargetId & 1) + { + bmClearStat = bmClearStat | BM_DRV1_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } + else + { + bmClearStat = bmClearStat | BM_DRV0_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } + + outb(0, dma_base); + + /* + * Clear INTR and ERROR flags. + */ + outb(bmClearStat, dma_base + 2); + + /* + * Start DMA read/write. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, dma_base); + } + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + { + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, dma_base); + } + + dprintk("IdeStartTransfer exit\n"); + +} /* end IdeStartTransfer */ + +/************************************************************************ + * Setup the PRD table. + ************************************************************************/ +static int +IdeBuildSglist(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + int nents = 0; + u32 bytesRemaining = Srb->DataTransferLength; + unsigned char * virt_addr = Srb->DataBuffer; + struct scatterlist * sg = pChan->sg_table; + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + pChan->sg_dma_direction = PCI_DMA_FROMDEVICE; + } + else + { + pChan->sg_dma_direction = PCI_DMA_TODEVICE; + } + + /* + * The upper layer will never give the memory more than 64K bytes. + */ + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].dma_address = (dma_addr_t)virt_addr; + sg[nents].length = bytesRemaining; + nents++; + + return pci_map_sg(pChan->pPciDev, sg, nents, pChan->sg_dma_direction); + +} /* end IdeBuildSglist */ + +/************************************************************************ + * Prepares a dma request. + ************************************************************************/ +static int +IdeBuildDmaTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + unsigned long * table = pChan->dmatable_cpu; + unsigned int count = 0; + int i; + struct scatterlist * sg; + + i = IdeBuildSglist(pChan, Srb); + + sg = pChan->sg_table; + while (i && sg_dma_len(sg)) + { + u32 cur_len; + u32 cur_addr; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the dma table, without crossing any 64kB boundaries. + */ + while (cur_len) + { + if (count++ >= PRD_ENTRIES) + { + printk(KERN_WARNING "@@DMA table too small\n"); + } + else + { + u32 xcount, bcount = 0x10000 - (cur_addr & 0xFFFF); + + if (bcount > cur_len) bcount = cur_len; + *table++ = cpu_to_le32(cur_addr); + xcount = bcount & 0xFFFF; + if (xcount == 0x0000) + { + /* + * Most chipsets correctly interpret a length + * of 0x0000 as 64KB, but at least one + * (e.g. CS5530) misinterprets it as zero (!). + * So here we break the 64KB entry into two + * 32KB entries instead. + */ + if (count++ >= PRD_ENTRIES) + printk(KERN_WARNING "##DMA table too small\n"); + + *table++ = cpu_to_le32(0x8000); + *table++ = cpu_to_le32(cur_addr + 0x8000); + xcount = 0x8000; + } + *table++ = cpu_to_le32(xcount); + cur_addr += bcount; + cur_len -= bcount; + } + } + sg++; + i--; + } + + if (count) + { + *--table |= cpu_to_le32(0x80000000); + return count; + } + else + { + printk(KERN_WARNING "Empty DMA table?\n"); + } + + return count; + +} /* end IdeBuildDmaTable */ + +/************************************************************************ + * Prepares a dma scatter/gather request. + ************************************************************************/ +static void +IdeBuildDmaSgTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + int use_sg = 0; + int i; + PPRD_TABLE_ENTRY pSG = (PPRD_TABLE_ENTRY)pChan->dmatable_cpu; + struct scatterlist * sg = (struct scatterlist *)Srb->DataBuffer; + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + pChan->sg_dma_direction = PCI_DMA_FROMDEVICE; + } + else + { + pChan->sg_dma_direction = PCI_DMA_TODEVICE; + } + + use_sg = pci_map_sg(pChan->pPciDev, Srb->DataBuffer, Srb->UseSg, pChan->sg_dma_direction); + + for (i = 0; i < use_sg; i++) + { + pSG[i].PhysicalBaseAddress = sg_dma_address(&sg[i]); + pSG[i].ByteCount = sg_dma_len(&sg[i]); + pSG[i].EndOfTable = (i == use_sg - 1) ? SG_FLAG_EOT : 0; + } + +} /* end IdeBuildDmaSgTable */ + +/************************************************************************ + * Setup DMA table for channel. + ************************************************************************/ +static void +IdeSetupDma(PChannel pChan, unsigned long dma_base, unsigned short num_ports) +{ + + printk("Channel[%d] BM-DMA at 0x%lX-0x%lX\n", pChan->channel, dma_base, + dma_base + num_ports - 1); + + /* + * Allocate IDE DMA buffer. + */ + pChan->dmatable_cpu = pci_alloc_consistent(pChan->pPciDev, + PRD_ENTRIES * PRD_BYTES, &pChan->dmatable_dma); + + if (pChan->dmatable_cpu == NULL) + { + printk("IdeSetupDma: allocate prd table failed.\n"); + return; + } + + memset(pChan->dmatable_cpu, 0, PRD_ENTRIES * PRD_BYTES); + + /* + * Allocate SCATTER/GATHER table buffer. + */ + pChan->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, + GFP_KERNEL); + + if (pChan->sg_table == NULL) + { + printk("IdeSetupDma: allocate sg_table failed.\n"); + pci_free_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES, + pChan->dmatable_cpu, pChan->dmatable_dma); + return; + } + + return; + +} /* end IdeSetupDma */ + +/************************************************************************ + * This will be only used in RAID mode. + ************************************************************************/ +void +IT8212ReconfigChannel(PChannel pChan, u8 ArrayId, u8 Operation) +{ + + u8 enableVirtualChannel; + struct pci_dev * pPciDev = pChan->pPciDev; + + pci_read_config_byte(pPciDev, 0x43, &enableVirtualChannel); + + if (Operation == DisableChannel) + { + enableVirtualChannel &= ~(1 << ArrayId); + printk("IT8212ReconfigChannel: disable channel %X\n", ArrayId); + } + else + { + enableVirtualChannel |= ~(1 << ArrayId); + printk("IT8212ReconfigChannel: enable channel %X\n", ArrayId); + } + + printk("IT8212ReconfigChannel: channel enabled after set 0x%X\n", + enableVirtualChannel); + + /* + * Set enabled virtual channels. + */ + pci_write_config_byte(pPciDev, 0x43, enableVirtualChannel); + +} /* end IT8212ReconfigChannel */ + +/************************************************************************ + * Get the chip status. This is a vendor specific command. According to + * all of the device configurations, the BIOS then can consider the + * existing RAID configuration reasonable. If the existing RAID configur- + * ation is not reasonable, or if there is NO existing RAID configuration + * , the BIOS can ask the user to setup the RAID configuration. Finally, + * the BIOS or AP should send the SET CHIP STATUS to every virtual device. + * Only after receiving SET CHIP STATUS command, the corresponding virtual + * device will be active. + ************************************************************************/ +u8 +IT8212GetChipStatus(uioctl_t *ioc) +{ + + u8 PriMasterIsNull = FALSE; + u8 statusByte; + u8 srbStatus; + PChannel pChan; + PITE_ADAPTER pAdap; + PHYSICAL_DISK_STATUS * pPhyDiskInfo; + dprintk("IT8212GetChipStatus enter\n"); + + /* + * Only support one controller now! In the future, we can pass the + * argument (user ioctl structure) to know which controller need to be + * identified. + */ + pAdap = ite_adapters[0]; + pChan = &pAdap->IDEChannel[0]; + + /* + * Allocate space for PHYSICAL_DISK_STATUS. + */ + if ((pPhyDiskInfo = kmalloc(sizeof(PHYSICAL_DISK_STATUS) * 4, GFP_KERNEL)) == NULL) + { + printk("IT8212GetChipStatus: error kmalloc for PHYSCIAL_DISK_STATUS.\n"); + return -ENOMEM; + } + + memset(pPhyDiskInfo, 0, sizeof(PHYSICAL_DISK_STATUS)); + + /* + * Always send GET CHIP STATUS command to primary channel master device. + * Select device. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * If primary master channel is not enabled, enable it. + */ + statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]); + if (statusByte == 0) + { + PriMasterIsNull = TRUE; + IT8212ReconfigChannel(pChan, 0, EnableChannel); + } + + /* + * Wait for device ready (Not BUSY and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212GetChipStatus: disk[0] not ready. status=0x%X\n", + statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue the command. + */ + outb(IDE_COMMAND_GET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0, DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte) + if (statusByte != 0x58) + { + printk("IT8212GetChipStatus: disk[0] return unexpected status after"); + printk("issue command. status=0x%X\n", statusByte); + goto exit_error; + } + + /* + * Read the physical disk info. + */ + ReadBuffer(pChan, (unsigned short *)pPhyDiskInfo, 256); + +#if (0) + HexDump((unsigned char *)pPhyDiskInfo, 512); +#endif + + /* + * Copy physical disk info to user area. + */ + copy_to_user((unsigned short *)ioc->data, (unsigned char *)pPhyDiskInfo, 512); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + + if (statusByte != IDE_STATUS_IDLE) + { + printk("IT8212GetChipStatus: disk[0] return unexpected status after"); + printk("read data. status=0x%X\n", statusByte); + goto exit_error; + } + + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + +exit_error: + + /* + * If fail, hard reset to avoid the DRQ status pending. + */ + srbStatus = SRB_STATUS_ERROR; + IdeHardReset(pChan, statusByte); + +exit: + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * If primary master is null, disable primary master channel before we leave. + */ + if (PriMasterIsNull) + { + IT8212ReconfigChannel(pChan, 0, DisableChannel); + } + + + dprintk("IT8212GetChipStatus exit\n"); + + return srbStatus; + +} /* end IT8212GetChipStatus */ + +/************************************************************************ + * Erase the partition table. + ************************************************************************/ +unsigned char +IT8212ErasePartition(uioctl_t *pioc) +{ + + unsigned char drvSelect; + unsigned char statusByte = 0; + unsigned char srbStatus; + unsigned char * buffer; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) pioc->data; + PITE_ADAPTER pAdap; + PChannel pChan; + printk("IT8212ErasePartition enter\n"); + + printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId); + + if (createInfo->ErasePartition == 0 || (createInfo->RaidType == RAID_LEVEL_NODISK)) + return SRB_STATUS_SUCCESS; + + pAdap = ite_adapters[0]; + + if (createInfo->DiskArrayId < 2) + { + pChan = &pAdap->IDEChannel[0]; + } + else + { + pChan = &pAdap->IDEChannel[1]; + } + + /* + * Allocate 512 bytes for buffer. + */ + if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) + { + printk("IT8212ErasePartition: error kmalloc.\n"); + return -ENOMEM; + } + + memset(buffer, 0, 512); + + /* + * Select device. + */ + drvSelect = (((u8) createInfo->DiskArrayId & 0x1) << 4) | 0xA0 | 0x40; + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212ErasePartition: disk[%d] not ready. status=0x%X\n", + createInfo->DiskArrayId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Write LBA 0 (1 sector). + */ + outb(1, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb(0, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + outb(IDE_COMMAND_WRITE_SECTOR, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0, DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) + { + printk("IT8212ErasePartition: disk[%d] error status. status=0x%X\n", + createInfo->DiskArrayId, statusByte); + goto exit_error; + } + + /* + * Start erase partition table. + */ + WriteBuffer(pChan, (unsigned short *)buffer, 256); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + + if (statusByte != IDE_STATUS_IDLE) + { + printk("IT8212ErasePartition: disk[%d] error status. status=0x%X\n", + createInfo->DiskArrayId, statusByte); + goto exit_error; + } + + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + +exit_error: + + /* + * If failed, hard reset to avoid the DRQ status pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; + +exit: + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + printk("IT8212ErasePartition exit\n"); + return srbStatus; + +} /* end IT8212ErasePartition */ + +/************************************************************************ + * + ************************************************************************/ +u32 +IT8212TruncateReduentSectors +( + u32 OriginalSectorCount, + u16 StripeSizeInKBytes +) +{ + + u16 stripeSizeInSector; + + /* + * 0 means using default value (32 sectors). + */ + if (StripeSizeInKBytes == 0) + { + stripeSizeInSector = 64 * 2; + } + else + { + stripeSizeInSector = StripeSizeInKBytes * 2; + } + + return ((OriginalSectorCount / stripeSizeInSector) * stripeSizeInSector); + + +} /* end IT8212TruncateReduentSectors */ + +/************************************************************************ + * Calculate the addressable sector for this RAID. + ************************************************************************/ +u32 +IT8212DiskArrayAddressableSector(unsigned char * DiskArrayCreateInfo) +{ + + u8 DiskNo; + u8 NumOfDisks; + u32 MinDiskCapacity; + u32 ArrayCapacity; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) DiskArrayCreateInfo; + + MinDiskCapacity = ArrayCapacity = NumOfDisks = 0; + + printk("createInfo->AddressableSectors[0] = 0x%X\n", createInfo->AddressableSectors[0]); + printk("createInfo->AddressableSectors[1] = 0x%X\n", createInfo->AddressableSectors[1]); + printk("createInfo->AddressableSectors[2] = 0x%X\n", createInfo->AddressableSectors[2]); + printk("createInfo->AddressableSectors[3] = 0x%X\n", createInfo->AddressableSectors[3]); + + for (DiskNo = 0; DiskNo < 4; DiskNo++) + { + /* + * If disk exist. + */ + if ((createInfo->ContainingDisks >> DiskNo) & 0x1) + { + NumOfDisks += 1; + if (!MinDiskCapacity || (createInfo->AddressableSectors[DiskNo] < + MinDiskCapacity)) + { + MinDiskCapacity = createInfo->AddressableSectors[DiskNo]; + } + } + } + + switch (createInfo->RaidType) + { + /* + * Containing 2 or 3 or 4 disks. + */ + case RAID_LEVEL_0: + MinDiskCapacity = IT8212TruncateReduentSectors(MinDiskCapacity - 2, + createInfo->StripeSize); + ArrayCapacity = MinDiskCapacity * NumOfDisks; + break; + + /* + * Containing 2 disks. + */ + case RAID_LEVEL_1: + ArrayCapacity = MinDiskCapacity - 2; + break; + + /* + * Containing 4 disks. + */ + case RAID_LEVEL_10: + MinDiskCapacity = IT8212TruncateReduentSectors(MinDiskCapacity - 2, + createInfo->StripeSize); + ArrayCapacity = MinDiskCapacity * 2; + break; + + /* + * Containing 2, 3, or 4 disks. + */ + case RAID_LEVEL_JBOD: + for (DiskNo = 0; DiskNo < 4; DiskNo++) + { + if ((createInfo->ContainingDisks >> DiskNo) & 0x1) + { + ArrayCapacity = ArrayCapacity + (createInfo->AddressableSectors[DiskNo] - 2); + } + } + break; + + /* + * Containing only 1 disk. + */ + case RAID_LEVEL_NORMAL: + ArrayCapacity = MinDiskCapacity; + break; + + } + + return ArrayCapacity; + +} /* end IT8212DiskArrayAddressableSector */ + +/************************************************************************ + * Create a new array. + ************************************************************************/ +u8 +IT8212CreateDiskArray(uioctl_t *pioc) +{ + + u8 i; + u8 subCommand = 0xFF; + u8 statusByte; + u8 dmaSupported; + u8 udmaSupported; + u8 srbStatus; + u8 PriMasterIsNull = FALSE; + u32 UserAddressableSectors; + void * buffer; + PChannel pChan; + PITE_ADAPTER pAdap; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO)pioc->data; + PIDENTIFY_DATA2 identifyData; + PIT8212_SET_CHIP_STATUS_INFO setChipStatus; + + static const u16 IT8212TimingTable[7] = + { + 0x3133, /* UDMA timimg register 01 */ + 0x2121, /* UDMA timimg register 23 */ + 0x9111, /* UDMA timimg register 45 */ + 0x0091, /* UDMA timimg register 6 */ + 0x3266, /* DMA timimg register 01 */ + 0x0021, /* DMA timimg register 2 */ + 0x0021 /* PIO timimg register */ + }; + + static const u16 IT8212ClockTable[7] = + { + 0x0505, /* UDMA clock register 01 */ + 0x0005, /* UDMA clock register 23 */ + 0x0500, /* UDMA clock register 45 */ + 0x0000, /* UDMA clock register 6 */ + 0x0005, /* DMA clock register 01 */ + 0x0005, /* DMA clock register 2 */ + 0x0005 /* PIO clock register */ + }; + + printk("IT8212CreateDiskArray enter\n"); + +#if (MARK_DUMP_CREATE_INFO) + printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId); + printk("createInfo->ModelNumber = %s\n", createInfo->ModelNumber); + printk("createInfo->RaidType = %d\n", createInfo->RaidType); + printk("createInfo->ContainingDisks = %d\n", createInfo->ContainingDisks); + printk("createInfo->AutoRebuildEnable = %d\n", createInfo->AutoRebuildEnable); + printk("createInfo->StripeSize = %d\n", createInfo->StripeSize); + printk("createInfo->BootableDisk = %d\n", createInfo->BootableDisk); + printk("createInfo->NewlyCreated = %d\n", createInfo->NewlyCreated); + printk("createInfo->ErasePartition = %d\n", createInfo->ErasePartition); + printk("createInfo->DMASupported[0] = 0x%x\n", createInfo->DMASupported[0]); + printk("createInfo->DMASupported[1] = 0x%x\n", createInfo->DMASupported[1]); + printk("createInfo->DMASupported[2] = 0x%x\n", createInfo->DMASupported[2]); + printk("createInfo->DMASupported[3] = 0x%x\n", createInfo->DMASupported[3]); + printk("createInfo->UDMASupported[0] = 0x%x\n", createInfo->UDMASupported[0]); + printk("createInfo->UDMASupported[1] = 0x%x\n", createInfo->UDMASupported[1]); + printk("createInfo->UDMASupported[2] = 0x%x\n", createInfo->UDMASupported[2]); + printk("createInfo->UDMASupported[3] = 0x%x\n", createInfo->UDMASupported[3]); + printk("createInfo->AddressableSectors[0] = 0x%lX\n", createInfo->AddressableSectors[0]); + printk("createInfo->AddressableSectors[1] = 0x%lX\n", createInfo->AddressableSectors[1]); + printk("createInfo->AddressableSectors[2] = 0x%lX\n", createInfo->AddressableSectors[2]); + printk("createInfo->AddressableSectors[3] = 0x%lX\n", createInfo->AddressableSectors[3]); +#endif + + switch (createInfo->RaidType) + { + case RAID_LEVEL_0: + case RAID_LEVEL_1: + case RAID_LEVEL_10: + case RAID_LEVEL_JBOD: + case RAID_LEVEL_NORMAL: + subCommand = 0x50; + break; + + case RAID_LEVEL_NODISK: + subCommand = 0x48; + break; + } + + /* + * The command should be sent to virtual primary master. + */ + pAdap = ite_adapters[0]; + pChan = &pAdap->IDEChannel[0]; + + /* + * Allocate 512-bytes buffer. + */ + if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) + { + printk("IT8212CreateDiskArray: error kmalloc.\n"); + return -ENOMEM; + } + + identifyData = (PIDENTIFY_DATA2) buffer; + + /* + * 2003/05/08 + * Remember the vendor specific parameters starts from word 129 not 128. + */ + setChipStatus = (PIT8212_SET_CHIP_STATUS_INFO) (buffer + 258); + + /* + * Configure to RAID or NORMAL. + */ + if (subCommand == 0x50) + { + /* + * Zero identify data structure. + */ + memset((unsigned char *) identifyData, 0, sizeof(IDENTIFY_DATA)); + + /* + * Fill up identify data. + */ + memmove(identifyData->ModelNumber, createInfo->ModelNumber, 40); + memmove(identifyData->SerialNumber, &createInfo->SerialNumber, sizeof(RAID_SERIAL_NUMBER)); + + /* + * Set disk array virtual capacity. + */ + UserAddressableSectors = IT8212DiskArrayAddressableSector(pioc->data); + + printk("IT8212CreateDiskArray: array capacity = %X\n", UserAddressableSectors); + + identifyData->Capacity_48bit_LOW = UserAddressableSectors; + identifyData->Capacity_48bit_HIGH = 0; + + if (UserAddressableSectors > 0x0FFFFFFF) + { + identifyData->UserAddressableSectors = 0x0FFFFFFF; + } + else + { + identifyData->UserAddressableSectors = UserAddressableSectors; + } + + /* + * Get DMA supported mode and UDMA supported mode. + */ + dmaSupported = udmaSupported = 0xFF; + + for (i = 0; i < 4; i++) + { + if ((createInfo->ContainingDisks >> i) & 1) + { + dmaSupported &= (u8) createInfo->DMASupported[i]; + udmaSupported &= (u8) createInfo->UDMASupported[i]; + } + } + + identifyData->MultiWordDMASupport = dmaSupported; + identifyData->UltraDMASupport = udmaSupported; + + /* + * Fill up SET CHIP STATUS data (word 129 - 153) + */ + setChipStatus->RaidType = createInfo->RaidType; + setChipStatus->ContainingDisks = createInfo->ContainingDisks; + setChipStatus->UltraDmaTiming01 = IT8212TimingTable[0]; + setChipStatus->UltraDmaTiming23 = IT8212TimingTable[1]; + setChipStatus->UltraDmaTiming45 = IT8212TimingTable[2]; + setChipStatus->UltraDmaTiming6 = IT8212TimingTable[3]; + setChipStatus->MultiWordDmaTiming01 = IT8212TimingTable[4]; + setChipStatus->UltraDmaTiming2 = IT8212TimingTable[5]; + setChipStatus->PioTiming4 = IT8212TimingTable[6]; + setChipStatus->AutoRebuildEnable = createInfo->AutoRebuildEnable; + setChipStatus->IdeClkUDma01 = IT8212ClockTable[0]; + setChipStatus->IdeClkUDma23 = IT8212ClockTable[1]; + setChipStatus->IdeClkUDma45 = IT8212ClockTable[2]; + setChipStatus->IdeClkUDma6 = IT8212ClockTable[3]; + setChipStatus->IdeClkMDma01 = IT8212ClockTable[4]; + setChipStatus->IdeClkMDma2 = IT8212ClockTable[5]; + setChipStatus->IdeClkPio4 = IT8212ClockTable[6]; + setChipStatus->StripeSize = createInfo->StripeSize; + setChipStatus->BootableDisk = createInfo->BootableDisk; + setChipStatus->CheckHotSwapInterval = 0; + setChipStatus->TargetSourceDisk = createInfo->TargetSourceDisk; + setChipStatus->RebuildBlockSize = 0; + setChipStatus->ResetInterval1 = 0; + setChipStatus->ResetInterval2 = 0; + setChipStatus->RebuildRetryTimes = 0; + setChipStatus->NewlyCreated = createInfo->NewlyCreated; + } + +#if (MARK_DEBUG_DUMP_MEM) + HexDump(buffer, 512); +#endif + + /* + * There are some contrains of disk placement. AP will take care of it. + */ + + /* + * Select device. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * If primary master channel is not enabled, enable it. + */ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + if (statusByte == 0) + { + PriMasterIsNull = TRUE; + IT8212ReconfigChannel(pChan, 0, EnableChannel); + } + + /* + * Wait for device ready (not BUSY and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212CreateDiskArray: disk[0] not ready. status=0x%X\n", + statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + outb(subCommand, pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(createInfo->DiskArrayId, pChan->io_ports[IDE_SELECT_OFFSET]); + outb(IDE_COMMAND_SET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * No disk (no data command protocol) + */ + if (subCommand == 0x48) + { + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) + { + printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); + goto exit_error; + } + + IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, DisableChannel); + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + } + + /* + * Create RAID (PIO data out command protocol). + */ + if (subCommand == 0x50) + { + /* + * Wait for BUSY=0, DRQ=1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) + { + printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); + goto exit_error; + } + + WriteBuffer(pChan, buffer, 256); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + + if (statusByte != IDE_STATUS_IDLE) + { + printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); + goto exit_error; + } + + IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, EnableChannel); + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + } + +exit_error: + + /* + * If fail, hard reset to avoid the DRQ pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; + +exit: + + /* + * If primary master is null, and we are not configuring array 0. Disable + * primary master channel again. + */ + if (PriMasterIsNull && createInfo->DiskArrayId) + { + IT8212ReconfigChannel(pChan, 0 , DisableChannel); + } + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + printk("IT8212CreateDiskArray exit\n"); + + return srbStatus; + + +} /* end IT8212CreateDiskArray */ + +/************************************************************************ + * Return "virtual" drive 512 bytes identification data. + ************************************************************************/ +u8 +IT8212IssueIdentify(uioctl_t *pioc) +{ + + u8 channum; + u8 devnum; + u8 statusByte; + u8 srbStatus; + PITE_ADAPTER pAdap; + PChannel pChan; + + /* + * Only support one adapter now! In the future, we can pass the argument + * to know which adapter need to be identified. + */ + pAdap = ite_adapters[0]; + + memset(pioc->data, 0, 512 * 4); + + /* + * Two channels per controller. + */ + for (channum = 0; channum < pAdap->num_channels; channum++) + { + pChan = &pAdap->IDEChannel[channum]; + + /* + * Two devices per channel. + */ + for (devnum = 0; devnum < 2; devnum++) + { + /* + * Select device. + */ + outb((u8)((devnum << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check if disk online? + */ + statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]); + if ((statusByte & 0x40) != 0x40) + { + printk("IT8212IssueIdentify: disk[%d] is offline\n", + devnum + channum * 2); + continue; + } + + /* + * Wait for device ready (Not busy and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212IssueIdentify: disk[%d] not ready. status=0x%X\n", + devnum + channum * 2, statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue command. + */ + outb(IDE_COMMAND_IDENTIFY, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0 and DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) + { + printk("IT8212IssueIndetify: disk[%d] returns unexpedted status after issue command. status=0x%X\n", + devnum + channum * 2, statusByte); + goto error; + } + + /* + * Read the identify data. + */ + ReadBuffer(pChan, (unsigned short *)&pChan->FullIdentifyData, 256); + + /* + * Then copy to user area. + * unsigned long copy_to_user(void *to, const void *from, unsigned long count). + */ + copy_to_user((unsigned short *) (pioc->data + ((devnum + channum * 2) * 512)), + (unsigned short *)&pChan->FullIdentifyData, 256); + + /* + * Check error after reading data. + */ + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) + { + printk("IT8212IssueIdentify: disk[%d] returns unexpected status after read data. status=0x%X\n", + devnum + channum * 2, statusByte); + goto error; + } + + } /* end for each device */ + + } /* end for each channel */ + + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + +error: + + /* + * If failed, hard reset to avoid the IRQ pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; + +exit: + + /* + * Reenable interrupt after command complete. + */ + for (channum = 0; channum < pAdap->num_channels; channum++) + { + pChan = &pAdap->IDEChannel[channum]; + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + } + + return srbStatus; + +} /* end IT8212IssueIdentify */ + +/************************************************************************ + * Reset the controller. + ************************************************************************/ +static u8 +IT8212ResetAdapter(PITE_ADAPTER pAdap) +{ + + u8 resetChannel[2]; + u8 channel; + u8 device; + u8 status[4]; + int i; + PChannel pChan; + + /* + * First, perform ATAPI soft reset if ATAPI devices are attached. + */ + for (channel = 0; channel < 2; channel++) + { + pChan = &pAdap->IDEChannel[channel]; + resetChannel[channel] = FALSE; + for (device = 0; device < 2; device++) + { + if (pChan->DeviceFlags[device] & DFLAGS_DEVICE_PRESENT) + { + if (pChan->DeviceFlags[device] & DFLAGS_ATAPI_DEVICE) + { + printk("IT8212ResetAdapter: perform ATAPI soft reset (%d, %d)\n", + channel, device); + AtapiSoftReset(pChan, device); + } + else + { + resetChannel[channel] = TRUE; + } + } + } + } + + /* + * If ATA device is present on this channel, perform channel reset. + */ + for (channel = 0; channel < 2; channel++) + { + pChan = &pAdap->IDEChannel[channel]; + if (resetChannel[channel]) + { + printk("IT8212ResetAdapter: reset channel %d\n", channel); + outb(IDE_DC_RESET_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + mdelay(50); + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + } + } + + /* + * Check device status after reset. + */ + for (i = 0; i < 1000 * 1000; i++) + { + for (channel = 0; channel < 2; channel++) + { + pChan = &pAdap->IDEChannel[channel]; + for (device = 0; device < 2; device++) + { + if (pChan->DeviceFlags[device] & DFLAGS_DEVICE_PRESENT) + { + outb((u8)((device << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + status[(channel * 2) + device] = inb(pChan->io_ports[IDE_COMMAND_OFFSET]); + } + else + { + status[(channel * 2) + device] = 0; + } + } + } + + /* + * ATA device should present status 0x50 after reset. + * ATAPI device should present status 0 after reset. + */ + if ((status[0] != IDE_STATUS_IDLE && status[0] != 0x0) || + (status[1] != IDE_STATUS_IDLE && status[1] != 0x0) || + (status[2] != IDE_STATUS_IDLE && status[2] != 0x0) || + (status[3] != IDE_STATUS_IDLE && status[3] != 0x0)) + { + udelay(30); + } + else + { + break; + } + } + + if (i == 1000 * 1000) + { + printk("IT8212ResetAdapter Fail!\n"); + printk("Device status after reset = [0x%x, 0x%x, 0x%x, 0x%x]\n", + status[0], status[1], status[2], status[3]); + return FALSE; + } + else + { + printk("IT8212ResetAdapter Success!\n"); + return TRUE; + } + +} /* end IT8212ResetAdapter */ + +/************************************************************************ + * Rebuild disk array. + ************************************************************************/ +u8 +IT8212Rebuild(uioctl_t *pioc) +{ + + u8 rebuildDirection; + u8 statusByte = 0; + PRAID_REBUILD_INFO apRebuildInfo = (PRAID_REBUILD_INFO) pioc->data; + PITE_ADAPTER pAdap; + PChannel pChan; + dprintk("IT8212Rebuild enter\n"); + + rebuildDirection = (apRebuildInfo->Resume << 4) | (apRebuildInfo->DestDisk << 2) | + apRebuildInfo->SrcDisk; + + apRebuildInfo->Status = 0xFF; + + pAdap = ite_adapters[0]; + + printk("IT8212Rebuild: diskArrayId=%d\n", apRebuildInfo->DiskArrayId); + + if (apRebuildInfo->DiskArrayId < 2) + { + pChan = &pAdap->IDEChannel[0]; + } + else + { + pChan = &pAdap->IDEChannel[1]; + } + + /* + * Selcet device. + */ + outb((u8)((apRebuildInfo->DiskArrayId & 0x1) << 4 | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + apRebuildInfo->Status = REBUILD_ERR_DISK_BUSY; + printk("IT8212Rebuild: disk[%d] not ready. status=0x%X\n", + apRebuildInfo->DiskArrayId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Give a direction. + */ + outb(rebuildDirection, pChan->io_ports[IDE_FEATURE_OFFSET]); + + /* + * Issue a REBUILD commmand. + */ + outb(IDE_COMMAND_REBUILD, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check for errors. + */ + WaitForCommandComplete(pChan, statusByte); + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + if (statusByte != IDE_STATUS_IDLE) + { + if (statusByte & IDE_STATUS_ERROR) + { + apRebuildInfo->Status = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); + printk("IT8212Rebuild: rebuild error. reason=0x%X\n", apRebuildInfo->Status); + } + return apRebuildInfo->Status; + } + + dprintk("IT8212Rebuild exit\n"); + + return SRB_STATUS_PENDING; + +} /* end IT8212Rebuild */ + +/************************************************************************ + * Switch to DMA mode if necessary. + * + * offset 0x50 = PCI Mode Control Register + * + * Bit 0 = PCI Mode Select (1=firmware mode, 0=transparent mode) + * Bit 1 = Primary Channel IDE Clock Frequency Select (1=50, 0=66) + * Bit 2 = Secondary Channel IDE Clock Freq Select (1=50, 0=66) + * Bit 3 = Primary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 4 = Primary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 5 = Secondary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 6 = Secondary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 7 = PCI Mode Reset + ************************************************************************/ +void +IT8212SwitchDmaMode(PChannel pChan, u8 DeviceId) +{ + + u8 pciControl; + u8 channel; + u8 device; + u8 configByte = 0; + u8 RevisionID; + struct pci_dev * pPciDev = pChan->pPciDev; + + /* + * These tables are for performance issue. Better formance than lots + * of "Shifts". + */ + static const u8 dmaModeV10[4] = {0x18, 0x18, 0x60, 0x60}; + static const u8 udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F}; + static const u8 ideClock[4] = {0xFD, 0xFD, 0xFB, 0xFB}; + + /* + * channel --> 0-1; device --> 0-1; DeviceId --> 0-3; + */ + channel = DeviceId >> 1; + device = DeviceId & 1; + + /* + * Do nothing if the mode switch is unnecessary. + */ + if (!pChan->DoSwitch || pChan->ActiveDevice == DeviceId) + { + dprintk("IT8212SwitchDmaMode: do not need to switch mode!\n"); + return; + } + + printk("IT8212SwitchDmaMode: switch DMA mode for dev (%x)\n", DeviceId); + + pci_read_config_byte(pPciDev, 0x50, &pciControl); + pci_read_config_byte(pPciDev, 0x08, &RevisionID); + + /* + * Running on MULTIWORD_DMA mode. + */ + if (pChan->DmaType[device] == USE_MULTIWORD_DMA) + { + /* + * Switch to DMA mode. + */ + if (RevisionID == 0x10) + { + configByte = pciControl | dmaModeV10[DeviceId]; + } + pci_write_config_byte(pPciDev, 0x50, configByte); + } + /* + * Running on ULTRA DMA mode. + */ + else + { + /* + * Select UDMA mode. + */ + configByte = pciControl; + if (RevisionID == 0x10) + { + configByte &= udmaModeV10[DeviceId]; + } + + /* + * Select IDE clock. + */ + configByte = (configByte & ideClock[DeviceId]) | + (pChan->IdeClock[device] << (channel + 1)); + + pci_write_config_byte(pPciDev, 0x50, configByte); + + /* + * Set UDMA timing. + * + * offset 0x56 = PCI Mode Primary Device 0 Ultra DMA Timing Registers + * offset 0x57 = PCI Mode Primary Device 1 Ultra DMA Timing Registers + * offset 0x5A = PCI Mode Secondary Device 0 Ultra DMA Timing Registers + * offset 0x5B = PCI Mode Secondary Device 1 Ultra DMA Timing Registers + */ + if (RevisionID == 0x10) + { + configByte = pChan->UdmaTiming[device]; + pci_write_config_byte(pPciDev, (u8) (0x56 + (channel * 4)), configByte); + pci_write_config_byte(pPciDev, (u8) (0x56 + (channel * 4) + 1), configByte); + } + + /* + * Set PIO/DMA timing (Becasuse maybe the IDE clock is changed.) + */ + configByte = pChan->PioDmaTiming[pChan->IdeClock[device]]; + pci_write_config_byte(pPciDev, (u8) (0x54 + (channel * 4)), configByte); + } + + /* + * Record the Active device on this channel + */ + pChan->ActiveDevice = device; + +} /* end IT8212SwitchDmaMode */ + +/************************************************************************ + * IT8212 read/write routine. + ************************************************************************/ +u32 +IT8212ReadWrite(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u8 statusByte = 0; + u32 startingSector; + u32 sectorNumber; + u32 capacity; + PITE_ADAPTER pAdap; + + if (Srb->TargetId >= 4) + { + pAdap = ite_adapters[1]; + if (Srb->TargetId < 6) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + else + { + pAdap = ite_adapters[0]; + if (Srb->TargetId < 2) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + + /* + * Return error if overrun. + */ + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + sectorNumber = (unsigned short) ((Srb->DataTransferLength + 0x1FF) / 0x200); + + capacity = pChan->IdentifyData[Srb->TargetId & 0x1].UserAddressableSectors; + if (capacity == 0x0FFFFFFF) + { + capacity = pChan->IdentifyData[Srb->TargetId & 0x1].Capacity_48bit_LOW; + } + + if ((startingSector + sectorNumber - 1) > capacity) + { + printk("IT8212ReadWrite: disk[%d] over disk size.\n", Srb->TargetId); + printk("capacity: %d. starting sector: %d. sector number: %d\n", + capacity, startingSector, sectorNumber); + return SRB_STATUS_ERROR; + } + + /* + * Select device. + */ + outb((u8)((Srb->TargetId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212ReadWrite: disk[%d] not ready. status=0x%x\n", + Srb->TargetId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * First, switch to DMA or UDMA mode if running on bypass mode. + */ + if (pAdap->bypass_mode) + { + IT8212SwitchDmaMode(pChan, Srb->TargetId); + } + + /* + * Check the SCATTER/GATHER count. The upper will give the different + * memory address depend on whether use_sg is used or not. + */ + if (Srb->UseSg == 0) + { + IdeBuildDmaTable(pChan, Srb); + } + else + { + IdeBuildDmaSgTable(pChan, Srb); + } + + /* + * Start transfer the data. + */ + IdeStartTransfer(pChan, Srb, startingSector, sectorNumber); + + /* + * Wait for interrupt. + */ + return SRB_STATUS_PENDING; + +} /* end IT8212ReadWrite */ + +/************************************************************************ + * Setup the transfer mode. + ************************************************************************/ +void +IT8212SetTransferMode +( + PChannel pChan, + u32 DiskId, + u8 TransferMode, + u8 ModeNumber +) +{ + + u8 statusByte = 0; + + /* + * Select device. + */ + outb((u8) ((DiskId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) + { + printk("IT8212SetTransferMode: disk[%d] not ready. status=0x%x\n", + DiskId, statusByte); + return; + } + + /* + * Feature number ==> 03 + * + * Mode contained in Sector Count Register. + * + * Bits(7:3) Bits(2:0) Mode + * + * 00000 000 PIO default mode + * 00000 001 PIO default mode, disable IORDY + * 00001 mode PIO flow control transfer mode + * 00010 mode Single Word DMA mode + * 00100 mode Multi-word DMA mode + * 01000 mode Ultra DMA mode + */ + TransferMode |= ModeNumber; + + outb(0x03, pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(TransferMode, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb(0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb(IDE_COMMAND_SET_FEATURE, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check error. + */ + WaitForBaseCommandComplete(pChan, statusByte); + + if ((statusByte != IDE_STATUS_IDLE) && (statusByte != 0)) + { + printk("IT8212SetTransferMode: disk[%d]", DiskId); + printk("return unexpected status after issue command. 0x%x\n", + statusByte); + } + +} /* end IT8212SetTransferMode */ + +/************************************************************************ + * Set the best transfer mode for device. + ************************************************************************/ +void +IT8212SetBestTransferMode(PITE_ADAPTER pAdap, PChannel pChan, u8 channel) +{ + + u8 i; + u8 k; + u8 transferMode; + u8 modeNumber; + u8 pciControl; + u8 device; + u8 configByte; + u8 cableStatus[2] = {CABLE_40_PIN, CABLE_40_PIN}; + u8 RevisionID; + struct pci_dev * pPciDev = pChan->pPciDev; + PIDENTIFY_DATA2 ideIdentifyData; + + /* + * UDMA timing table for 66MHz clock. + * UDMA timing table for 50MHz clock. + * Best of IDE clock in this mode. + */ + static const u8 udmaTiming[3][7] = + { + {0x44, 0x42, 0x31, 0x21, 0x11, 0x22, 0x11}, + {0x33, 0x31, 0x21, 0x21, 0x11, 0x11, 0x11}, + {IDE_CLOCK_66, IDE_CLOCK_50, IDE_CLOCK_66, IDE_CLOCK_66, IDE_CLOCK_66, IDE_CLOCK_50, IDE_CLOCK_66} + }; + + /* + * DMA timing table for 66 MHz clock. + * DMA timing table for 50 MHz clock. + */ + static const u8 dmaTiming[2][3] = + { + {0x88, 0x32, 0x31}, + {0x66, 0x22, 0x21} + }; + + /* + * PIO timing table for 66 MHz clock. + * PIO timing table for 50 MHz clock. + */ + static const u8 pioTiming[2][5] = + { + {0xAA, 0xA3, 0xA1, 0x33, 0x31}, + {0x88, 0x82, 0x81, 0x32, 0x21} + }; + + u8 pio_dma_timing[2][2][4] = + { + {{0, 0, 0, 0}, {0, 0, 0, 0}}, + {{0, 0, 0, 0}, {0, 0, 0, 0}} + }; + + /* + * These tables are for performance issue. Better formance than lots + * of "Shifts". + */ + static const u8 udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F}; + static const u8 dmaMode[4] = {0x08, 0x10, 0x20, 0x40}; + static const u8 udmaMode[4] = {0xF7, 0xEF, 0xDF, 0xBF}; + static const u8 ideClock[4] = {0xFD, 0xFD, 0xFB, 0xFB}; + + /* + * 2003/07/24 + * If running on Firmware mode, get cable status from it. + */ + + for (i = 0; i < 2; i++) + { + /* + * The dafault of cable status is in PCI configuration 0x40. + */ + cableStatus[i] = pChan->Cable80[i]; + + /* + * channel -->0 to 1. + * device -->0 or 1. + */ + pChan->UseDma[i] = TRUE; + device = i & 1; + + if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) || + (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) + { + pio_dma_timing[0][channel][device] = pio_dma_timing[0][channel][device + 2] = 0; + pio_dma_timing[1][channel][device] = pio_dma_timing[1][channel][device + 2] = 0; + continue; + } + + /* + * Set PIO Mode. + */ + ideIdentifyData = &pChan->IdentifyData[i]; + if ((!(ideIdentifyData->ValidFieldIndicator & 0x02)) || (ideIdentifyData->AdvancedPIOModes == 0)) + { + transferMode = PIO_FLOW_CONTROL; + modeNumber = 2; + } + else + { + transferMode = PIO_FLOW_CONTROL; + modeNumber = RaidGetHighestBit((u8) ideIdentifyData->AdvancedPIOModes) + 3; + } + + IT8212SetTransferMode(pChan, i, transferMode, modeNumber); + + /* + * Record the PIO timing for later use.(0 to 4) + */ + pio_dma_timing[0][channel][device] = pioTiming[0][modeNumber]; + pio_dma_timing[1][channel][device] = pioTiming[1][modeNumber]; + + /* + * Get the best transfer mode (maybe Ultra DMA or Multi-Word DMA). + */ + ideIdentifyData = &pChan->IdentifyData[i]; + if ((!(ideIdentifyData->ValidFieldIndicator & 0x04)) || (ideIdentifyData->UltraDMASupport == 0)) + { + /* + * UltraDMA is not valid. + */ + transferMode = MULTIWORD_DMA; + modeNumber = RaidGetHighestBit(ideIdentifyData->MultiWordDMASupport); + printk("The best transfer mode of Device[%d] is DMA-%d\n", i, modeNumber); + } + else + { + transferMode = ULTRA_DMA; + modeNumber = RaidGetHighestBit(ideIdentifyData->UltraDMASupport); + printk("The best transfer mode of Device[%d] is Ultra-%d\n", i, modeNumber); + + /* + * If this is 40-pin cable. Limit to Ultra DMA mode 2. + */ +# if (0) + if ((cableStatus[i] == CABLE_40_PIN) && (modeNumber > 2)) + { + printk("Reduce trans mode of Device[%d] to Ultra-2 for cable issue.\n", i); + modeNumber = 0x02; + } +# endif + } + + IT8212SetTransferMode(pChan, i, transferMode, modeNumber); + + /* + * If running on ByPass mode, driver must take the responsibility to + * set the PIO/DMA/UDMA timing. + */ + if (pAdap->bypass_mode) + { + pci_read_config_byte(pPciDev, 0x50, &pciControl); + pci_read_config_byte(pPciDev, 0x08, &RevisionID); + + if (transferMode == ULTRA_DMA) + { + /* + * Set this channel to UDMA mode (not only the device). + */ + if (RevisionID == 0x10) + { + configByte = pciControl & udmaModeV10[i + channel * 2]; + } + else + { + configByte = pciControl & udmaMode[i + channel * 2]; + } + + /* + * Select IDE clock (50MHz or 66MHz). + */ + configByte &= ideClock[i + channel * 2]; + configByte |= (udmaTiming[2][modeNumber] << (channel + 1)); + + pci_write_config_byte(pPciDev, 0x50, configByte); + + /* + * Set UDMA timing. + */ + configByte = udmaTiming[udmaTiming[2][modeNumber]][modeNumber]; + if (modeNumber == 5 || modeNumber == 6) + { + /* + * Enable UDMA mode 5/6 + */ + configByte |= UDMA_MODE_5_6; + } + + /* + * Bug Bug. Fill these two fields into the same value. + */ + if (RevisionID == 0x10) + { + pci_write_config_byte(pPciDev, (u8) (0x56 + (channel * 4)), configByte); + pci_write_config_byte(pPciDev, (u8) (0x56 + (channel * 4) + 1), configByte); + } + else + { + pci_write_config_byte(pPciDev, (u8) (0x56 + (channel * 4) + device), configByte); + } + + /* + * Record the best UDMA mode for this device. + */ + pChan->DmaType[i] = ULTRA_DMA; + pChan->IdeClock[i] = udmaTiming[2][modeNumber]; + pChan->UdmaTiming[i] = configByte; + } + else if (transferMode == MULTIWORD_DMA) + { + /* + * If an ATAPI device with DMA mode, force it to run in PIO mode. + */ + if (RevisionID == 0x10 && pChan->DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) + { + pChan->UseDma[i] = FALSE; + } + else + { + /* + * Set this device to DMA mode. + */ + configByte = pciControl | dmaMode[i + channel * 2]; + pci_write_config_byte(pPciDev, 0x50, configByte); + + /* + * Record DMA timing (for later use). + */ + pio_dma_timing[0][channel][device + 2] = dmaTiming[0][modeNumber]; + pio_dma_timing[1][channel][device + 2] = dmaTiming[1][modeNumber]; + } + pChan->DmaType[i] = USE_MULTIWORD_DMA; + } + pChan->ActiveDevice = device; + } + } + + /* + * Because each channel owns one PIO/DMA timimg register only, so we + * must set the timing to the slowest one to fit all. Really stupid H/W! :( + */ + if (pAdap->bypass_mode) + { + /* + * Loop for the two IDE clocks (50 MHz and 66 MHz). + */ + for (i = 0; i < 2; i++) + { + configByte = 0; + for (k = 0; k < 4; k++) + { + /* + * High part. + */ + if ((pio_dma_timing[i][channel][k] & 0xF0) > (configByte & 0xF0)) + { + configByte = (configByte & 0xF) | (pio_dma_timing[i][channel][k] & 0xF0); + } + + /* + * Low part. + */ + if ((pio_dma_timing[i][channel][k] & 0xF) > (configByte & 0xF)) + { + configByte = (configByte & 0xF0) | (pio_dma_timing[i][channel][k] & 0xF); + } + } + + /* + * Record the PIO/DMA timing for this channel. + */ + pChan->PioDmaTiming[i] = configByte; + } + + /* + * Set PIO/DMA timing register for each channel. + */ + configByte = pChan->PioDmaTiming[(pciControl >> (channel + 1)) & 1]; + if (configByte != 0) + { + pci_write_config_byte(pPciDev, (u8) (0x54 + (channel * 4)), configByte); + } + + /* + * Check shall we do switch between the two devices + */ + for (i = 0; i < 2; i++) + { + pChan->DoSwitch = TRUE; + + /* + * Master is not present + */ + if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) || + (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) + { + printk("Channel %x: master is not present. No switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * Slave is not present + */ + if (!(pChan->DeviceFlags[i + 1] & DFLAGS_DEVICE_PRESENT) || + (pChan->DeviceFlags[i + 1] & DFLAGS_CONFIG_CHANGED)) + { + printk("Channel %x: slave is not present. No switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * If both devices are running on DMA mode, no switch. + */ + if (pChan->DmaType[i] == USE_MULTIWORD_DMA && pChan->DmaType[i + 1] == USE_MULTIWORD_DMA) + { + printk("Channel %x: run on DMA mode only. No switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * No switch if the two devices are running on the same mode. + */ + if ((pChan->DmaType[i] == pChan->DmaType[i + 1] ) && + (pChan->UdmaTiming[i] == pChan->UdmaTiming[i + 1]) && + (pChan->IdeClock[i] == pChan->IdeClock[i + 1] )) + { + printk("Channel %x: two dev run on the same mode. No switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + printk("Channel %x: switch mode if needed.\n", channel); + } + } + +} /* end IT8212SetBestTransferMode */ + +/************************************************************************ + * Initialize bypass(transparent) mode if BIOS is not ready. + ************************************************************************/ +u8 +IT8212InitBypassMode(struct pci_dev *pPciDev) +{ + + /* + * Reset local CPU, and set BIOS not ready. + */ + pci_write_config_byte(pPciDev, 0x5E, 0x01); + + /* + * Set to bypass mode, and reset PCI bus. + */ + pci_write_config_byte(pPciDev, 0x50, 0x00); + + pci_write_config_word(pPciDev, 0x4, 0x0047); + pci_write_config_word(pPciDev, 0x40, 0xA0F3); + + pci_write_config_dword(pPciDev,0x4C, 0x02040204); + pci_write_config_byte(pPciDev, 0x42, 0x36); + pci_write_config_byte(pPciDev, 0x0D, 0x00); + + return TRUE; + +} /* end IT8212InitBypassMode */ + +/************************************************************************ + * This is the interrupt service routine for ATAPI IDE miniport driver. + * TURE if expecting an interrupt. + ************************************************************************/ +u8 +IT8212Interrupt(PChannel pChan, u8 bypass_mode) +{ + + u8 statusByte; + u8 bmstatus; + u32 i; + unsigned long bmbase; + PSCSI_REQUEST_BLOCK Srb; + + bmstatus = 0; + bmbase = pChan->dma_base; + Srb = pChan->CurrentSrb; + + if (Srb == 0 || pChan->ExpectingInterrupt == 0) + { + dprintk("IT8212Interrupt: suspicious interrupt!\n"); + + /* + * Clear interrupt by reading status register. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + outb((u8) 0xB0, pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + outb(bmbase + 2, (u8) (inb(bmbase + 2) | BM_STAT_FLG_INT)); + return FALSE; + } + + /* + * To handle share IRQ condition. If the interrupt is not ours, just + * return FALSE. + */ + bmstatus = inb(bmbase + 2); + if ((bmstatus & BM_STAT_FLG_INT) == 0) + { + dprintk("IT8212Interrupt: suspicious interrupt (int bit is not on)\n"); + return FALSE; + } + + /* + * Bug Fixed: All PIO access are blocked during bus master operation, so + * stop bus master operation before we try to access IDE registers. + */ + if (bypass_mode) + { + outb(bmbase, 0); + } + + /* + * Clear interrupt by reading status register. + */ + GetBaseStatus(pChan, statusByte); + outb(bmbase + 2, (u8) (bmstatus | BM_STAT_FLG_INT)); + + /* + * Handle ATAPI interrupt. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) + { + return AtapiInterrupt(pChan); + } + + pChan->ExpectingInterrupt = FALSE; + + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) + { + /* + * Ensure BUSY and DRQ is non-asserted. + */ + for (i = 0; i < 100; i++) + { + GetBaseStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY) && !(statusByte & IDE_STATUS_DRQ)) + { + break; + } + mdelay(5); + } + + if (i == 100) + { + printk("IT8212Interrupt: disk[%x] return busy or drq status. status = 0x%x\n", + Srb->TargetId, statusByte); + return FALSE; + } + } + + if (statusByte & IDE_STATUS_ERROR) + { + /* + * Stop bus master operation. + */ + outb(bmbase, 0); + + printk("IT8212Interrupt: error!\n"); + + /* + * Map error to specific SRB status and handle request sense. + */ + Srb->SrbStatus = MapError(pChan, Srb); + } + else + { + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + + pChan->CurrentSrb = NULL; + + TaskDone(pChan, Srb); + + return TRUE; + +} /* end IT8212Interrupt */ + +/************************************************************************ + * This is the interrupt service routine for ATAPI IDE miniport driver. + * TRUE if expecting an interrupt. Remember the ATAPI io registers are + * different from IDE io registers and this is for each channel not for + * entire controller. + ************************************************************************/ +static u8 +AtapiInterrupt(PChannel pChan) +{ + + u32 wordCount; + u32 wordsThisInterrupt; + u32 status; + u32 i; + u8 statusByte; + u8 interruptReason; + u8 target_id; + PSCSI_REQUEST_BLOCK srb; + PITE_ADAPTER pAdap; + + wordCount = 0; + wordsThisInterrupt = 256; + srb = pChan->CurrentSrb; + target_id = srb->TargetId; + + if (target_id >= 4) + { + pAdap = ite_adapters[1]; + if (target_id < 6) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + else + { + pAdap = ite_adapters[0]; + if (target_id < 2) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + + /* + * Clear interrupt by reading status. + */ + GetBaseStatus(pChan, statusByte); + + dprintk("AtapiInterrupt: entered with status (%x)\n", statusByte); + + if (statusByte & IDE_STATUS_BUSY) + { + /* + * Ensure BUSY is non-asserted. + */ + for (i = 0; i < 10; i++) + { + GetBaseStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) + { + break; + } + mdelay(5); + } + + if (i == 10) + { + printk("AtapiInterrupt: BUSY on entry. Status %x\n", statusByte); + return FALSE; + } + } + + /* + * Check for error conditions. + */ + if (statusByte & IDE_STATUS_ERROR) + { + if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) + { + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + } + + /* + * Check reason for this interrupt. + */ + interruptReason = (inb(pChan->io_ports[ATAPI_INTREASON_OFFSET]) & 0x3); + wordsThisInterrupt = 256; + + if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) + { + /* + * Write the packet. + */ + printk("AtapiInterrupt: writing Atapi packet.\n"); + + /* + * Send CDB to device. + */ + WriteBuffer(pChan, (unsigned short *)srb->Cdb, 6); + + return TRUE; + } + else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) + { + /* + * Write the data. + */ + + /* + * Pick up bytes to transfer and convert to words. + */ + wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); + wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; + + /* + * Covert bytes to words. + */ + wordCount >>= 1; + + if (wordCount != pChan->WordsLeft) + { + printk( "AtapiInterrupt: %d words requested; %d words xferred\n", + pChan->WordsLeft, wordCount); + } + + /* + * Verify this makes sense. + */ + if (wordCount > pChan->WordsLeft) + { + wordCount = pChan->WordsLeft; + } + + /* + * Ensure that this is a write command. + */ + if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) + { + dprintk("AtapiInterrupt: write interrupt\n"); + WaitOnBusy(pChan, statusByte); + WriteBuffer(pChan, pChan->DataBuffer, wordCount); + +# if (0) + /* + * Translate ATAPI data back to SCSI data if needed (don't + * convert if the original command is SCSIOP_MODE_SELECT10) + */ + if (srb->Cdb[0] == ATAPI_MODE_SELECT && pchan->ConvertCdb) + { + Atapi2Scsi(pChan, srb, (char *)pChan->DataBuffer, wordCount << 1); + } +# endif + } + else + { + printk("AtapiInterrupt: int reason %x, but srb is for a write %p.\n", + interruptReason, srb); + + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + /* + * Advance data buffer pointer and bytes left. + */ + pChan->DataBuffer += wordCount; + pChan->WordsLeft -= wordCount; + + return TRUE; + } + else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) + { + /* + * Pick up bytes to transfer and convert to words. + */ + wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); + wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; + + /* + * Covert bytes to words. + */ + wordCount >>= 1; + if (wordCount != pChan->WordsLeft) + { + printk("AtapiInterrupt: %d words requested; %d words xferred\n", + pChan->WordsLeft, wordCount); + } + + /* + * Verify this makes sense. + */ + if (wordCount > pChan->WordsLeft) + { + wordCount = pChan->WordsLeft; + } + + /* + * Ensure that this is a read command. + */ + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + dprintk("AtapiInterrupt: read interrupt\n"); + WaitOnBusy(pChan, statusByte); + ReadBuffer(pChan, pChan->DataBuffer, wordCount); + + /* + * From Windows DDK + * You should typically set the ANSI-approved Version field, + * in the INQUIRY response, to at least 2. + */ + if (srb->Cdb[0] == SCSIOP_INQUIRY) + { + /* + * Maybe it's not necessary in Linux driver. + */ + *((unsigned char *)pChan->DataBuffer + 2) = 2; + } + } + else + { + printk("AtapiInterrupt: int reason %x, but srb is for a read %p.\n", + interruptReason, srb); + + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + /* + * Advance data buffer pointer and bytes left. + */ + pChan->DataBuffer += wordCount; + pChan->WordsLeft -= wordCount; + + /* + * Check for read command complete. + */ + if (pChan->WordsLeft == 0) + { + /* + * Work around to make many atapi devices return correct sector size + * of 2048. Also certain devices will have sector count == 0x00, check + * for that also. + */ + if ((srb->Cdb[0] == 0x25) && + ((pChan->IdentifyData[srb->TargetId & 1].GeneralConfiguration >> 8) & 0x1F) == 0x05) + { + pChan->DataBuffer -= wordCount; + if (pChan->DataBuffer[0] == 0x00) + { + *((u32 *) &(pChan->DataBuffer[0])) = 0xFFFFFF7F; + } + + *((u32 *) &(pChan->DataBuffer[2])) = 0x00080000; + pChan->DataBuffer += wordCount; + } + } + return TRUE; + } + else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ)) + { + dprintk("AtapiInterrupt: command complete!\n"); + /* + * Command complete. + */ + if (pChan->WordsLeft) + { + status = SRB_STATUS_DATA_OVERRUN; + } + else + { + status = SRB_STATUS_SUCCESS; + } + +CompleteRequest: + + if (status == SRB_STATUS_ERROR) + { + /* + * Map error to specific SRB status and handle request sense. + */ + printk("AtapiInterrupt error\n"); + + status = MapError(pChan, srb); + + /* + * Try to recover it.... 2003/02/27 + */ + + + pChan->RDP = FALSE; + } + else + { + /* + * Wait for busy to drop. + */ + for (i = 0; i < 30; i++) + { + GetStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) + { + break; + } + udelay(500); + } + + if (i == 30) + { + /* + * Reset the controller. + */ + printk("AtapiInterrupt: resetting due to BSY still up - %x.\n", + statusByte); + AtapiResetController(pAdap, pChan); + return TRUE; + } + + /* + * Check to see if DRQ is still up. + */ + if (statusByte & IDE_STATUS_DRQ) + { + for (i = 0; i < 500; i++) + { + GetStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) + { + break; + } + udelay(100); + } + + if (i == 500) + { + /* + * Reset the controller. + */ + printk("AtapiInterrupt: resetting due to DRQ still up - %x\n", + statusByte); + AtapiResetController(pAdap, pChan); + return TRUE; + } + } + } + + /* + * Clear interrupt expecting flag. + */ + pChan->ExpectingInterrupt = FALSE; + + /* + * Sanity check that there is a current request. + */ + if (srb != NULL) + { + /* + * Set status in SRB. + */ + srb->SrbStatus = (u8)status; + + /* + * Check for underflow. + */ + if (pChan->WordsLeft) + { + /* + * Subtract out residual words and update if filemark hit, + * setmark hit , end of data, end of media... + */ + if (!(pChan->DeviceFlags[srb->TargetId & 1] & DFLAGS_TAPE_DEVICE)) + { + if (status == SRB_STATUS_DATA_OVERRUN) + { + srb->DataTransferLength -= pChan->WordsLeft * 2; + } + else + { + srb->DataTransferLength = 0; + } + } + else + { + srb->DataTransferLength -= pChan->WordsLeft * 2; + } + } + + GetBaseStatus(pChan, statusByte); + if (pChan->RDP && !(statusByte & IDE_STATUS_DSC)) + { + printk("-@@-\n"); + } + else + { + /* + * Clear current SRB. Indicate ready for next request. + */ + pChan->CurrentSrb = NULL; + TaskDone(pChan, srb); + } + } + else + { + printk("AtapiInterrupt: no SRB!\n"); + } + + return TRUE; + } + else + { + /* + * Unexpected int. + */ + printk("AtapiInterrupt: unexpected interrupt. intReason %x. status %x.\n", + interruptReason, statusByte); + return FALSE; + } + + return TRUE; + +} /* end AtapiInterrupt */ + +/************************************************************************ + * IRQ handler. + ************************************************************************/ +static irqreturn_t +Irq_Handler(int irq, void *dev_id, struct pt_regs *regs) +{ + + int handled = 0; + u8 i; + u8 j; + unsigned long flags; + PITE_ADAPTER pAdap; + + spin_lock_irqsave(&io_request_lock, flags); + + /* + * Scan for interrupt to process. + */ + for (i = 0; i < NumAdapters; i++) + { + pAdap = ite_adapters[i]; + if (pAdap->irq != irq) continue; + + handled = 1; + for (j = 0; j < pAdap->num_channels; j++) + { + IT8212Interrupt(&pAdap->IDEChannel[j], pAdap->bypass_mode); + } + } + + spin_unlock_irqrestore(&io_request_lock, flags); + + return IRQ_RETVAL(handled); + +} /* end Irq_Handler */ + +/************************************************************************ + * This routine handles IDE Verify. + ************************************************************************/ +u8 +IdeVerify(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u8 drvSelect; + u8 statusByte = 0; + u32 startingSector; + u32 sectors; + u32 endSector; + u32 sectorCount; + + /* + * Select device + */ + outb((u8)((Srb->TargetId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not BUSY and Not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IdeVerify: disk[%d] not ready. status=0x%x\n", + Srb->TargetId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Get the starting sector number from CDB. + */ + startingSector = ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + + sectorCount = (u16)(((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8 | + ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb ); + + endSector = startingSector + sectorCount; + + /* + * Drive has these number sectors. + * + * 48-bit addressing. + */ + if (endSector > 0x0FFFFFFF) + { + sectors = pChan->IdentifyData[Srb->TargetId & 0x01].Capacity_48bit_LOW; + + printk("IdeVerify (48-bit): starting sector %d, Ending sector %d\n", + startingSector, endSector); + + if (endSector > sectors) + { + /* + * Too big, round down. + */ + printk("IdeVerify: truncating request to %x blocks\n", + sectors - startingSector - 1); + + outb((u8)((sectors - startingSector - 1) >> 8), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8)(sectors - startingSector - 1), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + else + { + /* + * Set up sector count register. Round up to next block. + */ + if (sectorCount > 0xFFFF) + { + sectorCount = (u16)0xFFFF; + } + + outb((u8)(sectorCount >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Set up LBA address + */ + outb((u8) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) 0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb((u8) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Send verify command. + */ + outb(IDE_COMMAND_READ_VERIFY_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + /* + * 28-bit addressing + */ + else + { + sectors = pChan->IdentifyData[Srb->TargetId & 0x01].UserAddressableSectors; + + printk("IdeVerify: starting sector %d, ending sector %d\n", + startingSector, endSector); + + if (endSector > sectors) + { + /* + * Too big, round down. + */ + printk("IdeVerify: truncating request to %d blocks\n", + sectors - startingSector - 1); + outb((u8)(sectors - startingSector - 1), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + else + { + /* + * Set up sector count register. Round up to next block. + */ + if (sectorCount > 0xFF) + { + sectorCount = (u16)0xFF; + } + outb((u8)sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Set up LBA address + */ + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 16),pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Select driver, set LBA mode, set LBA (27:27) + */ + drvSelect = (u8) (startingSector >> 24); + drvSelect = drvSelect | (((u8) Srb->TargetId & 0x1) << 4) | 0xA0 | 0x40; + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Send verify command. + */ + outb(IDE_COMMAND_READ_VERIFY, pChan->io_ports[IDE_COMMAND_OFFSET]); + } + + /* + * Wait for interrupt. + */ + return SRB_STATUS_PENDING; + +} /* end IdeVerify */ + +/************************************************************************ + * This function is used to copy memory with overlapped destination and + * source. I guess ScsiPortMoveMemory cannot handle this well. Can it? + ************************************************************************/ +void +IT8212MoveMemory(unsigned char * DestAddr, unsigned char * SrcAddr, u32 ByteCount) +{ + + long i; + + dprintk("IT8212MoveMemory: DestAddr=0x%p, SrcAddr=0x%p, ByteCount=0x%x\n", + DestAddr, SrcAddr, ByteCount); + + if (DestAddr > SrcAddr) + { + /* + * If Destination Area is in the back of the Source Area, copy from + * the end of the requested area. + */ + for (i = (ByteCount - 1); i >= 0; i--) + { + *(DestAddr + i) = *(SrcAddr + i); + } + } + else if (DestAddr < SrcAddr) + { + /* + * If Destination Area is in the front of the Source Area, copy from + * the begin of the requested area. + */ + for (i = 0; i < ByteCount; i++) + { + *(DestAddr + i) = *(SrcAddr + i); + } + } + +} /* end IT8212MoveMemory */ + +/************************************************************************ + * Convert SCSI packet command to Atapi packet command. + ************************************************************************/ +void +Scsi2Atapi(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + /* + * Change the cdb length. + */ + Srb->CdbLength = 12; + + /* + * Because the block descripter and the header translation, we must + * adjust the requested length. + */ + Srb->DataTransferLength -= 4; + + /* + * Record the original CDB for later restore. + */ + memcpy(pChan->TempCdb, Srb->Cdb, MAXIMUM_CDB_SIZE); + + /* + * Indicate that we have performed Scsi2Atapi function. And we must + * restore the CDB back once the command complete. + */ + pChan->ConvertCdb = TRUE; + + switch (Srb->Cdb[0]) + { + /* + * Convert the command from SCSIOP_MODE_SENSE (0x1A) to + * SCSIOP_MODE_SENSE10 (0x5A). + */ + case SCSIOP_MODE_SENSE: + { + PSCSI_MODE_SENSE10 modeSense10 = (PSCSI_MODE_SENSE10) Srb->Cdb; + PSCSI_MODE_SENSE6 modeSense6 = (PSCSI_MODE_SENSE6) pChan->TempCdb; + + /* + * 1. Zero out the whole CDB. + */ + memset((unsigned char *)modeSense10, 0, MAXIMUM_CDB_SIZE); + + /* + * 2. Fill in command code (SCSI_MODE_SENSE10). + */ + modeSense10->OperationCode = ATAPI_MODE_SENSE; + modeSense10->Dbd = modeSense6->Dbd; + modeSense10->PageCode = modeSense6->PageCode; + modeSense10->Pc = modeSense6->Pc; + modeSense10->SubpageCode = modeSense6->SubpageCode; + modeSense10->AllocationLengthLsb = modeSense6->AllocationLength; + modeSense10->Control = modeSense6->Control; + + /* + * 3. Becasuse we will fake a block descripter (-8), and + * translate the header (+4), so the requested length + * should be modified. That is, -8+4=-4 bytes. + */ + modeSense10->AllocationLengthLsb -= 4; + + break; + } + + /* + * Convert the command from SCSIOP_MODE_SELECT (0x15) to + * SCSIOP_MODE_SELECT10 (0x5A). + */ + case SCSIOP_MODE_SELECT: + { + u8 tempHeader[sizeof(PSCSI_MODE_PARAMETER_HEADER6)]; + u16 byteCount; + PSCSI_MODE_PARAMETER_HEADER10 header10 = + (PSCSI_MODE_PARAMETER_HEADER10)Srb->DataBuffer; + PSCSI_MODE_PARAMETER_HEADER6 header6 = + (PSCSI_MODE_PARAMETER_HEADER6)tempHeader; + PSCSI_MODE_SELECT10 modeSelect10 = (PSCSI_MODE_SELECT10)Srb->Cdb; + PSCSI_MODE_SELECT6 modeSelect6 = (PSCSI_MODE_SELECT6)pChan->TempCdb; + + /* + * First, convert the command block. + */ + + /* + * 1. Zero out the whole CDB. + */ + memset((unsigned char *)modeSelect10, 0, MAXIMUM_CDB_SIZE); + + /* + * 2. Fill in command code (SCSI_MODE_SENSE10). + */ + modeSelect10->OperationCode = ATAPI_MODE_SELECT; + modeSelect10->SPBit = modeSelect6->SPBit; + modeSelect10->PFBit = modeSelect6->PFBit; + modeSelect10->ParameterListLengthLsb = modeSelect6->ParameterListLength; + modeSelect10->Control = modeSelect6->Control; + + /* + * 3. Becasuse we will remove the block descripter (-8), and + * translate the header (+4), so the requested length should + * be modified. That is, -8+4=-4 bytes. + */ + modeSelect10->ParameterListLengthLsb -= 4; + + /* + * Second, convert the parameter page format from SCSI to ATAPI. + */ + + /* + * Remove the mode parameter data (except the header and the + * block descripter). + */ + byteCount = modeSelect6->ParameterListLength - + sizeof(SCSI_MODE_PARAMETER_HEADER6) - + sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER); + if (byteCount > 0) + { + IT8212MoveMemory( + (unsigned char *) header10 + sizeof(SCSI_MODE_PARAMETER_HEADER10), + (unsigned char *) header10 + sizeof(SCSI_MODE_PARAMETER_HEADER6) + + sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER), + byteCount); + } + + /* + * Keep the original header6 (4 bytes) in tempHeader for later use + */ + memcpy(tempHeader, header10, sizeof(SCSI_MODE_PARAMETER_HEADER6)); + + /* + * Change the "mode parameter header(6)" to "mode parameter header(10)" + * Notice: Remove the block descripter in SCSI-2 command out. It + * won't be used in MMC. + */ + memset((unsigned char *)header10, 0, sizeof(SCSI_MODE_PARAMETER_HEADER10)); + header10->ModeDataLengthLsb = header6->ModeDataLength; + header10->MediumType = header6->MediumType; + header10->DeviceSpecificParameter = header6->DeviceSpecificParameter; + header10->BlockDescriptorLengthLsb = header6->BlockDescriptorLength; + + /* + * ATAPI doesn't support block descripter, so remove it from the + * mode paramter. + */ + header10->BlockDescriptorLengthLsb = 0; + + break; + } + } + +} /* end Scsi2Atapi */ + +/************************************************************************ + * Send ATAPI packet command to device. + ************************************************************************/ +u32 +AtapiSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u8 statusByte; + u8 byteCountLow; + u8 byteCountHigh; + u8 useDMA; + u8 RevisionID = 0; + u8 bmClearStat; + u32 flags; + int i; + unsigned long bmAddress = pChan->dma_base; + PITE_ADAPTER pAdap = ite_adapters[0]; + + dprintk("AtapiSendCommand: command 0x%X to device %d\n", + Srb->Cdb[0], Srb->TargetId); + + /* + * Default use PIO mode. + */ + useDMA = 0; + pChan->ConvertCdb = FALSE; + + /* + * Make sure command is to ATAPI device. + */ + flags = pChan->DeviceFlags[Srb->TargetId & 1]; + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) + { + if ((Srb->Lun) > (pChan->DiscsPresent[Srb->TargetId & 1] - 1)) + { + /* + * Indicate no device found at this address. + */ + return SRB_STATUS_SELECTION_TIMEOUT; + } + } + else if (Srb->Lun > 0) + { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + if (!(flags & DFLAGS_ATAPI_DEVICE)) + { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + /* + * Select device 0 or 1. + */ + outb((u8)(((Srb->TargetId & 0x1) << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + + /* + * Try to enable interrupt again. (2003/02/25) + */ +#if (0) + outb(0x00, pChan->io_ports[ATAPI_CONTROL_OFFSET]); +#endif + + /* + * Verify that controller is ready for next command. + */ + GetStatus(pChan, statusByte); + + dprintk("AtapiSendCommand: entered with status %x\n", statusByte); + + if (statusByte & IDE_STATUS_BUSY) + { + printk("AtapiSendCommand: device busy (%x)\n", statusByte); + return SRB_STATUS_BUSY; + } + + if (statusByte & IDE_STATUS_ERROR) + { + if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) + { + printk("AtapiSendCommand: error on entry: (%x)\n", statusByte); + + /* + * Read the error reg. to clear it and fail this request. + */ + return MapError(pChan, Srb); + } + } + + /* + * If a tape drive doesn't have DSC set and the last command is + * restrictive, don't send the next command. See discussion of + * Restrictive Delayed Process commands in QIC-157. + */ + if ((!(statusByte & IDE_STATUS_DSC)) && (flags & DFLAGS_TAPE_DEVICE) && pChan->RDP) + { + mdelay(1); + printk("AtapiSendCommand: DSC not set. %x\n", statusByte); + return SRB_STATUS_BUSY; + } + + if (statusByte & IDE_STATUS_DRQ) + { + printk("AtapiSendCommand: enter with status (%x). Attempt to recover.\n", + statusByte); + + /* + * Try to drain the data that one preliminary device thinks that it has + * to transfer. Hopefully this random assertion of DRQ will not be present + * in production devices. + */ + for (i = 0; i < 0x10000; i++) + { + GetStatus(pChan, statusByte); + + if (statusByte & IDE_STATUS_DRQ) + { + /* + * Note: The data register is always referenced as a 16-bit word. + */ + inw(pChan->io_ports[ATAPI_DATA_OFFSET]); + } + else + { + break; + } + } + + if (i == 0x10000) + { + printk("AtapiSendCommand: DRQ still asserted.Status (%x)\n", statusByte); + printk("AtapiSendCommand: issued soft reset to Atapi device. \n"); + + AtapiSoftReset(pChan, Srb->TargetId); + + /* + * Re-initialize Atapi device. + */ + IssueIdentify(pChan, (Srb->TargetId & 1), IDE_COMMAND_ATAPI_IDENTIFY); + + /* + * Inform the port driver that the bus has been reset. + */ + + /* + * Clean up device extension fields that AtapiStartIo won't. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->RDP = FALSE; + + return SRB_STATUS_BUS_RESET; + } + } + + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) + { + /* + * As the cdrom driver sets the LUN field in the cdb, it must be removed. + */ + Srb->Cdb[1] &= ~0xE0; + + if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) + { + /* + * Torisan changer. TUR's are overloaded to be platter switches. + */ + Srb->Cdb[7] = Srb->Lun; + } + } + + /* + * Convert SCSI to ATAPI commands if needed + */ + switch (Srb->Cdb[0]) + { + case SCSIOP_MODE_SENSE: + case SCSIOP_MODE_SELECT: + if (flags & DFLAGS_ATAPI_DEVICE) + { + Scsi2Atapi(pChan, Srb); + } + break; + } + + if (pChan->UseDma[Srb->TargetId & 1]) + { + switch (Srb->Cdb[0]) + { + case SCSIOP_READ: /* (0x28) */ + case 0xA8: /* READ(12) */ + case SCSIOP_READ_CD: + + if (Srb->DataTransferLength == 0) + { + break; + } + + /* + * First, switch to DMA or UDMA mode if running on Bypass mode. + */ + if (pAdap->bypass_mode) + { + IT8212SwitchDmaMode(pChan, Srb->TargetId); + } + + /* + * Check the SCATTER/GATHER count. The upper will give the + * different memory address depend on whether use_sg is used + * or not. + */ + if (Srb->UseSg == 0) + { + IdeBuildDmaTable(pChan, Srb); + } + else + { + IdeBuildDmaSgTable(pChan, Srb); + } + + bmClearStat = inb(bmAddress + 2); + if (Srb->TargetId & 0x01) + { + bmClearStat = bmClearStat | BM_DRV1_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } + else + { + bmClearStat = bmClearStat | BM_DRV0_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } + + useDMA = 1; + + outb(0, bmAddress); + + /* + * Setup PRD table physical address. + */ + outl(pChan->dmatable_dma, bmAddress + 4); + + /* + * Clear the status. + */ + outb(bmClearStat, bmAddress + 2); + + break; + } /* end switch (Srb->Cdb[0]) */ + } + + /* + * Set data buffer pointer and words left. + */ + pChan->DataBuffer = (unsigned short *)Srb->DataBuffer; + + if (useDMA) + { + pChan->WordsLeft = 0; + } + else + { + pChan->WordsLeft = Srb->DataTransferLength / 2; + } + + outb((u8)(((Srb->TargetId & 0x1) << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + + WaitOnBusy(pChan, statusByte); + + /* + * Write transfer byte count to registers. + */ + byteCountLow = (u8)(Srb->DataTransferLength & 0xFF); + byteCountHigh = (u8)(Srb->DataTransferLength >> 8); + + if (Srb->DataTransferLength >= 0x10000) + { + byteCountLow = byteCountHigh = 0xFF; + } + + outb(byteCountLow, pChan->io_ports[ATAPI_LCYL_OFFSET]); + outb(byteCountHigh, pChan->io_ports[ATAPI_HCYL_OFFSET]); + outb(0, pChan->io_ports[ATAPI_INTREASON_OFFSET]); + outb(0, pChan->io_ports[ATAPI_UNUSED1_OFFSET]); + outb(useDMA, pChan->io_ports[ATAPI_FEATURE_OFFSET]); + + WaitOnBusy(pChan, statusByte); + + if (flags & DFLAGS_INT_DRQ) + { + /* + * This device interrupts when ready to receive the packet. + * + * Write ATAPI packet command. + */ + outb(IDE_COMMAND_ATAPI_PACKET, pChan->io_ports[IDE_COMMAND_OFFSET]); + + printk("AtapiSendCommand: wait for int. to send packet. status (%x)\n", + statusByte); + + pChan->ExpectingInterrupt = TRUE; + + return SRB_STATUS_PENDING; + } + else + { + /* + * Write ATAPI packet command. + */ + outb(IDE_COMMAND_ATAPI_PACKET, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for DRQ. + */ + WaitOnBusy(pChan, statusByte); + WaitForDrq(pChan, statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) + { + printk("AtapiSendCommand: DRQ never asserted (%x)\n", statusByte); + return SRB_STATUS_ERROR; + } + } + + /* + * Need to read status register. + */ + GetBaseStatus(pChan, statusByte); + + /* + * Send CDB to device. + * After detecting DRQ, the host writes the 12 bytes(6 words) of Command + * to the Data Register. + */ + WaitOnBusy(pChan, statusByte); + WriteBuffer(pChan, (unsigned short *)Srb->Cdb, 6); + + /* + * If running on DMA mode, start BUS MASTER operation. + */ + if (useDMA) + { + /* + * If SCSIOP_READ command is sent to an Audio CD, error will be + * returned. But the error will be blocked by our controller if bus + * master operation started. So wait for a short period to check if + * error occurs. If error occurs, don't start bus master operation. + */ + if (RevisionID == 0x10) + { + for (i = 0; i < 500; i++) + { + udelay(1); + statusByte = inb(bmAddress + 2); + if (statusByte & BM_STAT_FLG_INT) + { + /* + * If error occurs, give up this round. + */ + printk("AtapiSendCommand: command failed. Don't start bus master."); + printk("status=%x, i=%d\n", statusByte, i); + + pChan->ExpectingInterrupt = TRUE; + return SRB_STATUS_PENDING; + } + } + } + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + { + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, bmAddress); + } + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + { + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, bmAddress); + } + } /* end if (useDMA) */ + + /* + * Indicate expecting an interrupt and wait for it. + */ + pChan->ExpectingInterrupt = TRUE; + + return SRB_STATUS_PENDING; + +} /* end AtapiSendCommand */ + +/************************************************************************ + * Program ATA registers for IDE disk transfer. + ************************************************************************/ +static u32 +IdeSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u8 statusByte; + u32 status; + u32 i; + Scsi_Cmnd * pREQ; + unsigned char * request_buffer; + PINQUIRYDATA inquiryData; + + pREQ = Srb->pREQ; + status = SRB_STATUS_SUCCESS; + statusByte = 0; + + switch (Srb->Cdb[0]) + { + case SCSIOP_INQUIRY: + dprintk("SCSIOP_INQUIRY\n"); + + /* + * Filter out all TIDs but 0 and 1 since this is an IDE interface + * which support up to two devices. + */ + if ((pREQ->device->lun != 0) || + (!pChan->DeviceFlags[pREQ->device->id & 1] & DFLAGS_DEVICE_PRESENT)) + { + /* + * Indicate no device found at this address. + */ + status = SRB_STATUS_INVALID_TARGET_ID; + break; + } + else + { + request_buffer = Srb->DataBuffer; + inquiryData = Srb->DataBuffer; + + /* + * Zero INQUIRY data structure. + */ + memset(request_buffer, 0, Srb->DataTransferLength); + + /* + * Standard IDE interface only supports disks. + */ + inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; + + /* + * Device type modifer. + */ + request_buffer[1] = 0; + + /* + * No ANSI/ISO compliance. + */ + request_buffer[2] = 0; + + /* + * Additional length. + */ + request_buffer[4] = 31; + memcpy(&request_buffer[8], "ITE ", 8); + memcpy(&request_buffer[16], "IT8212F ", 16); + memcpy(&request_buffer[32], "1.3", 3); + + /* + * Set the removable bit, if applicable. + */ + if (pChan->DeviceFlags[pREQ->device->id & 1] & DFLAGS_REMOVABLE_DRIVE) + { + inquiryData->RemovableMedia = 1; + } + + status = SRB_STATUS_SUCCESS; + } + + break; + + case SCSIOP_MODE_SENSE: + status = SRB_STATUS_INVALID_REQUEST; + break; + + case SCSIOP_TEST_UNIT_READY: + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_READ_CAPACITY: + /* + * Claim 512 byte blocks (big-endian). + */ + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock = 0x20000; + + /* + * Calculate last sector. + */ + if (pChan->IdentifyData[pREQ->device->id & 0x01].UserAddressableSectors == 0x0FFFFFFF) + { + i = pChan->IdentifyData[pREQ->device->id & 0x01].Capacity_48bit_LOW - 1; + } + else + { + i = pChan->IdentifyData[pREQ->device->id & 0x01].UserAddressableSectors - 1; + } + + ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress = + (((unsigned char *)&i)[0] << 24) | (((unsigned char *)&i)[1] << 16) | + (((unsigned char *)&i)[2] << 8) | ((unsigned char *)&i)[3]; + + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_VERIFY: + status = IdeVerify(pChan, Srb); + break; + + case SCSIOP_READ: + case SCSIOP_WRITE: + status = IT8212ReadWrite(pChan, Srb); + break; + + case SCSIOP_START_STOP_UNIT: + /* + * Determine what type of operation we should perform + */ + status = SRB_STATUS_SUCCESS; + break; + + case SCSIOP_REQUEST_SENSE: + /* + * This function makes sense buffers to report the results + * of the original GET_MEDIA_STATUS command + */ + status = SRB_STATUS_INVALID_REQUEST; + break; + + default: + printk("IdeSendCommand: unsupported command %x\n", Srb->Cdb[0]); + status = SRB_STATUS_INVALID_REQUEST; + } /* end switch */ + + return status; + +} /* end IdeSendCommand */ + +/************************************************************************ + * This routine is called from the SCSI port driver synchronized with + * the kernel to start an IO request. If the current SRB is busy, return + * FALSE, else return TURE. + ************************************************************************/ +static void +AtapiStartIo(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + u32 status = 0; + + /* + * Determine which function. + */ + switch (Srb->Function) + { + case SRB_FUNCTION_EXECUTE_SCSI: + /* + * Sanity check. Only one request can be outstanding on a + * controller. + */ + if (pChan->CurrentSrb) + { + printk("AtapiStartIo: already have a request!\n"); + status = SRB_STATUS_BUSY; + Srb->SrbStatus = SRB_STATUS_BUSY; + goto busy; + } + + /* + * Indicate that a request is active on the controller. + */ + pChan->CurrentSrb = Srb; + Srb->SrbStatus = SRB_STATUS_PENDING; + + /* + * Send command to device. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) + { + /* + * If this is ATAPI device. + */ + status = AtapiSendCommand(pChan, Srb); + } + else if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_DEVICE_PRESENT) + { + /* + * If this is IDE device. + */ + status = IdeSendCommand(pChan, Srb); + } + else + { + /* + * Nothing else. + */ + status = SRB_STATUS_SELECTION_TIMEOUT; + } + + break; + + case SRB_FUNCTION_IO_CONTROL: + /* + * IO control function. + */ + printk("AtapiStartIo: IO control\n"); + break; + + default: + /* + * Indicate unsupported command. + */ + status = SRB_STATUS_INVALID_REQUEST; + break; + + } /* end switch */ + + busy: + if (status != SRB_STATUS_PENDING) + { + /* + * Set status in SRB. + */ + Srb->SrbStatus = (u8)status; + dprintk("AtapiStartIo: status=%x\n", status); + TaskDone(pChan, Srb); + } + +} /* end AtapiStartIo */ + +/************************************************************************ + * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. + ************************************************************************/ +static void +MapRequest(Scsi_Cmnd * pREQ, PSCSI_REQUEST_BLOCK Srb) +{ + + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + Srb->CdbLength = pREQ->cmd_len; + Srb->TargetId = pREQ->device->id; + Srb->Lun = pREQ->device->lun; + Srb->UseSg = pREQ->use_sg; + + /* + * Copy the actual command from Scsi_Cmnd to CDB. + */ + memcpy(Srb->Cdb, pREQ->cmnd, Srb->CdbLength); + + /* + * Always the SCSI_FUNCTION_EXECUTE_SCSI now. + */ + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + Srb->SrbStatus = 0; + Srb->ScsiStatus = 0; + Srb->SenseInfoBufferLength = 16; + + /* + * The CDB's first byte is operation code. + */ + if ((Srb->Cdb[0] == SCSIOP_WRITE6) || (Srb->Cdb[0] == SCSIOP_WRITE) || + (Srb->Cdb[0] == SCSIOP_MODE_SELECT10)) + { + Srb->SrbFlags = SRB_FLAGS_DATA_OUT; + } + else + { + Srb->SrbFlags = SRB_FLAGS_DATA_IN; + } + + Srb->TimeOutValue = 0; + Srb->SenseInfoBuffer = pREQ->sense_buffer; + + if (Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) + { + Srb->DataTransferLength = 0x40; + Srb->DataBuffer = pREQ->sense_buffer; + } + else + { + Srb->DataTransferLength = pREQ->request_bufflen; + Srb->DataBuffer = pREQ->request_buffer; + } + + if (pREQ->use_sg) + { + Srb->WorkingFlags |= SRB_WFLAGS_USE_SG; + } + + Srb->pREQ = pREQ; + +} /* end MapRequest */ + +/************************************************************************ + * A task execution has been done. For OS request, we need to Notify OS + * and invoke next take which wait at queue. + ************************************************************************/ +static void +TaskDone(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + Scsi_Cmnd * pREQ = Srb->pREQ; + + pChan->CurrentSrb = NULL; + pChan->RetryCount = 0; + + switch (SRB_STATUS(Srb->SrbStatus)) + { + case SRB_STATUS_SUCCESS: + pREQ->result = (DID_OK << 16); + break; + + case SRB_STATUS_SELECTION_TIMEOUT: + pREQ->result = (DID_NO_CONNECT << 16); + break; + + case SRB_STATUS_BUSY: + pREQ->result = (DID_BUS_BUSY << 16); + break; + + case SRB_STATUS_BUS_RESET: + pREQ->result = (DID_RESET << 16); + break; + + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_INVALID_PATH_ID: + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_NO_HBA: + pREQ->result = (DID_BAD_TARGET << 16); + break; + + case SRB_STATUS_NO_DEVICE: + pREQ->result = (DID_BAD_TARGET << 16); + break; + + case SRB_STATUS_ERROR: + pREQ->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + (CHECK_CONDITION << 1); + break; + } + + dprintk("TaskDone(pChan=%p, pREQ=%p, result=%x)\n", + pChan, pREQ, pREQ->result); + + /* + * Notify OS that this OS request has been done. + */ + pREQ->scsi_done(pREQ); + + /* + * Check the queue again. + */ + TaskQueue(); + +} /* end TaskDone */ + +/************************************************************************ + * Start a command, doing convert first. + ************************************************************************/ +static void +TaskStart(PChannel pChan, Scsi_Cmnd *pREQ) +{ + + PSCSI_REQUEST_BLOCK Srb; + + dprintk("TaskStart(pChan=%p, pREQ=%p)\n", pChan, pREQ); + + Srb = &pChan->_Srb; + + /* + * Clear the SRB structure. + */ + memset(Srb, 0, sizeof(SCSI_REQUEST_BLOCK)); + + /* + * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. + */ + MapRequest(pREQ, Srb); + + /* + * Start IDE I/O command. + */ + AtapiStartIo(pChan, Srb); + +} /* end TaskStart */ + +/************************************************************************ + * Check if queue is empty. If there are request in queue, transfer the + * request to HIM's request and execute the request. + ************************************************************************/ +static void +TaskQueue(void) +{ + + unsigned long flags; + Scsi_Cmnd * SCpnt; + PChannel pChan; + PITE_ADAPTER pAdap; + + check_next: + + if (it8212_req_last != NULL) + { + spin_lock_irqsave(&queue_request_lock, flags); + SCpnt = (Scsi_Cmnd *)it8212_req_last->SCp.ptr; + + if (it8212_req_last == SCpnt) + { + it8212_req_last = NULL; + } + else + { + it8212_req_last->SCp.ptr = (char *)SCpnt->SCp.ptr; + } + spin_unlock_irqrestore(&queue_request_lock, flags); + + /* + * Check the command. + */ + if (SCpnt->device->host) + { + if ((SCpnt->device->channel != 0) || + (SCpnt->device->id >= (4 * NumAdapters))) + { + /* + * Returns that we have a bad target. + */ + SCpnt->result = (DID_BAD_TARGET << 16); + SCpnt->scsi_done(SCpnt); + goto check_next; + } + } + + if (SCpnt->device->id >= 4) + { + pAdap = ite_adapters[1]; + if (SCpnt->device->id < 6) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + else + { + pAdap = ite_adapters[0]; + if (SCpnt->device->id < 2) pChan = &pAdap->IDEChannel[0]; + else pChan = &pAdap->IDEChannel[1]; + } + + TaskStart(pChan, SCpnt); + return; + } + +} /* end TaskQueue */ + +/**************************************************************** + * Name: iteraid_queuecommand + * Description: Process a queued command from the SCSI manager. + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * Returns: Status code. + ****************************************************************/ +int +iteraid_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + + unsigned long flags; + dprintk("##Queuecommand enter##\n"); + + /* + * Hooks the done routine. + */ + SCpnt->scsi_done = (void *)done; + + spin_lock_irqsave(&queue_request_lock, flags); + if (it8212_req_last == NULL) + { + SCpnt->SCp.ptr = (char *)SCpnt; + } + else + { + SCpnt->SCp.ptr = it8212_req_last->SCp.ptr; + it8212_req_last->SCp.ptr = (char *)SCpnt; + } + it8212_req_last = SCpnt; + spin_unlock_irqrestore(&queue_request_lock, flags); + + TaskQueue(); + + dprintk("@@Queuecommand exit@@\n"); + return 0; + +} /* end iteraid_queuecommand */ + +/**************************************************************** + * Name: internal_done :LOCAL + * Description: Done handler for non-queued commands + * Parameters: SCpnt - Pointer to SCSI command structure. + * Returns: Nothing. + ****************************************************************/ +static void +internal_done(Scsi_Cmnd * SCpnt) +{ + + SCpnt->SCp.Status++; + +} /* end internal_done */ + +/**************************************************************** + * Name: iteraid_command + * Description: Process a command from the SCSI manager. + * Parameters: SCpnt - Pointer to SCSI command structure. + * Returns: Status code. + ****************************************************************/ +int +iteraid_command(Scsi_Cmnd *SCpnt) +{ + + unsigned long timeout; + + SCpnt->SCp.Status = 0; + iteraid_queuecommand(SCpnt, internal_done); + + /* + * Should be longer than hard-reset time. + */ + timeout = jiffies + 60 * HZ; + while (!SCpnt->SCp.Status && time_before(jiffies, timeout)) + { + barrier(); + } + + if (!SCpnt->SCp.Status) SCpnt->result = (DID_ERROR << 16); + + return SCpnt->result; + +} /* end iteraid_command */ + +/************************************************************************ + * Enables/disables media status notification. + ************************************************************************/ +static void +IdeMediaStatus(u8 EnableMSN, PChannel pChan, u8 Device) +{ + + u8 statusByte; + u8 errorByte; + + statusByte = 0; + + if (EnableMSN == TRUE) + { + /* + * If supported enable Media Status Notification support. + */ + if ((pChan->DeviceFlags[Device] & DFLAGS_REMOVABLE_DRIVE)) + { + outb((u8) (0x95), pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + WaitOnBaseBusy(pChan, statusByte); + + if (statusByte & IDE_STATUS_ERROR) + { + /* + * Read the error register. + */ + errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]); + + printk("IdeMediaStatus: error enabling media status. status %u, error byte %u\n", + statusByte, errorByte); + } + else + { + pChan->DeviceFlags[Device] |= DFLAGS_MEDIA_STATUS_ENABLED; + printk("IdeMediaStatus: media status notification supported!\n"); + pChan->ReturningMediaStatus = 0; + } + } + } + else /* end if EnableMSN == TRUE */ + { + /* + * Disable if previously enabled. + */ + if ((pChan->DeviceFlags[Device] & DFLAGS_MEDIA_STATUS_ENABLED)) + { + outb((u8)(0x31), pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, + pChan->io_ports[IDE_COMMAND_OFFSET]); + + WaitOnBaseBusy(pChan, statusByte); + pChan->DeviceFlags[Device] &= ~DFLAGS_MEDIA_STATUS_ENABLED; + } + } + +} /* end IdeMediaStatus */ + +/************************************************************************ + * Issue IDENTIFY command to a device. + * Either the standard (EC) or the ATAPI packet (A1) IDENTIFY. + ************************************************************************/ +static u8 +IssueIdentify(PChannel pChan, u8 DeviceNumber, u8 Command) +{ + + u8 statusByte = 0; + u32 i; + u32 j; + + /* + * Check that the status register makes sense. + */ + GetBaseStatus(pChan, statusByte); + + if (Command == IDE_COMMAND_IDENTIFY) + { + /* + * Mask status byte ERROR bits. + */ + statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX); + + dprintk("IssueIdentify: checking for IDE. status (%x)\n", statusByte); + + /* + * Check if register value is reasonable. + */ + if (statusByte != IDE_STATUS_IDLE) + { + /* + * Reset the channel. + */ + printk("IssueIdentify: resetting channel.\n"); + IdeHardReset(pChan, statusByte); + + outb((u8)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + statusByte &= ~IDE_STATUS_INDEX; + + if (statusByte != IDE_STATUS_IDLE) + { + /* + * Give up on this. + */ + printk("IssueIdentify(IDE): disk[%d] not ready. status=0x%x\n", + DeviceNumber, statusByte); + return FALSE; + } + } + } + else + { + dprintk("IssueIdentify: checking for ATAPI. status (%x)\n", statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) + { + /* + * Reset the device. + */ + dprintk("IssueIdentify: resetting device.\n"); + AtapiSoftReset(pChan, DeviceNumber); + + outb((u8)((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + if (statusByte != 0) + { + /* + * Give up on this. + */ + printk("IssueIdentify(ATAPI): disk[%d] not ready. status=0x%x\n", + DeviceNumber, statusByte); + return FALSE; + } + } + } + + for (j = 0; j < 2; j++) + { + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + outb((u8)((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) + { + printk("IssueIdentify: disk[%d] not ready. status=0x%x\n", + DeviceNumber, statusByte); + continue; + } + + /* + * Send IDENTIFY command. + */ + outb(Command, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for DRQ. + */ + WaitForBaseDrq(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) + { + printk("IssueIdentify: disk[%d] DRQ never asserted. status=%x\n", + DeviceNumber, statusByte); + + /* + * Give one more chance. + */ + if (Command == IDE_COMMAND_IDENTIFY) + { + IdeHardReset(pChan, statusByte); + } + else + { + AtapiSoftReset(pChan, DeviceNumber); + } + } + else + { + break; + } + } + + /* + * Check for error on really stupid master devices that assert random + * patterns of bits in the status register at the slave address. + */ + outb((u8)((DeviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_ERROR) + { + printk("IssueIdentify: disk[%d] returns error status\n", DeviceNumber); + return FALSE; + } + + dprintk("IssueIdentify: status before read words %x\n", statusByte); + + /* + * Suck out 256 words. After waiting for one model that asserts busy + * after receiving the Packet Identify command. + */ + WaitOnBusy(pChan, statusByte); + + if (!(statusByte & IDE_STATUS_DRQ)) { return FALSE; } + + ReadBuffer(pChan, (unsigned short *)&pChan->FullIdentifyData, 256); + + /* + * Check out a few capabilities / limitations of the device. + * 01/29/2003 + */ + if (pChan->FullIdentifyData.SpecialFunctionsEnabled & 1) + { + /* + * Determine if this drive supports the MSN functions. + */ + printk("Marking drive %x as removable. SFE = %x\n", + DeviceNumber, pChan->FullIdentifyData.SpecialFunctionsEnabled); + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE; + } + + memcpy(&pChan->IdentifyData[DeviceNumber], &pChan->FullIdentifyData, + sizeof(IDENTIFY_DATA2)); + + if (pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0x20 && + Command != IDE_COMMAND_IDENTIFY) + { + /* + * This device interrupts with the assertion of DRQ after receiving + * Atapi Packet Command. + */ + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_INT_DRQ; + + dprintk(KERN_NOTICE "Device interrupts on assertion of DRQ.\n"); + } + else + { + dprintk(KERN_NOTICE "Device does't interrupt on assertion of DRQ.\n"); + } + + if (((pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0xF00) + == 0x100) && Command != IDE_COMMAND_IDENTIFY) + { + /* + * This is a tape. + */ + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_TAPE_DEVICE; + printk(KERN_NOTICE "IssueIdentify: device is a tape drive.\n"); + } + else + { + dprintk(KERN_NOTICE "IssueIdentify: device is not a tape drive.\n"); + } + + /* + * Work around for some IDE and one model Atapi that will present more + * then 256 bytes for the Identify data. + */ + WaitOnBaseBusy(pChan, statusByte); + + for (i = 0; i < 0x10000; i++) + { + GetStatus(pChan, statusByte); + + if (statusByte & IDE_STATUS_DRQ) + { + /* + * Suck out any remaining bytes and throw away. + */ + inw(pChan->io_ports[IDE_DATA_OFFSET]); + } + else + { + break; + } + } + + return TRUE; + +} /* end IssueIdentify() */ + +/************************************************************************ + * Check this is the IDE or ATAPI disk then identify it. + ************************************************************************/ +static u8 +iteraid_find_device(PChannel pChan, u8 channel) +{ + + u8 deviceNumber; + u8 signatureLow; + u8 signatureHigh; + u8 deviceResponded = FALSE; + u8 statusByte = 0; + + /* + * Clear expecting interrupt flag and current SRB field. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->CurrentSrb = NULL; + + /* + * Search for devices in each channel. + */ + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) + { + /* + * Select the device. + */ + outb((u8)((deviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Disable interrupts during initialization. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Check here for some SCSI adapters that incorporate IDE emulation. + */ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Do soft reset on selected device. (AtapiSoftReset) + */ + AtapiSoftReset(pChan, deviceNumber); + WaitOnBusy(pChan, statusByte); + + signatureLow = inb(pChan->io_ports[IDE_MIDCYL_OFFSET]); + signatureHigh = inb(pChan->io_ports[IDE_HCYL_OFFSET]); + + if (signatureLow == 0x14 && signatureHigh == 0xEB) + { + /* + * ATAPI signature found. Issue ATAPI packet identify command. + */ + if (IssueIdentify(pChan, deviceNumber, IDE_COMMAND_ATAPI_IDENTIFY)) + { + /* + * Indicate ATAPI device. + */ + printk("iteraid_find_device: channel %x device %x is ATAPI.\n", + channel, deviceNumber); + + pChan->DeviceFlags[deviceNumber] |= DFLAGS_ATAPI_DEVICE; + pChan->DeviceFlags[deviceNumber] |= DFLAGS_DEVICE_PRESENT; + pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_CONFIG_CHANGED; + + deviceResponded = TRUE; + + GetStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_ERROR) + { + AtapiSoftReset(pChan, deviceNumber); + } + } + else + { + /* + * Indicate no working device. + */ + printk("iteraid_find_device: channel %x device %x doesn't respond.\n", + channel, deviceNumber); + pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_DEVICE_PRESENT; + } + } + else + { + /* + * Select the device. + */ + outb((u8)((deviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check here for some SCSI adapters that incorporate IDE emulation. + */ + GetStatus(pChan, statusByte); + + /* + * No Disk. + */ + if (statusByte == 0xFF || statusByte == 0x7F || statusByte == 0x0) + { + dprintk("FindDevices: cannot find IDE device. status = %x\n", statusByte); + continue; + } + + /* + * Issue IDE Identify. If an ATAPI device is actually present, + * the signature will be asserted, and the drive will be + * recognized as such. + */ + if (IssueIdentify(pChan, deviceNumber, IDE_COMMAND_IDENTIFY)) + { + /* + * IDE drive found. + */ + printk(KERN_WARNING "FindDevices: device %u is IDE\n", + (channel * 2) + deviceNumber); + pChan->DeviceFlags[deviceNumber] |= DFLAGS_DEVICE_PRESENT; + pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_ATAPI_DEVICE; + pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_CONFIG_CHANGED; + deviceResponded = TRUE; + } + else + { + printk(KERN_WARNING "FindDevices: device %u is not present\n", + (channel * 2) + deviceNumber); + pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_DEVICE_PRESENT; + } + } + } + + return deviceResponded; + +} /* end iteraid_find_device */ + +/************************************************************************ + * IDE disk hardware initialize. + ************************************************************************/ +u8 +AtapiHwInitialize(PITE_ADAPTER pAdap, PChannel pChan, u8 channel) +{ + + u8 i; + u8 statusByte = 0; + + /* + * For two devices in this channel. + */ + for (i = 0; i < 2; i++) + { + /* + * only check in Fireware mode. + */ + if (pAdap->bypass_mode == FALSE) + { + outb((u8) (0xA0 | ((u8) i << 4)), pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check if card at this address. + */ + outb(0xAA, pChan->io_ports[IDE_MIDCYL_OFFSET]); + + /* + * Check if indentifier can be read back. + */ + if ((statusByte = inb(pChan->io_ports[IDE_MIDCYL_OFFSET])) != 0xAA) + { + printk("AtapiHwInitialize: identifier read back from (%x, %x) = %x\n", + channel, i, statusByte); + + /* + * ***** Dont free it....For later use ***** + * ScsiPortFreeDeviceBase(HwDeviceExtension, ioSpace1); + */ + continue; + } + + printk("AtapiHwInitialize: found ATA device (%x, %x)n", channel, i); + } + + } + + return TRUE; + +} /* end AtapiHwInitialize */ + +/************************************************************************ + * Initialize a adapter, return 0 means success. + ************************************************************************/ +static int +iteraid_init(PITE_ADAPTER pAdap, struct pci_dev *pPciDev) +{ + + u8 z; + u8 i; + u8 j; + u8 set_irq; + unsigned long control_addr; /* Control reg base address */ + unsigned long base_addr; /* IDE I/O port base address */ + unsigned long bm_base_addr; /* Bus Master base address */ + PChannel pChan; /* Use for each channel */ + + dprintk("iteraid_init enter\n"); + + /* + * Common settings. + */ + pAdap->pci_bus = pPciDev->bus->number; + pAdap->devfn = pPciDev->devfn; + pAdap->irq = pPciDev->irq; + pAdap->irqOwned = 0; + + printk(KERN_NOTICE "Found Controller: %s\n", pAdap->name); + + /* + * Allocate buffer for IDE channles (One IT8212 supports two channels) + */ + pAdap->IDEChannel = + (PChannel)kmalloc(sizeof(Channel) * pAdap->num_channels, GFP_ATOMIC); + + if (pAdap->IDEChannel == 0) + { + printk("iteraid_init: pChan allocate failed.\n"); + return -1; + } + + memset(pAdap->IDEChannel, 0, sizeof(Channel) * pAdap->num_channels); + + set_irq = 1; + for (i = 0; i < NumAdapters; i++) + { + if (ite_adapters[i]->irqOwned == pAdap->irq) set_irq = 0; + } + + /* + * Request the irq (share irq) and hook the interrupt service routine. + */ + if (set_irq) + { + if (request_irq(pAdap->irq, Irq_Handler, SA_SHIRQ, PROC_DIR_NAME, pAdap) < 0) + { + printk("iteraid_init: unable to allocate IRQ for %s\n", pAdap->name); + return -1; + } + pAdap->irqOwned = pAdap->irq; + } + + /* + * Get the IDE port and DMA registers. + */ + for (i = 0; i < pAdap->num_channels; i++) + { + pChan = &pAdap->IDEChannel[i]; + /* + * Reference the book "LINUX DEVICE DRIVER 2nd", Page 484 + * unsigned long pci_resource_start(struct pci_dev *dev, int bar); + */ + base_addr = pci_resource_start(pPciDev, i * 2); + control_addr = pci_resource_start(pPciDev, i * 2 + 1); + bm_base_addr = pci_resource_start(pPciDev, 4); + pChan->dma_base = bm_base_addr + i * 8; + for (j = 0; j <= IDE_STATUS_OFFSET; j++) + { + pChan->io_ports[j] = base_addr; + base_addr += 1; + } + pChan->io_ports[IDE_CONTROL_OFFSET] = control_addr + 2; + } + + /* + * Initialize channels. + */ + for (z = 0; z < pAdap->num_channels; z++) + { + pChan = &pAdap->IDEChannel[z]; + pChan->pPciDev = pPciDev; + pChan->channel = z; + + /* + * This section should be masked off if BIOS is ready. + */ +# if (MARK_DEBUG_BYPASS_MODE) + /* + * BIOS is not ready, so I change to ByPass Mode by myself. + */ + pAdap->bypass_mode = TRUE; + + /* + * Change to bypass mode. + */ + IT8212InitBypassMode(pPciDev); +# endif + + /* + * Hardware initialize. + */ +# if (0) + AtapiHwInitialize(pAdap, pChan, z); +# endif + + /* + * Find and identify the IDE or ATAPI device. + */ + iteraid_find_device(pChan, z); + + /* + * Set the best transfer mode. + */ +# if (MARK_SET_BEST_TRANSFER) + IT8212SetBestTransferMode(pAdap, pChan, z); +# endif + + /* + * Set Scatter/Gather List buffer for the channel. + */ + IdeSetupDma(pChan, pChan->dma_base, 8); + } + + dprintk("iteraid_init exit\n"); + return 0; + +} /* end iteraid_init */ + +/************************************************************************ + * This function will find and initialize any cards. + ************************************************************************/ +int +iteraid_detect(Scsi_Host_Template *tpnt) +{ + + u8 i; + u8 j; + u8 mode; + u8 pci_id; + PChannel pChan; + PITE_ADAPTER pAdap; + struct pci_dev * pPciDev; + dprintk("iteraid_detect enter\n"); + + /* + * Search ITE IT8212 chip. + */ + pPciDev = NULL; + pci_id = 0; + while ((pPciDev = pci_find_device(ITE_VENDOR_ID, ITE_DEVICE_ID, pPciDev))) + { + if (PCI_FUNC(pPciDev->devfn)) continue; + + /* + * Allocate memory for Adapter. + */ + pAdap = (PITE_ADAPTER)kmalloc(sizeof(ITE_ADAPTER), GFP_ATOMIC); + if (pAdap == NULL) + { + printk("iteraid_detect: pAdap allocate failed.\n"); + continue; + } + + memset(pAdap, 0, sizeof(ITE_ADAPTER)); + pAdap->name = CONTROLLER_NAME_IT8212; + pAdap->num_channels = 2; + pAdap->pci_dev = pPciDev; + + /* + * Check if we are in bypass(transparent) or firmware mode. + */ + pci_read_config_byte(pPciDev, 0x50, &mode); + if (mode & 1) + { + dprintk("Firmware mode in PCI#%d\n", pci_id); + pAdap->bypass_mode = FALSE; + } + else + { + dprintk("Transparent mode in PCI#%d\n", pci_id); + pAdap->bypass_mode = TRUE; + } + + if (iteraid_init(pAdap, pPciDev) == 0) + { + ite_adapters[NumAdapters++] = pAdap; + } + pci_id++; + } + + /* + * Reenable interrupt after initialization. 2003/04/28 + */ + for (i = 0; i < NumAdapters; i++) + { + pAdap = ite_adapters[i]; + for (j = 0; j < pAdap->num_channels; j++) + { + pChan = &pAdap->IDEChannel[j]; + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + } + } + + if (NumAdapters) + { + /* + * Register a virtual host. + */ + ite_vhost = scsi_register(tpnt, 0); + + ite_vhost->io_port = 0; + ite_vhost->n_io_port = 0; + ite_vhost->max_channel = 0; + ite_vhost->max_id = MAX_DEVICES; + ite_vhost->max_lun = 1; + +# if (0) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + scsi_set_device(ite_vhost, &pPciDev->dev); +# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) + scsi_set_pci_device(ite_vhost, pPciDev); +# endif +# endif + + /* + * Register the driver as a character device, for applications to + * acess it for ioctls. Ideally, this should go in the init_module() + * routine, but since it is hidden in the file "scsi_module.c" ( + * included in the end), we define it here. First argument (major) + * to register_chrdev implies a dynamic major number allocation. + */ + ite_major = register_chrdev(0, "itedev", &itedev_fops); + + /* + * Register the Shutdown Notification hook in the kernel. + */ + register_reboot_notifier(&ite_notifier); + + /* + * Initialize ioctl semphore. + */ + init_MUTEX(&mimd_entry_mtx); + } + + dprintk("iteraid_detect exit\n"); + return 1; + +} /* end iteraid_detect() */ + +/************************************************************************ + * Name: iteraid_release + * Description: Release resources allocated for a single each adapter. + * Parameters: pshost - Pointer to SCSI command structure. + * Returns: zero. + ************************************************************************/ +int +iteraid_release(struct Scsi_Host *pshost) +{ + + u8 i; + PITE_ADAPTER pAdap; + + /* + * Unregister the character device. + */ + if (ite_major > 0) + { + unregister_chrdev(ite_major, "itedev"); + ite_major = -1; + } + + /* + * Free irq and memory. + */ + for (i = 0; i < NumAdapters; i++) + { + pAdap = ite_adapters[i]; + + if (pAdap->irqOwned) free_irq(pAdap->irq, pAdap); + if (pAdap->IDEChannel != NULL) { kfree(pAdap->IDEChannel); } + if (pAdap != NULL) { kfree(pAdap); } + } + + /* + * Unregister the reboot notifier. + */ + unregister_reboot_notifier(&ite_notifier); + + /* + * Tell kernel scsi-layer we are gone. + */ + scsi_unregister(pshost); + + return 0; + +} /* end iteraid_Release */ + +/************************************************************************ + * This is the new scsi eh reset function. + ************************************************************************/ +int +iteraid_reset_eh(Scsi_Cmnd *SCpnt) +{ + + u8 i; + u8 j; + PChannel pChan; + PITE_ADAPTER pAdap; + + if (SCpnt == NULL) + { + printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); + return FAILED; + } + + for (i = 0; i < NumAdapters; i++) + { + pAdap = ite_adapters[i]; + for (j = 0; j < pAdap->num_channels; j++) + { + pChan = &pAdap->IDEChannel[j]; + AtapiResetController(pAdap, pChan); + } + } + + return SUCCESS; + +} /* end iteraid_reset_eh */ + +/************************************************************************ + * The new error handling code. + ************************************************************************/ +int +iteraid_abort_eh(Scsi_Cmnd *SCpnt) +{ + + if (SCpnt == NULL) + { + printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); + return FAILED; + } + + return SUCCESS; + +} /* end iteraid_abort_eh */ + +/************************************************************************ + * Name: iteraid_biosparam + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * Returns: zero. + ************************************************************************/ +int +iteraid_biosparam +( + struct scsi_device * sdev, + struct block_device * bdev, + sector_t capacity, + int geom[] +) +{ + + int heads; + int sectors; + int cylinders; + + /* + * Default heads (64) & sectors (32) + * Handle extended translation size for logical drives > 1Gb + */ + if (capacity >= 0x200000) + { + heads = 255; + sectors = 63; + } + else + { + heads = 64; + sectors = 32; + } + + cylinders = (unsigned long)capacity / (heads * sectors); + + /* + * Return result + */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; + +} /* end iteraid_biosparam */ + +/************************************************************************ + * Shutdown routine. + ************************************************************************/ +static int +ite_halt(struct notifier_block *nb, ulong event, void *buf) +{ + + if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) + { + return NOTIFY_DONE; + } + + unregister_reboot_notifier(&ite_notifier); + + return NOTIFY_OK; + +} /* end ite_halt */ + +/************************************************************************ + * PROC information. + ************************************************************************/ +int +iteraid_proc_info +( + struct Scsi_Host * shost, + char * buffer, + char ** start, + off_t offset, + int length, + int inout +) +{ + + return 0; + +} /* end iteraid_proc_info */ + +/************************************************************************ + * IOCTL open entry. + ************************************************************************/ +static int +itedev_open(struct inode *inode, struct file *filep) +{ + + MOD_INC_USE_COUNT; + + return 0; + +} /* end itedev_open */ + +/************************************************************************ + * IOCTL code entry. + ************************************************************************/ +static int +itedev_ioctl_entry +( + struct inode * inode, + struct file * filep, + unsigned int cmd, + unsigned long arg +) +{ + + int ret = -1; + + /* + * We do not allow parallel ioctls to the driver as of now. + */ + down(&mimd_entry_mtx); + ret = itedev_ioctl(inode, filep, cmd, arg); + up(&mimd_entry_mtx); + + return ret; + +} /* end itedev_ioctl_entry */ + +/************************************************************************ + * Real IOCTL function handles ioctl for the character device. + ************************************************************************/ +static int +itedev_ioctl +( + struct inode * inode, + struct file * filep, + unsigned int cmd, + unsigned long arg +) +{ + + u8 diskArrayId; + u8 statusByte = 0; + u8 srbStatus; + u8 progress = 0; + u8 status = 0; + uioctl_t * pioc; + PITE_ADAPTER pAdap; + PChannel pChan; + PRAID_REBUILD_INFO rebuild_info; + dprintk("itedev_ioctl enter\n"); + + /* + * Extract the type and number bitfield. + */ + if (_IOC_TYPE(cmd) != ITE_IOCMAGIC) return -EINVAL; + + /* + * Allocate space for ioctl data structure. + */ + if ((pioc = kmalloc(sizeof(uioctl_t), GFP_KERNEL)) == NULL) + { + printk("itedev_ioctl: error kmalloc on ioctl\n"); + return -ENOMEM; + } + + /* + * Get the user ioctl structure. + */ + if (copy_from_user(pioc, (uioctl_t *)arg, sizeof(uioctl_t))) + { + kfree(pioc); + return -EFAULT; + } + + /* + * Check which command to do. + */ + switch (cmd) + { + case ITE_IOC_GET_PHY_DISK_STATUS: + dprintk("ITE_IOC_GET_PHY_DISK_STATUS\n"); + + /* + * Get the physical disk status. + */ + status = IT8212GetChipStatus(pioc); + return 0; + + case ITE_IOC_CREATE_DISK_ARRAY: + dprintk("ITE_IOC_CREATE_DISK_ARRAY\n"); + + /* + * Create disk array. + */ + status = IT8212CreateDiskArray(pioc); + + if (status != SRB_STATUS_SUCCESS) return status; + + status = IT8212ErasePartition(pioc); + return 0; + + case ITE_IOC_REBUILD_START: + dprintk("ITE_IOC_REBUILD_START\n"); + + /* + * Rebuild array. + */ + status = IT8212Rebuild(pioc); + put_user(status, (u8 *)pioc->data); + return 0; + + case ITE_IOC_GET_REBUILD_STATUS: + dprintk("ITE_IOC_GET_REBUILD_STATUS\n"); + + pAdap = ite_adapters[0]; + + /* + * Get the rebuild disk ID. + */ + rebuild_info = (PRAID_REBUILD_INFO) pioc->data; + diskArrayId = rebuild_info->DiskArrayId; + + /* + * Select channel. + */ + if (diskArrayId < 2) + { + pChan = &pAdap->IDEChannel[0]; + } + else + { + pChan = &pAdap->IDEChannel[1]; + } + + /* + * Select device. + */ + outb(((u8) (diskArrayId << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || + (statusByte == 0)) + { + printk("IT8212GetRebuildStatus: Disk[%d] busy. Status=0x%X\n", + diskArrayId, statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue command. + */ + outb(IDE_COMMAND_REBUILD_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + if (statusByte != IDE_STATUS_IDLE) + { + srbStatus = SRB_STATUS_ERROR; + printk("GetRebuildStatus: ERROR\n"); + goto exit; + } + + progress = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); + srbStatus = SRB_STATUS_SUCCESS; + + if (progress != 0xFF) progress = 0x64 - progress; + + /* + * Put the rebuild status to user space. + */ + put_user(progress, (u8 *)pioc->data); + +exit: + return 0; + + case ITE_IOC_RESET_ADAPTER: + dprintk("ITE_IOC_RESET_ADAPTER\n"); + + /* + * Reset the adapter. + */ +# if (0) + status = IT8212ResetAdapter(); +# endif + + /* + * Return TURE or FALSE to user space. + */ + put_user(status, (u8 *)arg); + return 0; + + case ITE_IOC_GET_DRIVER_VERSION: + dprintk("ITE_IOC_GET_DRIVER_VERSION\n"); + + /* + * Get the current driver version. + */ + put_user(driver_ver, (int *)arg); + return 0; + + default: + return -EINVAL; + + } /* end switch */ + +} /* end itedev_ioctl */ + +/************************************************************************ + * IOCTL close routine. + ************************************************************************/ +static int +itedev_close(struct inode *inode, struct file *filep) +{ + + MOD_DEC_USE_COUNT; + + return 0; + +} /* end itedev_close */ + +/************************************************************************ + * Scsi_Host_Template Initializer + ************************************************************************/ +static Scsi_Host_Template driver_template = +{ + .proc_name = "IT8212", + .proc_info = iteraid_proc_info, + .name = "ITE RAIDExpress133", + .detect = iteraid_detect, + .release = iteraid_release, + .queuecommand = iteraid_queuecommand, + .eh_abort_handler = iteraid_abort_eh, + .eh_host_reset_handler = iteraid_reset_eh, + .bios_param = iteraid_biosparam, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = 32, + .max_sectors = 256, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .emulated = 1 +}; +#include "scsi_module.c" diff -Nur --exclude '*.orig' linux-2.6.8.1.org/drivers/scsi/iteraid.h linux-2.6.8.1/drivers/scsi/iteraid.h --- linux-2.6.8.1.org/drivers/scsi/iteraid.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.8.1/drivers/scsi/iteraid.h 2004-05-11 10:09:04.000000000 +0200 @@ -0,0 +1,1514 @@ +/* + * linux/drivers/scsi/iteraid.h + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ITE IT8212 RAID Controller device driver header for Linux. + */ + +#ifndef _ITERAID_H_ +#define _ITERAID_H_ + +#include +#include + +#define ITE_VENDOR_ID 0x1283 /* Vendor ID (ITE) */ +#define ITE_DEVICE_ID 0x8212 /* Device IF (IT8212) */ +#define MAX_ADAPTERS 2 /* Max Board supported */ +#define MAX_DEVICES (MAX_ADAPTERS * 4) /* Max Dev supported */ + +#define TRUE 1 +#define FALSE 0 + +/* + * Undef macros which may conflict + */ +#undef START_STOP + +/************************************************************************ + * Debugging macro + ************************************************************************/ +#ifdef ITE_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + +/************************************************************************ + * Raid level definitions + ************************************************************************/ +#define RAID_LEVEL_0 0 +#define RAID_LEVEL_1 1 +#define RAID_LEVEL_10 2 +#define RAID_LEVEL_JBOD 3 +#define RAID_LEVEL_NORMAL 4 +#define RAID_LEVEL_NODISK 5 + +/************************************************************************ + * Physical disk status definitions + ************************************************************************/ +#define DISK_KEY_OFF 0 +#define DISK_OFF_LINE 1 +#define DISK_ON_LINE 2 +#define DISK_REBUILDING 3 +#define DISK_PLUGGING 4 +#define DISK_PLUGGING_OK 5 + +#define MaximumLBAOf28Bit 0x10000000 + +#define DisableChannel 1 +#define EnableChannel 2 + +#define CABLE_40_PIN 0 +#define CABLE_80_PIN 1 + +#define RaidActive 0 +#define RaidInactive 1 + +#define IDE_CLOCK_66 0 +#define IDE_CLOCK_50 1 + +#define USE_ULTRA_DMA 0 +#define USE_MULTIWORD_DMA 1 + +/************************************************************************ + * Vendor specific information + ************************************************************************/ +typedef struct _PHYSICAL_DISK_STATUS +{ + u8 ModelNumber[40]; /* Byte 00-39 */ + u32 UserAddressableSectors_LOW; /* Byte 40-43 */ + u32 UserAddressableSectors_HIGH; /* Byte 44-47 */ + u8 MultiWordDMASupport; /* Byte 48 */ + u8 MultiWordDMAActive; /* Byte 49 */ + u8 UltraDMASupport; /* Byte 50 */ + u8 UltraDMAActive; /* Byte 51 */ + u8 RaidType; /* Byte 52 */ + u8 RaidNumber; /* Byte 53 */ + u8 SerialNumber[20]; /* Byte 54-73 */ + u8 DiskStatus; /* Byte 74 */ + u8 DiskOriginalLocation; /* Byte 75 */ + u8 Cable80Pin; /* Byte 76 */ + u8 BootableDisk; /* Byte 77 */ + u8 StorageSize[8]; /* Byte 78-85 */ + u8 Reserved[35]; /* Byte 86-120 */ + u8 UpdateYear; /* Byte 121 */ + u8 UpdateMonth; /* Byte 122 */ + u8 UpdateDay; /* Byte 123 */ + u8 FirmwareVer; /* Byte 124 */ + u8 RebuildStatus; /* Byte 125 */ + u8 StripeSize; /* Byte 126 */ + u8 AutoRebuildEnable; /* Byte 127 */ +} PHYSICAL_DISK_STATUS, *PPHYSICAL_DISK_STATUS; + +/************************************************************************ + * vendor specific information + ************************************************************************/ +typedef struct _IT8212_SET_CHIP_STATUS_INFO +{ + u16 RaidType; /* Word 129 */ + u16 ContainingDisks; /* Word 130 */ + u16 UltraDmaTiming01; /* Word 131 */ + u16 UltraDmaTiming23; /* Word 132 */ + u16 UltraDmaTiming45; /* Word 133 */ + u16 UltraDmaTiming6; /* Word 134 */ + u16 MultiWordDmaTiming01; /* Word 135 */ + u16 UltraDmaTiming2; /* Word 136 */ + u16 PioTiming4; /* Word 137 */ + u16 AutoRebuildEnable; /* Word 138 */ + u16 IdeClkUDma01; /* Word 139 */ + u16 IdeClkUDma23; /* Word 140 */ + u16 IdeClkUDma45; /* Word 141 */ + u16 IdeClkUDma6; /* Word 142 */ + u16 IdeClkMDma01; /* Word 143 */ + u16 IdeClkMDma2; /* Word 144 */ + u16 IdeClkPio4; /* Word 145 */ + u16 StripeSize; /* Word 146 */ + u16 BootableDisk; /* Word 147 */ + u16 CheckHotSwapInterval; /* Word 148 */ + u16 TargetSourceDisk; /* Word 149 */ + u16 RebuildBlockSize; /* Word 150 */ + u16 ResetInterval1; /* Word 151 */ + u16 ResetInterval2; /* Word 152 */ + u16 RebuildRetryTimes; /* Word 153 */ + u16 NewlyCreated; /* Word 154 */ +} IT8212_SET_CHIP_STATUS_INFO, *PIT8212_SET_CHIP_STATUS_INFO; + +/************************************************************************ + * Serial number written to HDD (20 bytes) + ************************************************************************/ +typedef struct _RAID_SERIAL_NUMBER +{ + u16 Year; + u8 Month; + u8 Date; + u8 Day; + u8 Hour; + u8 Minute; + u8 Second; + u8 MiniSec; + u8 RaidType; + u8 ContainingDisks; + u8 DontCare[9]; +} RAID_SERIAL_NUMBER, *PRAID_SERIAL_NUMBER; + +/************************************************************************ + * Disk array create information + * + * Following items index definition + * 0: Primary Master + * 1: Secondary Master + * 2: Primary Slave + * 3: Secondary Slave + ************************************************************************/ +typedef struct _RAID_CREATE_INFO +{ + u8 DiskArrayId; + RAID_SERIAL_NUMBER SerialNumber; + u8 ModelNumber[40]; + u16 RaidType; + u16 ContainingDisks; + u16 AutoRebuildEnable; + u16 StripeSize; + u16 BootableDisk; + u16 TargetSourceDisk; + u8 ErasePartition; + u8 DMASupported[4]; + u8 UDMASupported[4]; + u8 AddressableSectors[4]; + u8 NewlyCreated; + u8 Reserved; +} RAID_CREATE_INFO, *PRAID_CREATE_INFO; + +/************************************************************************ + * Rebuild data structure + ************************************************************************/ +typedef struct _RAID_REBUILD_INFO +{ + u8 DiskArrayId; /* Virtual device number (0-3) */ + u8 SrcDisk; /* Source disk (0-3) */ + u8 DestDisk; /* Destination disk (0-3) */ + u8 Resume; /* 1: Resume the last time rebuild */ + /* 0: Rebuild from LBA 0 */ + u8 Status; /* Indicate the status of the current */ + /* rebuild command filled by drivers */ + u8 Reserved[3]; /* For aligement */ +} RAID_REBUILD_INFO, *PRAID_REBUILD_INFO; + +/************************************************************************ + * ATA transfer modes + ************************************************************************/ +#define PIO_DEFAULT 0x00 +#define PIO_DEFAULT_IORDY_DISABLE 0x01 +#define PIO_FLOW_CONTROL 0x08 +#define SINGLEWORD_DMA 0x10 +#define MULTIWORD_DMA 0x20 +#define ULTRA_DMA 0x40 + +#define ITE_DRV_SIGNATURE "ITE RAID CONTROLLER" +#define ITE_DRV_BYPASS "ITE BYPASS MODE" + +/************************************************************************ + * Extra IDE commands supported by Accusys + ************************************************************************/ +#define IDE_COMMAND_GET_CHIP_STATUS 0xFA +#define IDE_COMMAND_SET_CHIP_STATUS 0xFB +#define IDE_COMMAND_REBUILD 0xFC +#define IDE_COMMAND_REBUILD_STATUS 0xFD + +#define REBUILD_ERR_WRONG_ARRAY_TYPE 0x01 +#define REBUILD_ERR_DISK_TOO_SMALL 0x02 +#define REBUILD_ERR_SRC_DISK_LOCATION_INCORRECT 0x03 +#define REBUILD_ERR_SRC_DISK_OFFLINE 0x04 +#define REBUILD_ERR_DEST_DISK_OFFLINE 0x05 +#define REBUILD_ERR_DISK_BUSY 0x10 + +/************************************************************************ + * ATA transfer modes + ************************************************************************/ +#define PIO_DEFAULT 0x00 +#define PIO_DEFAULT_IORDY_DISABLE 0x01 +#define PIO_FLOW_CONTROL 0x08 +#define SINGLEWORD_DMA 0x10 +#define MULTIWORD_DMA 0x20 +#define ULTRA_DMA 0x40 + +/************************************************************************ + * IDE registers offset + ************************************************************************/ +#define IDE_NR_PORTS 10 + +#define IDE_DATA_OFFSET 0 +#define IDE_ERROR_OFFSET 1 +#define IDE_NSECTOR_OFFSET 2 +#define IDE_LOCYL_OFFSET 3 +#define IDE_MIDCYL_OFFSET 4 +#define IDE_HCYL_OFFSET 5 +#define IDE_SELECT_OFFSET 6 +#define IDE_STATUS_OFFSET 7 +#define IDE_CONTROL_OFFSET 8 +#define IDE_IRQ_OFFSET 9 + +#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET +#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET +#define IDE_ALTERNATE_OFFSET IDE_CONTROL_OFFSET + +/************************************************************************ + * ATAPI registers offset + ************************************************************************/ +#define ATAPI_DATA_OFFSET 0 +#define ATAPI_ERROR_OFFSET 1 +#define ATAPI_INTREASON_OFFSET 2 +#define ATAPI_UNUSED1_OFFSET 3 +#define ATAPI_LCYL_OFFSET 4 +#define ATAPI_HCYL_OFFSET 5 +#define ATAPI_SELECT_OFFSET 6 +#define ATAPI_STATUS_OFFSET 7 +#define ATAPI_CONTROL_OFFSET 8 + +#define ATAPI_COMMAND_OFFSET ATAPI_STATUS_OFFSET +#define ATAPI_FEATURE_OFFSET ATAPI_ERROR_OFFSET + +/************************************************************************ + * Following structures are according to SPC-3 (by Chanel) + ************************************************************************/ +typedef struct _SCSI_MODE_SENSE6 +{ + u8 OperationCode; + u8 Reserved1 : 3; + u8 Dbd : 1; + u8 Reserved2 : 4; + u8 PageCode : 6; + u8 Pc : 2; + u8 SubpageCode; + u8 AllocationLength; + u8 Control; +} SCSI_MODE_SENSE6, *PSCSI_MODE_SENSE6; + +typedef struct _SCSI_MODE_SENSE10 +{ + u8 OperationCode; + u8 Reserved1 : 3; + u8 Dbd : 1; + u8 LLBAA : 1; + u8 Reserved2 : 3; + u8 PageCode : 6; + u8 Pc : 2; + u8 SubpageCode; + u8 Reserved3[3]; + u8 AllocationLengthMsb; + u8 AllocationLengthLsb; + u8 Control; +} SCSI_MODE_SENSE10, *PSCSI_MODE_SENSE10; + +typedef struct _SCSI_MODE_SELECT6 +{ + u8 OperationCode; + u8 SPBit : 1; + u8 Reserved1 : 3; + u8 PFBit : 1; + u8 Reserved2 : 3; + u8 Reserved3[2]; + u8 ParameterListLength; + u8 Control; +} SCSI_MODE_SELECT6, *PSCSI_MODE_SELECT6; + +typedef struct _SCSI_MODE_SELECT10 +{ + u8 OperationCode; + u8 SPBit : 1; + u8 Reserved1 : 3; + u8 PFBit : 1; + u8 Reserved2 : 3; + u8 Reserved3[5]; + u8 ParameterListLengthMsb; + u8 ParameterListLengthLsb; + u8 Control; +} SCSI_MODE_SELECT10, *PSCSI_MODE_SELECT10; + +typedef struct _SCSI_MODE_PARAMETER_HEADER6 +{ + u8 ModeDataLength; + u8 MediumType; + u8 DeviceSpecificParameter; + u8 BlockDescriptorLength; +} SCSI_MODE_PARAMETER_HEADER6, *PSCSI_MODE_PARAMETER_HEADER6; + +typedef struct _SCSI_MODE_PARAMETER_HEADER10 +{ + u8 ModeDataLengthMsb; + u8 ModeDataLengthLsb; + u8 MediumType; + u8 DeviceSpecificParameter; + u8 Reserved[2]; + u8 BlockDescriptorLengthMsb; + u8 BlockDescriptorLengthLsb; +} SCSI_MODE_PARAMETER_HEADER10, *PSCSI_MODE_PARAMETER_HEADER10; + +typedef struct _SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER +{ + u8 DesityCode; + u8 NumberOfBlocks2; + u8 NumberOfBlocks1; + u8 NumberOfBlocks0; + u8 Reserved; + u8 BlockLength2; + u8 BlockLength1; + u8 BlockLength0; +} SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER, *PSCSI_MODE_PARAMTER_BLOCK_DESCRIPTER; + +/************************************************************************ + * IDE command definitions + ************************************************************************/ +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_READ_SECTOR 0x20 +#define IDE_COMMAND_READ_SECTOR_EXT 0x24 +#define IDE_COMMAND_READ_DMA_EXT 0x25 +#define IDE_COMMAND_READ_MULTIPLE_EXT 0x29 +#define IDE_COMMAND_WRITE_SECTOR 0x30 +#define IDE_COMMAND_WRITE_SECTOR_EXT 0x34 +#define IDE_COMMAND_WRITE_DMA_EXT 0x35 +#define IDE_COMMAND_WRITE_MULTIPLE_EXT 0x39 +#define IDE_COMMAND_READ_VERIFY 0x40 +#define IDE_COMMAND_READ_VERIFY_EXT 0x42 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_DRIVE_PARAMETERS 0x91 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_COMMAND_READ_MULTIPLE 0xC4 +#define IDE_COMMAND_WRITE_MULTIPLE 0xC5 +#define IDE_COMMAND_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_GET_MEDIA_STATUS 0xDA +#define IDE_COMMAND_ENABLE_MEDIA_STATUS 0xEF +#define IDE_COMMAND_SET_FEATURE 0xEF +#define IDE_COMMAND_IDENTIFY 0xEC +#define IDE_COMMAND_MEDIA_EJECT 0xED + +/************************************************************************ + * IDE status definitions + ************************************************************************/ +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_IDLE 0x50 +#define IDE_STATUS_BUSY 0x80 + +/************************************************************************ + * IDE drive control definitions. + ************************************************************************/ +#define IDE_DC_DISABLE_INTERRUPTS 0x02 +#define IDE_DC_RESET_CONTROLLER 0x04 +#define IDE_DC_REENABLE_CONTROLLER 0x00 + +/************************************************************************ + * IDE error definitions. + ************************************************************************/ +#define IDE_ERROR_BAD_BLOCK 0x80 +#define IDE_ERROR_DATA_ERROR 0x40 +#define IDE_ERROR_MEDIA_CHANGE 0x20 +#define IDE_ERROR_ID_NOT_FOUND 0x10 +#define IDE_ERROR_MEDIA_CHANGE_REQ 0x08 +#define IDE_ERROR_COMMAND_ABORTED 0x04 +#define IDE_ERROR_END_OF_MEDIA 0x02 +#define IDE_ERROR_ILLEGAL_LENGTH 0x01 + +/************************************************************************ + * IDENTIFY data. + ************************************************************************/ +typedef struct _IDENTIFY_DATA +{ + u16 GeneralConfiguration; /* 00 00 */ + u16 NumberOfCylinders; /* 02 1 */ + u16 Reserved1; /* 04 2 */ + u16 NumberOfHeads; /* 06 3 */ + u16 UnformattedBytesPerTrack; /* 08 4 */ + u16 UnformattedBytesPerSector; /* 0A 5 */ + u16 SectorsPerTrack; /* 0C 6 */ + u16 VendorUnique1[3]; /* 0E 7-9 */ + u16 SerialNumber[10]; /* 14 10-19 */ + u16 BufferType; /* 28 20 */ + u16 BufferSectorSize; /* 2A 21 */ + u16 NumberOfEccBytes; /* 2C 22 */ + u16 FirmwareRevision[4]; /* 2E 23-26 */ + u16 ModelNumber[20]; /* 36 27-46 */ + u8 MaximumBlockTransfer; /* 5E 47 */ + u8 VendorUnique2; /* 5F */ + u16 DoubleWordIo; /* 60 48 */ + u16 Capabilities; /* 62 49 */ + u16 Reserved2; /* 64 50 */ + u8 VendorUnique3; /* 66 51 */ + u8 PioCycleTimingMode; /* 67 */ + u8 VendorUnique4; /* 68 52 */ + u8 DmaCycleTimingMode; /* 69 */ + u16 TranslationFieldsValid : 1; /* 6A 53 */ + u16 Reserved3 : 15; /* */ + u16 NumberOfCurrentCylinders; /* 6C 54 */ + u16 NumberOfCurrentHeads; /* 6E 55 */ + u16 CurrentSectorsPerTrack; /* 70 56 */ + u32 CurrentSectorCapacity; /* 72 57-58 */ + u16 CurrentMultiSectorSetting; /* 59 */ + u32 UserAddressableSectors; /* 60-61 */ + u16 SingleWordDMASupport : 8; /* 62 */ + u16 SingleWordDMAActive : 8; /* */ + u16 MultiWordDMASupport : 8; /* 63 */ + u16 MultiWordDMAActive : 8; /* */ + u16 AdvancedPIOModes : 8; /* 64 */ + u16 Reserved4 : 8; /* */ + u16 MinimumMWXferCycleTime; /* 65 */ + u16 RecommendedMWXferCycleTime; /* 66 */ + u16 MinimumPIOCycleTime; /* 67 */ + u16 MinimumPIOCycleTimeIORDY; /* 68 */ + u16 Reserved5[2]; /* 69-70 */ + u16 ReleaseTimeOverlapped; /* 71 */ + u16 ReleaseTimeServiceCommand; /* 72 */ + u16 MajorRevision; /* 73 */ + u16 MinorRevision; /* 74 */ + u16 Reserved6[50]; /* 75-126 */ + u16 SpecialFunctionsEnabled; /* 127 */ + u16 Reserved7[128]; /* 128-255 */ +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +/************************************************************************ + * Identify data without the Reserved4. + ************************************************************************/ +typedef struct _IDENTIFY_DATA2 +{ + u16 GeneralConfiguration; /* 00 */ + u16 NumberOfCylinders; /* 01 */ + u16 Reserved1; /* 02 */ + u16 NumberOfHeads; /* 03 */ + u16 Reserved2[2]; /* 04-05 */ + u16 SectorsPerTrack; /* 06 */ + u16 Reserved3[3]; /* 07-09 */ + u16 SerialNumber[10]; /* 10-19 */ + u16 Reserved4[3]; /* 20-22 */ + u16 FirmwareRevision[4]; /* 23-26 */ + u16 ModelNumber[20]; /* 27-46 */ + u16 MaximumBlockTransfer; /* 47 */ + u16 Reserved5; /* 48 */ + u16 Capabilities[2]; /* 49-50 */ + u16 Reserved6[2]; /* 51-52 */ + u16 ValidFieldIndicator; /* 53 */ + u16 NumberOfCurrentCylinders; /* 54 */ + u16 NumberOfCurrentHeads; /* 55 */ + u16 CurrentSectorsPerTrack; /* 56 */ + u16 CurrentSectorCapacityLow; /* 57 */ + u16 CurrentSectorCapacityHigh; /* 58 */ + u16 CurrentMultiSectorSetting; /* 59 */ + u32 UserAddressableSectors; /* 60-61 */ + u16 Reserved7; /* 62 */ + u8 MultiWordDMASupport; /* 63 */ + u8 MultiWordDMAActive; /* */ + u16 AdvancedPIOModes; /* 64 */ + u16 MinimumMWXferCycleTime; /* 65 */ + u16 RecommendedMWXferCycleTime; /* 66 */ + u16 MinimumPIOCycleTime; /* 67 */ + u16 MinimumPIOCycleTimeIORDY; /* 68 */ + u16 Reserved8[6]; /* 69-74 */ + u16 QueueDepth; /* 75 */ + u16 Reserved9[4]; /* 76-79 */ + u16 MajorVersionNumber; /* 80 */ + u16 MinorVersionNumber; /* 81 */ + u32 CmdSetSupported; /* 82-83 */ + u16 CmdSetFeatureSupportedExt; /* 84 */ + u16 CmdSetFeatureEnabledLow; /* 85 */ + u16 CmdSetFeatureEnabledHigh; /* 86 */ + u16 CmdSetFeatureDefault; /* 87 */ + u8 UltraDMASupport; /* 88 */ + u8 UltraDMAActive; /* */ + u16 SecurityEraseTime; /* 89 */ + u16 EnhancedSecurityEraseTime; /* 90 */ + u16 PowerManagementValue; /* 91 */ + u16 MasterPasswordRevision; /* 92 */ + u16 HwResetResult; /* 93 */ + u16 Reserved11[6]; /* 94-99 */ + u32 Capacity_48bit_LOW; /* 100-101 */ + u32 Capacity_48bit_HIGH; /* 102-103 */ + u16 Reserved12[24]; /* 104-127 */ + u16 SecurityStatus; /* 128 */ + u16 Reserved13[31]; /* 129-159 vendor spec */ + u16 Reserved14[96]; /* 160-255 */ +} IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + +/************************************************************************ + * IDENTIFY capability bit definitions. + ************************************************************************/ +#define IDENTIFY_CAPABILITIES_DMA_SUPPORTED 0x0100 +#define IDENTIFY_CAPABILITIES_LBA_SUPPORTED 0x0200 + +/************************************************************************ + * IDENTIFY DMA timing cycle modes. + ************************************************************************/ +#define IDENTIFY_DMA_CYCLES_MODE_0 0x00 +#define IDENTIFY_DMA_CYCLES_MODE_1 0x01 +#define IDENTIFY_DMA_CYCLES_MODE_2 0x02 + +typedef struct _SENSE_DATA +{ + u8 ErrorCode:7; + u8 Valid:1; + u8 SegmentNumber; + u8 SenseKey:4; + u8 Reserved:1; + u8 IncorrectLength:1; + u8 EndOfMedia:1; + u8 FileMark:1; + u8 Information[4]; + u8 AdditionalSenseLength; + u8 CommandSpecificInformation[4]; + u8 AdditionalSenseCode; + u8 AdditionalSenseCodeQualifier; + u8 FieldReplaceableUnitCode; + u8 SenseKeySpecific[3]; +} SENSE_DATA, *PSENSE_DATA; + +/************************************************************************ + * Sense codes + ************************************************************************/ +#define SCSI_SENSE_NO_SENSE 0x00 +#define SCSI_SENSE_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_DATA_PROTECT 0x07 +#define SCSI_SENSE_BLANK_CHECK 0x08 +#define SCSI_SENSE_UNIQUE 0x09 +#define SCSI_SENSE_COPY_ABORTED 0x0A +#define SCSI_SENSE_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_EQUAL 0x0C +#define SCSI_SENSE_VOL_OVERFLOW 0x0D +#define SCSI_SENSE_MISCOMPARE 0x0E +#define SCSI_SENSE_RESERVED 0x0F + +/************************************************************************ + * Additional Sense codes + ************************************************************************/ +#define SCSI_ADSENSE_NO_SENSE 0x00 +#define SCSI_ADSENSE_MAN_INTERV 0x03 +#define SCSI_ADSENSE_LUN_NOT_READY 0x04 +#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20 +#define SCSI_ADSENSE_ILLEGAL_BLOCK 0x21 +#define SCSI_ADSENSE_INVALID_LUN 0x25 +#define SCSI_ADSENSE_SELECT_TIMEOUT 0x45 +#define SCSI_ADSENSE_MUSIC_AREA 0xA0 +#define SCSI_ADSENSE_DATA_AREA 0xA1 +#define SCSI_ADSENSE_VOLUME_OVERFLOW 0xA7 + +#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3A +#define SCSI_ADWRITE_PROTECT 0x27 +#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28 +#define SCSI_ADSENSE_BUS_RESET 0x29 +#define SCSI_ADSENSE_TRACK_ERROR 0x14 +#define SCSI_ADSENSE_SEEK_ERROR 0x15 +#define SCSI_ADSENSE_REC_DATA_NOECC 0x17 +#define SCSI_ADSENSE_REC_DATA_ECC 0x18 +#define SCSI_ADSENSE_ILLEGAL_MODE 0x64 +#define SCSI_ADSENSE_BAD_CDB 0x24 +#define SCSI_ADSENSE_BAD_PARM_LIST 0x26 +#define SCSI_ADSENSE_CANNOT_READ_MEDIUM 0x30 + +#define SCSISTAT_CHECK_CONDITION 0x02 + +/************************************************************************ + * Inquiry buffer structure. This is the data returned from the target + * after it receives an inquiry. + * + * This structure may be extended by the number of bytes specified + * in the field AdditionalLength. The defined size constant only + * includes fields through ProductRevisionLevel. + * + * The NT SCSI drivers are only interested in the first 36 bytes of data. + ************************************************************************/ + +#define INQUIRYDATABUFFERSIZE 36 + +typedef struct _INQUIRYDATA +{ + u8 DeviceType : 5; + u8 DeviceTypeQualifier : 3; + u8 DeviceTypeModifier : 7; + u8 RemovableMedia : 1; + u8 Versions; + u8 ResponseDataFormat; + u8 AdditionalLength; + u8 Reserved[2]; + u8 SoftReset : 1; + u8 CommandQueue : 1; + u8 Reserved2 : 1; + u8 LinkedCommands : 1; + u8 Synchronous : 1; + u8 Wide16Bit : 1; + u8 Wide32Bit : 1; + u8 RelativeAddressing : 1; + u8 VendorId[8]; + u8 ProductId[16]; + u8 ProductRevisionLevel[4]; + u8 VendorSpecific[20]; + u8 Reserved3[40]; +} INQUIRYDATA, *PINQUIRYDATA; + +#define DIRECT_ACCESS_DEVICE 0x00 /* Disks */ + +/************************************************************************ + * Read Capacity Data - returned in Big Endian format + ************************************************************************/ +typedef struct _READ_CAPACITY_DATA +{ + u32 LogicalBlockAddress; + u32 BytesPerBlock; +} READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +#define MAXIMUM_CDB_SIZE 12 + +/************************************************************************ + * CDB (Command Descriptor Block) + ************************************************************************/ +typedef union _CDB +{ + /* + * Standard 6-byte CDB + */ + struct _CDB6READWRITE + { + u8 OperationCode; /* Opcode */ + u8 LogicalBlockMsb1 : 5; /* Logical block MSB 5-bit */ + u8 LogicalUnitNumber : 3; /* LUN */ + u8 LogicalBlockMsb0; /* Logical block MSB 8-bit */ + u8 LogicalBlockLsb; /* Logical block LSB 8-bit */ + u8 TransferBlocks; /* Data length */ + u8 Control; /* Control byte */ + } CDB6READWRITE, *PCDB6READWRITE; + + /* + * Standard 10-byte CDB + */ + struct _CDB10 + { + u8 OperationCode; + u8 Reserved1 : 5; + u8 LogicalUnitNumber : 3; + u8 LogicalBlockByte0; + u8 LogicalBlockByte1; + u8 LogicalBlockByte2; + u8 LogicalBlockByte3; + u8 Reserved2; + u8 TransferBlocksMsb; + u8 TransferBlocksLsb; + u8 Control; + } CDB10, *PCDB10; + + struct _START_STOP + { + u8 OperationCode; + u8 Immediate: 1; + u8 Reserved1 : 4; + u8 LogicalUnitNumber : 3; + u8 Reserved2[2]; + u8 Start : 1; + u8 LoadEject : 1; + u8 Reserved3 : 6; + u8 Control; + } START_STOP, *PSTART_STOP; + +} CDB, *PCDB; + +/************************************************************************ + * SCSI CDB operation codes + ************************************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +#define DRIVER_NAME "Device Driver for IT8212 RAID Controller" +#define COMPANY_NAME "Integrated Technology Express, Inc." +#define CONTROLLER_NAME_IT8212 "IT8212 UDMA/ATA133 RAID Controller" +#define PROC_DIR_NAME "it8212" +#define ITE_MAX_CMDS 124 + +#define PCI_IOSEN 0x01 /* Enable IO space */ +#define PCI_BMEN 0x04 /* Enable IDE bus master */ + +/************************************************************************ + * PRD (Physical Region Descriptor) = Scatter-gather table + * + * | byte3 | byte2 | byte1 | byte0 | + * +--------------------------------------------+ + * | Memory Region Physical Base Address[31:1] | + * +----+----------------+----------------------+ + * |EOT | reserved | Byte count[15:1] | + * +----+----------------+----------------------+ + ************************************************************************/ +typedef struct _PRD_TABLE_ENTRY +{ + u32 PhysicalBaseAddress; /* Byte0 - Byte3 */ + u16 ByteCount; /* Byte4 - Byte5 */ + u16 EndOfTable; /* Byte6 - Byte7 */ +} PRD_TABLE_ENTRY, *PPRD_TABLE_ENTRY; + +#define SG_FLAG_EOT 0x8000 /* End of PRD */ +#define MAX_SG_DESCRIPTORS 17 /* 17 -- maximum 64K */ + +#define NUM_OF_PRD_TABLE_ENTRY 0x10 + +/************************************************************************ + * Bus master register bits definition + ************************************************************************/ +#define BM_CMD_FLG_START 0x01 +#define BM_CMD_FLG_WRTTOMEM 0x08 +#define BM_CMD_FLG_WRTTODSK 0x00 + +#define BM_STAT_FLG_ACTIVE 0x01 +#define BM_STAT_FLG_ERR 0x02 +#define BM_STAT_FLG_INT 0x04 +#define BM_DRV0_DMA_CAPABLE 0x20 +#define BM_DRV1_DMA_CAPABLE 0x40 + +#define BM_PRD_FLG_EOT 0x8000 + +/************************************************************************ + * SRB Functions + ************************************************************************/ +#define SRB_FUNCTION_EXECUTE_SCSI 0x00 +#define SRB_FUNCTION_IO_CONTROL 0x02 +#define SRB_FUNCTION_SHUTDOWN 0x07 +#define SRB_FUNCTION_FLUSH 0x08 + +/************************************************************************ + * SRB Status + ************************************************************************/ +#define SRB_STATUS_PENDING 0x00 +#define SRB_STATUS_SUCCESS 0x01 +#define SRB_STATUS_ABORTED 0x02 +#define SRB_STATUS_ABORT_FAILED 0x03 +#define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS_BUSY 0x05 +#define SRB_STATUS_INVALID_REQUEST 0x06 +#define SRB_STATUS_INVALID_PATH_ID 0x07 +#define SRB_STATUS_NO_DEVICE 0x08 +#define SRB_STATUS_TIMEOUT 0x09 +#define SRB_STATUS_SELECTION_TIMEOUT 0x0A +#define SRB_STATUS_COMMAND_TIMEOUT 0x0B +#define SRB_STATUS_MESSAGE_REJECTED 0x0D +#define SRB_STATUS_BUS_RESET 0x0E +#define SRB_STATUS_PARITY_ERROR 0x0F +#define SRB_STATUS_REQUEST_SENSE_FAILED 0x10 +#define SRB_STATUS_NO_HBA 0x11 +#define SRB_STATUS_DATA_OVERRUN 0x12 +#define SRB_STATUS_UNEXPECTED_BUS_FREE 0x13 +#define SRB_STATUS_BAD_SRB_BLOCK_LENGTH 0x15 +#define SRB_STATUS_REQUEST_FLUSHED 0x16 +#define SRB_STATUS_INVALID_LUN 0x20 +#define SRB_STATUS_INVALID_TARGET_ID 0x21 +#define SRB_STATUS_BAD_FUNCTION 0x22 +#define SRB_STATUS_ERROR_RECOVERY 0x23 +#define SRB_STATUS_NEED_REQUEUE 0x24 + +/************************************************************************ + * SRB Status Masks + ************************************************************************/ +#define SRB_STATUS_QUEUE_FROZEN 0x40 +#define SRB_STATUS_AUTOSENSE_VALID 0x80 + +#define SRB_STATUS(Status) \ + (Status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) + +/************************************************************************ + * SRB Flag Bits + ************************************************************************/ +#define SRB_FLAGS_DATA_IN 0x00000040 +#define SRB_FLAGS_DATA_OUT 0x00000080 + +/************************************************************************ + * SRB Working flags define area + ************************************************************************/ +#define SRB_WFLAGS_USE_INTERNAL_BUFFER 0x00000001 +#define SRB_WFLAGS_IGNORE_ARRAY 0x00000002 +#define SRB_WFLAGS_HAS_CALL_BACK 0x00000004 +#define SRB_WFLAGS_MUST_DONE 0x00000008 +#define SRB_WFLAGS_ON_MIRROR_DISK 0x00000010 +#define SRB_WFLAGS_ON_SOURCE_DISK 0x00000020 +#define SRB_WFLAGS_ARRAY_IO_STARTED 0x10000000 +#define SRB_WFLAGS_WATCHTIMER_CALLED 0x20000000 +#define SRB_WFLAGS_USE_SG 0x40000000 + +/************************************************************************ + * SCSI I/O Request Block + ************************************************************************/ +typedef struct _SCSI_REQUEST_BLOCK +{ + u16 Length; + u8 Function; + u8 SrbStatus; + u8 ScsiStatus; + u8 TargetId; + u8 Lun; + u8 CdbLength; + u8 SenseInfoBufferLength; + u8 UseSg; + u8 reseved[2]; + u32 WorkingFlags; + u32 SrbFlags; + u32 DataTransferLength; + u32 TimeOutValue; + void * DataBuffer; + void * SenseInfoBuffer; + u8 Cdb[16]; + Scsi_Cmnd * pREQ; +} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK; + +#define SCSI_REQUEST_BLOCK_SIZE sizeof(SCSI_REQUEST_BLOCK) + +/************************************************************************ + * Second device flags + ***********************************************************************/ +#define DFLAGS_REDUCE_MODE 0x00010000 +#define DFLAGS_DEVICE_DISABLED 0x00020000 +#define DFLAGS_BOOTABLE_DEVICE 0x00080000 +#define DFLAGS_BOOT_MARK 0x00100000 +#define DFLAGS_NEW_ADDED 0x40000000 +#define DFLAGS_REMAINED_MEMBER 0x80000000 + +/************************************************************************ + * Device Extension Device Flags + ************************************************************************/ +/* + * Indicates that some device is present. + */ +#define DFLAGS_DEVICE_PRESENT 0x0001 + +/* + * Indicates whether ATAPI commands can be used. + */ +#define DFLAGS_ATAPI_DEVICE 0x0002 + +/* + * Indicates whether this is a tape device. + */ +#define DFLAGS_TAPE_DEVICE 0x0004 + +/* + * Indicates whether device interrupts as DRQ is set after + * receiving Atapi Packet Command. + */ +#define DFLAGS_INT_DRQ 0x0008 + +/* + * Indicates that the drive has the 'removable' bit set in + * identify data (offset 128) + */ +#define DFLAGS_REMOVABLE_DRIVE 0x0010 + +/* + * Media status notification enabled. + */ +#define DFLAGS_MEDIA_STATUS_ENABLED 0x0020 + +/* + * Indicates atapi 2.5 changer present. + */ +#define DFLAGS_ATAPI_CHANGER 0x0040 + +/* + * Indicates multi-platter device, not conforming to the 2.5 spec. + */ +#define DFLAGS_SANYO_ATAPI_CHANGER 0x0080 + +/* + * Indicates that the init path for changers has already been done. + */ +#define DFLAGS_CHANGER_INITED 0x0100 +#define DFLAGS_CONFIG_CHANGED 0x0200 + +#define UDMA_MODE_5_6 0x80 + +/************************************************************************ + * Used to disable 'advanced' features. + ************************************************************************/ +#define MAX_ERRORS 4 + +/************************************************************************ + * ATAPI command definitions + ************************************************************************/ +#define ATAPI_MODE_SENSE 0x5A +#define ATAPI_MODE_SELECT 0x55 +#define ATAPI_FORMAT_UNIT 0x24 + +/************************************************************************ + * User IOCTL structure + * Notes: + * (1) Data transfers are limited to PAGE_SIZE (4k on i386, 8k for alpha) + ************************************************************************/ +typedef struct _uioctl_t +{ + u16 inlen; /* Length of data written to device */ + u16 outlen; /* Length of data read from device */ + void * data; /* Data read from devic starts here */ + u8 status; /* Status return from driver */ + u8 reserved[3]; /* For 4-byte alignment */ +} uioctl_t; + +/************************************************************************ + * IOCTL commands for RAID + ************************************************************************/ +#define ITE_IOCMAGIC 't' + +#define ITE_IOC_GET_PHY_DISK_STATUS _IO(ITE_IOCMAGIC, 1) +#define ITE_IOC_CREATE_DISK_ARRAY _IO(ITE_IOCMAGIC, 2) +#define ITE_IOC_REBUILD_START _IO(ITE_IOCMAGIC, 3) +#define ITE_IOC_GET_REBUILD_STATUS _IO(ITE_IOCMAGIC, 4) +#define ITE_IOC_RESET_ADAPTER _IO(ITE_IOCMAGIC, 5) +#define ITE_IOC_GET_DRIVER_VERSION _IO(ITE_IOCMAGIC, 6) + +/************************************************************************ + * _Channel + ************************************************************************/ +typedef struct _Channel +{ + /* + * IDE (ATAPI) io port address. + */ + unsigned long io_ports[IDE_NR_PORTS]; + + /* + * DMA base address. + */ + unsigned long dma_base; + + /* + * Flags word for each possible device. + */ + u16 DeviceFlags[2]; + + /* + * Indicates number of platters on changer-ish devices. + */ + u32 DiscsPresent[2]; + + /* + * Indicates expecting an interrupt. + */ + u8 ExpectingInterrupt; + + /* + * Indicate last tape command was DSC Restrictive. + */ + u8 RDP; + + /* + * Interrupt level. + */ + u8 InterruptLevel; + + /* + * Placeholder for status register after a GET_MEDIA_STATUS command. + */ + u8 ReturningMediaStatus; + + /* + * Remember the channel number (0, 1) + */ + u8 channel; + + /* + * Indicates cable status. + */ + u8 Cable80[2]; + + /* + * Reserved for alignment. + */ + u8 reserved1[0]; + + /* + * Data buffer pointer. + */ + unsigned short * DataBuffer; + + /* + * Data words left. + */ + u32 WordsLeft; + + /* + * Retry count. + */ + u32 RetryCount; + + /* + * Keep DMA type (MULTIWORD_DMA or ULTRA_DMA) for each device. + */ + u8 DmaType[2]; + + /* + * Keep UDMA timing for each device. + */ + u8 UdmaTiming[2]; + + /* + * Keep PIO/DMA timing for each channel. PioDmaTiming[clock][channel] + */ + u8 PioDmaTiming[2]; + + /* + * Keep IDE clock (50 MHz or 66 MHz) for each device. + */ + u8 IdeClock[2]; + + /* + * Keep the active device for each channel. + */ + u8 ActiveDevice; + + /* + * Indicate whether we should perform DMA mode switch on this channel? + */ + u8 DoSwitch; + + /* + * ??? + */ + u8 ConvertCdb; + + /* + * Use or do not use DMA. + */ + u8 UseDma[2]; + + /* + * Reserved for alignment. + */ + u8 reserved2[3]; + + /* + * Identify data for device. + */ + IDENTIFY_DATA FullIdentifyData; + IDENTIFY_DATA2 IdentifyData[2]; + + /* + * DMA PRD table physical address. + */ + dma_addr_t dmatable_dma; + + /* + * DMA PRD table virtual address. + */ + unsigned long * dmatable_cpu; + + /* + * Point to SCATTER/GATHER data buffer. + */ + struct scatterlist * sg_table; + + /* + * DMA read or write. + */ + int sg_dma_direction; + + /* + * Current request on controller. + */ + PSCSI_REQUEST_BLOCK CurrentSrb; + + /* + * Original request on controller. + */ + PSCSI_REQUEST_BLOCK OriginalSrb; + + /* + * Internal SRB. + */ + SCSI_REQUEST_BLOCK _Srb; + + /* + * Remember the PCI device. + */ + struct pci_dev * pPciDev; + + /* + * Placeholder for CDB. + */ + u8 TempCdb[MAXIMUM_CDB_SIZE]; +} Channel, *PChannel; + +/************************************************************************ + * _Adapter + ************************************************************************/ +typedef struct _Adapter +{ + char * name; /* Adapter's name */ + u8 num_channels; /* How many channels support */ + u8 irq; /* irq number */ + u8 irqOwned; /* If any irq is use */ + u8 pci_bus; /* PCI bus number */ + u8 devfn; /* Device and function number */ + u8 offline; /* On line or off line */ + u8 bypass_mode; /* bypass or firware mode */ + u8 reserved2[1]; /* Reserved for alignment */ + Channel * IDEChannel; /* IT8212 supports two channels */ + struct pci_dev * pci_dev; /* For PCI device */ +} +ITE_ADAPTER, *PITE_ADAPTER; + +/************************************************************************ + * Beautification macros + ************************************************************************/ +#define ScheduleRetryProcess(pChan) do { \ + pChan->retry_timer->expires = jiffies + 10; \ + add_timer(pChan->retry_timer); \ + } while (0) + +#define CancelRetryProcess(pChan) del_timer(pChan->retry_timer) + +#define GetStatus(pChan, Status) \ + Status = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + +#define GetBaseStatus(pChan, Status) \ + Status = inb(pChan->io_ports[IDE_COMMAND_OFFSET]); + +#define GetError(pChan, Error) \ + Error = inb(pChan->io_ports[IDE_ERROR_OFFSET]); + +#define ReadBuffer(pChan, Buffer, Count) \ + insw(pChan->io_ports[IDE_DATA_OFFSET], Buffer, Count); + +#define WriteCommand(BaseIoAddress, Command) \ + outb(pChan->io_ports[IDE_COMMAND_OFFSET], Command); + +#define WriteBuffer(pChan, Buffer, Count) \ + outsw(pChan->io_ports[IDE_DATA_OFFSET], Buffer, Count); + +#define WaitOnBusy(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 20000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(150); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitOnBaseBusy(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 20000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(150); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitForDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 1000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(200); \ + } \ + } \ +} + +#define WaitForBaseDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(200); \ + } \ + } \ +} + +#define CheckBusyDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if ((Status & IDE_STATUS_BUSY) || \ + !(Status & IDE_STATUS_DRQ)) \ + { \ + udelay(200); \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitShortForDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 2; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(100); \ + } \ + } \ +} + +#define WaitForDeviceReady(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status == 0) \ + { \ + break; \ + } \ + if ((Status & IDE_STATUS_BUSY) || (Status & IDE_STATUS_DRQ)) \ + { \ + udelay(200); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitForCommandComplete(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetStatus(pChan, Status); \ + if ((Status == 0) || (Status & IDE_STATUS_ERROR) \ + || (Status == IDE_STATUS_IDLE)) \ + { \ + break; \ + } \ + udelay(200); \ + continue; \ + } \ +} + +#define WaitForBaseCommandComplete(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if ((Status == 0) || (Status & IDE_STATUS_ERROR) \ + || (Status == IDE_STATUS_IDLE)) \ + { \ + break; \ + } \ + udelay(200); \ + continue; \ + } \ +} + +#define AtapiSoftReset(pChan, DevNum) \ +{ \ + unsigned char statusByte; \ + outb((unsigned char)(((DevNum & 0x1) << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); \ + udelay(500); \ + outb(IDE_COMMAND_ATAPI_RESET, pChan->io_ports[IDE_COMMAND_OFFSET]); \ + mdelay(1000); \ + outb((unsigned char)(((DevNum & 0x1) << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); \ + WaitOnBusy(pChan, statusByte); \ + udelay(500); \ +} + +#define IdeHardReset(pChan, result) \ +{ \ + unsigned char statusByte; \ + int i; \ + outb(IDE_DC_RESET_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); \ + mdelay(50); \ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); \ + for (i = 0; i < 1000 * 1000; i++) \ + { \ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); \ + if (statusByte != IDE_STATUS_IDLE && statusByte != 0x0) \ + { \ + udelay(30); \ + } \ + else \ + { \ + break; \ + } \ + } \ + if (i == 1000 * 1000) \ + { \ + printk("IdeHardReset Fail!\n"); \ + result = FALSE; \ + } \ + else \ + { \ + dprintk("IdeHardReset Success!\n"); \ + result = TRUE; \ + } \ +} + +/************************************************************************ + * Function prototypes + ************************************************************************/ +const char * iteraid_info (struct Scsi_Host *); +int iteraid_detect (Scsi_Host_Template *); +int iteraid_release (struct Scsi_Host *); +int iteraid_command (Scsi_Cmnd *); +int iteraid_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int iteraid_biosparam (struct scsi_device *, struct block_device *, sector_t , int *); +int iteraid_proc_info (struct Scsi_Host *, char *buffer, char **start, off_t offset, + int length, int inout); + +static void TaskStart(PChannel, Scsi_Cmnd *); +static void TaskQueue(void); +static void TaskDone(PChannel, PSCSI_REQUEST_BLOCK); +static u32 IdeSendCommand(PChannel, PSCSI_REQUEST_BLOCK); +static void IdeMediaStatus(u8, PChannel, u8); +static void IdeSetupDma(PChannel, unsigned long, unsigned short); +static void MapRequest(Scsi_Cmnd *, PSCSI_REQUEST_BLOCK); +static u8 IssueIdentify(PChannel, u8, u8); +static u8 IT8212ResetAdapter(PITE_ADAPTER); +static void AtapiStartIo(PChannel, PSCSI_REQUEST_BLOCK); +static u8 AtapiInterrupt(PChannel); +static void AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan); + +static int itedev_open(struct inode *, struct file *); +static int itedev_ioctl_entry(struct inode *, struct file *, unsigned int, unsigned long); +static int itedev_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +static int itedev_close(struct inode *, struct file *); + +#endif /* #ifndef _ITERAID_H_ */ diff -Nur --exclude '*.orig' linux-2.6.8.1.org/drivers/scsi/Kconfig linux-2.6.8.1/drivers/scsi/Kconfig --- linux-2.6.8.1.org/drivers/scsi/Kconfig 2004-10-22 13:05:11.701669856 +0200 +++ linux-2.6.8.1/drivers/scsi/Kconfig 2004-10-22 13:16:18.963230680 +0200 @@ -239,6 +239,12 @@ Please read the comments at the top of . +config BLK_DEV_IT82XX_RAID + tristate "ITE IT8212F/8211F RAID support" + depends on PCI && SCSI + help + + config SCSI_3W_9XXX tristate "3ware 9xxx SATA-RAID support" depends on PCI && SCSI diff -Nur --exclude '*.orig' linux-2.6.8.1.org/drivers/scsi/Makefile linux-2.6.8.1/drivers/scsi/Makefile --- linux-2.6.8.1.org/drivers/scsi/Makefile 2004-08-14 12:55:59.000000000 +0200 +++ linux-2.6.8.1/drivers/scsi/Makefile 2004-10-22 13:17:11.259280472 +0200 @@ -108,6 +108,7 @@ obj-$(CONFIG_SCSI_PLUTO) += pluto.o obj-$(CONFIG_SCSI_DECNCR) += NCR53C9x.o dec_esp.o obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o +obj-$(CONFIG_BLK_DEV_IT82XX_RAID) += iteraid.o obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o obj-$(CONFIG_SCSI_PPA) += ppa.o obj-$(CONFIG_SCSI_IMM) += imm.o