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 | 5147 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/iteraid.h | 1357 ++++++++++++ 4 files changed, 6514 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-27 01:46:29.000000000 -0700 @@ -0,0 +1,5147 @@ +/* + * 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" +MODULE_VERSION(DRV_VER_8212); +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 ac