diff options
Diffstat (limited to 'src/dev/arm/ufs_device.cc')
-rw-r--r-- | src/dev/arm/ufs_device.cc | 2363 |
1 files changed, 2363 insertions, 0 deletions
diff --git a/src/dev/arm/ufs_device.cc b/src/dev/arm/ufs_device.cc new file mode 100644 index 000000000..d271656cd --- /dev/null +++ b/src/dev/arm/ufs_device.cc @@ -0,0 +1,2363 @@ +/* + * Copyright (c) 2013-2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Rene de Jong + */ + +/** @file + * This is a simulation model for a UFS interface + * The UFS interface consists of a host controller and (at least) one device. + * The device can contain multiple logic units. + * To make this interface as usefull as possible for future development, the + * decision has been made to split the UFS functionality from the SCSI + * functionality. The class UFS SCSIDevice can therefor be used as a starting + * point for creating a more generic SCSI device. This has as a consequence + * that the UFSHostDevice class contains functionality from both the host + * controller and the device. The current UFS standard (1.1) allows only one + * device, and up to 8 logic units. the logic units only handle the SCSI part + * of the command, and the device mainly the UFS part. Yet the split between + * the SCSIresume function and the SCSICMDHandle might seem a bit awkward. + * The SCSICMDHandle function is in essence a SCSI reply generator, and it + * distils the essential information from the command. A disktransfer cannot + * be made from this position because the scatter gather list is not included + * in the SCSI command, but in the Transfer Request descriptor. The device + * needs to manage the data transfer. This file is build up as follows: first + * the UFSSCSIDevice functions will be presented; then the UFSHostDevice + * functions. The UFSHostDevice functions are split in three parts: UFS + * transaction flow, data write transfer and data read transfer. The + * functions are then ordered in the order in which a transfer takes place. + */ + +/** + * Reference material can be found at the JEDEC website: + * UFS standard + * http://www.jedec.org/standards-documents/results/jesd220 + * UFS HCI specification + * http://www.jedec.org/standards-documents/results/jesd223 + */ + +#include "dev/arm/ufs_device.hh" + +/** + * Constructor and destructor functions of UFSHCM device + */ +UFSHostDevice::UFSSCSIDevice::UFSSCSIDevice(const UFSHostDeviceParams* p, + uint32_t lun_id, Callback *transfer_cb, + Callback *read_cb): + SimObject(p), + flashDisk(p->image[lun_id]), + flashDevice(p->internalflash[lun_id]), + blkSize(p->img_blk_size), + lunAvail(p->image.size()), + diskSize(flashDisk->size()), + capacityLower((diskSize - 1) & 0xffffffff), + capacityUpper((diskSize - SectorSize) >> 32), + lunID(lun_id), + transferCompleted(false), + readCompleted(false), + totalRead(0), + totalWrite(0), + amountOfWriteTransfers(0), + amountOfReadTransfers(0) +{ + /** + * These callbacks are used to communicate the events that are + * triggered upstream; e.g. from the Memory Device to the UFS SCSI Device + * or from the UFS SCSI device to the UFS host. + */ + signalDone = transfer_cb; + memReadCallback = new MakeCallback<UFSSCSIDevice, + &UFSHostDevice::UFSSCSIDevice::readCallback>(this); + deviceReadCallback = read_cb; + memWriteCallback = new MakeCallback<UFSSCSIDevice, + &UFSHostDevice::UFSSCSIDevice::SSDWriteDone>(this); + + /** + * make ascii out of lun_id (and add more characters) + * UFS allows up to 8 logic units, so the numbering should work out + */ + uint32_t temp_id = ((lun_id | 0x30) << 24) | 0x3A4449; + lunInfo.dWord0 = 0x02060000; //data + lunInfo.dWord1 = 0x0200001F; + lunInfo.vendor0 = 0x484D5241; //ARMH (HMRA) + lunInfo.vendor1 = 0x424D4143; //CAMB (BMAC) + lunInfo.product0 = 0x356D6567; //gem5 (5meg) + lunInfo.product1 = 0x4D534655; //UFSM (MSFU) + lunInfo.product2 = 0x4C45444F; //ODEL (LEDO) + lunInfo.product3 = temp_id; // ID:"lun_id" ("lun_id":DI) + lunInfo.productRevision = 0x01000000; //0x01 + + DPRINTF(UFSHostDevice, "Logic unit %d assumes that %d logic units are" + " present in the system\n", lunID, lunAvail); + DPRINTF(UFSHostDevice,"The disksize of lun: %d should be %d blocks\n", + lunID, diskSize); + flashDevice->initializeMemory(diskSize, SectorSize); +} + + +/** + * These pages are SCSI specific. For more information refer to: + * Universal Flash Storage (UFS) JESD220 FEB 2011 (JEDEC) + * http://www.jedec.org/standards-documents/results/jesd220 + */ +const unsigned int UFSHostDevice::UFSSCSIDevice::controlPage[3] = + {0x01400A0A, 0x00000000, + 0x0000FFFF}; +const unsigned int UFSHostDevice::UFSSCSIDevice::recoveryPage[3] = + {0x03800A01, 0x00000000, + 0xFFFF0003}; +const unsigned int UFSHostDevice::UFSSCSIDevice::cachingPage[5] = + {0x00011208, 0x00000000, + 0x00000000, 0x00000020, + 0x00000000}; + +UFSHostDevice::UFSSCSIDevice::~UFSSCSIDevice() {} + +/** + * UFS specific SCSI handling function. + * The following attributes may still be added: SCSI format unit, + * Send diagnostic and UNMAP; + * Synchronize Cache and buffer read/write could not be tested yet + * All parameters can be found in: + * Universal Flash Storage (UFS) JESD220 FEB 2011 (JEDEC) + * http://www.jedec.org/standards-documents/results/jesd220 + * (a JEDEC acount may be required {free of charge}) + */ + +struct UFSHostDevice::SCSIReply +UFSHostDevice::UFSSCSIDevice::SCSICMDHandle(uint32_t* SCSI_msg) +{ + struct SCSIReply scsi_out; + memset(&scsi_out, 0, sizeof(struct SCSIReply)); + + /** + * Create the standard SCSI reponse information + * These values might changes over the course of a transfer + */ + scsi_out.message.header.dWord0 = UPIUHeaderDataIndWord0 | + lunID << 16; + scsi_out.message.header.dWord1 = UPIUHeaderDataIndWord1; + scsi_out.message.header.dWord2 = UPIUHeaderDataIndWord2; + statusCheck(SCSIGood, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.LUN = lunID; + scsi_out.status = SCSIGood; + + DPRINTF(UFSHostDevice, "SCSI command:%2x\n", SCSI_msg[4]); + /**Determine what the message is and fill the response packet*/ + + switch (SCSI_msg[4] & 0xFF) { + + case SCSIInquiry: { + /** + * SCSI inquiry: tell about this specific logic unit + */ + scsi_out.msgSize = 36; + scsi_out.message.dataMsg.resize(9); + + for (uint8_t count = 0; count < 9; count++) + scsi_out.message.dataMsg[count] = + (reinterpret_cast<uint32_t*> (&lunInfo))[count]; + } break; + + case SCSIRead6: { + /** + * Read command. Number indicates the length of the command. + */ + scsi_out.expectMore = 0x02; + scsi_out.msgSize = 0; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned. Apart from that it only has + * information in five bits of the first byte that is relevant + * for this field. + */ + uint32_t tmp = *reinterpret_cast<uint32_t*>(tempptr); + uint64_t read_offset = betoh(tmp) & 0x1FFFFF; + + uint32_t read_size = tempptr[4]; + + + scsi_out.msgSize = read_size * blkSize; + scsi_out.offset = read_offset * blkSize; + + if ((read_offset + read_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Read6 offset: 0x%8x, for %d blocks\n", + read_offset, read_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIRead10: { + scsi_out.expectMore = 0x02; + scsi_out.msgSize = 0; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t read_offset = betoh(tmp); + + uint16_t tmpsize = *reinterpret_cast<uint16_t*>(&tempptr[7]); + uint32_t read_size = betoh(tmpsize); + + scsi_out.msgSize = read_size * blkSize; + scsi_out.offset = read_offset * blkSize; + + if ((read_offset + read_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Read10 offset: 0x%8x, for %d blocks\n", + read_offset, read_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIRead16: { + scsi_out.expectMore = 0x02; + scsi_out.msgSize = 0; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t read_offset = betoh(tmp); + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[6]); + read_offset = (read_offset << 32) | betoh(tmp); + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[10]); + uint32_t read_size = betoh(tmp); + + scsi_out.msgSize = read_size * blkSize; + scsi_out.offset = read_offset * blkSize; + + if ((read_offset + read_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Read16 offset: 0x%8x, for %d blocks\n", + read_offset, read_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIReadCapacity10: { + /** + * read the capacity of the device + */ + scsi_out.msgSize = 8; + scsi_out.message.dataMsg.resize(2); + scsi_out.message.dataMsg[0] = + betoh(capacityLower);//last block + scsi_out.message.dataMsg[1] = betoh(blkSize);//blocksize + + } break; + case SCSIReadCapacity16: { + scsi_out.msgSize = 32; + scsi_out.message.dataMsg.resize(8); + scsi_out.message.dataMsg[0] = + betoh(capacityUpper);//last block + scsi_out.message.dataMsg[1] = + betoh(capacityLower);//last block + scsi_out.message.dataMsg[2] = betoh(blkSize);//blocksize + scsi_out.message.dataMsg[3] = 0x00;// + scsi_out.message.dataMsg[4] = 0x00;//reserved + scsi_out.message.dataMsg[5] = 0x00;//reserved + scsi_out.message.dataMsg[6] = 0x00;//reserved + scsi_out.message.dataMsg[7] = 0x00;//reserved + + } break; + + case SCSIReportLUNs: { + /** + * Find out how many Logic Units this device has. + */ + scsi_out.msgSize = (lunAvail * 8) + 8;//list + overhead + scsi_out.message.dataMsg.resize(2 * lunAvail + 2); + scsi_out.message.dataMsg[0] = (lunAvail * 8) << 24;//LUN listlength + scsi_out.message.dataMsg[1] = 0x00; + + for (uint8_t count = 0; count < lunAvail; count++) { + //LUN "count" + scsi_out.message.dataMsg[2 + 2 * count] = (count & 0x7F) << 8; + scsi_out.message.dataMsg[3 + 2 * count] = 0x00; + } + + } break; + + case SCSIStartStop: { + //Just acknowledge; not deemed relevant ATM + scsi_out.msgSize = 0; + + } break; + + case SCSITestUnitReady: { + //Just acknowledge; not deemed relevant ATM + scsi_out.msgSize = 0; + + } break; + + case SCSIVerify10: { + /** + * See if the blocks that the host plans to request are in range of + * the device. + */ + scsi_out.msgSize = 0; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t read_offset = betoh(tmp); + + uint16_t tmpsize = *reinterpret_cast<uint16_t*>(&tempptr[7]); + uint32_t read_size = betoh(tmpsize); + + if ((read_offset + read_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIWrite6: { + /** + * Write command. + */ + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned. Apart from that it only has + * information in five bits of the first byte that is relevant + * for this field. + */ + uint32_t tmp = *reinterpret_cast<uint32_t*>(tempptr); + uint64_t write_offset = betoh(tmp) & 0x1FFFFF; + + uint32_t write_size = tempptr[4]; + + scsi_out.msgSize = write_size * blkSize; + scsi_out.offset = write_offset * blkSize; + scsi_out.expectMore = 0x01; + + if ((write_offset + write_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Write6 offset: 0x%8x, for %d blocks\n", + write_offset, write_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIWrite10: { + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t write_offset = betoh(tmp); + + uint16_t tmpsize = *reinterpret_cast<uint16_t*>(&tempptr[7]); + uint32_t write_size = betoh(tmpsize); + + scsi_out.msgSize = write_size * blkSize; + scsi_out.offset = write_offset * blkSize; + scsi_out.expectMore = 0x01; + + if ((write_offset + write_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Write10 offset: 0x%8x, for %d blocks\n", + write_offset, write_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIWrite16: { + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t write_offset = betoh(tmp); + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[6]); + write_offset = (write_offset << 32) | betoh(tmp); + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[10]); + uint32_t write_size = betoh(tmp); + + scsi_out.msgSize = write_size * blkSize; + scsi_out.offset = write_offset * blkSize; + scsi_out.expectMore = 0x01; + + if ((write_offset + write_size) > diskSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Write16 offset: 0x%8x, for %d blocks\n", + write_offset, write_size); + + /** + * Renew status check, for the request may have been illegal + */ + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIFormatUnit: {//not yet verified + scsi_out.msgSize = 0; + scsi_out.expectMore = 0x01; + + } break; + + case SCSISendDiagnostic: {//not yet verified + scsi_out.msgSize = 0; + + } break; + + case SCSISynchronizeCache: { + //do we have cache (we don't have cache at this moment) + //TODO: here will synchronization happen when cache is modelled + scsi_out.msgSize = 0; + + } break; + + //UFS SCSI additional command set for full functionality + case SCSIModeSelect10: + //TODO: + //scsi_out.expectMore = 0x01;//not supported due to modepage support + //code isn't dead, code suggest what is to be done when implemented + break; + + case SCSIModeSense6: case SCSIModeSense10: { + /** + * Get more discriptive information about the SCSI functionality + * within this logic unit. + */ + if ((SCSI_msg[4] & 0x3F0000) >> 16 == 0x0A) {//control page + scsi_out.message.dataMsg.resize((sizeof(controlPage) >> 2) + 2); + scsi_out.message.dataMsg[0] = 0x00000A00;//control page code + scsi_out.message.dataMsg[1] = 0x00000000;//See JEDEC220 ch8 + + for (uint8_t count = 0; count < 3; count++) + scsi_out.message.dataMsg[2 + count] = controlPage[count]; + + scsi_out.msgSize = 20; + DPRINTF(UFSHostDevice, "CONTROL page\n"); + + } else if ((SCSI_msg[4] & 0x3F0000) >> 16 == 0x01) {//recovery page + scsi_out.message.dataMsg.resize((sizeof(recoveryPage) >> 2) + + 2); + + scsi_out.message.dataMsg[0] = 0x00000100;//recovery page code + scsi_out.message.dataMsg[1] = 0x00000000;//See JEDEC220 ch8 + + for (uint8_t count = 0; count < 3; count++) + scsi_out.message.dataMsg[2 + count] = recoveryPage[count]; + + scsi_out.msgSize = 20; + DPRINTF(UFSHostDevice, "RECOVERY page\n"); + + } else if ((SCSI_msg[4] & 0x3F0000) >> 16 == 0x08) {//caching page + + scsi_out.message.dataMsg.resize((sizeof(cachingPage) >> 2) + 2); + scsi_out.message.dataMsg[0] = 0x00001200;//caching page code + scsi_out.message.dataMsg[1] = 0x00000000;//See JEDEC220 ch8 + + for (uint8_t count = 0; count < 5; count++) + scsi_out.message.dataMsg[2 + count] = cachingPage[count]; + + scsi_out.msgSize = 20; + DPRINTF(UFSHostDevice, "CACHE page\n"); + + } else if ((SCSI_msg[4] & 0x3F0000) >> 16 == 0x3F) {//ALL the pages! + + scsi_out.message.dataMsg.resize(((sizeof(controlPage) + + sizeof(recoveryPage) + + sizeof(cachingPage)) >> 2) + + 2); + scsi_out.message.dataMsg[0] = 0x00003200;//all page code + scsi_out.message.dataMsg[1] = 0x00000000;//See JEDEC220 ch8 + + for (uint8_t count = 0; count < 3; count++) + scsi_out.message.dataMsg[2 + count] = recoveryPage[count]; + + for (uint8_t count = 0; count < 5; count++) + scsi_out.message.dataMsg[5 + count] = cachingPage[count]; + + for (uint8_t count = 0; count < 3; count++) + scsi_out.message.dataMsg[10 + count] = controlPage[count]; + + scsi_out.msgSize = 52; + DPRINTF(UFSHostDevice, "Return ALL the pages!!!\n"); + + } else inform("Wrong mode page requested\n"); + + scsi_out.message.dataCount = scsi_out.msgSize << 24; + } break; + + case SCSIRequestSense: { + scsi_out.msgSize = 0; + + } break; + + case SCSIUnmap:break;//not yet verified + + case SCSIWriteBuffer: { + scsi_out.expectMore = 0x01; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t write_offset = betoh(tmp) & 0xFFFFFF; + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[5]); + uint32_t write_size = betoh(tmp) & 0xFFFFFF; + + scsi_out.msgSize = write_size; + scsi_out.offset = write_offset; + + } break; + + case SCSIReadBuffer: { + /** + * less trivial than normal read. Size is in bytes instead + * of blocks, and it is assumed (though not guaranteed) that + * reading is from cache. + */ + scsi_out.expectMore = 0x02; + + uint8_t* tempptr = reinterpret_cast<uint8_t*>(&SCSI_msg[4]); + + /**BE and not nicely aligned.*/ + uint32_t tmp = *reinterpret_cast<uint32_t*>(&tempptr[2]); + uint64_t read_offset = betoh(tmp) & 0xFFFFFF; + + tmp = *reinterpret_cast<uint32_t*>(&tempptr[5]); + uint32_t read_size = betoh(tmp) & 0xFFFFFF; + + scsi_out.msgSize = read_size; + scsi_out.offset = read_offset; + + if ((read_offset + read_size) > capacityLower * blkSize) + scsi_out.status = SCSIIllegalRequest; + + DPRINTF(UFSHostDevice, "Read buffer location: 0x%8x\n", + read_offset); + DPRINTF(UFSHostDevice, "Number of bytes: 0x%8x\n", read_size); + + statusCheck(scsi_out.status, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + + } break; + + case SCSIMaintenanceIn: { + /** + * linux sends this command three times from kernel 3.9 onwards, + * UFS does not support it, nor does this model. Linux knows this, + * but tries anyway (useful for some SD card types). + * Lets make clear we don't want it and just ignore it. + */ + DPRINTF(UFSHostDevice, "Ignoring Maintenance In command\n"); + statusCheck(SCSIIllegalRequest, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + scsi_out.msgSize = 0; + } break; + + default: { + statusCheck(SCSIIllegalRequest, scsi_out.senseCode); + scsi_out.senseSize = scsi_out.senseCode[0]; + scsi_out.status = (scsi_out.status == SCSIGood) ? SCSIGood : + SCSICheckCondition; + scsi_out.msgSize = 0; + inform("Unsupported scsi message type: %2x\n", SCSI_msg[4] & 0xFF); + inform("0x%8x\n", SCSI_msg[0]); + inform("0x%8x\n", SCSI_msg[1]); + inform("0x%8x\n", SCSI_msg[2]); + inform("0x%8x\n", SCSI_msg[3]); + inform("0x%8x\n", SCSI_msg[4]); + } break; + } + + return scsi_out; +} + +/** + * SCSI status check function. generic device test, creates sense codes + * Future versions may include TODO: device checks, which is why this is + * in a separate function. + */ + +void +UFSHostDevice::UFSSCSIDevice::statusCheck(uint8_t status, + uint8_t* sensecodelist) +{ + for (uint8_t count = 0; count < 19; count++) + sensecodelist[count] = 0; + + sensecodelist[0] = 18; //sense length + sensecodelist[1] = 0x70; //we send a valid frame + sensecodelist[3] = status & 0xF; //mask to be sure + sensecode + sensecodelist[8] = 0x1F; //data length +} + +/** + * read from the flashdisk + */ + +void +UFSHostDevice::UFSSCSIDevice::readFlash(uint8_t* readaddr, uint64_t offset, + uint32_t size) +{ + /** read from image, and get to memory */ + for(int count = 0; count < (size / SectorSize); count++) + flashDisk->read(&(readaddr[SectorSize*count]), (offset / + SectorSize) + count); +} + +/** + * Write to the flashdisk + */ + +void +UFSHostDevice::UFSSCSIDevice::writeFlash(uint8_t* writeaddr, uint64_t offset, + uint32_t size) +{ + /** Get from fifo and write to image*/ + for(int count = 0; count < (size / SectorSize); count++) + flashDisk->write(&(writeaddr[SectorSize * count]), + (offset / SectorSize) + count); +} + +/** + * Constructor for the UFS Host device + */ + +UFSHostDevice::UFSHostDevice(const UFSHostDeviceParams* p) : + DmaDevice(p), + pioAddr(p->pio_addr), + pioSize(0x0FFF), + pioDelay(p->pio_latency), + intNum(p->int_num), + gic(p->gic), + lunAvail(p->image.size()), + UFSSlots(p->ufs_slots - 1), + readPendingNum(0), + writePendingNum(0), + activeDoorbells(0), + pendingDoorbells(0), + countInt(0), + transferTrack(0), + taskCommandTrack(0), + idlePhaseStart(0), + drainManager(NULL), + SCSIResumeEvent(this), + UTPEvent(this) +{ + DPRINTF(UFSHostDevice, "The hostcontroller hosts %d Logic units\n", + lunAvail); + UFSDevice.resize(lunAvail); + + transferDoneCallback = new MakeCallback<UFSHostDevice, + &UFSHostDevice::LUNSignal>(this); + memReadCallback = new MakeCallback<UFSHostDevice, + &UFSHostDevice::readCallback>(this); + + for(int count = 0; count < lunAvail; count++) { + UFSDevice[count] = new UFSSCSIDevice(p, count, transferDoneCallback, + memReadCallback); + } + + if (UFSSlots > 31) + warn("UFSSlots = %d, this will results in %d command slots", + UFSSlots, (UFSSlots & 0x1F)); + + if ((UFSSlots & 0x1F) == 0) + fatal("Number of UFS command slots should be between 1 and 32."); + + setValues(); +} + +/** + * Create the parameters of this device + */ + +UFSHostDevice* +UFSHostDeviceParams::create() +{ + return new UFSHostDevice(this); +} + + +void +UFSHostDevice::regStats() +{ + using namespace Stats; + + std::string UFSHost_name = name() + ".UFSDiskHost"; + + // Register the stats + /** Queue lengths */ + stats.currentSCSIQueue + .name(UFSHost_name + ".currentSCSIQueue") + .desc("Most up to date length of the command queue") + .flags(none); + stats.currentReadSSDQueue + .name(UFSHost_name + ".currentReadSSDQueue") + .desc("Most up to date length of the read SSD queue") + .flags(none); + stats.currentWriteSSDQueue + .name(UFSHost_name + ".currentWriteSSDQueue") + .desc("Most up to date length of the write SSD queue") + .flags(none); + + /** Amount of data read/written */ + stats.totalReadSSD + .name(UFSHost_name + ".totalReadSSD") + .desc("Number of bytes read from SSD") + .flags(none); + + stats.totalWrittenSSD + .name(UFSHost_name + ".totalWrittenSSD") + .desc("Number of bytes written to SSD") + .flags(none); + + stats.totalReadDiskTransactions + .name(UFSHost_name + ".totalReadDiskTransactions") + .desc("Number of transactions from disk") + .flags(none); + stats.totalWriteDiskTransactions + .name(UFSHost_name + ".totalWriteDiskTransactions") + .desc("Number of transactions to disk") + .flags(none); + stats.totalReadUFSTransactions + .name(UFSHost_name + ".totalReadUFSTransactions") + .desc("Number of transactions from device") + .flags(none); + stats.totalWriteUFSTransactions + .name(UFSHost_name + ".totalWriteUFSTransactions") + .desc("Number of transactions to device") + .flags(none); + + /** Average bandwidth for reads and writes */ + stats.averageReadSSDBW + .name(UFSHost_name + ".averageReadSSDBandwidth") + .desc("Average read bandwidth (bytes/s)") + .flags(nozero); + + stats.averageReadSSDBW = stats.totalReadSSD / simSeconds; + + stats.averageWriteSSDBW + .name(UFSHost_name + ".averageWriteSSDBandwidth") + .desc("Average write bandwidth (bytes/s)") + .flags(nozero); + + stats.averageWriteSSDBW = stats.totalWrittenSSD / simSeconds; + + stats.averageSCSIQueue + .name(UFSHost_name + ".averageSCSIQueueLength") + .desc("Average command queue length") + .flags(nozero); + stats.averageReadSSDQueue + .name(UFSHost_name + ".averageReadSSDQueueLength") + .desc("Average read queue length") + .flags(nozero); + stats.averageWriteSSDQueue + .name(UFSHost_name + ".averageWriteSSDQueueLength") + .desc("Average write queue length") + .flags(nozero); + + /** Number of doorbells rung*/ + stats.curDoorbell + .name(UFSHost_name + ".curDoorbell") + .desc("Most up to date number of doorbells used") + .flags(none); + + stats.curDoorbell = activeDoorbells; + + stats.maxDoorbell + .name(UFSHost_name + ".maxDoorbell") + .desc("Maximum number of doorbells utilized") + .flags(none); + stats.averageDoorbell + .name(UFSHost_name + ".averageDoorbell") + .desc("Average number of Doorbells used") + .flags(nozero); + + /** Latency*/ + stats.transactionLatency + .init(100) + .name(UFSHost_name + ".transactionLatency") + .desc("Histogram of transaction times") + .flags(pdf); + + stats.idleTimes + .init(100) + .name(UFSHost_name + ".idlePeriods") + .desc("Histogram of idle times") + .flags(pdf); + +} + +/** + * Register init + */ +void UFSHostDevice::setValues() +{ + /** + * The capability register is built up as follows: + * 31-29 RES; Testmode support; O3 delivery; 64 bit addr; + * 23-19 RES; 18-16 #TM Req slots; 15-5 RES;4-0 # TR slots + */ + UFSHCIMem.HCCAP = 0x06070000 | (UFSSlots & 0x1F); + UFSHCIMem.HCversion = 0x00010000; //version is 1.0 + UFSHCIMem.HCHCDDID = 0xAA003C3C;// Arbitrary number + UFSHCIMem.HCHCPMID = 0x41524D48; //ARMH (not an official MIPI number) + UFSHCIMem.TRUTRLDBR = 0x00; + UFSHCIMem.TMUTMRLDBR = 0x00; + UFSHCIMem.CMDUICCMDR = 0x00; + // We can process CMD, TM, TR, device present + UFSHCIMem.ORHostControllerStatus = 0x08; + UFSHCIMem.TRUTRLBA = 0x00; + UFSHCIMem.TRUTRLBAU = 0x00; + UFSHCIMem.TMUTMRLBA = 0x00; + UFSHCIMem.TMUTMRLBAU = 0x00; +} + +/** + * Determine address ranges + */ + +AddrRangeList +UFSHostDevice::getAddrRanges() const +{ + AddrRangeList ranges; + ranges.push_back(RangeSize(pioAddr, pioSize)); + return ranges; +} + +/** + * UFSHCD read register. This function allows the system to read the + * register entries + */ + +Tick +UFSHostDevice::read(PacketPtr pkt) +{ + uint32_t data = 0; + + switch (pkt->getAddr() & 0xFF) + { + + case regControllerCapabilities: + data = UFSHCIMem.HCCAP; + break; + + case regUFSVersion: + data = UFSHCIMem.HCversion; + break; + + case regControllerDEVID: + data = UFSHCIMem.HCHCDDID; + break; + + case regControllerPRODID: + data = UFSHCIMem.HCHCPMID; + break; + + case regInterruptStatus: + data = UFSHCIMem.ORInterruptStatus; + UFSHCIMem.ORInterruptStatus = 0x00; + //TODO: Revise and extend + clearInterrupt(); + break; + + case regInterruptEnable: + data = UFSHCIMem.ORInterruptEnable; + break; + + case regControllerStatus: + data = UFSHCIMem.ORHostControllerStatus; + break; + + case regControllerEnable: + data = UFSHCIMem.ORHostControllerEnable; + break; + + case regUICErrorCodePHYAdapterLayer: + data = UFSHCIMem.ORUECPA; + break; + + case regUICErrorCodeDataLinkLayer: + data = UFSHCIMem.ORUECDL; + break; + + case regUICErrorCodeNetworkLayer: + data = UFSHCIMem.ORUECN; + break; + + case regUICErrorCodeTransportLayer: + data = UFSHCIMem.ORUECT; + break; + + case regUICErrorCodeDME: + data = UFSHCIMem.ORUECDME; + break; + + case regUTPTransferREQINTAGGControl: + data = UFSHCIMem.ORUTRIACR; + break; + + case regUTPTransferREQListBaseL: + data = UFSHCIMem.TRUTRLBA; + break; + + case regUTPTransferREQListBaseH: + data = UFSHCIMem.TRUTRLBAU; + break; + + case regUTPTransferREQDoorbell: + data = UFSHCIMem.TRUTRLDBR; + break; + + case regUTPTransferREQListClear: + data = UFSHCIMem.TRUTRLCLR; + break; + + case regUTPTransferREQListRunStop: + data = UFSHCIMem.TRUTRLRSR; + break; + + case regUTPTaskREQListBaseL: + data = UFSHCIMem.TMUTMRLBA; + break; + + case regUTPTaskREQListBaseH: + data = UFSHCIMem.TMUTMRLBAU; + break; + + case regUTPTaskREQDoorbell: + data = UFSHCIMem.TMUTMRLDBR; + break; + + case regUTPTaskREQListClear: + data = UFSHCIMem.TMUTMRLCLR; + break; + + case regUTPTaskREQListRunStop: + data = UFSHCIMem.TMUTMRLRSR; + break; + + case regUICCommand: + data = UFSHCIMem.CMDUICCMDR; + break; + + case regUICCommandArg1: + data = UFSHCIMem.CMDUCMDARG1; + break; + + case regUICCommandArg2: + data = UFSHCIMem.CMDUCMDARG2; + break; + + case regUICCommandArg3: + data = UFSHCIMem.CMDUCMDARG3; + break; + + default: + data = 0x00; + break; + } + + pkt->set<uint32_t>(data); + pkt->makeResponse(); + return pioDelay; +} + +/** + * UFSHCD write function. This function allows access to the writeable + * registers. If any function attempts to write value to an unwriteable + * register entry, then the value will not be written. + */ +Tick +UFSHostDevice::write(PacketPtr pkt) +{ + uint32_t data = 0; + + switch (pkt->getSize()) { + + case 1: + data = pkt->get<uint8_t>(); + break; + + case 2: + data = pkt->get<uint16_t>(); + break; + + case 4: + data = pkt->get<uint32_t>(); + break; + + default: + panic("Undefined UFSHCD controller write size!\n"); + break; + } + + switch (pkt->getAddr() & 0xFF) + { + case regControllerCapabilities://you shall not write to this + break; + + case regUFSVersion://you shall not write to this + break; + + case regControllerDEVID://you shall not write to this + break; + + case regControllerPRODID://you shall not write to this + break; + + case regInterruptStatus://you shall not write to this + break; + + case regInterruptEnable: + UFSHCIMem.ORInterruptEnable = data; + break; + + case regControllerStatus: + UFSHCIMem.ORHostControllerStatus = data; + break; + + case regControllerEnable: + UFSHCIMem.ORHostControllerEnable = data; + break; + + case regUICErrorCodePHYAdapterLayer: + UFSHCIMem.ORUECPA = data; + break; + + case regUICErrorCodeDataLinkLayer: + UFSHCIMem.ORUECDL = data; + break; + + case regUICErrorCodeNetworkLayer: + UFSHCIMem.ORUECN = data; + break; + + case regUICErrorCodeTransportLayer: + UFSHCIMem.ORUECT = data; + break; + + case regUICErrorCodeDME: + UFSHCIMem.ORUECDME = data; + break; + + case regUTPTransferREQINTAGGControl: + UFSHCIMem.ORUTRIACR = data; + break; + + case regUTPTransferREQListBaseL: + UFSHCIMem.TRUTRLBA = data; + if (((UFSHCIMem.TRUTRLBA | UFSHCIMem.TRUTRLBAU) != 0x00) && + ((UFSHCIMem.TMUTMRLBA | UFSHCIMem.TMUTMRLBAU)!= 0x00)) + UFSHCIMem.ORHostControllerStatus |= UICCommandReady; + break; + + case regUTPTransferREQListBaseH: + UFSHCIMem.TRUTRLBAU = data; + if (((UFSHCIMem.TRUTRLBA | UFSHCIMem.TRUTRLBAU) != 0x00) && + ((UFSHCIMem.TMUTMRLBA | UFSHCIMem.TMUTMRLBAU) != 0x00)) + UFSHCIMem.ORHostControllerStatus |= UICCommandReady; + break; + + case regUTPTransferREQDoorbell: + if (!(UFSHCIMem.TRUTRLDBR) && data) + stats.idleTimes.sample(curTick() - idlePhaseStart); + UFSHCIMem.TRUTRLDBR |= data; + requestHandler(); + break; + + case regUTPTransferREQListClear: + UFSHCIMem.TRUTRLCLR = data; + break; + + case regUTPTransferREQListRunStop: + UFSHCIMem.TRUTRLRSR = data; + break; + + case regUTPTaskREQListBaseL: + UFSHCIMem.TMUTMRLBA = data; + if (((UFSHCIMem.TRUTRLBA | UFSHCIMem.TRUTRLBAU) != 0x00) && + ((UFSHCIMem.TMUTMRLBA | UFSHCIMem.TMUTMRLBAU) != 0x00)) + UFSHCIMem.ORHostControllerStatus |= UICCommandReady; + break; + + case regUTPTaskREQListBaseH: + UFSHCIMem.TMUTMRLBAU = data; + if (((UFSHCIMem.TRUTRLBA | UFSHCIMem.TRUTRLBAU) != 0x00) && + ((UFSHCIMem.TMUTMRLBA | UFSHCIMem.TMUTMRLBAU) != 0x00)) + UFSHCIMem.ORHostControllerStatus |= UICCommandReady; + break; + + case regUTPTaskREQDoorbell: + UFSHCIMem.TMUTMRLDBR |= data; + requestHandler(); + break; + + case regUTPTaskREQListClear: + UFSHCIMem.TMUTMRLCLR = data; + break; + + case regUTPTaskREQListRunStop: + UFSHCIMem.TMUTMRLRSR = data; + break; + + case regUICCommand: + UFSHCIMem.CMDUICCMDR = data; + requestHandler(); + break; + + case regUICCommandArg1: + UFSHCIMem.CMDUCMDARG1 = data; + break; + + case regUICCommandArg2: + UFSHCIMem.CMDUCMDARG2 = data; + break; + + case regUICCommandArg3: + UFSHCIMem.CMDUCMDARG3 = data; + break; + + default:break;//nothing happens, you try to access a register that + //does not exist + + } + + pkt->makeResponse(); + return pioDelay; +} + +/** + * Request handler. Determines where the request comes from and initiates the + * appropriate actions accordingly. + */ + +void +UFSHostDevice::requestHandler() +{ + Addr address = 0x00; + int mask = 0x01; + int size; + int count = 0; + struct taskStart task_info; + struct transferStart transferstart_info; + transferstart_info.done = 0; + + /** + * step1 determine what called us + * step2 determine where to get it + * Look for any request of which we where not yet aware + */ + while (((UFSHCIMem.CMDUICCMDR > 0x00) | + ((UFSHCIMem.TMUTMRLDBR ^ taskCommandTrack) > 0x00) | + ((UFSHCIMem.TRUTRLDBR ^ transferTrack) > 0x00)) ) { + + if (UFSHCIMem.CMDUICCMDR > 0x00) { + /** + * Command; general control of the Host controller. + * no DMA transfer needed + */ + commandHandler(); + UFSHCIMem.ORInterruptStatus |= UICCommandCOMPL; + generateInterrupt(); + UFSHCIMem.CMDUICCMDR = 0x00; + return; //command, nothing more we can do + + } else if ((UFSHCIMem.TMUTMRLDBR ^ taskCommandTrack) > 0x00) { + /** + * Task; flow control, meant for the device/Logic unit + * DMA transfer is needed, flash will not be approached + */ + size = sizeof(UTPUPIUTaskReq); + /**Find the position that is not handled yet*/ + count = findLsbSet((UFSHCIMem.TMUTMRLDBR ^ taskCommandTrack)); + address = UFSHCIMem.TMUTMRLBAU; + //<-64 bit + address = (count * size) + (address << 32) + + UFSHCIMem.TMUTMRLBA; + taskCommandTrack |= mask << count; + + inform("UFSmodel received a task from the system; this might" + " lead to untested behaviour.\n"); + + task_info.mask = mask << count; + task_info.address = address; + task_info.size = size; + task_info.done = UFSHCIMem.TMUTMRLDBR; + taskInfo.push_back(task_info); + taskEventQueue.push_back(this); + writeDevice(&taskEventQueue.back(), false, address, size, + reinterpret_cast<uint8_t*> + (&taskInfo.back().destination), 0, 0); + + } else if ((UFSHCIMem.TRUTRLDBR ^ transferTrack) > 0x00) { + /** + * Transfer; Data transfer from or to the disk. There will be DMA + * transfers, and the flash might be approached. Further + * commands, are needed to specify the exact command. + */ + size = sizeof(UTPTransferReqDesc); + /**Find the position that is not handled yet*/ + count = findLsbSet((UFSHCIMem.TRUTRLDBR ^ transferTrack)); + address = UFSHCIMem.TRUTRLBAU; + //<-64 bit + address = (count * size) + (address << 32) + UFSHCIMem.TRUTRLBA; + + transferTrack |= mask << count; + DPRINTF(UFSHostDevice, "Doorbell register: 0x%8x select #:" + " 0x%8x completion info: 0x%8x\n", UFSHCIMem.TRUTRLDBR, + count, transferstart_info.done); + + transferstart_info.done = UFSHCIMem.TRUTRLDBR; + + /**stats**/ + transactionStart[count] = curTick(); //note the start time + ++activeDoorbells; + stats.maxDoorbell = (stats.maxDoorbell.value() < activeDoorbells) + ? activeDoorbells : stats.maxDoorbell.value(); + stats.averageDoorbell = stats.maxDoorbell.value(); + + /** + * step3 start transfer + * step4 register information; allowing the host to respond in + * the end + */ + transferstart_info.mask = mask << count; + transferstart_info.address = address; + transferstart_info.size = size; + transferstart_info.done = UFSHCIMem.TRUTRLDBR; + transferStartInfo.push_back(transferstart_info); + + /**Deleted in readDone, queued in finalUTP*/ + transferStartInfo.back().destination = new struct + UTPTransferReqDesc; + DPRINTF(UFSHostDevice, "Initial transfer start: 0x%8x\n", + transferstart_info.done); + transferEventQueue.push_back(this); + + if (transferEventQueue.size() < 2) { + writeDevice(&transferEventQueue.front(), false, + address, size, reinterpret_cast<uint8_t*> + (transferStartInfo.front().destination),0, 0); + DPRINTF(UFSHostDevice, "Transfer scheduled\n"); + } + } + } +} + +/** + * Task start event + */ + +void +UFSHostDevice::taskStart() +{ + DPRINTF(UFSHostDevice, "Task start"); + taskHandler(&taskInfo.front().destination, taskInfo.front().mask, + taskInfo.front().address, taskInfo.front().size); + taskInfo.pop_front(); + taskEventQueue.pop_front(); +} + +/** + * Transfer start event + */ + +void +UFSHostDevice::transferStart() +{ + DPRINTF(UFSHostDevice, "Enter transfer event\n"); + transferHandler(transferStartInfo.front().destination, + transferStartInfo.front().mask, + transferStartInfo.front().address, + transferStartInfo.front().size, + transferStartInfo.front().done); + + transferStartInfo.pop_front(); + DPRINTF(UFSHostDevice, "Transfer queue size at end of event: " + "0x%8x\n", transferEventQueue.size()); +} + +/** + * Handles the commands that are given. At this point in time, not many + * commands have been implemented in the driver. + */ + +void +UFSHostDevice::commandHandler() +{ + if (UFSHCIMem.CMDUICCMDR == 0x16) { + UFSHCIMem.ORHostControllerStatus |= 0x0F;//link startup + } + +} + +/** + * Handles the tasks that are given. At this point in time, not many tasks + * have been implemented in the driver. + */ + +void +UFSHostDevice::taskHandler(struct UTPUPIUTaskReq* request_in, + uint32_t req_pos, Addr finaladdress, uint32_t + finalsize) +{ + /** + * For now, just unpack and acknowledge the task without doing anything. + * TODO Implement UFS tasks. + */ + inform("taskHandler\n"); + inform("%8x\n", request_in->header.dWord0); + inform("%8x\n", request_in->header.dWord1); + inform("%8x\n", request_in->header.dWord2); + + request_in->header.dWord2 &= 0xffffff00; + + UFSHCIMem.TMUTMRLDBR &= ~(req_pos); + taskCommandTrack &= ~(req_pos); + UFSHCIMem.ORInterruptStatus |= UTPTaskREQCOMPL; + + readDevice(true, finaladdress, finalsize, reinterpret_cast<uint8_t*> + (request_in), true, NULL); + +} + +/** + * Obtains the SCSI command (if any) + * Two possibilities: if it contains a SCSI command, then it is a usable + * message; if it doesnt contain a SCSI message, then it can't be handeld + * by this code. + * This is the second stage of the transfer. We have the information about + * where the next command can be found and what the type of command is. The + * actions that are needed from the device its side are: get the information + * and store the information such that we can reply. + */ + +void +UFSHostDevice::transferHandler(struct UTPTransferReqDesc* request_in, + int req_pos, Addr finaladdress, uint32_t + finalsize, uint32_t done) +{ + + Addr cmd_desc_addr = 0x00; + + + //acknowledge handling of the message + DPRINTF(UFSHostDevice, "SCSI message detected\n"); + request_in->header.dWord2 &= 0xffffff00; + SCSIInfo.RequestIn = request_in; + SCSIInfo.reqPos = req_pos; + SCSIInfo.finalAddress = finaladdress; + SCSIInfo.finalSize = finalsize; + SCSIInfo.destination.resize(request_in->PRDTableOffset * 4 + + request_in->PRDTableLength * sizeof(UFSHCDSGEntry)); + SCSIInfo.done = done; + + assert(!SCSIResumeEvent.scheduled()); + /** + *Get the UTP command that has the SCSI command + */ + cmd_desc_addr = request_in->commandDescBaseAddrHi; + cmd_desc_addr = (cmd_desc_addr << 32) | + (request_in->commandDescBaseAddrLo & 0xffffffff); + + writeDevice(&SCSIResumeEvent, false, cmd_desc_addr, + SCSIInfo.destination.size(), &SCSIInfo.destination[0],0, 0); + + DPRINTF(UFSHostDevice, "SCSI scheduled\n"); + + transferEventQueue.pop_front(); +} + +/** + * Obtain LUN and put it in the right LUN queue. Each LUN has its own queue + * of commands that need to be executed. This is the first instance where it + * can be determined which Logic unit should handle the transfer. Then check + * wether it should wait and queue or if it can continue. + */ + +void +UFSHostDevice::SCSIStart() +{ + DPRINTF(UFSHostDevice, "SCSI message on hold until ready\n"); + uint32_t LUN = SCSIInfo.destination[2]; + UFSDevice[LUN]->SCSIInfoQueue.push_back(SCSIInfo); + + DPRINTF(UFSHostDevice, "SCSI queue %d has %d elements\n", LUN, + UFSDevice[LUN]->SCSIInfoQueue.size()); + + /**There are 32 doorbells, so at max there can be 32 transactions*/ + if (UFSDevice[LUN]->SCSIInfoQueue.size() < 2) //LUN is available + SCSIResume(LUN); + + else if (UFSDevice[LUN]->SCSIInfoQueue.size() > 32) + panic("SCSI queue is getting too big %d\n", UFSDevice[LUN]-> + SCSIInfoQueue.size()); + + /** + * First transfer is done, fetch the next; + * At this point, the device is busy, not the HC + */ + if (!transferEventQueue.empty()) { + + /** + * loading next data packet in case Another LUN + * is approached in the mean time + */ + writeDevice(&transferEventQueue.front(), false, + transferStartInfo.front().address, + transferStartInfo.front().size, reinterpret_cast<uint8_t*> + (transferStartInfo.front().destination), 0, 0); + + DPRINTF(UFSHostDevice, "Transfer scheduled"); + } +} + +/** + * Handles the transfer requests that are given. + * There can be three types of transfer. SCSI specific, Reads and writes + * apart from the data transfer, this also generates its own reply (UPIU + * response). Information for this reply is stored in transferInfo and will + * be used in transferDone + */ + +void +UFSHostDevice::SCSIResume(uint32_t lun_id) +{ + DPRINTF(UFSHostDevice, "SCSIresume\n"); + if (UFSDevice[lun_id]->SCSIInfoQueue.empty()) + panic("No SCSI message scheduled lun:%d Doorbell: 0x%8x", lun_id, + UFSHCIMem.TRUTRLDBR); + + /**old info, lets form it such that we can understand it*/ + struct UTPTransferReqDesc* request_in = UFSDevice[lun_id]-> + SCSIInfoQueue.front().RequestIn; + + uint32_t req_pos = UFSDevice[lun_id]->SCSIInfoQueue.front().reqPos; + + Addr finaladdress = UFSDevice[lun_id]->SCSIInfoQueue.front(). + finalAddress; + + uint32_t finalsize = UFSDevice[lun_id]->SCSIInfoQueue.front().finalSize; + + uint32_t* transfercommand = reinterpret_cast<uint32_t*> + (&(UFSDevice[lun_id]->SCSIInfoQueue.front().destination[0])); + + DPRINTF(UFSHostDevice, "Task tag: 0x%8x\n", transfercommand[0]>>24); + /**call logic unit to handle SCSI command*/ + request_out_datain = UFSDevice[(transfercommand[0] & 0xFF0000) >> 16]-> + SCSICMDHandle(transfercommand); + + DPRINTF(UFSHostDevice, "LUN: %d\n", request_out_datain.LUN); + + /** + * build response stating that it was succesful + * command completion, Logic unit number, and Task tag + */ + request_in->header.dWord0 = ((request_in->header.dWord0 >> 24) == 0x21) + ? 0x36 : 0x21; + UFSDevice[lun_id]->transferInfo.requestOut.header.dWord0 = + request_in->header.dWord0 | (request_out_datain.LUN << 8) + | (transfercommand[0] & 0xFF000000); + /**SCSI status reply*/ + UFSDevice[lun_id]->transferInfo.requestOut.header.dWord1 = 0x00000000 | + (request_out_datain.status << 24); + /**segment size + EHS length (see UFS standard ch7)*/ + UFSDevice[lun_id]->transferInfo.requestOut.header.dWord2 = 0x00000000 | + ((request_out_datain.senseSize + 2) << 24) | 0x05; + /**amount of data that will follow*/ + UFSDevice[lun_id]->transferInfo.requestOut.senseDataLen = + request_out_datain.senseSize; + + //data + for (uint8_t count = 0; count<request_out_datain.senseSize; count++) { + UFSDevice[lun_id]->transferInfo.requestOut.senseData[count] = + request_out_datain.senseCode[count + 1]; + } + + /* + * At position defined by "request_in->PRDTableOffset" (counting 32 bit + * words) in array "transfercommand" we have a scatter gather list, which + * is usefull to us if we interpreted it as a UFSHCDSGEntry structure. + */ + struct UFSHCDSGEntry* sglist = reinterpret_cast<UFSHCDSGEntry*> + (&(transfercommand[(request_in->PRDTableOffset)])); + + uint32_t length = request_in->PRDTableLength; + DPRINTF(UFSHostDevice, "# PRDT entries: %d\n", length); + + Addr response_addr = request_in->commandDescBaseAddrHi; + response_addr = (response_addr << 32) | + ((request_in->commandDescBaseAddrLo + + (request_in->responseUPIULength << 2)) & 0xffffffff); + + /**transferdone information packet filling*/ + UFSDevice[lun_id]->transferInfo.responseStartAddr = response_addr; + UFSDevice[lun_id]->transferInfo.reqPos = req_pos; + UFSDevice[lun_id]->transferInfo.size = finalsize; + UFSDevice[lun_id]->transferInfo.address = finaladdress; + UFSDevice[lun_id]->transferInfo.destination = reinterpret_cast<uint8_t*> + (UFSDevice[lun_id]->SCSIInfoQueue.front().RequestIn); + UFSDevice[lun_id]->transferInfo.finished = true; + UFSDevice[lun_id]->transferInfo.lunID = request_out_datain.LUN; + + /** + * In this part the data that needs to be transfered will be initiated + * and the chain of DMA (and potentially) disk transactions will be + * started. + */ + if (request_out_datain.expectMore == 0x01) { + /**write transfer*/ + manageWriteTransfer(request_out_datain.LUN, request_out_datain.offset, + length, sglist); + + } else if (request_out_datain.expectMore == 0x02) { + /**read transfer*/ + manageReadTransfer(request_out_datain.msgSize, request_out_datain.LUN, + request_out_datain.offset, length, sglist); + + } else { + /**not disk related transfer, SCSI maintanance*/ + uint32_t count = 0; + uint32_t size_accum = 0; + DPRINTF(UFSHostDevice, "Data DMA size: 0x%8x\n", + request_out_datain.msgSize); + + /**Transport the SCSI reponse data according to the SG list*/ + while ((length > count) && size_accum + < (request_out_datain.msgSize - 1) && + (request_out_datain.msgSize != 0x00)) { + Addr SCSI_start = sglist[count].upperAddr; + SCSI_start = (SCSI_start << 32) | + (sglist[count].baseAddr & 0xFFFFFFFF); + DPRINTF(UFSHostDevice, "Data DMA start: 0x%8x\n", SCSI_start); + DPRINTF(UFSHostDevice, "Data DMA size: 0x%8x\n", + (sglist[count].size + 1)); + /** + * safetynet; it has been shown that sg list may be optimistic in + * the amount of data allocated, which can potentially lead to + * some garbage data being send over. Hence this construction + * that finds the least amount of data that needs to be + * transfered. + */ + uint32_t size_to_send = sglist[count].size + 1; + + if (request_out_datain.msgSize < (size_to_send + size_accum)) + size_to_send = request_out_datain.msgSize - size_accum; + + readDevice(false, SCSI_start, size_to_send, + reinterpret_cast<uint8_t*> + (&(request_out_datain.message.dataMsg[size_accum])), + false, NULL); + + size_accum += size_to_send; + DPRINTF(UFSHostDevice, "Total remaining: 0x%8x,accumulated so far" + " : 0x%8x\n", (request_out_datain.msgSize - size_accum), + size_accum); + + ++count; + DPRINTF(UFSHostDevice, "Transfer #: %d\n", count); + } + + /**Go to the next stage of the answering process*/ + transferDone(response_addr, req_pos, UFSDevice[lun_id]-> + transferInfo.requestOut, finalsize, finaladdress, + reinterpret_cast<uint8_t*>(request_in), true, lun_id); + } + + DPRINTF(UFSHostDevice, "SCSI resume done\n"); +} + +/** + * Find finished transfer. Callback function. One of the LUNs is done with + * the disk transfer and reports back to the controller. This function finds + * out who it was, and calls transferDone. + */ +void +UFSHostDevice::LUNSignal() +{ + uint8_t this_lun = 0; + + //while we haven't found the right lun, keep searching + while((this_lun < lunAvail) && !UFSDevice[this_lun]->finishedCommand()) + ++this_lun; + + if (this_lun < lunAvail) { + //Clear signal. + UFSDevice[this_lun]->clearSignal(); + //found it; call transferDone + transferDone(UFSDevice[this_lun]->transferInfo.responseStartAddr, + UFSDevice[this_lun]->transferInfo.reqPos, + UFSDevice[this_lun]->transferInfo.requestOut, + UFSDevice[this_lun]->transferInfo.size, + UFSDevice[this_lun]->transferInfo.address, + UFSDevice[this_lun]->transferInfo.destination, + UFSDevice[this_lun]->transferInfo.finished, + UFSDevice[this_lun]->transferInfo.lunID); + } + + else + panic("no LUN finished in tick %d\n", curTick()); +} + +/** + * Transfer done. When the data transfer is done, this function ensures + * that the application is notified. + */ + +void +UFSHostDevice::transferDone(Addr responseStartAddr, uint32_t req_pos, + struct UTPUPIURSP request_out, uint32_t size, + Addr address, uint8_t* destination, + bool finished, uint32_t lun_id) +{ + /**Test whether SCSI queue hasn't popped prematurely*/ + if (UFSDevice[lun_id]->SCSIInfoQueue.empty()) + panic("No SCSI message scheduled lun:%d Doorbell: 0x%8x", lun_id, + UFSHCIMem.TRUTRLDBR); + + DPRINTF(UFSHostDevice, "DMA start: 0x%8x; DMA size: 0x%8x\n", + responseStartAddr, sizeof(request_out)); + + struct transferStart lastinfo; + lastinfo.mask = req_pos; + lastinfo.done = finished; + lastinfo.address = address; + lastinfo.size = size; + lastinfo.destination = reinterpret_cast<UTPTransferReqDesc*> + (destination); + lastinfo.lun_id = lun_id; + + transferEnd.push_back(lastinfo); + + DPRINTF(UFSHostDevice, "Transfer done start\n"); + + readDevice(false, responseStartAddr, sizeof(request_out), + reinterpret_cast<uint8_t*> + (&(UFSDevice[lun_id]->transferInfo.requestOut)), + true, &UTPEvent); +} + +/** + * finalUTP. Second part of the transfer done event. + * this sends the final response: the UTP response. After this transaction + * the doorbell shall be cleared, and the interupt shall be set. + */ + +void +UFSHostDevice::finalUTP() +{ + uint32_t lun_id = transferEnd.front().lun_id; + + UFSDevice[lun_id]->SCSIInfoQueue.pop_front(); + DPRINTF(UFSHostDevice, "SCSIInfoQueue size: %d, lun: %d\n", + UFSDevice[lun_id]->SCSIInfoQueue.size(), lun_id); + + /**stats**/ + if (UFSHCIMem.TRUTRLDBR & transferEnd.front().mask) { + uint8_t count = 0; + while (!(transferEnd.front().mask & (0x1 << count))) + ++count; + stats.transactionLatency.sample(curTick() - + transactionStart[count]); + } + + /**Last message that will be transfered*/ + readDevice(true, transferEnd.front().address, + transferEnd.front().size, reinterpret_cast<uint8_t*> + (transferEnd.front().destination), true, NULL); + + /**clean and ensure that the tracker is updated*/ + transferTrack &= ~(transferEnd.front().mask); + --activeDoorbells; + ++pendingDoorbells; + garbage.push_back(transferEnd.front().destination); + transferEnd.pop_front(); + DPRINTF(UFSHostDevice, "UTP handled\n"); + + /**stats**/ + stats.averageDoorbell = stats.maxDoorbell.value(); + + DPRINTF(UFSHostDevice, "activeDoorbells: %d, pendingDoorbells: %d," + " garbage: %d, TransferEvent: %d\n", activeDoorbells, + pendingDoorbells, garbage.size(), transferEventQueue.size()); + + /**This is the moment that the device is available again*/ + if (!UFSDevice[lun_id]->SCSIInfoQueue.empty()) + SCSIResume(lun_id); +} + +/** + * Read done handling function, is only initiated at the end of a transaction + */ +void +UFSHostDevice::readDone() +{ + DPRINTF(UFSHostDevice, "Read done start\n"); + --readPendingNum; + + /**Garbage collection; sort out the allocated UTP descriptor*/ + if (garbage.size() > 0) { + delete garbage.front(); + garbage.pop_front(); + } + + /**done, generate interrupt if we havent got one already*/ + if(!(UFSHCIMem.ORInterruptStatus & 0x01)) { + UFSHCIMem.ORInterruptStatus |= UTPTransferREQCOMPL; + generateInterrupt(); + } + + + if(!readDoneEvent.empty()) { + readDoneEvent.pop_front(); + } +} + +/** + * set interrupt and sort out the doorbell register. + */ + +void +UFSHostDevice::generateInterrupt() +{ + /**just to keep track of the transactions*/ + countInt++; + + /**step5 clear doorbell*/ + UFSHCIMem.TRUTRLDBR &= transferTrack; + pendingDoorbells = 0; + DPRINTF(UFSHostDevice, "Clear doorbell %X\n", UFSHCIMem.TRUTRLDBR); + + /**step6 raise interrupt*/ + gic->sendInt(intNum); + DPRINTF(UFSHostDevice, "Send interrupt @ transaction: 0x%8x!\n", + countInt); +} + +/** + * Clear interrupt + */ + +void +UFSHostDevice::clearInterrupt() +{ + gic->clearInt(intNum); + DPRINTF(UFSHostDevice, "Clear interrupt: 0x%8x!\n", countInt); + + if (!(UFSHCIMem.TRUTRLDBR)) { + idlePhaseStart = curTick(); + } + /**end of a transaction*/ +} + +/** + * Important to understand about the transfer flow: + * We have basically three stages, The "system memory" stage, the "device + * buffer" stage and the "disk" stage. In this model we assume an infinite + * buffer, or a buffer that is big enough to store all the data in the + * biggest transaction. Between the three stages are two queues. Those queues + * store the messages to simulate their transaction from one stage to the + * next. The manage{Action} function fills up one of the queues and triggers + * the first event, which causes a chain reaction of events executed once + * they pass through their queues. For a write action the stages are ordered + * "system memory", "device buffer" and "disk", whereas the read transfers + * happen "disk", "device buffer" and "system memory". The dma action in the + * dma device is written from a bus perspective whereas this model is written + * from a device perspective. To avoid confusion, the translation between the + * two has been made in the writeDevice and readDevice funtions. + */ + + +/** + * Dma transaction function: write device. Note that the dma action is + * from a device perspective, while this function is from an initiator + * perspective + */ + +void +UFSHostDevice::writeDevice(Event* additional_action, bool toDisk, Addr + start, int size, uint8_t* destination, uint64_t + SCSIDiskOffset, uint32_t lun_id) +{ + DPRINTF(UFSHostDevice, "Write transaction Start: 0x%8x; Size: %d\n", + start, size); + + /**check whether transfer is all the way to the flash*/ + if (toDisk) { + ++writePendingNum; + + while(!writeDoneEvent.empty() && (writeDoneEvent.front().when() + < curTick())) + writeDoneEvent.pop_front(); + + writeDoneEvent.push_back(this); + assert(!writeDoneEvent.back().scheduled()); + + /**destination is an offset here since we are writing to a disk*/ + struct transferInfo new_transfer; + new_transfer.offset = SCSIDiskOffset; + new_transfer.size = size; + new_transfer.lunID = lun_id; + new_transfer.filePointer = 0; + SSDWriteinfo.push_back(new_transfer); + + /**allocate appropriate buffer*/ + SSDWriteinfo.back().buffer.resize(size); + + /**transaction*/ + dmaPort.dmaAction(MemCmd::ReadReq, start, size, + &writeDoneEvent.back(), + &SSDWriteinfo.back().buffer[0], 0); + //yes, a readreq at a write device function is correct. + DPRINTF(UFSHostDevice, "Write to disk scheduled\n"); + + } else { + assert(!additional_action->scheduled()); + dmaPort.dmaAction(MemCmd::ReadReq, start, size, + additional_action, destination, 0); + DPRINTF(UFSHostDevice, "Write scheduled\n"); + } +} + +/** + * Manage write transfer. Manages correct transfer flow and makes sure that + * the queues are filled on time + */ + +void +UFSHostDevice::manageWriteTransfer(uint8_t LUN, uint64_t offset, uint32_t + sg_table_length, struct UFSHCDSGEntry* + sglist) +{ + struct writeToDiskBurst next_packet; + + next_packet.SCSIDiskOffset = offset; + + UFSDevice[LUN]->setTotalWrite(sg_table_length); + + /** + * Break-up the transactions into actions defined by the scatter gather + * list. + */ + for (uint32_t count = 0; count < sg_table_length; count++) { + next_packet.start = sglist[count].upperAddr; + next_packet.start = (next_packet.start << 32) | + (sglist[count].baseAddr & 0xFFFFFFFF); + next_packet.LUN = LUN; + DPRINTF(UFSHostDevice, "Write data DMA start: 0x%8x\n", + next_packet.start); + DPRINTF(UFSHostDevice, "Write data DMA size: 0x%8x\n", + (sglist[count].size + 1)); + assert(sglist[count].size > 0); + + if (count != 0) + next_packet.SCSIDiskOffset = next_packet.SCSIDiskOffset + + (sglist[count - 1].size + 1); + + next_packet.size = sglist[count].size + 1; + + /**If the queue is empty, the transaction should be initiated*/ + if (dmaWriteInfo.empty()) + writeDevice(NULL, true, next_packet.start, next_packet.size, + NULL, next_packet.SCSIDiskOffset, next_packet.LUN); + else + DPRINTF(UFSHostDevice, "Write not initiated queue: %d\n", + dmaWriteInfo.size()); + + dmaWriteInfo.push_back(next_packet); + DPRINTF(UFSHostDevice, "Write Location: 0x%8x\n", + next_packet.SCSIDiskOffset); + + DPRINTF(UFSHostDevice, "Write transfer #: 0x%8x\n", count + 1); + + /** stats **/ + stats.totalWrittenSSD += (sglist[count].size + 1); + } + + /**stats**/ + ++stats.totalWriteUFSTransactions; +} + +/** + * Write done handling function. Is only initiated when the flash is directly + * approached + */ + +void +UFSHostDevice::writeDone() +{ + /**DMA is done, information no longer needed*/ + assert(dmaWriteInfo.size() > 0); + dmaWriteInfo.pop_front(); + assert(SSDWriteinfo.size() > 0); + uint32_t lun = SSDWriteinfo.front().lunID; + + /**If there is nothing on the way, we need to start the events*/ + DPRINTF(UFSHostDevice, "Write done entered, queue: %d\n", + UFSDevice[lun]->SSDWriteDoneInfo.size()); + /**Write the disk*/ + UFSDevice[lun]->writeFlash(&SSDWriteinfo.front().buffer[0], + SSDWriteinfo.front().offset, + SSDWriteinfo.front().size); + + /** + * Move to the second queue, enter the logic unit + * This is where the disk is approached and the flash transaction is + * handled SSDWriteDone will take care of the timing + */ + UFSDevice[lun]->SSDWriteDoneInfo.push_back(SSDWriteinfo.front()); + SSDWriteinfo.pop_front(); + + --writePendingNum; + /**so far, only the DMA part has been handled, lets do the disk delay*/ + UFSDevice[lun]->SSDWriteStart(); + + /** stats **/ + stats.currentWriteSSDQueue = UFSDevice[lun]->SSDWriteDoneInfo.size(); + stats.averageWriteSSDQueue = UFSDevice[lun]->SSDWriteDoneInfo.size(); + ++stats.totalWriteDiskTransactions; + + /**initiate the next dma action (if any)*/ + if (!dmaWriteInfo.empty()) + writeDevice(NULL, true, dmaWriteInfo.front().start, + dmaWriteInfo.front().size, NULL, + dmaWriteInfo.front().SCSIDiskOffset, + dmaWriteInfo.front().LUN); + DPRINTF(UFSHostDevice, "Write done end\n"); +} + +/** + * SSD write start. Starts the write action in the timing model + */ +void +UFSHostDevice::UFSSCSIDevice::SSDWriteStart() +{ + assert(SSDWriteDoneInfo.size() > 0); + flashDevice->writeMemory( + SSDWriteDoneInfo.front().offset, + SSDWriteDoneInfo.front().size, memWriteCallback); + + SSDWriteDoneInfo.pop_front(); + + DPRINTF(UFSHostDevice, "Write is started; left in queue: %d\n", + SSDWriteDoneInfo.size()); +} + + +/** + * SSDisk write done + */ + +void +UFSHostDevice::UFSSCSIDevice::SSDWriteDone() +{ + DPRINTF(UFSHostDevice, "Write disk, aiming for %d messages, %d so far\n", + totalWrite, amountOfWriteTransfers); + + //we have done one extra transfer + ++amountOfWriteTransfers; + + /**test whether call was correct*/ + assert(totalWrite >= amountOfWriteTransfers && totalWrite != 0); + + /**are we there yet? (did we do everything)*/ + if (totalWrite == amountOfWriteTransfers) { + DPRINTF(UFSHostDevice, "Write transactions finished\n"); + totalWrite = 0; + amountOfWriteTransfers = 0; + + //Callback UFS Host + setSignal(); + signalDone->process(); + } + +} + +/** + * Dma transaction function: read device. Notice that the dma action is from + * a device perspective, while this function is from an initiator perspective + */ + +void +UFSHostDevice::readDevice(bool lastTransfer, Addr start, uint32_t size, + uint8_t* destination, bool no_cache, Event* + additional_action) +{ + DPRINTF(UFSHostDevice, "Read start: 0x%8x; Size: %d, data[0]: 0x%8x\n", + start, size, (reinterpret_cast<uint32_t *>(destination))[0]); + + /** check wether interrupt is needed */ + if (lastTransfer) { + ++readPendingNum; + readDoneEvent.push_back(this); + assert(!readDoneEvent.back().scheduled()); + dmaPort.dmaAction(MemCmd::WriteReq, start, size, + &readDoneEvent.back(), destination, 0); + //yes, a writereq at a read device function is correct. + + } else { + if (additional_action != NULL) + assert(!additional_action->scheduled()); + + dmaPort.dmaAction(MemCmd::WriteReq, start, size, + additional_action, destination, 0); + + } + +} + +/** + * Manage read transfer. Manages correct transfer flow and makes sure that + * the queues are filled on time + */ + +void +UFSHostDevice::manageReadTransfer(uint32_t size, uint32_t LUN, uint64_t + offset, uint32_t sg_table_length, + struct UFSHCDSGEntry* sglist) +{ + uint32_t size_accum = 0; + + DPRINTF(UFSHostDevice, "Data READ size: %d\n", size); + + /** + * Break-up the transactions into actions defined by the scatter gather + * list. + */ + for (uint32_t count = 0; count < sg_table_length; count++) { + struct transferInfo new_transfer; + new_transfer.offset = sglist[count].upperAddr; + new_transfer.offset = (new_transfer.offset << 32) | + (sglist[count].baseAddr & 0xFFFFFFFF); + new_transfer.filePointer = offset + size_accum; + new_transfer.size = (sglist[count].size + 1); + new_transfer.lunID = LUN; + + DPRINTF(UFSHostDevice, "Data READ start: 0x%8x; size: %d\n", + new_transfer.offset, new_transfer.size); + + UFSDevice[LUN]->SSDReadInfo.push_back(new_transfer); + UFSDevice[LUN]->SSDReadInfo.back().buffer.resize(sglist[count].size + + 1); + + /** + * The disk image is read here; but the action is simultated later + * You can see this as the preparation stage, whereas later is the + * simulation phase. + */ + UFSDevice[LUN]->readFlash(&UFSDevice[LUN]-> + SSDReadInfo.back().buffer[0], + offset + size_accum, + sglist[count].size + 1); + + size_accum += (sglist[count].size + 1); + + DPRINTF(UFSHostDevice, "Transfer %d; Remaining: 0x%8x, Accumulated:" + " 0x%8x\n", (count + 1), (size-size_accum), size_accum); + + /** stats **/ + stats.totalReadSSD += (sglist[count].size + 1); + stats.currentReadSSDQueue = UFSDevice[LUN]->SSDReadInfo.size(); + stats.averageReadSSDQueue = UFSDevice[LUN]->SSDReadInfo.size(); + } + + UFSDevice[LUN]->SSDReadStart(sg_table_length); + + /** stats **/ + ++stats.totalReadUFSTransactions; + +} + + + +/** + * SSDisk start read; this function was created to keep the interfaces + * between the layers simpler. Without this function UFSHost would need to + * know about the flashdevice. + */ + +void +UFSHostDevice::UFSSCSIDevice::SSDReadStart(uint32_t total_read) +{ + totalRead = total_read; + for (uint32_t number_handled = 0; number_handled < SSDReadInfo.size(); + number_handled++) { + /** + * Load all the read request to the Memory device. + * It will call back when done. + */ + flashDevice->readMemory(SSDReadInfo.front().filePointer, + SSDReadInfo.front().size, memReadCallback); + } + +} + + +/** + * SSDisk read done + */ + +void +UFSHostDevice::UFSSCSIDevice::SSDReadDone() +{ + DPRINTF(UFSHostDevice, "SSD read done at lun %d, Aiming for %d messages," + " %d so far\n", lunID, totalRead, amountOfReadTransfers); + + if (totalRead == amountOfReadTransfers) { + totalRead = 0; + amountOfReadTransfers = 0; + + /**Callback: transferdone*/ + setSignal(); + signalDone->process(); + } + +} + +/** + * Read callback, on the way from the disk to the DMA. Called by the flash + * layer. Intermediate step to the host layer + */ +void +UFSHostDevice::UFSSCSIDevice::readCallback() +{ + ++amountOfReadTransfers; + + /**Callback; make sure data is transfered upstream: + * UFSHostDevice::readCallback + */ + setReadSignal(); + deviceReadCallback->process(); + + //Are we done yet? + SSDReadDone(); +} + +/** + * Read callback, on the way from the disk to the DMA. Called by the UFSSCSI + * layer. + */ + +void +UFSHostDevice::readCallback() +{ + DPRINTF(UFSHostDevice, "Read Callback\n"); + uint8_t this_lun = 0; + + //while we haven't found the right lun, keep searching + while((this_lun < lunAvail) && !UFSDevice[this_lun]->finishedRead()) + ++this_lun; + + DPRINTF(UFSHostDevice, "Found LUN %d messages pending for clean: %d\n", + this_lun, SSDReadPending.size()); + + if (this_lun < lunAvail) { + //Clear signal. + UFSDevice[this_lun]->clearReadSignal(); + SSDReadPending.push_back(UFSDevice[this_lun]->SSDReadInfo.front()); + UFSDevice[this_lun]->SSDReadInfo.pop_front(); + readGarbageEventQueue.push_back(this); + + //make sure the queue is popped a the end of the dma transaction + readDevice(false, SSDReadPending.front().offset, + SSDReadPending.front().size, + &SSDReadPending.front().buffer[0], false, + &readGarbageEventQueue.back()); + + /**stats*/ + ++stats.totalReadDiskTransactions; + } + else + panic("no read finished in tick %d\n", curTick()); +} + +/** + * After a disk read DMA transfer, the structure needs to be freed. This is + * done in this function. + */ +void +UFSHostDevice::readGarbage() +{ + DPRINTF(UFSHostDevice, "Clean read data, %d\n", SSDReadPending.size()); + SSDReadPending.pop_front(); + readGarbageEventQueue.pop_front(); +} + +/** + * Serialize; needed to make checkpoints + */ + +void +UFSHostDevice::serialize(std::ostream &os) +{ + DmaDevice::serialize(os); + + uint8_t* temp_HCI_mem = reinterpret_cast<uint8_t*>(&UFSHCIMem); + SERIALIZE_ARRAY(temp_HCI_mem, sizeof(HCIMem)); + + uint32_t lun_avail = lunAvail; + SERIALIZE_SCALAR(lun_avail); +} + + +/** + * Unserialize; needed to restore from checkpoints + */ + +void +UFSHostDevice::unserialize(Checkpoint *cp, const std::string §ion) +{ + DmaDevice::unserialize(cp, section); + uint8_t* temp_HCI_mem = reinterpret_cast<uint8_t*>(&UFSHCIMem); + UNSERIALIZE_ARRAY(temp_HCI_mem, sizeof(HCIMem)); + + uint32_t lun_avail; + UNSERIALIZE_SCALAR(lun_avail); + assert(lunAvail == lun_avail); +} + + +/** + * Drain; needed to enable checkpoints + */ + +unsigned int +UFSHostDevice::drain(DrainManager *dm) +{ + unsigned int count = 0; + + // check pio, dma port, and doorbells + count = pioPort.drain(dm) + dmaPort.drain(dm); + + if (UFSHCIMem.TRUTRLDBR) { + count += 1; + drainManager = dm; + } else { + DPRINTF(UFSHostDevice, "UFSHostDevice in drained state\n"); + } + + if (count) { + DPRINTF(UFSHostDevice, "UFSDevice is draining...\n"); + setDrainState(Drainable::Draining); + } else { + DPRINTF(UFSHostDevice, "UFSDevice drained\n"); + setDrainState(Drainable::Drained); + } + return count; +} + +/** + * Checkdrain; needed to enable checkpoints + */ + +void +UFSHostDevice::checkDrain() +{ + if (drainManager == NULL) { + return; + } + + if (UFSHCIMem.TRUTRLDBR) { + DPRINTF(UFSHostDevice, "UFSDevice is still draining; with %d active" + " doorbells\n", activeDoorbells); + } else { + DPRINTF(UFSHostDevice, "UFSDevice is done draining\n"); + drainManager->signalDrainDone(); + drainManager = NULL; + } +} |