From: Re-add this - someone might find it useful. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton --- drivers/scsi/Kconfig | 9 drivers/scsi/Makefile | 1 drivers/scsi/iteraid.c | 5152 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/iteraid.h | 1358 ++++++++++++ 4 files changed, 6520 insertions(+) diff -puN /dev/null drivers/scsi/iteraid.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/iteraid.c 2005-07-08 23:11:39.000000000 -0700 @@ -0,0 +1,5152 @@ +/* + * linux/drivers/scsi/iteraid.c + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * Aug 2 , 2004 Donald Huang published the ITE driver. + * + * 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 release 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 + +#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)) +static struct Scsi_Host *ite_vhost = NULL; /* SCSI virtual host */ +static Scsi_Cmnd *it8212_req_last = NULL; /* SRB request list */ +static unsigned int NumAdapters = 0; /* Adapters number */ +static PITE_ADAPTER ite_adapters[2]; /* How many adapters support */ + +/* + * Notifier block to get a notify on system shutdown/halt/reboot. + */ +static int ite_halt(struct notifier_block *nb, ulong event, void *buf); +static struct notifier_block ite_notifier = { + .notifier_call = ite_halt, + .next = NULL, + .priority = 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 void TaskQueue(void); +static void TaskDone(PChannel, PSCSI_REQUEST_BLOCK); +static u8 IssueIdentify(PChannel, u8, u8); +static u8 IT8212ResetAdapter(PITE_ADAPTER); +static u8 AtapiInterrupt(PChannel); + +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 *); + +#define DRV_VER_8212 "1.45" +static int driver_ver = 145; +static int ite_major = 0; + +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. + */ +static 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; + } + } else { + /* + * If this is IDE error. + */ + 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 */ + +/* + * 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. + */ +static 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]); + } + } else { + /* + * 28-bit addressing. + */ + + /* + * 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); + + 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. + */ +static 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 */ + +/* + * 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 configuration 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. + */ +static 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]; + + pPhyDiskInfo = kmalloc(sizeof(PHYSICAL_DISK_STATUS) * 4, GFP_KERNEL); + if (pPhyDiskInfo == NULL) { + printk("IT8212GetChipStatus: error kmalloc for " + "PHYSCIAL_DISK_STATUS.\n"); + return -ENOMEM; + } + memset(pPhyDiskInfo, 0, sizeof(PHYSICAL_DISK_STATUS) * 4); + + /* + * 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_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 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. + */ +static 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]; + + 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 */ + +static 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. + */ +static 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. + */ +static 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]; + + if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) { + printk("IT8212CreateDiskArray: error kmalloc.\n"); + return -ENOMEM; + } + identifyData = (PIDENTIFY_DATA2) buffer; + + /* + * 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) { + 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 */ + +#if 0 +/* + * Return "virtual" drive 512 bytes identification data. + */ +static 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); + + 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 */ +#endif + +/* + * 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. + */ +static 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]; + + /* + * Select 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 + */ +static 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); + } else { + /* + * Running on ULTRA DMA mode. + */ + + /* + * 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 */ + +static 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); + + IdeStartTransfer(pChan, Srb, startingSector, sectorNumber); + + /* + * Wait for interrupt. + */ + return SRB_STATUS_PENDING; +} /* end IT8212ReadWrite */ + +#if 0 +/* + * Setup the transfer mode. + */ +static 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 */ +#endif + +#if 0 +static 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 }; + + /* + * If running on Firmware mode, get cable status from it. + */ + for (i = 0; i < 2; i++) { + /* + * The default 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 */ +#endif + +#if 0 +/* + * Initialize bypass(transparent) mode if BIOS is not ready. + */ +static 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 */ +#endif + +/* + * This is the interrupt service routine for ATAPI IDE miniport driver. + * TRUE if expecting an interrupt. + */ +static 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; + + /* + * Convert 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; + } + + 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; + + /* + * Convert 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); + + /* + * 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; + } + + 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) { + 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 { + printk("AtapiInterrupt: unexpected interrupt. intReason %x. " + "status %x.\n", interruptReason, statusByte); + return FALSE; + } + return TRUE; +} /* end AtapiInterrupt */ + +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. + */ +static 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]); + } else { + /* + * 28-bit addressing + */ + 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 */ + +/* + * Convert SCSI packet command to Atapi packet command. + */ +static 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. Because 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. Because 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) { + memmove((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. + */ +static 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. + */ +#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], DRV_VER_8212, 4); + + /* + * 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 TRUE. + */ +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; + + 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. + */ +static 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 */ + +#if 0 +/* + * Done handler for non-queued commands + */ +static void internal_done(Scsi_Cmnd * SCpnt) +{ + SCpnt->SCp.Status++; +} +#endif + +#if 0 +/* + * Process a command from the SCSI manager. + */ +static 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 */ +#endif + +/* + * Enables/disables media status notification. + */ +#if 0 +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 */ + +#endif +/* + * 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. + */ + 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 */ + +#if 0 +/* + * IDE disk hardware initialize. + */ +static 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 */ +#endif + +/* + * Initialize an 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); + +#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 */ + +/* + * Find and initialize any cards. + */ +static 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; + + if (pci_enable_device(pPciDev)) + continue; + + pAdap = (PITE_ADAPTER) kmalloc(sizeof(ITE_ADAPTER), GFP_ATOMIC); + if (pAdap == NULL) { + printk("iteraid_detect: pAdap allocate failed.\n"); + pci_disable_device(pPciDev); + 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 + scsi_set_device(ite_vhost, &pPciDev->dev); +#endif + + /* + * Register the driver as a character device, for applications + * to access 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 semaphore. + */ + init_MUTEX(&mimd_entry_mtx); + } + dprintk("iteraid_detect exit\n"); + return 1; +} /* end iteraid_detect() */ + +static int iteraid_release(struct Scsi_Host *pshost) +{ + u8 i; + PITE_ADAPTER pAdap; + + if (ite_major > 0) { + unregister_chrdev(ite_major, "itedev"); + ite_major = -1; + } + + 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); + } + pci_disable_device(pAdap->pci_dev); + if (pAdap != NULL) { + kfree(pAdap); + } + } + + unregister_reboot_notifier(&ite_notifier); + + /* + * Tell kernel scsi-layer we are gone. + */ + scsi_unregister(pshost); + return 0; +} /* end iteraid_Release */ + +static 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. + */ +static int iteraid_abort_eh(Scsi_Cmnd * SCpnt) +{ + if (SCpnt == NULL) { + printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); + return FAILED; + } + return SUCCESS; +} + +/* + * Process the biosparam request from the SCSI manager to return C/H/S data. + */ +static 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; +} + +static int iteraid_proc_info(struct Scsi_Host *shost, char *buffer, + char **start, off_t offset, int length, int inout) +{ + return 0; +} + +/* + * IOCTL open entry. + */ +static int itedev_open(struct inode *inode, struct file *filep) +{ + return 0; +} + +/* + * 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; + + if ((pioc = kmalloc(sizeof(uioctl_t), GFP_KERNEL)) == NULL) { + printk("itedev_ioctl: error kmalloc on ioctl\n"); + return -ENOMEM; + } + + 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"); + + status = IT8212GetChipStatus(pioc); + return 0; + case ITE_IOC_CREATE_DISK_ARRAY: + dprintk("ITE_IOC_CREATE_DISK_ARRAY\n"); + + 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; + + return put_user(progress, (u8 *) pioc->data); +exit: + return 0; + case ITE_IOC_RESET_ADAPTER: + dprintk("ITE_IOC_RESET_ADAPTER\n"); + +#if 0 + status = IT8212ResetAdapter(); + +#endif + + put_user(status, (u8 __user *) arg); + return 0; + case ITE_IOC_GET_DRIVER_VERSION: + dprintk("ITE_IOC_GET_DRIVER_VERSION\n"); + + put_user(driver_ver, (int __user *)arg); + return 0; + default: + return -EINVAL; + } /* end switch */ +} /* end itedev_ioctl */ + +/* + * IOCTL close routine. + */ +static int itedev_close(struct inode *inode, struct file *filep) +{ + return 0; +} + +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 -puN /dev/null drivers/scsi/iteraid.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/scsi/iteraid.h 2005-07-08 23:11:39.000000000 -0700 @@ -0,0 +1,1358 @@ +/* + * linux/drivers/scsi/iteraid.h + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * Aug 2, 2004 Donald Huang published the ITE driver. + * + * 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 +#define ITE_DEVICE_ID 0x8212 /* IT8212 */ +#define MAX_ADAPTERS 2 +#define MAX_DEVICES (MAX_ADAPTERS * 4) + +#define TRUE 1 +#define FALSE 0 + +/* + * Undef macros which may conflict + */ +#undef START_STOP + +#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 + +#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 + +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) + +#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 + +/* + * 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 + +#define SRB_FUNCTION_EXECUTE_SCSI 0x00 +#define SRB_FUNCTION_IO_CONTROL 0x02 +#define SRB_FUNCTION_SHUTDOWN 0x07 +#define SRB_FUNCTION_FLUSH 0x08 + +#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 + +#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)) + +#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 + */ +#define DFLAGS_DEVICE_PRESENT 0x0001 +#define DFLAGS_ATAPI_DEVICE 0x0002 +#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 + * 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 device 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) + +typedef struct _Channel { + /* + * IDE (ATAPI) io port address. + */ + unsigned long io_ports[IDE_NR_PORTS]; + unsigned long dma_base; + u16 DeviceFlags[2]; + + /* + * Indicates number of platters on changer-ish devices. + */ + u32 DiscsPresent[2]; + u8 ExpectingInterrupt; + + /* + * Indicate last tape command was DSC Restrictive. + */ + u8 RDP; + u8 InterruptLevel; + + /* + * Placeholder for status register after a GET_MEDIA_STATUS command. + */ + u8 ReturningMediaStatus; + u8 channel; + + /* + * Indicates cable status. + */ + u8 Cable80[2]; + + /* + * Reserved for alignment. + */ + u8 reserved1[0]; + unsigned short *DataBuffer; + u32 WordsLeft; + u32 RetryCount; + + /* + * MULTIWORD_DMA or ULTRA_DMA + */ + u8 DmaType[2]; + u8 UdmaTiming[2]; + u8 PioDmaTiming[2]; + + /* + * Keep IDE clock (50 MHz or 66 MHz) for each device. + */ + u8 IdeClock[2]; + u8 ActiveDevice; + + /* + * Indicate whether we should perform DMA mode switch on this channel? + */ + u8 DoSwitch; + + /* + * ??? + */ + u8 ConvertCdb; + 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; + 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; + struct pci_dev *pPciDev; + + /* + * Placeholder for CDB. + */ + u8 TempCdb[MAXIMUM_CDB_SIZE]; +} Channel, *PChannel; + +typedef struct _Adapter { + char *name; + u8 num_channels; + unsigned int irq; + unsigned int irqOwned; /* If any irq is use */ + u8 pci_bus; + u8 devfn; + u8 offline; + u8 bypass_mode; /* bypass or firmware mode */ + u8 reserved2[1]; /* Reserved for alignment */ + Channel *IDEChannel; /* IT8212 supports two channels */ + struct pci_dev *pci_dev; +} ITE_ADAPTER, *PITE_ADAPTER; + +#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) \ +do { \ + 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; \ + } \ +} while (0) + +#endif /* #ifndef _ITERAID_H_ */ diff -puN drivers/scsi/Kconfig~iteraid drivers/scsi/Kconfig --- devel/drivers/scsi/Kconfig~iteraid 2005-07-08 23:11:39.000000000 -0700 +++ devel-akpm/drivers/scsi/Kconfig 2005-07-08 23:11:39.000000000 -0700 @@ -310,6 +310,15 @@ config SCSI_ACARD To compile this driver as a module, choose M here: the module will be called atp870u. +config SCSI_ITERAID + tristate "ITE IT8212 RAID CARD support" + depends on PCI && SCSI + help + This driver supports the ITE IT8212 RAID card. + + To compile this driver as a module, choose M here: the + module will be called iteraid. + config SCSI_AHA152X tristate "Adaptec AHA152X/2825 support" depends on ISA && SCSI && !64BIT diff -puN drivers/scsi/Makefile~iteraid drivers/scsi/Makefile --- devel/drivers/scsi/Makefile~iteraid 2005-07-08 23:11:39.000000000 -0700 +++ devel-akpm/drivers/scsi/Makefile 2005-07-08 23:11:39.000000000 -0700 @@ -98,6 +98,7 @@ obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o +obj-$(CONFIG_SCSI_ITERAID) += iteraid.o obj-$(CONFIG_SCSI_SUNESP) += esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o obj-$(CONFIG_SCSI_INITIO) += initio.o _