/* * linux/drivers/scsi/iteraid.c * * (C) Copyright 2002-2004 ITE Tech, inc. * * Nov 11, 2002 Mark Lu file created. * * ITE IT8212 RAID controller device driver for Linux. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * Revision 0.0 2002/12/05 15:12:12 root * Empty function bodies; detect() works. * * Revision 0.1 2002/12/17 19:21:37 root * First "dma thing doesn't work" version. * * Revision 0.3 2002/12/23 17:12:09 root * Rewrite the dma routines. Reference the ide-dma.c. * * Revision 0.4 2002/12/26 10:19:29 root * The dma read/write works, using some ways to prove it. But there is a * problem about the "unknown partition table". The fdisk is ok, but * after writing the created partitions, it still shows the "unknown * partition table" and i can't access the created partitions. * * Revision 0.5 2003/01/07 21:49:30 root * The problem of "unknown partition table" has been solved. * We must "ENABLE CLUSTERING". There is still a another problem about * the SCATTER/GATHER. * * Revision 0.6 2003/01/10 17:45:32 root * The SCATTER/GATHER problem has been solved. Now verify the read/write * function and make sure each RAID configurations are workable. If testing * is OK, then it will be a version 1.0..... * * Revision 1.0 2003/01/16 17:45:32 root * First release version. * * FixME 1: * In RedHat 7.3, if using SG_ALL, the SCSI will timeout. It looks like * an command is requested but the interrupt is not been asserted. So * try to add a watchdog timer to monitor the interrupts. But this kind * of situration will not happen in Mandrake 9.0 and also when using * SG_NONE in RedHat 7.3. * * FixME 2: * Module load problem in RedHat 7.3. * * Fixed: Compile in the graphic mode (GNOME or KDE) will fix the * module load problem. * * Revision 1.1 2003/02/10 10:32:21 root * Compile in the graphic mode (GNOME or KDE) will fix the * module load problem. * * Revision 1.2 2003/02/18 14:10:35 root * Fix the interrupt service routine for share irq problem. * * ATAPI support ---> schedule is three weeks. (2003/02/28) * * Revision 1.3 2003/02/27 * First relase ATAPI version. But there will be an error if no disc in the * CD-ROM. Because the commands like TEST_UNIT_READY and READ_CAPACITY will * get the error response. This situration in WINDOWS will be then send the * REQUEST SENSE command to the device but in Linux, it will never get * REQUEST SENSE command. So can we send by ourself??? * * 2003/03/05 root * * Note 1: * According to "The Linux SCSI Generic (sg) HOWTO", the device will respond * with a single byte value call the 'scsi_status'. GOOD is the scsi status * indicating everything has gone well. The most common other status is * CHECK CONDITION. In this latter case, the SCSI mid layer issues a REQUEST * SENSE SCSI command. The response of the REQUEST SENSE is 18 bytes or more * in length and is called the "sense buffer". It will indicate why the original * command may not have been executed. It is important to realize that a CHECK * CONDITION may very in severity from informative (e.g. command needed to be * retried before succeeding) to fatal (e.g. 'medium error' which often indicates * it is time to replace the disk). * * Note 2: * When using the ATAPI BIOS, we also do not need to set up the timimg in linux * driver. But it is necessary to write the timing routine in win system, cause it * has a s1, s2, s3 mode and devices wake up from these modes need to be initialized * again and do not pass through the BIOS. * * Note 3: * The 48-bit support and AP for RAID in linux will the next job. * * Revision 1.31 2003/03/14 09:40:35 root * Fix the 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. */ #include MODULE_AUTHOR ("ITE Tech,Inc."); MODULE_DESCRIPTION ("ITE IT8212 RAID Controller Linux Driver"); //MODULE_DESCRIPTION ("ITE IT8211 ATA/ATAPI 133 Controller Linux Driver"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sd.h" #include "scsi.h" #include "hosts.h" #include "iteraid.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10) MODULE_LICENSE("GPL"); #endif #define MARK_DEBUG_DUMP_MEM 0 /* 1=Enable dump memory content */ #define MARK_DEBUG_BYPASS_MODE 0 /* 1=Enable use bypass mode */ #define MARK_DUMP_CREATE_INFO 0 /* 1=Dump raid create info */ #define MARK_SET_BEST_TRANSFER 0 /* 0=BIOS set best trans mode */ #define PRD_BYTES 8 /* PRD table size */ #define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) struct Scsi_Host * ite_vhost = 0; /* SCSI virtual host */ Scsi_Cmnd * it8212_req_last = 0; /* SRB request list */ unsigned int NumAdapters = 0; /* Adapters number */ PITE_ADAPTER ite_adapters[2]; /* How many adapters support */ /************************************************************************ * Notifier blockto get a notify on system shutdown/halt/reboot. ************************************************************************/ static int ite_halt(struct notifier_block *nb, ulong event, void *buf); struct notifier_block ite_notifier = { ite_halt, NULL, 0 }; static struct semaphore mimd_entry_mtx; static spinlock_t queue_request_lock = SPIN_LOCK_UNLOCKED; static spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; static int driver_ver = 144; /* Current driver version */ static int ite_major = 0; /* itedev chardev major number */ #define ITE_DEBUG /************************************************************************ * The File Operations structure for the ioctl interface of the driver. ************************************************************************/ static struct file_operations itedev_fops = { 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. ************************************************************************/ UCHAR MapError(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { UCHAR errorByte; UCHAR srbStatus; UCHAR scsiStatus; /* * Read the error register. */ errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]); printk("MapError: error register is %x\n", errorByte); /* * If this is ATAPI error. */ if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) { switch (errorByte >> 4) { case SCSI_SENSE_NO_SENSE: printk("ATAPI: no sense information\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_RECOVERED_ERROR: printk("ATAPI: recovered error\n"); scsiStatus = 0; srbStatus = SRB_STATUS_SUCCESS; break; case SCSI_SENSE_NOT_READY: printk("ATAPI: device not ready\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_MEDIUM_ERROR: printk("ATAPI: media error\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_HARDWARE_ERROR: printk("ATAPI: hardware error\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_ILLEGAL_REQUEST: printk("ATAPI: illegal request\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_UNIT_ATTENTION: printk("ATAPI: unit attention\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_DATA_PROTECT: printk("ATAPI: data protect\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_BLANK_CHECK: printk("ATAPI: blank check\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; case SCSI_SENSE_ABORTED_COMMAND: printk("ATAPI: command Aborted\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; break; default: printk("ATAPI: invalid sense information\n"); scsiStatus = 0; srbStatus = SRB_STATUS_ERROR; break; } } /* * If this is IDE error. */ else { scsiStatus = 0; srbStatus = SRB_STATUS_ERROR; /* * Save errorByte, to be used by SCSIOP_REQUEST_SENSE. */ pChan->ReturningMediaStatus = errorByte; if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) { printk("IDE: media change\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; } else if (errorByte & IDE_ERROR_COMMAND_ABORTED) { printk("IDE: command abort\n"); srbStatus = SRB_STATUS_ABORTED; scsiStatus = SCSISTAT_CHECK_CONDITION; if (Srb->SenseInfoBuffer) { PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xB; senseBuffer->SenseKey = SCSI_SENSE_ABORTED_COMMAND; senseBuffer->AdditionalSenseCode = 0; senseBuffer->AdditionalSenseCodeQualifier = 0; srbStatus |= SRB_STATUS_AUTOSENSE_VALID; } /* * pChan->ErrorCount++; */ } else if (errorByte & IDE_ERROR_END_OF_MEDIA) { printk("IDE: end of media\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED)) { /* * pChan->ErrorCount++; */ } } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) { printk("IDE: illegal length\n"); srbStatus = SRB_STATUS_INVALID_REQUEST; } else if (errorByte & IDE_ERROR_BAD_BLOCK) { printk("IDE: bad block\n"); srbStatus = SRB_STATUS_ERROR; scsiStatus = SCSISTAT_CHECK_CONDITION; if (Srb->SenseInfoBuffer) { PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xB; senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; senseBuffer->AdditionalSenseCode = 0; senseBuffer->AdditionalSenseCodeQualifier = 0; srbStatus |= SRB_STATUS_AUTOSENSE_VALID; } } else if (errorByte & IDE_ERROR_ID_NOT_FOUND) { printk("IDE: id not found\n"); srbStatus = SRB_STATUS_ERROR; scsiStatus = SCSISTAT_CHECK_CONDITION; if (Srb->SenseInfoBuffer) { PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xb; senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; senseBuffer->AdditionalSenseCode = 0; senseBuffer->AdditionalSenseCodeQualifier = 0; srbStatus |= SRB_STATUS_AUTOSENSE_VALID; } /* * pChan->ErrorCount++; */ } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) { printk("IDE: media change\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; if (Srb->SenseInfoBuffer) { PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xb; senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION; senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; senseBuffer->AdditionalSenseCodeQualifier = 0; srbStatus |= SRB_STATUS_AUTOSENSE_VALID; } } else if (errorByte & IDE_ERROR_DATA_ERROR) { printk("IDE: data error\n"); scsiStatus = SCSISTAT_CHECK_CONDITION; srbStatus = SRB_STATUS_ERROR; if (!(pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_MEDIA_STATUS_ENABLED)) { /* * pChan->ErrorCount++; */ } /* * Build sense buffer. */ if (Srb->SenseInfoBuffer) { PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xB; senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; senseBuffer->AdditionalSenseCode = 0; senseBuffer->AdditionalSenseCodeQualifier = 0; srbStatus |= SRB_STATUS_AUTOSENSE_VALID; } } } /* * Set SCSI status to indicate a check condition. */ Srb->ScsiStatus = scsiStatus; return srbStatus; } /* end MapError */ /************************************************************************ * Just get the higest bit value. ************************************************************************/ UCHAR RaidGetHighestBit(UCHAR Number) { char bit; for (bit = 7; bit >= 0; bit--) { if (Number & (1 << bit)) return bit; } return 0xFF; } /* end RaidGetHighestBit */ /************************************************************************ * Reset IDE controller or ATAPI device. ************************************************************************/ BOOLEAN AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan) { ULONG i; UCHAR status; ULONG dma_base; BOOLEAN resetResult; 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((UCHAR)((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((UCHAR)(((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"); return resetResult; } /* end AtapiResetController */ /************************************************************************ * IDE start read/write transfer. ************************************************************************/ void IdeStartTransfer ( PChannel pChan, PSCSI_REQUEST_BLOCK Srb, ULONG startingSector, ULONG SectorNumber ) { UCHAR DiskId; UCHAR drvSelect; UCHAR bmClearStat; ULONG dma_base; dprintk("IdeStartTransfer enter\n"); DiskId = (UCHAR) Srb->TargetId; dma_base = pChan->dma_base; /* * 48-bit support. */ if ((startingSector + SectorNumber) > 0x0FFFFFFF) { /* * Select drive and set LBA mode. */ outb((UCHAR) (((DiskId & 0x1) << 4) | 0xA0 | 0x40), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Sector count register. */ outb((UCHAR) (SectorNumber >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]); outb((UCHAR) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); /* * LBA low register. */ outb((UCHAR) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]); outb((UCHAR) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); /* * LBA mid register. */ outb((UCHAR) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); outb((UCHAR) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); /* * LBA high register. */ outb((UCHAR) 0, pChan->io_ports[IDE_HCYL_OFFSET]); outb((UCHAR) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]); /* * Start the IDE read/write DMA command. */ if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { outb(IDE_COMMAND_READ_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { outb(IDE_COMMAND_WRITE_DMA_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); } } /* * 28-bit addressing. */ else { /* * Select drive and set LBA mode. */ drvSelect = (UCHAR) (startingSector >> 24); drvSelect = drvSelect | (((UCHAR) DiskId & 0x1) << 4) | 0x40 | 0xA0; outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); outb((UCHAR) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); outb((UCHAR) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); outb((UCHAR)(startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); outb((UCHAR)(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; ULONG 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].address = 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 int * table = pChan->dmatable_cpu; unsigned int count = 0; int i; struct scatterlist * sg; pChan->sg_nents = 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, ULONG dma_base, USHORT num_ports) { printk("Channel[%d] BM-DMA at 0x%04lX-0x%04lX\n", pChan->channel, dma_base, dma_base + num_ports - 1); /* * Allocate IDE DMA buffer. */ pChan->dmatable_cpu = pci_alloc_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES, &pChan->dmatable_dma); if (pChan->dmatable_cpu == NULL) { printk("IdeSetupDma: allocate prd table failed.\n"); return; } memset(pChan->dmatable_cpu, 0, PRD_ENTRIES * PRD_BYTES); /* * Allocate SCATTER/GATHER table buffer. */ pChan->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, GFP_KERNEL); if (pChan->sg_table == NULL) { printk("IdeSetupDma: allocate sg_table failed.\n"); pci_free_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES, pChan->dmatable_cpu, pChan->dmatable_dma); return; } return; } /* end IdeSetupDma */ /************************************************************************ * This will be only used in RAID mode. ************************************************************************/ void IT8212ReconfigChannel(PChannel pChan, UCHAR ArrayId, UCHAR Operation) { UCHAR enableVirtualChannel; struct pci_dev * pPciDev = pChan->pPciDev; pci_read_config_byte(pPciDev, 0x43, &enableVirtualChannel); if (Operation == DisableChannel) { enableVirtualChannel &= ~(1 << ArrayId); printk("IT8212ReconfigChannel: disable channel %X\n", ArrayId); } else { enableVirtualChannel |= ~(1 << ArrayId); printk("IT8212ReconfigChannel: enable channel %X\n", ArrayId); } printk("IT8212ReconfigChannel: channel enabled after set 0x%X\n", enableVirtualChannel); /* * Set enabled virtual channels. */ pci_write_config_byte(pPciDev, 0x43, enableVirtualChannel); } /* end IT8212ReconfigChannel */ /************************************************************************ * Get the chip status. This is a vendor specific command. According to * all of the device configurations, the BIOS then can consider the * existing RAID configuration reasonable. If the existing RAID configur- * ation is not reasonable, or if there is NO existing RAID configuration * , the BIOS can ask the user to setup the RAID configuration. Finally, * the BIOS or AP should send the SET CHIP STATUS to every virtual device. * Only after receiving SET CHIP STATUS command, the corresponding virtual * device will be active. ************************************************************************/ UCHAR IT8212GetChipStatus(uioctl_t *ioc) { ULONG PriMasterIsNull = FALSE; UCHAR statusByte; UCHAR srbStatus; PITE_ADAPTER pAdap; PChannel pChan; PHYSICAL_DISK_STATUS * pPhyDiskInfo; dprintk("IT8212GetChipStatus enter\n"); /* * Only support one controller now! In the future, we can pass the * argument (user ioctl structure) to know which controller need to be * identified. */ pAdap = ite_adapters[0]; pChan = &pAdap->IDEChannel[0]; /* * Allocate space for PHYSICAL_DISK_STATUS. */ if ((pPhyDiskInfo = kmalloc(sizeof(PHYSICAL_DISK_STATUS) * 4, GFP_KERNEL)) == NULL) { printk("IT8212GetChipStatus: error kmalloc for PHYSCIAL_DISK_STATUS.\n"); return -ENOMEM; } memset(pPhyDiskInfo, 0, sizeof(PHYSICAL_DISK_STATUS)); /* * Always send GET CHIP STATUS command to primary channel master device. * Select device. */ outb((UCHAR) 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, (PUSHORT)pPhyDiskInfo, 256); #if (MARK_DEBUG_DUMP_MEM) HexDump((unsigned char *)pPhyDiskInfo, 512); #endif /* * Copy physical disk info to user area. */ copy_to_user((PUSHORT)ioc->data, (PUSHORT)pPhyDiskInfo, 512); /* * Check error. */ WaitForCommandComplete(pChan, statusByte); if (statusByte != IDE_STATUS_IDLE) { printk("IT8212GetChipStatus: disk[0] return unexpected status after"); printk("read data. status=0x%X\n", statusByte); goto exit_error; } srbStatus = SRB_STATUS_SUCCESS; goto exit; exit_error: /* * If fail, hard reset to avoid the DRQ status pending. */ srbStatus = SRB_STATUS_ERROR; IdeHardReset(pChan, statusByte); exit: /* * Reenable interrupt after command complete. */ outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); /* * If primary master is null, disable primary master channel before we leave. */ if (PriMasterIsNull) { IT8212ReconfigChannel(pChan, 0, DisableChannel); } dprintk("IT8212GetChipStatus exit\n"); return srbStatus; } /* end IT8212GetChipStatus */ /************************************************************************ * Erase the partition table. ************************************************************************/ UCHAR IT8212ErasePartition(uioctl_t *pioc) { UCHAR drvSelect; UCHAR statusByte = 0; UCHAR srbStatus; UCHAR * buffer; PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) pioc->data; PITE_ADAPTER pAdap; PChannel pChan; printk("IT8212ErasePartition enter\n"); printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId); if (createInfo->ErasePartition == 0 || (createInfo->RaidType == RAID_LEVEL_NODISK)) return SRB_STATUS_SUCCESS; pAdap = ite_adapters[0]; if (createInfo->DiskArrayId < 2) { pChan = &pAdap->IDEChannel[0]; } else { pChan = &pAdap->IDEChannel[1]; } /* * Allocate 512 bytes for buffer. */ if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) { printk("IT8212ErasePartition: error kmalloc.\n"); return -ENOMEM; } memset(buffer, 0, 512); /* * Select device. */ drvSelect = (((UCHAR) 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, (PUSHORT)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 */ /************************************************************************ * ************************************************************************/ ULONG IT8212TruncateReduentSectors ( ULONG OriginalSectorCount, USHORT StripeSizeInKBytes ) { USHORT 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. ************************************************************************/ ULONG IT8212DiskArrayAddressableSector(PUCHAR DiskArrayCreateInfo) { PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) DiskArrayCreateInfo; ULONG MinDiskCapacity; ULONG ArrayCapacity; UCHAR DiskNo; UCHAR NumOfDisks; MinDiskCapacity = ArrayCapacity = NumOfDisks = 0; 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]); 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. ************************************************************************/ UCHAR IT8212CreateDiskArray(uioctl_t *pioc) { PVOID buffer; UCHAR i; UCHAR subCommand = 0xFF; UCHAR statusByte; UCHAR dmaSupported; UCHAR udmaSupported; UCHAR srbStatus; ULONG UserAddressableSectors; ULONG PriMasterIsNull = FALSE; PChannel pChan; PITE_ADAPTER pAdap; PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO)pioc->data; PIDENTIFY_DATA2 identifyData; PIT8212_SET_CHIP_STATUS_INFO setChipStatus; USHORT 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 */ }; USHORT IT8212ClockTable[7] = { 0x0505, /* UDMA clock register 01 */ 0x0005, /* UDMA clock register 23 */ 0x0500, /* UDMA clock register 45 */ 0x0000, /* UDMA clock register 6 */ 0x0005, /* DMA clock register 01 */ 0x0005, /* DMA clock register 2 */ 0x0005 /* PIO clock register */ }; printk("IT8212CreateDiskArray enter\n"); #if (MARK_DUMP_CREATE_INFO) printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId); printk("createInfo->ModelNumber = %s\n", createInfo->ModelNumber); printk("createInfo->RaidType = %d\n", createInfo->RaidType); printk("createInfo->ContainingDisks = %d\n", createInfo->ContainingDisks); printk("createInfo->AutoRebuildEnable = %d\n", createInfo->AutoRebuildEnable); printk("createInfo->StripeSize = %d\n", createInfo->StripeSize); printk("createInfo->BootableDisk = %d\n", createInfo->BootableDisk); printk("createInfo->NewlyCreated = %d\n", createInfo->NewlyCreated); printk("createInfo->ErasePartition = %d\n", createInfo->ErasePartition); printk("createInfo->DMASupported[0] = 0x%x\n", createInfo->DMASupported[0]); printk("createInfo->DMASupported[1] = 0x%x\n", createInfo->DMASupported[1]); printk("createInfo->DMASupported[2] = 0x%x\n", createInfo->DMASupported[2]); printk("createInfo->DMASupported[3] = 0x%x\n", createInfo->DMASupported[3]); printk("createInfo->UDMASupported[0] = 0x%x\n", createInfo->UDMASupported[0]); printk("createInfo->UDMASupported[1] = 0x%x\n", createInfo->UDMASupported[1]); printk("createInfo->UDMASupported[2] = 0x%x\n", createInfo->UDMASupported[2]); printk("createInfo->UDMASupported[3] = 0x%x\n", createInfo->UDMASupported[3]); printk("createInfo->AddressableSectors[0] = 0x%lX\n", createInfo->AddressableSectors[0]); printk("createInfo->AddressableSectors[1] = 0x%lX\n", createInfo->AddressableSectors[1]); printk("createInfo->AddressableSectors[2] = 0x%lX\n", createInfo->AddressableSectors[2]); printk("createInfo->AddressableSectors[3] = 0x%lX\n", createInfo->AddressableSectors[3]); #endif switch (createInfo->RaidType) { case RAID_LEVEL_0: case RAID_LEVEL_1: case RAID_LEVEL_10: case RAID_LEVEL_JBOD: case RAID_LEVEL_NORMAL: subCommand = 0x50; break; case RAID_LEVEL_NODISK: subCommand = 0x48; break; } /* * The command should be sent to virtual primary master. */ pAdap = ite_adapters[0]; pChan = &pAdap->IDEChannel[0]; /* * Allocate 512-bytes buffer. */ if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) { printk("IT8212CreateDiskArray: error kmalloc.\n"); return -ENOMEM; } identifyData = (PIDENTIFY_DATA2) buffer; /* * 2003/05/08 * Remember the vendor specific parameters starts from word 129 not 128. */ setChipStatus = (PIT8212_SET_CHIP_STATUS_INFO) (buffer + 258); /* * Configure to RAID or NORMAL. */ if (subCommand == 0x50) { /* * Zero identify data structure. */ memset((PUCHAR) 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 = %lX\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 &= (UCHAR) createInfo->DMASupported[i]; udmaSupported &= (UCHAR) 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((UCHAR) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); /* * If primary master channel is not enabled, enable it. */ statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); if (statusByte == 0) { PriMasterIsNull = TRUE; IT8212ReconfigChannel(pChan, 0, EnableChannel); } /* * Wait for device ready (not BUSY and not DRQ) */ WaitForDeviceReady(pChan, statusByte); if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || (statusByte == 0)) { printk("IT8212CreateDiskArray: disk[0] not ready. status=0x%X\n", statusByte); srbStatus = SRB_STATUS_BUSY; goto exit; } /* * Disable interrupt to avoid the unexpected interrupt. */ outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); outb(subCommand, pChan->io_ports[IDE_FEATURE_OFFSET]); outb(createInfo->DiskArrayId, pChan->io_ports[IDE_SELECT_OFFSET]); outb(IDE_COMMAND_SET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); /* * No disk (no data command protocol) */ if (subCommand == 0x48) { WaitForCommandComplete(pChan, statusByte); if (statusByte != IDE_STATUS_IDLE) { printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); goto exit_error; } IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, DisableChannel); srbStatus = SRB_STATUS_SUCCESS; goto exit; } /* * Create RAID (PIO data out command protocol). */ if (subCommand == 0x50) { /* * Wait for BUSY=0, DRQ=1. */ CheckBusyDrq(pChan, statusByte); if (statusByte != 0x58) { printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); goto exit_error; } WriteBuffer(pChan, buffer, 256); /* * Check error. */ WaitForCommandComplete(pChan, statusByte); if (statusByte != IDE_STATUS_IDLE) { printk("IT8212CreateDiskArray: disk[0] return unexpected status after issue command.\n"); goto exit_error; } IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, EnableChannel); srbStatus = SRB_STATUS_SUCCESS; goto exit; } exit_error: /* * If fail, hard reset to avoid the DRQ pending. */ IdeHardReset(pChan, statusByte); srbStatus = SRB_STATUS_ERROR; exit: /* * If primary master is null, and we are not configuring array 0. Disable * primary master channel again. */ if (PriMasterIsNull && createInfo->DiskArrayId) { IT8212ReconfigChannel(pChan, 0 , DisableChannel); } /* * Reenable interrupt after command complete. */ outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); printk("IT8212CreateDiskArray exit\n"); return srbStatus; } /* end IT8212CreateDiskArray */ /************************************************************************ * Return "virtual" drive 512 bytes identification data. ************************************************************************/ UCHAR IT8212IssueIdentify(uioctl_t *pioc) { UCHAR channum; UCHAR devnum; UCHAR statusByte; UCHAR 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((UCHAR)((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, (PUSHORT)&pChan->FullIdentifyData, 256); /* * Then copy to user area. * unsigned long copy_to_user(void *to, const void *from, unsigned long count). */ copy_to_user((PUSHORT) (pioc->data + ((devnum + channum * 2) * 512)), (PUSHORT)&pChan->FullIdentifyData, 256); /* * Check error after reading data. */ WaitForCommandComplete(pChan, statusByte); if (statusByte != IDE_STATUS_IDLE) { printk("IT8212IssueIdentify: disk[%d] returns unexpected status after read data. status=0x%X\n", devnum + channum * 2, statusByte); goto error; } } /* end for each device */ } /* end for each channel */ srbStatus = SRB_STATUS_SUCCESS; goto exit; error: /* * If failed, hard reset to avoid the IRQ pending. */ IdeHardReset(pChan, statusByte); srbStatus = SRB_STATUS_ERROR; exit: /* * Reenable interrupt after command complete. */ for (channum = 0; channum < pAdap->num_channels; channum++) { pChan = &pAdap->IDEChannel[channum]; outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); } return srbStatus; } /* end IT8212IssueIdentify */ /************************************************************************ * Reset the controller. ************************************************************************/ BOOLEAN IT8212ResetAdapter(PITE_ADAPTER pAdap) { BOOLEAN resetChannel[2]; UCHAR channel; UCHAR device; ULONG i; UCHAR status[4]; 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((UCHAR)((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. ************************************************************************/ UCHAR IT8212Rebuild(uioctl_t *pioc) { UCHAR rebuildDirection; UCHAR statusByte = 0; PRAID_REBUILD_INFO apRebuildInfo = (PRAID_REBUILD_INFO) pioc->data; PITE_ADAPTER pAdap; PChannel pChan; dprintk("IT8212Rebuild enter\n"); rebuildDirection = (apRebuildInfo->Resume << 4) | (apRebuildInfo->DestDisk << 2) | apRebuildInfo->SrcDisk; apRebuildInfo->Status = 0xFF; pAdap = ite_adapters[0]; printk("IT8212Rebuild: diskArrayId=%d\n", apRebuildInfo->DiskArrayId); if (apRebuildInfo->DiskArrayId < 2) { pChan = &pAdap->IDEChannel[0]; } else { pChan = &pAdap->IDEChannel[1]; } /* * Selcet device. */ outb((UCHAR)((apRebuildInfo->DiskArrayId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Wait for device ready (not BUSY and not DRQ). */ WaitForDeviceReady(pChan, statusByte); if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || (statusByte == 0)) { apRebuildInfo->Status = REBUILD_ERR_DISK_BUSY; printk("IT8212Rebuild: disk[%d] not ready. status=0x%X\n", apRebuildInfo->DiskArrayId, statusByte); return SRB_STATUS_BUSY; } /* * Disable interrupt to avoid the unexpected interrupt. */ outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); /* * Give a direction. */ outb(rebuildDirection, pChan->io_ports[IDE_FEATURE_OFFSET]); /* * Issue a REBUILD commmand. */ outb(IDE_COMMAND_REBUILD, pChan->io_ports[IDE_COMMAND_OFFSET]); /* * Check for errors. */ WaitForCommandComplete(pChan, statusByte); /* * Reenable interrupt after command complete. */ outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); if (statusByte != IDE_STATUS_IDLE) { if (statusByte & IDE_STATUS_ERROR) { apRebuildInfo->Status = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); printk("IT8212Rebuild: rebuild error. reason=0x%X\n", apRebuildInfo->Status); } return apRebuildInfo->Status; } dprintk("IT8212Rebuild exit\n"); return SRB_STATUS_PENDING; } /* end IT8212Rebuild */ /************************************************************************ * Switch to DMA mode if necessary. * * offset 0x50 = PCI Mode Control Register * * Bit 0 = PCI Mode Select (1=firmware mode, 0=transparent mode) * Bit 1 = Primary Channel IDE Clock Frequency Select (1=50, 0=66) * Bit 2 = Secondary Channel IDE Clock Freq Select (1=50, 0=66) * Bit 3 = Primary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) * Bit 4 = Primary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) * Bit 5 = Secondary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) * Bit 6 = Secondary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) * Bit 7 = PCI Mode Reset ************************************************************************/ VOID IT8212SwitchDmaMode(PChannel pChan, UCHAR DeviceId) { UCHAR pciControl; UCHAR channel; UCHAR device; UCHAR configByte = 0; UCHAR RevisionID; struct pci_dev * pPciDev = pChan->pPciDev; /* * These tables are for performance issue. Better formance than lots * of "Shifts". */ UCHAR dmaModeV10[4] = {0x18, 0x18, 0x60, 0x60}; UCHAR udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F}; UCHAR ideClock[4] = {0xFD, 0xFD, 0xFB, 0xFB}; #if (0) UCHAR dmaMode[4] = {0x08, 0x10, 0x20, 0x40}; UCHAR udmaMode[4] = {0xF7, 0xEF, 0xDF, 0xBF}; #endif /* * channel --> 0-1; device --> 0-1; DeviceId --> 0-3; */ channel = DeviceId >> 1; device = DeviceId & 1; /* * Do nothing if the mode switch is unnecessary. */ if (!pChan->DoSwitch || pChan->ActiveDevice == DeviceId) { dprintk("IT8212SwitchDmaMode: do not need to switch mode!\n"); return; } printk("IT8212SwitchDmaMode: switch DMA mode for dev (%x)\n", DeviceId); pci_read_config_byte(pPciDev, 0x50, &pciControl); pci_read_config_byte(pPciDev, 0x08, &RevisionID); /* * Running on MULTIWORD_DMA mode. */ if (pChan->DmaType[device] == USE_MULTIWORD_DMA) { /* * Switch to DMA mode. */ if (RevisionID == 0x10) { configByte = pciControl | dmaModeV10[DeviceId]; } pci_write_config_byte(pPciDev, 0x50, configByte); } /* * Running on ULTRA DMA mode. */ else { /* * Select UDMA mode. */ configByte = pciControl; if (RevisionID == 0x10) { configByte &= udmaModeV10[DeviceId]; } /* * Select IDE clock. */ configByte = (configByte & ideClock[DeviceId]) | (pChan->IdeClock[device] << (channel + 1)); pci_write_config_byte(pPciDev, 0x50, configByte); /* * Set UDMA timing. * * offset 0x56 = PCI Mode Primary Device 0 Ultra DMA Timing Registers * offset 0x57 = PCI Mode Primary Device 1 Ultra DMA Timing Registers * offset 0x5A = PCI Mode Secondary Device 0 Ultra DMA Timing Registers * offset 0x5B = PCI Mode Secondary Device 1 Ultra DMA Timing Registers */ if (RevisionID == 0x10) { configByte = pChan->UdmaTiming[device]; pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4)), configByte); pci_write_config_byte(pPciDev, (UCHAR) (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, (UCHAR) (0x54 + (channel * 4)), configByte); } /* * Record the Active device on this channel */ pChan->ActiveDevice = device; } /* end IT8212SwitchDmaMode */ /************************************************************************ * IT8212 read/write routine. ************************************************************************/ ULONG IT8212ReadWrite(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { UCHAR statusByte = 0; ULONG startingSector; ULONG sectorNumber; ULONG 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 = (USHORT) ((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: %ld. starting sector: %ld. sector number: %ld\n", capacity, startingSector, sectorNumber); return SRB_STATUS_ERROR; } /* * Select device. */ outb((UCHAR)((Srb->TargetId & 0x1) << 4 | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Wait for device ready (Not Busy and Not DRQ). */ WaitForDeviceReady(pChan, statusByte); if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || (statusByte == 0)) { printk("IT8212ReadWrite: disk[%d] not ready. status=0x%x\n", Srb->TargetId, statusByte); return SRB_STATUS_BUSY; } /* * First, switch to DMA or UDMA mode if running on bypass mode. */ if (pAdap->bypass_mode) { IT8212SwitchDmaMode(pChan, Srb->TargetId); } /* * Check the SCATTER/GATHER count. The upper will give the different * memory address depend on whether use_sg is used or not. */ if (Srb->UseSg == 0) { IdeBuildDmaTable(pChan, Srb); } else { IdeBuildDmaSgTable(pChan, Srb); } /* * Start transfer the data. */ IdeStartTransfer(pChan, Srb, startingSector, sectorNumber); /* * Wait for interrupt. */ return SRB_STATUS_PENDING; } /* end IT8212ReadWrite */ /************************************************************************ * Setup the transfer mode. ************************************************************************/ VOID IT8212SetTransferMode ( PChannel pChan, ULONG DiskId, UCHAR TransferMode, UCHAR ModeNumber ) { UCHAR statusByte = 0; /* * Select device. */ outb((UCHAR) ((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[%ld] 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[%ld]", DiskId); printk("return unexpected status after issue command. 0x%x\n", statusByte); } } /* end IT8212SetTransferMode */ /************************************************************************ * Set the best transfer mode for device. ************************************************************************/ VOID IT8212SetBestTransferMode(PITE_ADAPTER pAdap, PChannel pChan, UCHAR channel) { UCHAR i; UCHAR k; UCHAR transferMode; UCHAR modeNumber; UCHAR pciControl; UCHAR device; UCHAR configByte; UCHAR cableStatus[2] = {CABLE_40_PIN, CABLE_40_PIN}; UCHAR 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. */ UCHAR 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. */ UCHAR dmaTiming[2][3] = { {0x88, 0x32, 0x31}, {0x66, 0x22, 0x21} }; /* * PIO timing table for 66 MHz clock. * PIO timing table for 50 MHz clock. */ UCHAR pioTiming[2][5] = { {0xAA, 0xA3, 0xA1, 0x33, 0x31}, {0x88, 0x82, 0x81, 0x32, 0x21} }; UCHAR 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". */ //UCHAR dmaModeV10[4] = {0x18, 0x18, 0x60, 0x60}; UCHAR udmaModeV10[4] = {0xE7, 0xE7, 0x9F, 0x9F}; UCHAR dmaMode[4] = {0x08, 0x10, 0x20, 0x40}; UCHAR udmaMode[4] = {0xF7, 0xEF, 0xDF, 0xBF}; UCHAR ideClock[4] = {0xFD, 0xFD, 0xFB, 0xFB}; /* * 2003/07/24 * If running on Firmware mode, get cable status from it. */ for (i = 0; i < 2; i++) { /* * The dafault of cable status is in PCI configuration 0x40. */ cableStatus[i] = pChan->Cable80[i]; /* * channel -->0 to 1. * device -->0 or 1. */ pChan->UseDma[i] = TRUE; device = i & 1; if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) || (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) { pio_dma_timing[0][channel][device] = pio_dma_timing[0][channel][device + 2] = 0; pio_dma_timing[1][channel][device] = pio_dma_timing[1][channel][device + 2] = 0; continue; } /* * Set PIO Mode. */ ideIdentifyData = &pChan->IdentifyData[i]; if ((!(ideIdentifyData->ValidFieldIndicator & 0x02)) || (ideIdentifyData->AdvancedPIOModes == 0)) { transferMode = PIO_FLOW_CONTROL; modeNumber = 2; } else { transferMode = PIO_FLOW_CONTROL; modeNumber = RaidGetHighestBit((UCHAR) 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, (UCHAR) (0x56 + (channel * 4)), configByte); pci_write_config_byte(pPciDev, (UCHAR) (0x56 + (channel * 4) + 1), configByte); } else { pci_write_config_byte(pPciDev, (UCHAR) (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, (UCHAR) (0x54 + (channel * 4)), configByte); } /* * Check shall we do switch between the two devices */ for (i = 0; i < 2; i++) { pChan->DoSwitch = TRUE; /* * Master is not present */ if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) || (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) { printk("Channel %x: master is not present. No switch mode.\n", channel); pChan->DoSwitch = FALSE; continue; } /* * Slave is not present */ if (!(pChan->DeviceFlags[i + 1] & DFLAGS_DEVICE_PRESENT) || (pChan->DeviceFlags[i + 1] & DFLAGS_CONFIG_CHANGED)) { printk("Channel %x: slave is not present. No switch mode.\n", channel); pChan->DoSwitch = FALSE; continue; } /* * If both devices are running on DMA mode, no switch. */ if (pChan->DmaType[i] == USE_MULTIWORD_DMA && pChan->DmaType[i + 1] == USE_MULTIWORD_DMA) { printk("Channel %x: run on DMA mode only. No switch mode.\n", channel); pChan->DoSwitch = FALSE; continue; } /* * No switch if the two devices are running on the same mode. */ if ((pChan->DmaType[i] == pChan->DmaType[i + 1] ) && (pChan->UdmaTiming[i] == pChan->UdmaTiming[i + 1]) && (pChan->IdeClock[i] == pChan->IdeClock[i + 1] )) { printk("Channel %x: two dev run on the same mode. No switch mode.\n", channel); pChan->DoSwitch = FALSE; continue; } printk("Channel %x: switch mode if needed.\n", channel); } } } /* end IT8212SetBestTransferMode */ /************************************************************************ * Initialize bypass(transparent) mode if BIOS is not ready. ************************************************************************/ BOOLEAN IT8212InitBypassMode(struct pci_dev *pPciDev) { /* * Reset local CPU, and set BIOS not ready. */ pci_write_config_byte(pPciDev, 0x5E, 0x01); /* * Set to bypass mode, and reset PCI bus. */ pci_write_config_byte(pPciDev, 0x50, 0x00); pci_write_config_word(pPciDev, 0x4, 0x0047); pci_write_config_word(pPciDev, 0x40, 0xA0F3); pci_write_config_dword(pPciDev,0x4C, 0x02040204); pci_write_config_byte(pPciDev, 0x42, 0x36); pci_write_config_byte(pPciDev, 0x0D, 0x00); return TRUE; } /* end IT8212InitBypassMode */ /************************************************************************ * This is the interrupt service routine for ATAPI IDE miniport driver. * TURE if expecting an interrupt. ************************************************************************/ BOOLEAN IT8212Interrupt(PChannel pChan, BOOLEAN bypass_mode) { UCHAR statusByte; UCHAR bmstatus; ULONG i; ULONG 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((UCHAR) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); GetBaseStatus(pChan, statusByte); outb((UCHAR) 0xB0, pChan->io_ports[IDE_SELECT_OFFSET]); GetBaseStatus(pChan, statusByte); outb(bmbase + 2, (UCHAR) (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, (UCHAR) (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. ************************************************************************/ BOOLEAN AtapiInterrupt(PChannel pChan) { ULONG wordCount; ULONG wordsThisInterrupt; ULONG status; ULONG i; UCHAR statusByte; UCHAR interruptReason; UCHAR 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, (PUSHORT)srb->Cdb, 6); return TRUE; } else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) { /* * Write the data. */ /* * Pick up bytes to transfer and convert to words. */ wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; /* * Covert bytes to words. */ wordCount >>= 1; if (wordCount != pChan->WordsLeft) { printk( "AtapiInterrupt: %ld words requested; %ld words xferred\n", pChan->WordsLeft, wordCount); } /* * Verify this makes sense. */ if (wordCount > pChan->WordsLeft) { wordCount = pChan->WordsLeft; } /* * Ensure that this is a write command. */ if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { dprintk("AtapiInterrupt: write interrupt\n"); WaitOnBusy(pChan, statusByte); WriteBuffer(pChan, pChan->DataBuffer, wordCount); # if (0) /* * Translate ATAPI data back to SCSI data if needed (don't * convert if the original command is SCSIOP_MODE_SELECT10) */ if (srb->Cdb[0] == ATAPI_MODE_SELECT && pchan->ConvertCdb) { Atapi2Scsi(pChan, srb, (char *)pChan->DataBuffer, wordCount << 1); } # endif } else { printk("AtapiInterrupt: int reason %x, but srb is for a write %p.\n", interruptReason, srb); /* * Fail this request. */ status = SRB_STATUS_ERROR; goto CompleteRequest; } /* * Advance data buffer pointer and bytes left. */ pChan->DataBuffer += wordCount; pChan->WordsLeft -= wordCount; return TRUE; } else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) { /* * Pick up bytes to transfer and convert to words. */ wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; /* * Covert bytes to words. */ wordCount >>= 1; if (wordCount != pChan->WordsLeft) { printk("AtapiInterrupt: %ld words requested; %ld words xferred\n", pChan->WordsLeft, wordCount); } /* * Verify this makes sense. */ if (wordCount > pChan->WordsLeft) { wordCount = pChan->WordsLeft; } /* * Ensure that this is a read command. */ if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { dprintk("AtapiInterrupt: read interrupt\n"); WaitOnBusy(pChan, statusByte); ReadBuffer(pChan, pChan->DataBuffer, wordCount); /* * From Windows DDK * You should typically set the ANSI-approved Version field, * in the INQUIRY response, to at least 2. */ if (srb->Cdb[0] == SCSIOP_INQUIRY) { /* * Maybe it's not necessary in Linux driver. */ *((PUCHAR)pChan->DataBuffer + 2) = 2; } } else { printk("AtapiInterrupt: int reason %x, but srb is for a read %p.\n", interruptReason, srb); /* * Fail this request. */ status = SRB_STATUS_ERROR; goto CompleteRequest; } /* * Advance data buffer pointer and bytes left. */ pChan->DataBuffer += wordCount; pChan->WordsLeft -= wordCount; /* * Check for read command complete. */ if (pChan->WordsLeft == 0) { /* * Work around to make many atapi devices return correct sector size * of 2048. Also certain devices will have sector count == 0x00, check * for that also. */ if ((srb->Cdb[0] == 0x25) && ((pChan->IdentifyData[srb->TargetId & 1].GeneralConfiguration >> 8) & 0x1F) == 0x05) { pChan->DataBuffer -= wordCount; if (pChan->DataBuffer[0] == 0x00) { *((ULONG *) &(pChan->DataBuffer[0])) = 0xFFFFFF7F; } *((ULONG *) &(pChan->DataBuffer[2])) = 0x00080000; pChan->DataBuffer += wordCount; } } return TRUE; } else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ)) { dprintk("AtapiInterrupt: command complete!\n"); /* * Command complete. */ if (pChan->WordsLeft) { status = SRB_STATUS_DATA_OVERRUN; } else { status = SRB_STATUS_SUCCESS; } CompleteRequest: if (status == SRB_STATUS_ERROR) { /* * Map error to specific SRB status and handle request sense. */ printk("AtapiInterrupt error\n"); status = MapError(pChan, srb); /* * Try to recover it.... 2003/02/27 */ pChan->RDP = FALSE; } else { /* * Wait for busy to drop. */ for (i = 0; i < 30; i++) { GetStatus(pChan, statusByte); if (!(statusByte & IDE_STATUS_BUSY)) { break; } udelay(500); } if (i == 30) { /* * Reset the controller. */ printk("AtapiInterrupt: resetting due to BSY still up - %x.\n", statusByte); AtapiResetController(pAdap, pChan); return TRUE; } /* * Check to see if DRQ is still up. */ if (statusByte & IDE_STATUS_DRQ) { for (i = 0; i < 500; i++) { GetStatus(pChan, statusByte); if (!(statusByte & IDE_STATUS_DRQ)) { break; } udelay(100); } if (i == 500) { /* * Reset the controller. */ printk("AtapiInterrupt: resetting due to DRQ still up - %x\n", statusByte); AtapiResetController(pAdap, pChan); return TRUE; } } } /* * Clear interrupt expecting flag. */ pChan->ExpectingInterrupt = FALSE; /* * Sanity check that there is a current request. */ if (srb != NULL) { /* * Set status in SRB. */ srb->SrbStatus = (UCHAR)status; /* * Check for underflow. */ if (pChan->WordsLeft) { /* * Subtract out residual words and update if filemark hit, * setmark hit , end of data, end of media... */ if (!(pChan->DeviceFlags[srb->TargetId & 1] & DFLAGS_TAPE_DEVICE)) { if (status == SRB_STATUS_DATA_OVERRUN) { srb->DataTransferLength -= pChan->WordsLeft * 2; } else { srb->DataTransferLength = 0; } } else { srb->DataTransferLength -= pChan->WordsLeft * 2; } } GetBaseStatus(pChan, statusByte); if (pChan->RDP && !(statusByte & IDE_STATUS_DSC)) { printk("-@@-\n"); } else { /* * Clear current SRB. Indicate ready for next request. */ pChan->CurrentSrb = NULL; TaskDone(pChan, srb); } } else { printk("AtapiInterrupt: no SRB!\n"); } return TRUE; } else { /* * Unexpected int. */ printk("AtapiInterrupt: unexpected interrupt. interruptReason %x. status %x.\n", interruptReason, statusByte); return FALSE; } return TRUE; } /* end AtapiInterrupt */ /************************************************************************ * IRQ handler. ************************************************************************/ static void Irq_Handler(int irq, void *dev_id, struct pt_regs *regs) { UCHAR i; UCHAR j; ULONG flags; PITE_ADAPTER pAdap; dprintk("Irq_Handler enter\n"); 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; for (j = 0; j < pAdap->num_channels; j++) { IT8212Interrupt(&pAdap->IDEChannel[j], pAdap->bypass_mode); } } spin_unlock_irqrestore(&io_request_lock, flags); dprintk("Irq_Handler exit\n"); } /* end Irq_Handler */ /************************************************************************ * This routine handles IDE Verify. ************************************************************************/ UCHAR IdeVerify(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { ULONG startingSector; ULONG sectors; ULONG endSector; ULONG sectorCount; UCHAR drvSelect; UCHAR statusByte = 0; /* * Select device */ outb((UCHAR)((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 = (USHORT)(((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 %ld, Ending sector %ld\n", startingSector, endSector); if (endSector > sectors) { /* * Too big, round down. */ printk("IdeVerify: truncating request to %lx blocks\n", sectors - startingSector - 1); outb((UCHAR)((sectors - startingSector - 1) >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]); outb((UCHAR)(sectors - startingSector - 1), pChan->io_ports[IDE_NSECTOR_OFFSET]); } else { /* * Set up sector count register. Round up to next block. */ if (sectorCount > 0xFFFF) { sectorCount = (USHORT)0xFFFF; } outb((UCHAR)(sectorCount >> 8), pChan->io_ports[IDE_NSECTOR_OFFSET]); outb((UCHAR) sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]); } /* * Indicate expecting an interrupt. */ pChan->ExpectingInterrupt = TRUE; /* * Set up LBA address */ outb((UCHAR) (startingSector >> 24), pChan->io_ports[IDE_LOCYL_OFFSET]); outb((UCHAR) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); outb((UCHAR) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); outb((UCHAR) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); outb((UCHAR) 0, pChan->io_ports[IDE_HCYL_OFFSET]); outb((UCHAR) (startingSector >> 16), pChan->io_ports[IDE_HCYL_OFFSET]); /* * Send verify command. */ outb(IDE_COMMAND_READ_VERIFY_EXT, pChan->io_ports[IDE_COMMAND_OFFSET]); } /* * 28-bit addressing */ else { sectors = pChan->IdentifyData[Srb->TargetId & 0x01].UserAddressableSectors; printk("IdeVerify: starting sector %ld, ending sector %ld\n", startingSector, endSector); if (endSector > sectors) { /* * Too big, round down. */ printk("IdeVerify: truncating request to %ld blocks\n", sectors - startingSector - 1); outb((UCHAR)(sectors - startingSector - 1), pChan->io_ports[IDE_NSECTOR_OFFSET]); } else { /* * Set up sector count register. Round up to next block. */ if (sectorCount > 0xFF) { sectorCount = (USHORT)0xFF; } outb((UCHAR)sectorCount, pChan->io_ports[IDE_NSECTOR_OFFSET]); } /* * Indicate expecting an interrupt. */ pChan->ExpectingInterrupt = TRUE; /* * Set up LBA address */ outb((UCHAR) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); outb((UCHAR) (startingSector >> 8), pChan->io_ports[IDE_MIDCYL_OFFSET]); outb((UCHAR) (startingSector >> 16),pChan->io_ports[IDE_HCYL_OFFSET]); /* * Select driver, set LBA mode, set LBA (27:27) */ drvSelect = (UCHAR) (startingSector >> 24); drvSelect = drvSelect | (((UCHAR) Srb->TargetId & 0x1) << 4) | 0xA0 | 0x40; outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); /* * Send verify command. */ outb(IDE_COMMAND_READ_VERIFY, pChan->io_ports[IDE_COMMAND_OFFSET]); } /* * Wait for interrupt. */ return SRB_STATUS_PENDING; } /* end IdeVerify */ /************************************************************************ * This function is used to copy memory with overlapped destination and * source. I guess ScsiPortMoveMemory cannot handle this well. Can it? ************************************************************************/ void IT8212MoveMemory(PUCHAR DestAddr, PUCHAR SrcAddr, LONG ByteCount) { long i; dprintk("IT8212MoveMemory: DestAddr=0x%p, SrcAddr=0x%p, ByteCount=0x%lx\n", DestAddr, SrcAddr, ByteCount); if (DestAddr > SrcAddr) { /* * If Destination Area is in the back of the Source Area, copy from * the end of the requested area. */ for (i = (ByteCount - 1); i >= 0; i--) { *(DestAddr + i) = *(SrcAddr + i); } } else if (DestAddr < SrcAddr) { /* * If Destination Area is in the front of the Source Area, copy from * the begin of the requested area. */ for (i = 0; i < ByteCount; i++) { *(DestAddr + i) = *(SrcAddr + i); } } } /* end IT8212MoveMemory */ /************************************************************************ * Convert SCSI packet command to Atapi packet command. ************************************************************************/ void Scsi2Atapi(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { dprintk("++++++++++ Scsi2Atapi (0x%X) ++++++++++\n", Srb->Cdb[0]); dprintk("Srb->DataBuffer=0x%x\n", Srb->DataBuffer); dprintk("Srb->DataTransferLength=0x%lx\n", Srb->DataTransferLength); dprintk("CDB=0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5], Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11]); /* * 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((PUCHAR)modeSense10, 0, MAXIMUM_CDB_SIZE); /* * 2. Fill in command code (SCSI_MODE_SENSE10). */ modeSense10->OperationCode = ATAPI_MODE_SENSE; modeSense10->Dbd = modeSense6->Dbd; modeSense10->PageCode = modeSense6->PageCode; modeSense10->Pc = modeSense6->Pc; modeSense10->SubpageCode = modeSense6->SubpageCode; modeSense10->AllocationLengthLsb = modeSense6->AllocationLength; modeSense10->Control = modeSense6->Control; /* * 3. Becasuse we will fake a block descripter (-8), and * translate the header (+4), so the requested length * should be modified. That is, -8+4=-4 bytes. */ modeSense10->AllocationLengthLsb -= 4; break; } /* * Convert the command from SCSIOP_MODE_SELECT (0x15) to * SCSIOP_MODE_SELECT10 (0x5A). */ case SCSIOP_MODE_SELECT: { UCHAR tempHeader[sizeof(PSCSI_MODE_PARAMETER_HEADER6)]; USHORT 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((PUCHAR)modeSelect10, 0, MAXIMUM_CDB_SIZE); /* * 2. Fill in command code (SCSI_MODE_SENSE10). */ modeSelect10->OperationCode = ATAPI_MODE_SELECT; modeSelect10->SPBit = modeSelect6->SPBit; modeSelect10->PFBit = modeSelect6->PFBit; modeSelect10->ParameterListLengthLsb = modeSelect6->ParameterListLength; modeSelect10->Control = modeSelect6->Control; /* * 3. Becasuse we will remove the block descripter (-8), and * translate the header (+4), so the requested length should * be modified. That is, -8+4=-4 bytes. */ modeSelect10->ParameterListLengthLsb -= 4; /* * Second, convert the parameter page format from SCSI to ATAPI. */ /* * Remove the mode parameter data (except the header and the * block descripter). */ byteCount = modeSelect6->ParameterListLength - sizeof(SCSI_MODE_PARAMETER_HEADER6) - sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER); if (byteCount > 0) { IT8212MoveMemory( (PUCHAR) header10 + sizeof(SCSI_MODE_PARAMETER_HEADER10), (PUCHAR) 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((PUCHAR)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; } } dprintk("Srb->DataTransferLength=0x%lx\n", Srb->DataTransferLength); dprintk("Final CDB=0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5], Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11]); dprintk("---------- Scsi2Atapi (0x%X) ----------\n", Srb->Cdb[0]); } /* end Scsi2Atapi */ /************************************************************************ * Send ATAPI packet command to device. ************************************************************************/ UCHAR AtapiSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { ULONG bmAddress = pChan->dma_base; ULONG i; ULONG flags; UCHAR statusByte; UCHAR byteCountLow; UCHAR byteCountHigh; UCHAR useDMA; UCHAR RevisionID = 0; UCHAR bmClearStat; 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((UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]); /* * Try to enable interrupt again. (2003/02/25) */ #if (0) outb(0x00, pChan->io_ports[ATAPI_CONTROL_OFFSET]); #endif /* * Verify that controller is ready for next command. */ GetStatus(pChan, statusByte); dprintk("AtapiSendCommand: entered with status %x\n", statusByte); if (statusByte & IDE_STATUS_BUSY) { printk("AtapiSendCommand: device busy (%x)\n", statusByte); return SRB_STATUS_BUSY; } if (statusByte & IDE_STATUS_ERROR) { if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { printk("AtapiSendCommand: error on entry: (%x)\n", statusByte); /* * Read the error reg. to clear it and fail this request. */ return MapError(pChan, Srb); } } /* * If a tape drive doesn't have DSC set and the last command is * restrictive, don't send the next command. See discussion of * Restrictive Delayed Process commands in QIC-157. */ if ((!(statusByte & IDE_STATUS_DSC)) && (flags & DFLAGS_TAPE_DEVICE) && pChan->RDP) { mdelay(1); printk("AtapiSendCommand: DSC not set. %x\n", statusByte); return SRB_STATUS_BUSY; } if (statusByte & IDE_STATUS_DRQ) { printk("AtapiSendCommand: enter with status (%x). Attempt to recover.\n", statusByte); /* * Try to drain the data that one preliminary device thinks that it has * to transfer. Hopefully this random assertion of DRQ will not be present * in production devices. */ for (i = 0; i < 0x10000; i++) { GetStatus(pChan, statusByte); if (statusByte & IDE_STATUS_DRQ) { /* * Note: The data register is always referenced as a 16-bit word. */ inw(pChan->io_ports[ATAPI_DATA_OFFSET]); } else { break; } } if (i == 0x10000) { printk("AtapiSendCommand: DRQ still asserted.Status (%x)\n", statusByte); printk("AtapiSendCommand: issued soft reset to Atapi device. \n"); AtapiSoftReset(pChan, Srb->TargetId); /* * Re-initialize Atapi device. */ IssueIdentify(pChan, (Srb->TargetId & 1), IDE_COMMAND_ATAPI_IDENTIFY); /* * Inform the port driver that the bus has been reset. */ /* * Clean up device extension fields that AtapiStartIo won't. */ pChan->ExpectingInterrupt = FALSE; pChan->RDP = FALSE; return SRB_STATUS_BUS_RESET; } } if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { /* * As the cdrom driver sets the LUN field in the cdb, it must be removed. */ Srb->Cdb[1] &= ~0xE0; if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) { /* * Torisan changer. TUR's are overloaded to be platter switches. */ Srb->Cdb[7] = Srb->Lun; } } /* * Convert SCSI to ATAPI commands if needed */ switch (Srb->Cdb[0]) { case SCSIOP_MODE_SENSE: case SCSIOP_MODE_SELECT: if (flags & DFLAGS_ATAPI_DEVICE) { Scsi2Atapi(pChan, Srb); } break; } if (pChan->UseDma[Srb->TargetId & 1]) { switch (Srb->Cdb[0]) { case SCSIOP_READ: /* (0x28) */ case 0xA8: /* READ(12) */ case SCSIOP_READ_CD: if (Srb->DataTransferLength == 0) { break; } /* * First, switch to DMA or UDMA mode if running on Bypass mode. */ if (pAdap->bypass_mode) { IT8212SwitchDmaMode(pChan, Srb->TargetId); } /* * Check the SCATTER/GATHER count. The upper will give the * different memory address depend on whether use_sg is used * or not. */ if (Srb->UseSg == 0) { IdeBuildDmaTable(pChan, Srb); } else { IdeBuildDmaSgTable(pChan, Srb); } bmClearStat = inb(bmAddress + 2); if (Srb->TargetId & 0x01) { bmClearStat = bmClearStat | BM_DRV1_DMA_CAPABLE | BM_STAT_FLG_INT | BM_STAT_FLG_ERR; } else { bmClearStat = bmClearStat | BM_DRV0_DMA_CAPABLE | BM_STAT_FLG_INT | BM_STAT_FLG_ERR; } useDMA = 1; outb(0, bmAddress); /* * Setup PRD table physical address. */ outl(pChan->dmatable_dma, bmAddress + 4); /* * Clear the status. */ outb(bmClearStat, bmAddress + 2); break; } /* end switch (Srb->Cdb[0]) */ } /* * Set data buffer pointer and words left. */ pChan->DataBuffer = (PUSHORT)Srb->DataBuffer; if (useDMA) { pChan->WordsLeft = 0; } else { pChan->WordsLeft = Srb->DataTransferLength / 2; } outb((UCHAR)(((Srb->TargetId & 0x1) << 4) | 0xA0), pChan->io_ports[ATAPI_SELECT_OFFSET]); WaitOnBusy(pChan, statusByte); /* * Write transfer byte count to registers. */ byteCountLow = (UCHAR)(Srb->DataTransferLength & 0xFF); byteCountHigh = (UCHAR)(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, (PUSHORT)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=%ld\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. ************************************************************************/ ULONG IdeSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { UCHAR statusByte; ULONG status; ULONG 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->lun != 0) || (!pChan->DeviceFlags[pREQ->target & 1] & DFLAGS_DEVICE_PRESENT)) { /* * Indicate no device found at this address. */ status = SRB_STATUS_INVALID_TARGET_ID; break; } else { request_buffer = Srb->DataBuffer; inquiryData = Srb->DataBuffer; /* * Zero INQUIRY data structure. */ memset(request_buffer, 0, Srb->DataTransferLength); /* * Standard IDE interface only supports disks. */ inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; /* * Device type modifer. */ request_buffer[1] = 0; /* * No ANSI/ISO compliance. */ request_buffer[2] = 0; /* * Additional length. */ request_buffer[4] = 31; memcpy(&request_buffer[8], "ITE ", 8); memcpy(&request_buffer[16], "IT8212F ", 16); memcpy(&request_buffer[32], "1.3", 3); /* * Set the removable bit, if applicable. */ if (pChan->DeviceFlags[pREQ->target & 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->target & 0x01].UserAddressableSectors == 0x0FFFFFFF) { i = pChan->IdentifyData[pREQ->target & 0x01].Capacity_48bit_LOW - 1; } else { i = pChan->IdentifyData[pREQ->target & 0x01].UserAddressableSectors - 1; } ((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress = (((PUCHAR)&i)[0] << 24) | (((PUCHAR)&i)[1] << 16) | (((PUCHAR)&i)[2] << 8) | ((PUCHAR)&i)[3]; status = SRB_STATUS_SUCCESS; break; case SCSIOP_VERIFY: status = IdeVerify(pChan, Srb); break; case SCSIOP_READ: case SCSIOP_WRITE: status = IT8212ReadWrite(pChan, Srb); break; case SCSIOP_START_STOP_UNIT: /* * Determine what type of operation we should perform */ status = SRB_STATUS_SUCCESS; break; case SCSIOP_REQUEST_SENSE: /* * This function makes sense buffers to report the results * of the original GET_MEDIA_STATUS command */ status = SRB_STATUS_INVALID_REQUEST; break; default: printk("IdeSendCommand: unsupported command %x\n", Srb->Cdb[0]); status = SRB_STATUS_INVALID_REQUEST; } /* end switch */ return status; } /* end IdeSendCommand */ /************************************************************************ * This routine is called from the SCSI port driver synchronized with * the kernel to start an IO request. If the current SRB is busy, return * FALSE, else return TURE. ************************************************************************/ BOOLEAN AtapiStartIo(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) { ULONG 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 = (UCHAR)status; dprintk("AtapiStartIo: status=%lx\n", status); TaskDone(pChan, Srb); } return TRUE; } /* end AtapiStartIo */ /************************************************************************ * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. ************************************************************************/ void MapRequest(Scsi_Cmnd * pREQ, PSCSI_REQUEST_BLOCK Srb) { Srb->Length = sizeof(SCSI_REQUEST_BLOCK); Srb->CdbLength = pREQ->cmd_len; Srb->TargetId = pREQ->target; Srb->Lun = pREQ->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. ************************************************************************/ 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. ************************************************************************/ void TaskStart(PChannel pChan, Scsi_Cmnd *pREQ) { PSCSI_REQUEST_BLOCK Srb; dprintk("TaskStart(pChan=%p, pREQ=%p)\n", pChan, pREQ); Srb = &pChan->_Srb; /* * Clear the SRB structure. */ memset(Srb, 0, sizeof(SCSI_REQUEST_BLOCK)); /* * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. */ MapRequest(pREQ, Srb); /* * Start IDE I/O command. */ AtapiStartIo(pChan, Srb); } /* end TaskStart */ /************************************************************************ * Check if queue is empty. If there are request in queue, transfer the * request to HIM's request and execute the request. ************************************************************************/ 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->host) { if ((SCpnt->channel != 0) || (SCpnt->target >= (4 * NumAdapters))) { /* * Returns that we have a bad target. */ printk("BAD TARGET\n"); SCpnt->result = (DID_BAD_TARGET << 16); SCpnt->scsi_done(SCpnt); goto check_next; } } if (SCpnt->target >= 4) { pAdap = ite_adapters[1]; if (SCpnt->target < 6) pChan = &pAdap->IDEChannel[0]; else pChan = &pAdap->IDEChannel[1]; } else { pAdap = ite_adapters[0]; if (SCpnt->target < 2) pChan = &pAdap->IDEChannel[0]; else pChan = &pAdap->IDEChannel[1]; } TaskStart(pChan, SCpnt); return; } } /* end TaskQueue */ /**************************************************************** * Name: iteraid_queuecommand * Description: Process a queued command from the SCSI manager. * Parameters: SCpnt - Pointer to SCSI command structure. * done - Pointer to done function to call. * Returns: Status code. ****************************************************************/ int iteraid_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { unsigned long flags; dprintk("##Queuecommand enter##\n"); /* * Hooks the done routine. */ SCpnt->scsi_done = (void *)done; spin_lock_irqsave(&queue_request_lock, flags); if (it8212_req_last == NULL) { SCpnt->SCp.ptr = (char *)SCpnt; } else { SCpnt->SCp.ptr = it8212_req_last->SCp.ptr; it8212_req_last->SCp.ptr = (char *)SCpnt; } it8212_req_last = SCpnt; spin_unlock_irqrestore(&queue_request_lock, flags); TaskQueue(); dprintk("@@Queuecommand exit@@\n"); return 0; } /* end iteraid_queuecommand */ /**************************************************************** * Name: internal_done :LOCAL * Description: Done handler for non-queued commands * Parameters: SCpnt - Pointer to SCSI command structure. * Returns: Nothing. ****************************************************************/ static void internal_done(Scsi_Cmnd * SCpnt) { SCpnt->SCp.Status++; } /* end internal_done */ /**************************************************************** * Name: iteraid_command * Description: Process a command from the SCSI manager. * Parameters: SCpnt - Pointer to SCSI command structure. * Returns: Status code. ****************************************************************/ int iteraid_command(Scsi_Cmnd *SCpnt) { DWORD timeout; SCpnt->SCp.Status = 0; iteraid_queuecommand(SCpnt, internal_done); /* * Should be longer than hard-reset time. */ timeout = jiffies + 60 * HZ; while (!SCpnt->SCp.Status && time_before(jiffies, timeout)) { barrier(); } if (!SCpnt->SCp.Status) SCpnt->result = (DID_ERROR << 16); return SCpnt->result; } /* end iteraid_command */ /************************************************************************ * Enables/disables media status notification. ************************************************************************/ void IdeMediaStatus(BOOLEAN EnableMSN, PChannel pChan, ULONG Device) { UCHAR statusByte; UCHAR errorByte; statusByte = 0; if (EnableMSN == TRUE) { /* * If supported enable Media Status Notification support. */ if ((pChan->DeviceFlags[Device] & DFLAGS_REMOVABLE_DRIVE)) { outb((UCHAR) (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((UCHAR)(0x31), pChan->io_ports[IDE_FEATURE_OFFSET]); outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); WaitOnBaseBusy(pChan, statusByte); pChan->DeviceFlags[Device] &= ~DFLAGS_MEDIA_STATUS_ENABLED; } } } /* end IdeMediaStatus */ /************************************************************************ * Issue IDENTIFY command to a device. * Either the standard (EC) or the ATAPI packet (A1) IDENTIFY. ************************************************************************/ BOOLEAN IssueIdentify(PChannel pChan, UCHAR DeviceNumber, UCHAR Command) { ULONG i; ULONG j; UCHAR statusByte = 0; /* * 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((UCHAR)((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((UCHAR)((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((UCHAR)((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((UCHAR)((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, (PUSHORT)&pChan->FullIdentifyData, 256); /* * Check out a few capabilities / limitations of the device. * 01/29/2003 */ if (pChan->FullIdentifyData.SpecialFunctionsEnabled & 1) { /* * Determine if this drive supports the MSN functions. */ printk("Marking drive %x as removable. SFE = %x\n", DeviceNumber, pChan->FullIdentifyData.SpecialFunctionsEnabled); pChan->DeviceFlags[DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE; } memcpy(&pChan->IdentifyData[DeviceNumber], &pChan->FullIdentifyData, sizeof(IDENTIFY_DATA2)); if (pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0x20 && Command != IDE_COMMAND_IDENTIFY) { /* * This device interrupts with the assertion of DRQ after receiving * Atapi Packet Command. */ pChan->DeviceFlags[DeviceNumber] |= DFLAGS_INT_DRQ; dprintk(KERN_NOTICE "Device interrupts on assertion of DRQ.\n"); } else { dprintk(KERN_NOTICE "Device does't interrupt on assertion of DRQ.\n"); } if (((pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0xF00) == 0x100) && Command != IDE_COMMAND_IDENTIFY) { /* * This is a tape. */ pChan->DeviceFlags[DeviceNumber] |= DFLAGS_TAPE_DEVICE; printk(KERN_NOTICE "IssueIdentify: device is a tape drive.\n"); } else { dprintk(KERN_NOTICE "IssueIdentify: device is not a tape drive.\n"); } /* * Work around for some IDE and one model Atapi that will present more * then 256 bytes for the Identify data. */ WaitOnBaseBusy(pChan, statusByte); for (i = 0; i < 0x10000; i++) { GetStatus(pChan, statusByte); if (statusByte & IDE_STATUS_DRQ) { /* * Suck out any remaining bytes and throw away. */ inw(pChan->io_ports[IDE_DATA_OFFSET]); } else { break; } } return TRUE; } /* end IssueIdentify() */ /************************************************************************ * Check this is the IDE or ATAPI disk then identify it. ************************************************************************/ static BOOLEAN iteraid_find_device(PChannel pChan, UCHAR channel) { BOOLEAN deviceResponded = FALSE; UCHAR deviceNumber; UCHAR signatureLow; UCHAR signatureHigh; UCHAR 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((UCHAR)((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((UCHAR)((deviceNumber << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Check here for some SCSI adapters that incorporate IDE emulation. */ GetStatus(pChan, statusByte); /* * No Disk. */ if (statusByte == 0xFF || statusByte == 0x7F || statusByte == 0x0) { dprintk("FindDevices: cannot find IDE device. status = %x\n", statusByte); continue; } /* * Issue IDE Identify. If an ATAPI device is actually present, * the signature will be asserted, and the drive will be * recognized as such. */ if (IssueIdentify(pChan, deviceNumber, IDE_COMMAND_IDENTIFY)) { /* * IDE drive found. */ printk(KERN_WARNING "FindDevices: device %u is IDE\n", (channel * 2) + deviceNumber); pChan->DeviceFlags[deviceNumber] |= DFLAGS_DEVICE_PRESENT; pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_ATAPI_DEVICE; pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_CONFIG_CHANGED; deviceResponded = TRUE; } else { printk(KERN_WARNING "FindDevices: device %u is not present\n", (channel * 2) + deviceNumber); pChan->DeviceFlags[deviceNumber] &= ~DFLAGS_DEVICE_PRESENT; } } } return deviceResponded; } /* end iteraid_find_device */ /************************************************************************ * IDE disk hardware initialize. ************************************************************************/ BOOLEAN AtapiHwInitialize(PITE_ADAPTER pAdap, PChannel pChan, UCHAR channel) { UCHAR i; UCHAR 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((UCHAR) (0xA0 | ((UCHAR) i << 4)), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Check if card at this address. */ outb(0xAA, pChan->io_ports[IDE_MIDCYL_OFFSET]); /* * Check if indentifier can be read back. */ if ((statusByte = inb(pChan->io_ports[IDE_MIDCYL_OFFSET])) != 0xAA) { printk("AtapiHwInitialize: identifier read back from (%x, %x) = %x\n", channel, i, statusByte); /* * ***** Dont free it....For later use ***** * ScsiPortFreeDeviceBase(HwDeviceExtension, ioSpace1); */ continue; } printk("AtapiHwInitialize: found ATA device (%x, %x)n", channel, i); } } return TRUE; } /* end AtapiHwInitialize */ /************************************************************************ * Initialize a adapter, return 0 means success. ************************************************************************/ static int iteraid_init(PITE_ADAPTER pAdap, struct pci_dev *pPciDev) { unsigned char z; unsigned char i; unsigned char j; unsigned int set_irq; ULONG control_addr; /* Control reg base address */ ULONG base_addr; /* IDE I/O port base address */ ULONG bm_base_addr; /* Bus Master base address */ PChannel pChan; /* Use for each channel */ dprintk("iteraid_init enter\n"); /* * Common settings. */ pAdap->pci_bus = pPciDev->bus->number; pAdap->devfn = pPciDev->devfn; pAdap->irq = pPciDev->irq; pAdap->irqOwned = 0; printk(KERN_NOTICE "Found Controller: %s\n", pAdap->name); /* * Allocate buffer for IDE channles (One IT8212 supports two channels) */ pAdap->IDEChannel = (PChannel)kmalloc(sizeof(Channel) * pAdap->num_channels, GFP_ATOMIC); if (pAdap->IDEChannel == 0) { printk("iteraid_init: pChan allocate failed.\n"); return -1; } memset(pAdap->IDEChannel, 0, sizeof(Channel) * pAdap->num_channels); set_irq = 1; for (i = 0; i < NumAdapters; i++) { if (ite_adapters[i]->irqOwned == pAdap->irq) set_irq = 0; } /* * Request the irq (share irq) and hook the interrupt service routine. */ if (set_irq) { if (request_irq(pAdap->irq, Irq_Handler, SA_SHIRQ, PROC_DIR_NAME, pAdap) < 0) { printk("iteraid_init: unable to allocate IRQ for %s\n", pAdap->name); return -1; } pAdap->irqOwned = pAdap->irq; } /* * Get the IDE port and DMA registers. */ for (i = 0; i < pAdap->num_channels; i++) { pChan = &pAdap->IDEChannel[i]; /* * Reference the book "LINUX DEVICE DRIVER 2nd", Page 484 * unsigned long pci_resource_start(struct pci_dev *dev, int bar); */ base_addr = pci_resource_start(pPciDev, i * 2); control_addr = pci_resource_start(pPciDev, i * 2 + 1); bm_base_addr = pci_resource_start(pPciDev, 4); pChan->dma_base = bm_base_addr + i * 8; for (j = 0; j <= IDE_STATUS_OFFSET; j++) { pChan->io_ports[j] = base_addr; base_addr += 1; } pChan->io_ports[IDE_CONTROL_OFFSET] = control_addr + 2; } /* * Initialize channels. */ for (z = 0; z < pAdap->num_channels; z++) { pChan = &pAdap->IDEChannel[z]; pChan->pPciDev = pPciDev; pChan->channel = z; /* * This section should be masked off if BIOS is ready. */ # if (MARK_DEBUG_BYPASS_MODE) /* * BIOS is not ready, so I change to ByPass Mode by myself. */ pAdap->bypass_mode = TRUE; /* * Change to bypass mode. */ IT8212InitBypassMode(pPciDev); # endif /* * Hardware initialize. */ # if (0) AtapiHwInitialize(pAdap, pChan, z); # endif /* * Find and identify the IDE or ATAPI device. */ iteraid_find_device(pChan, z); /* * Set the best transfer mode. */ # if (MARK_SET_BEST_TRANSFER) IT8212SetBestTransferMode(pAdap, pChan, z); # endif /* * Set Scatter/Gather List buffer for the channel. */ IdeSetupDma(pChan, pChan->dma_base, 8); } dprintk("iteraid_init exit\n"); return 0; } /* end iteraid_init */ /************************************************************************ * This function will find and initialize any cards. ************************************************************************/ int iteraid_detect(Scsi_Host_Template *tpnt) { UCHAR i; UCHAR j; UCHAR mode; UCHAR pci_id; PChannel pChan; PITE_ADAPTER pAdap; struct pci_dev * pPciDev; dprintk("iteraid_detect enter\n"); /* * Search ITE IT8212 chip. */ pPciDev = NULL; pci_id = 0; while ((pPciDev = pci_find_device(ITE_VENDOR_ID, ITE_DEVICE_ID, pPciDev))) { if (PCI_FUNC(pPciDev->devfn)) continue; /* * Allocate memory for Adapter. */ pAdap = (PITE_ADAPTER)kmalloc(sizeof(ITE_ADAPTER), GFP_ATOMIC); if (pAdap == NULL) { printk("iteraid_detect: pAdap allocate failed.\n"); continue; } memset(pAdap, 0, sizeof(ITE_ADAPTER)); pAdap->name = CONTROLLER_NAME_IT8212; pAdap->num_channels = 2; pAdap->pci_dev = pPciDev; /* * Check if we are in bypass(transparent) or firmware mode. */ pci_read_config_byte(pPciDev, 0x50, &mode); if (mode & 1) { dprintk("Firmware mode in PCI#%d\n", pci_id); pAdap->bypass_mode = FALSE; } else { dprintk("Transparent mode in PCI#%d\n", pci_id); pAdap->bypass_mode = TRUE; } if (iteraid_init(pAdap, pPciDev) == 0) { ite_adapters[NumAdapters++] = pAdap; } pci_id++; } /* * Reenable interrupt after initialization. 2003/04/28 */ for (i = 0; i < NumAdapters; i++) { pAdap = ite_adapters[i]; for (j = 0; j < pAdap->num_channels; j++) { pChan = &pAdap->IDEChannel[j]; outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); } } if (NumAdapters) { /* * Register a virtual host. */ ite_vhost = scsi_register(tpnt, 0); ite_vhost->io_port = 0; ite_vhost->n_io_port = 0; ite_vhost->max_channel = 0; ite_vhost->max_id = MAX_DEVICES; ite_vhost->max_lun = 1; scsi_set_pci_device(ite_vhost, pPciDev); /* * Register the driver as a character device, for applications to * acess it for ioctls. Ideally, this should go in the init_module() * routine, but since it is hidden in the file "scsi_module.c" ( * included in the end), we define it here. First argument (major) * to register_chrdev implies a dynamic major number allocation. */ ite_major = register_chrdev(0, "itedev", &itedev_fops); /* * Register the Shutdown Notification hook in the kernel. */ register_reboot_notifier(&ite_notifier); /* * Initialize ioctl semphore. */ init_MUTEX(&mimd_entry_mtx); } dprintk("iteraid_detect exit\n"); return 1; } /* end iteraid_detect() */ /************************************************************************ * Name: iteraid_release * Description: Release resources allocated for a single each adapter. * Parameters: pshost - Pointer to SCSI command structure. * Returns: zero. ************************************************************************/ int iteraid_release(struct Scsi_Host *pshost) { UCHAR i; PITE_ADAPTER pAdap; /* * Unregister the character device. */ if (ite_major > 0) { unregister_chrdev(ite_major, "itedev"); ite_major = -1; } /* * Free irq and memory. */ for (i = 0; i < NumAdapters; i++) { pAdap = ite_adapters[i]; if (pAdap->irqOwned) free_irq(pAdap->irq, pAdap); if (pAdap->IDEChannel != NULL) { kfree(pAdap->IDEChannel); } if (pAdap != NULL) { kfree(pAdap); } } /* * Unregister the reboot notifier. */ unregister_reboot_notifier(&ite_notifier); /* * Tell kernel scsi-layer we are gone. */ scsi_unregister(pshost); return 0; } /* end iteraid_Release */ /************************************************************************ * Name: iteraid_old_abort * Description: Process the Abort command from the SCSI manager. * Parameters: SCpnt - Pointer to SCSI command structure. * Returns: Always snooze(A short sleep). ************************************************************************/ int iteraid_old_abort(Scsi_Cmnd *SCpnt) { #if (0) return SCSI_ABORT_SNOOZE; #else return SCSI_ABORT_ERROR; #endif } /* end iteraid_old_abort */ /************************************************************************ * Name: iteraid_old_reset * Description: Process the Reset command from the SCSI manager. * Parameters: SCpnt - Pointer to SCSI command structure. * flags - Flags about the reset command * Returns: No active command at this time, so this means * that each time we got some kind of response the * last time through. Tell the mid-level code to * request sense information in order to decide what * to do next. ************************************************************************/ int iteraid_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) { return SCSI_RESET_SUCCESS; } /* end iteraid_old_reset */ /************************************************************************ * This is the new scsi eh reset function. ************************************************************************/ int iteraid_reset_eh(Scsi_Cmnd *SCpnt) { UCHAR i; UCHAR j; PChannel pChan; PITE_ADAPTER pAdap; if (SCpnt == NULL) { printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); return FAILED; } for (i = 0; i < NumAdapters; i++) { pAdap = ite_adapters[i]; for (j = 0; j < pAdap->num_channels; j++) { pChan = &pAdap->IDEChannel[j]; AtapiResetController(pAdap, pChan); } } return SUCCESS; } /* end iteraid_reset_eh */ /************************************************************************ * The new error handling code. ************************************************************************/ int iteraid_abort_eh(Scsi_Cmnd *SCpnt) { if (SCpnt == NULL) { printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); return FAILED; } return SUCCESS; } /* end iteraid_abort_eh */ /************************************************************************ * Name: iteraid_biosparam * Description: Process the biosparam request from the SCSI manager to * return C/H/S data. * Parameters: disk - Pointer to SCSI disk structure. * dev - Major/minor number from kernel. * geom - Pointer to integer array to place geometry data. * Returns: zero. ************************************************************************/ int iteraid_biosparam(Scsi_Disk *disk, kdev_t dev, int geom[]) { int heads; int sectors; int cylinders; /* * Default heads (64) & sectors (32) * Handle extended translation size for logical drives > 1Gb */ if (disk->capacity >= 0x200000) { heads = 255; sectors = 63; } else { heads = 64; sectors = 32; } cylinders = disk->capacity / (heads * sectors); /* * Return result */ geom[0] = heads; geom[1] = sectors; geom[2] = cylinders; return 0; } /* end iteraid_biosparam */ /************************************************************************ * Shutdown routine. ************************************************************************/ static int ite_halt(struct notifier_block *nb, ulong event, void *buf) { if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) { return NOTIFY_DONE; } unregister_reboot_notifier(&ite_notifier); return NOTIFY_OK; } /* end ite_halt */ /************************************************************************ * PROC information. ************************************************************************/ int iteraid_proc_info ( char * buffer, char ** start, off_t offset, int length, int host_no, int inout ) { return 0; } /* end iteraid_proc_info */ /************************************************************************ * IOCTL open entry. ************************************************************************/ static int itedev_open(struct inode *inode, struct file *filep) { MOD_INC_USE_COUNT; return 0; } /* end itedev_open */ /************************************************************************ * IOCTL code entry. ************************************************************************/ static int itedev_ioctl_entry ( struct inode * inode, struct file * filep, unsigned int cmd, unsigned long arg ) { int ret = -1; /* * We do not allow parallel ioctls to the driver as of now. */ down(&mimd_entry_mtx); ret = itedev_ioctl(inode, filep, cmd, arg); up(&mimd_entry_mtx); return ret; } /* end itedev_ioctl_entry */ /************************************************************************ * Real IOCTL code. ************************************************************************/ static int itedev_ioctl ( struct inode * inode, struct file * filep, unsigned int cmd, unsigned long arg ) { UCHAR diskArrayId; UCHAR statusByte = 0; UCHAR srbStatus; UCHAR progress = 0; UCHAR status = 0; uioctl_t * pioc; PITE_ADAPTER pAdap; PChannel pChan; PRAID_REBUILD_INFO rebuild_info; dprintk("itedev_ioctl enter\n"); /* * Extract the type and number bitfield. */ if (_IOC_TYPE(cmd) != ITE_IOCMAGIC) return -EINVAL; /* * Allocate space for ioctl data structure. */ if ((pioc = kmalloc(sizeof(uioctl_t), GFP_KERNEL)) == NULL) { printk("itedev_ioctl: error kmalloc on ioctl\n"); return -ENOMEM; } /* * Get the user ioctl structure. */ if (copy_from_user(pioc, (uioctl_t *)arg, sizeof(uioctl_t))) { kfree(pioc); return -EFAULT; } /* * Check which command to do. */ switch (cmd) { case ITE_IOC_GET_PHY_DISK_STATUS: dprintk("ITE_IOC_GET_PHY_DISK_STATUS\n"); /* * Get the physical disk status. */ status = IT8212GetChipStatus(pioc); return 0; case ITE_IOC_CREATE_DISK_ARRAY: dprintk("ITE_IOC_CREATE_DISK_ARRAY\n"); /* * Create disk array. */ status = IT8212CreateDiskArray(pioc); if (status != SRB_STATUS_SUCCESS) return status; status = IT8212ErasePartition(pioc); return 0; case ITE_IOC_REBUILD_START: dprintk("ITE_IOC_REBUILD_START\n"); /* * Rebuild array. */ status = IT8212Rebuild(pioc); put_user(status, (UCHAR *)pioc->data); return 0; case ITE_IOC_GET_REBUILD_STATUS: dprintk("ITE_IOC_GET_REBUILD_STATUS\n"); pAdap = ite_adapters[0]; /* * Get the rebuild disk ID. */ rebuild_info = (PRAID_REBUILD_INFO) pioc->data; diskArrayId = rebuild_info->DiskArrayId; /* * Select channel. */ if (diskArrayId < 2) { pChan = &pAdap->IDEChannel[0]; } else { pChan = &pAdap->IDEChannel[1]; } /* * Select device. */ outb(((UCHAR) (diskArrayId << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); /* * Wait for device ready (not BUSY and not DRQ). */ WaitForDeviceReady(pChan, statusByte); if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) || (statusByte == 0)) { printk("IT8212GetRebuildStatus: Disk[%d] busy. Status=0x%X\n", diskArrayId, statusByte); srbStatus = SRB_STATUS_BUSY; goto exit; } /* * Disable interrupt to avoid the unexpected interrupt. */ outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); /* * Issue command. */ outb(IDE_COMMAND_REBUILD_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); /* * Check error. */ WaitForCommandComplete(pChan, statusByte); /* * Reenable interrupt after command complete. */ outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); if (statusByte != IDE_STATUS_IDLE) { srbStatus = SRB_STATUS_ERROR; printk("GetRebuildStatus: ERROR\n"); goto exit; } progress = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); srbStatus = SRB_STATUS_SUCCESS; if (progress != 0xFF) progress = 0x64 - progress; /* * Put the rebuild status to user space. */ put_user(progress, (UCHAR *)pioc->data); exit: return 0; case ITE_IOC_RESET_ADAPTER: dprintk("ITE_IOC_RESET_ADAPTER\n"); /* * Reset the adapter. */ # if (0) status = IT8212ResetAdapter(); # endif /* * Return TURE or FALSE to user space. */ put_user(status, (UCHAR *)arg); return 0; case ITE_IOC_GET_DRIVER_VERSION: dprintk("ITE_IOC_GET_DRIVER_VERSION\n"); /* * Get the current driver version. */ put_user(driver_ver, (int *)arg); return 0; default: return -EINVAL; } /* end switch */ } /* end itedev_ioctl */ /************************************************************************ * IOCTL close routine. ************************************************************************/ static int itedev_close(struct inode *inode, struct file *filep) { MOD_DEC_USE_COUNT; return 0; } /* end itedev_close */ /* * To make our driver a SCSI module, we have to include some magic at the * end of the file. This generates the init_module and clean_module code * nedded for a SCSI device, rather than having the author replicate it * each time a new driver is written. */ static Scsi_Host_Template driver_template = ITERAID; #include "scsi_module.c"