summaryrefslogtreecommitdiff
path: root/src/dev/arm/ufs_device.cc
diff options
context:
space:
mode:
authorRene de Jong <rene.dejong@arm.com>2015-04-23 13:37:50 -0400
committerRene de Jong <rene.dejong@arm.com>2015-04-23 13:37:50 -0400
commit483f873d0138a668f160ba06510600084cdc8473 (patch)
tree8975ff9c7a210fa8d925b6e79e8d273c59c34999 /src/dev/arm/ufs_device.cc
parentfff28ce954cf2749b277b038dab2bee807603681 (diff)
downloadgem5-483f873d0138a668f160ba06510600084cdc8473.tar.xz
arm, dev: Add a UFS device
This patch introduces a UFS host controller and a UFS device. More information about the UFS standard can be found at the JEDEC site: http://www.jedec.org/standards-documents/results/jesd220 Note that the model does not implement the complete standard, and as such is not an actual implementation of UFS. The following SCSI commands are implemented: inquiry, read, read capacity, report LUNs, start/stop, test unit ready, verify, write, format unit, send diagnostic, synchronize cache, mode select, mode sense, request sense, unmap, write buffer and read buffer. This is sufficient for usage with Linux and Android. To interact with this model a kernel version 3.9 or above is needed.
Diffstat (limited to 'src/dev/arm/ufs_device.cc')
-rw-r--r--src/dev/arm/ufs_device.cc2363
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 &section)
+{
+ 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;
+ }
+}