summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dev/arm/SConscript3
-rw-r--r--src/dev/arm/UFSHostDevice.py66
-rw-r--r--src/dev/arm/ufs_device.cc2363
-rw-r--r--src/dev/arm/ufs_device.hh1230
4 files changed, 3662 insertions, 0 deletions
diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript
index 09ee7b4e5..06398ed15 100644
--- a/src/dev/arm/SConscript
+++ b/src/dev/arm/SConscript
@@ -44,6 +44,7 @@ if env['TARGET_ISA'] == 'arm':
SimObject('FlashDevice.py')
SimObject('Gic.py')
SimObject('RealView.py')
+ SimObject('UFSHostDevice.py')
SimObject('EnergyCtrl.py')
Source('a9scu.cc')
@@ -64,6 +65,7 @@ if env['TARGET_ISA'] == 'arm':
Source('rtc_pl031.cc')
Source('timer_cpulocal.cc')
Source('vgic.cc')
+ Source('ufs_device.cc')
Source('energy_ctrl.cc')
DebugFlag('AMBA')
@@ -75,4 +77,5 @@ if env['TARGET_ISA'] == 'arm':
DebugFlag('GIC')
DebugFlag('RVCTRL')
DebugFlag('EnergyCtrl')
+ DebugFlag('UFSHostDevice')
DebugFlag('VGIC')
diff --git a/src/dev/arm/UFSHostDevice.py b/src/dev/arm/UFSHostDevice.py
new file mode 100644
index 000000000..f9369ebc9
--- /dev/null
+++ b/src/dev/arm/UFSHostDevice.py
@@ -0,0 +1,66 @@
+# 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
+#
+import sys
+from m5.params import *
+from m5.proxy import *
+from Device import DmaDevice
+from AbstractNVM import *
+
+class UFSHostDevice(DmaDevice):
+ type = 'UFSHostDevice'
+ cxx_header = "dev/arm/ufs_device.hh"
+ pio_addr = Param.Addr("Address for SCSI configuration slave interface")
+ pio_latency = Param.Latency("10ns", "Time between action and write/read \
+ result by AMBA DMA Device")
+ gic = Param.BaseGic(Parent.any, "Gic to use for interrupting")
+ int_num = Param.UInt32("Interrupt number that connects to GIC")
+ img_blk_size = Param.UInt32(512, "Size of one image block in bytes")
+ # Every image that is added to the vector will generate a new logic unit
+ # in the UFS device; Theoretically (when using the driver from Linux
+ # kernel 3.9 onwards), this can be as many as eigth. Up to two have been
+ # tested.
+ image = VectorParam.DiskImage("Disk images")
+ # Every logic unit can have its own flash dimensions. So the number of
+ # images that have been provided in the image vector, should be equal to
+ # the number of flash objects that are created. Each logic unit can have
+ # its own flash dimensions; to allow the system to define a hetrogeneous
+ # storage system.
+ internalflash = VectorParam.AbstractNVM("Describes the internal flash")
+ ufs_slots = Param.UInt32(32, "Number of commands that can be queued in \
+ the Host controller (min: 1, max: 32)")
+
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;
+ }
+}
diff --git a/src/dev/arm/ufs_device.hh b/src/dev/arm/ufs_device.hh
new file mode 100644
index 000000000..07f038175
--- /dev/null
+++ b/src/dev/arm/ufs_device.hh
@@ -0,0 +1,1230 @@
+/*
+ * 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 base class for UFS devices
+ * The UFS interface consists out of one host controller which connects a
+ * number of devices which together contain up to 8 logic units. A Logical
+ * Unit is an externally addressable, independent, processing entity that
+ * processes SCSI tasks (commands) and performs task management functions.
+ * The decision has been made to abstract the device away, and model the
+ * different logic units. Effectively this means that there is one Host layer
+ * which handles all the UFS commands (everything UTP, UPIU and UNIpro
+ * related) and a SCSI layer, which handles the SCSI interpretation and the
+ * interaction with the "disk" (effectively the LBA that that particular
+ * Logic Unit controls). Each Logic unit should therefor control a disk image
+ * and a timing model. The UFS protocol has three types of commands
+ * (explained later). Each has different phases and each phase need to wait
+ * for its particular data. This is the reason that this model contains a lot
+ * of events. For clarity, a state diagram in doxygen has been provided. To
+ * fully apreciate the stages that the model flows through, the states have
+ * been broken into three state diagrams. It is best if one imagines the
+ * command flow state machine to be happening in the UFSHost layer, and the
+ * other two to flow through all the layers of the model (UFS host, SCSI and
+ * NVM model). See it as a quarry, one state diagram is moving the crane into
+ * place, and the other ones are transporting the dirt down, or the valuables
+ * up. For complete information about the working of UFS please refer to
+ * http://www.jedec.org/standards-documents/results/jesd220 or
+ * http://www.jedec.org/standards-documents/results/jesd223
+ * The documents are available free of charge, although you will need to have
+ * an acount.
+ */
+
+/** UFS command flow state machine
+ *digraph CommandFlow{
+ node [fontsize=10];
+ IDLE -> transferHandler
+ [ label=" transfer/task/command request " fontsize=6];
+ transferHandler -> command
+ [ label=" It is a command " fontsize=6];
+ command -> IDLE
+ [ label=" Command done, no further action " fontsize=6];
+ transferHandler -> taskStart
+ [ label=" It is a task " fontsize=6];
+ taskStart -> finalUTP
+ [ label=" Task handled, now acknowledge (UFS) " fontsize=6];
+ transferHandler -> transferStart
+ [ label=" It is a transfer " fontsize=6];
+ transferStart -> SCSIResume
+ [ label=" Transfer, obtain the specific command " fontsize=6];
+ SCSIResume -> DiskDataFlowPhase
+ [ label=" Disk data transfer (see other graphs) " fontsize=6];
+ SCSIResume -> DeviceDataPhase
+ [ label=" Device info transfer (handled in SCSIResume) "
+ fontsize=6];
+ DiskDataFlowPhase -> transferDone
+ [ label=" Transfer done, acknowledge SCSI command " fontsize=6];
+ DeviceDataPhase -> transferDone
+ [ label=" Transfer done, acknowledge SCSI command " fontsize=6];
+ transferDone -> finalUTP
+ [ label=" Transfer handled, now acknowledge (UFS) " fontsize=6];
+ finalUTP -> readDone
+ [ label=" All handled, clear data structures " fontsize=6];
+ readDone -> IDLE
+ [ label=" All handled, nothing outstanding " fontsize=6];
+ readDone -> transferHandler
+ [ label=" All handled, handle next outstanding " fontsize=6];
+ }
+ */
+/** UFS read transaction flow state machine
+ digraph readFlow{
+ node [fontsize=10];
+ getScatterGather -> commitReadFromDisk
+ [ label=" Put the information about the data transfer to the disk "
+ fontsize=6];
+ commitReadFromDisk -> waitForReads
+ [ label=" Push the reads to the flashmodel and wait for callbacks "
+ fontsize=6];
+ waitForReads -> pushToDMA
+ [ label=" Push to the DMA and wait for them to finish " fontsize=6];
+ pushToDMA -> waitForReads
+ [ label=" Wait for the next disk event " fontsize=6];
+ pushToDMA -> waitForDMA
+ [ label=" Wait for the last DMA transfer to finish " fontsize=6];
+ waitForDMA -> finishTransfer
+ [ label=" Continue with the command flow " fontsize=6];
+ }
+ */
+/** UFS write transaction flow state machine
+ digraph WriteFlow{
+ node [fontsize=10];
+ getScatterGather -> getFromDMA
+ [ label=" Put the transfer information to the DMA " fontsize=6];
+ getFromDMA -> waitForDMA
+ [ label=" Wait for dma actions to arrive " fontsize=6];
+ waitForDMA -> pushToDisk
+ [ label=" Push arrived DMA to disk " fontsize=6];
+ pushToDisk -> waitForDMA
+ [ label=" Wait for next DMA action " fontsize=6];
+ pushToDisk -> waitForDisk
+ [ label=" All DMA actions are done, wait for disk " fontsize=6];
+ waitForDisk -> finishTransfer
+ [ label=" All transactions are done , continue the command flow "
+ fontsize=6];
+ }
+ */
+
+#ifndef __DEV_ARM_UFS_DEVICE_HH__
+#define __DEV_ARM_UFS_DEVICE_HH__
+
+#include <deque>
+
+#include "base/addr_range.hh"
+#include "base/bitfield.hh"
+#include "base/statistics.hh"
+#include "debug/UFSHostDevice.hh"
+#include "dev/arm/abstract_nvm.hh"
+#include "dev/arm/base_gic.hh"
+#include "dev/disk_image.hh"
+#include "dev/dma_device.hh"
+#include "dev/io_device.hh"
+#include "mem/packet.hh"
+#include "mem/packet_access.hh"
+#include "params/UFSHostDevice.hh"
+#include "sim/serialize.hh"
+#include "sim/stats.hh"
+
+/**
+ * Host controller layer: This is your Host controller
+ * This layer handles the UFS functionality.
+ * It tracks all the different transaction stages and uses
+ * the device layer and the flash layer to determine the transaction flow.
+ */
+class UFSHostDevice : public DmaDevice
+{
+ public:
+
+ UFSHostDevice(const UFSHostDeviceParams* p);
+
+ unsigned int drain(DrainManager *dm);
+ void checkDrain();
+ void serialize(std::ostream &os);
+ void unserialize(Checkpoint *cp, const std::string &section);
+
+ private:
+ /**
+ * Host Controller Interface
+ * This is a set of registers that allow the driver to control the
+ * transactions to the flash devices.
+ * As defined in:
+ * http://www.jedec.org/standards-documents/results/jesd223
+ */
+ struct HCIMem {
+ /**
+ * Specify the host capabilities
+ */
+ uint32_t HCCAP;
+ uint32_t HCversion;
+ uint32_t HCHCDDID;
+ uint32_t HCHCPMID;
+
+ /**
+ * Operation and runtime registers
+ */
+ uint32_t ORInterruptStatus;
+ uint32_t ORInterruptEnable;
+ uint32_t ORHostControllerStatus;
+ uint32_t ORHostControllerEnable;
+ uint32_t ORUECPA;
+ uint32_t ORUECDL;
+ uint32_t ORUECN;
+ uint32_t ORUECT;
+ uint32_t ORUECDME;
+ uint32_t ORUTRIACR;
+
+ /**
+ * vendor specific register
+ */
+ uint32_t vendorSpecific;
+
+ /**
+ * Transfer control registers
+ */
+ uint32_t TRUTRLBA;
+ uint32_t TRUTRLBAU;
+ uint32_t TRUTRLDBR;
+ uint32_t TRUTRLCLR;
+ uint32_t TRUTRLRSR;
+
+ /**
+ * Task control registers
+ */
+ uint32_t TMUTMRLBA;
+ uint32_t TMUTMRLBAU;
+ uint32_t TMUTMRLDBR;
+ uint32_t TMUTMRLCLR;
+ uint32_t TMUTMRLRSR;
+
+ /**
+ * Command registers
+ */
+ uint32_t CMDUICCMDR;
+ uint32_t CMDUCMDARG1;
+ uint32_t CMDUCMDARG2;
+ uint32_t CMDUCMDARG3;
+ };
+
+ /**
+ * All the data structures are defined in the UFS standard
+ * This standard be found at the JEDEC website free of charge
+ * (login required):
+ * http://www.jedec.org/standards-documents/results/jesd220
+ */
+
+ /**
+ * struct UTPUPIUHeader - UPIU header structure
+ * dWord0: UPIU header DW-0
+ * dWord1: UPIU header DW-1
+ * dWord2: UPIU header DW-2
+ */
+ struct UTPUPIUHeader {
+ uint32_t dWord0;
+ uint32_t dWord1;
+ uint32_t dWord2;
+ };
+
+ /**
+ * struct UTPUPIURSP - Response UPIU structure
+ * header: UPIU header DW-0 to DW-2
+ * residualTransferCount: Residual transfer count DW-3
+ * reserved: Reserved DW-4 to DW-7
+ * senseDataLen: Sense data length DW-8 U16
+ * senseData: Sense data field DW-8 to DW-12
+ */
+ struct UTPUPIURSP {
+ struct UTPUPIUHeader header;
+ uint32_t residualTransferCount;
+ uint32_t reserved[4];
+ uint16_t senseDataLen;
+ uint8_t senseData[18];
+ };
+
+ /**
+ * struct UTPUPIUTaskReq - Task request UPIU structure
+ * header - UPIU header structure DW0 to DW-2
+ * inputParam1: Input param 1 DW-3
+ * inputParam2: Input param 2 DW-4
+ * inputParam3: Input param 3 DW-5
+ * reserved: Reserver DW-6 to DW-7
+ */
+ struct UTPUPIUTaskReq {
+ struct UTPUPIUHeader header;
+ uint32_t inputParam1;
+ uint32_t inputParam2;
+ uint32_t inputParam3;
+ uint32_t reserved[2];
+ };
+
+ /**
+ * struct UFSHCDSGEntry - UFSHCI PRD Entry
+ * baseAddr: Lower 32bit physical address DW-0
+ * upperAddr: Upper 32bit physical address DW-1
+ * reserved: Reserved for future use DW-2
+ * size: size of physical segment DW-3
+ */
+ struct UFSHCDSGEntry {
+ uint32_t baseAddr;
+ uint32_t upperAddr;
+ uint32_t reserved;
+ uint32_t size;
+ };
+
+ /**
+ * struct UTPTransferCMDDesc - UFS Commad Descriptor structure
+ * commandUPIU: Command UPIU Frame address
+ * responseUPIU: Response UPIU Frame address
+ * PRDTable: Physcial Region Descriptor
+ * All lengths as defined by JEDEC220
+ */
+ struct UTPTransferCMDDesc {
+ uint8_t commandUPIU[128];
+ uint8_t responseUPIU[128];
+ struct UFSHCDSGEntry PRDTable[128];
+ };
+
+ /**
+ * UPIU tranfer message.
+ */
+ struct UPIUMessage {
+ struct UTPUPIUHeader header;
+ uint32_t dataOffset;
+ uint32_t dataCount;
+ std::vector<uint32_t> dataMsg;
+ };
+
+ /**
+ * struct UTPTransferReqDesc - UTRD structure
+ * header: UTRD header DW-0 to DW-3
+ * commandDescBaseAddrLo: UCD base address low DW-4
+ * commandDescBaseAddrHi: UCD base address high DW-5
+ * responseUPIULength: response UPIU length DW-6
+ * responseUPIUOffset: response UPIU offset DW-6
+ * PRDTableLength: Physical region descriptor length DW-7
+ * PRDTableOffset: Physical region descriptor offset DW-7
+ */
+ struct UTPTransferReqDesc {
+
+ /**
+ * struct RequestDescHeader
+ * dword0: Descriptor Header DW0
+ * dword1: Descriptor Header DW1
+ * dword2: Descriptor Header DW2
+ * dword3: Descriptor Header DW3
+ */
+ struct RequestDescHeader {
+ uint32_t dWord0;
+ uint32_t dWord1;
+ uint32_t dWord2;
+ uint32_t dWord3;
+ } header;
+
+ /* DW 4-5*/
+ uint32_t commandDescBaseAddrLo;
+ uint32_t commandDescBaseAddrHi;
+
+ /* DW 6 */
+ uint16_t responseUPIULength;
+ uint16_t responseUPIUOffset;
+
+ /* DW 7 */
+ uint16_t PRDTableLength;
+ uint16_t PRDTableOffset;
+ };
+
+ /**
+ * SCSI reply structure. In here is all the information that is needed to
+ * build a SCSI reply.
+ */
+ struct SCSIReply {
+ uint8_t status;
+ uint32_t msgSize;
+ uint8_t LUN;
+ struct UPIUMessage message;
+ uint8_t senseSize;
+ uint8_t expectMore;//0x01 is for writes, 0x02 is for reads
+ uint64_t offset;
+ uint8_t senseCode[19];
+ };
+
+ /**
+ * Logic unit information structure. SCSI requires information of each LUN.
+ * This structure is defined in the SCSI standard, and can also be found in
+ * the UFS standard. http://www.jedec.org/standards-documents/results/jesd220
+ */
+ struct LUNInfo {
+ uint32_t dWord0;
+ uint32_t dWord1;
+ uint32_t vendor0;
+ uint32_t vendor1;
+ uint32_t product0;
+ uint32_t product1;
+ uint32_t product2;
+ uint32_t product3;
+ uint32_t productRevision;
+ };
+
+ /**
+ * Different events, and scenarios require different types of information.
+ * Keep in mind that for a read-from-disk transaction the host at first a
+ * datastructure fetches to determine where and what the command is, then the
+ * command fetches and the structure fetches to determine where the
+ * different read transactions should be placed and then transfers all the
+ * read fragments. It then answers to the original caller with two replies,
+ * one for the command, and one for UFS. Each of these stages trigger a
+ * different event, and each event needs to know what happened in the
+ * previous stage and what is going to happen in the current one. This
+ * happens also for writes, SCSI maintanance, UFS maintanance, command
+ * management and task management.
+ */
+
+ /**
+ * Transfer information.
+ * @filePointer this does not point to a file, but to a position on the disk
+ * image (which is from the software systems perspective a position in a file)
+ */
+ struct transferInfo {
+ std::vector <uint8_t> buffer;
+ uint32_t size;
+ uint64_t offset;
+ uint32_t filePointer;
+ uint32_t lunID;
+ };
+
+ /**
+ * transfer completion info.
+ * This information is needed by transferDone to finish the transfer.
+ */
+ struct transferDoneInfo {
+ Addr responseStartAddr;
+ uint32_t reqPos;
+ struct UTPUPIURSP requestOut;
+ uint32_t size;
+ Addr address;
+ uint8_t *destination;
+ bool finished;
+ uint32_t lunID;
+ };
+
+ /**
+ * Transfer start information.
+ */
+ struct transferStart {
+ struct UTPTransferReqDesc* destination;
+ uint32_t mask;
+ Addr address;
+ uint32_t size;
+ uint32_t done;
+ uint32_t lun_id;
+ };
+
+ /**
+ * Task start information. This is for the device, so no lun id needed.
+ */
+ struct taskStart {
+ struct UTPUPIUTaskReq destination;
+ uint32_t mask;
+ Addr address;
+ uint32_t size;
+ bool done;
+ };
+
+ /**
+ * After a SCSI command has been identified, the SCSI resume function will
+ * handle it. This information will provide context information.
+ */
+ struct SCSIResumeInfo {
+ struct UTPTransferReqDesc* RequestIn;
+ int reqPos;
+ Addr finalAddress;
+ uint32_t finalSize;
+ std::vector <uint8_t> destination;
+ uint32_t done;
+ };
+
+ /**
+ * Disk transfer burst information. Needed to allow communication between the
+ * disk transactions and dma transactions.
+ */
+ struct writeToDiskBurst {
+ Addr start;
+ uint64_t SCSIDiskOffset;
+ uint32_t size;
+ uint32_t LUN;
+ };
+
+ /**
+ * Statistics
+ */
+ struct UFSHostDeviceStats {
+ /** Queue lengths */
+ Stats::Scalar currentSCSIQueue;
+ Stats::Scalar currentReadSSDQueue;
+ Stats::Scalar currentWriteSSDQueue;
+
+ /** Amount of data read/written */
+ Stats::Scalar totalReadSSD;
+ Stats::Scalar totalWrittenSSD;
+ Stats::Scalar totalReadDiskTransactions;
+ Stats::Scalar totalWriteDiskTransactions;
+ Stats::Scalar totalReadUFSTransactions;
+ Stats::Scalar totalWriteUFSTransactions;
+
+ /** Average bandwidth for reads and writes */
+ Stats::Formula averageReadSSDBW;
+ Stats::Formula averageWriteSSDBW;
+
+ /** Average Queue lengths*/
+ Stats::Average averageSCSIQueue;
+ Stats::Average averageReadSSDQueue;
+ Stats::Average averageWriteSSDQueue;
+
+ /** Number of doorbells rung*/
+ Stats::Formula curDoorbell;
+ Stats::Scalar maxDoorbell;
+ Stats::Average averageDoorbell;
+
+ /** Histogram of latencies*/
+ Stats::Histogram transactionLatency;
+ Stats::Histogram idleTimes;
+ };
+
+ /**
+ * device layer: This is your Logic unit
+ * This layer implements the SCSI functionality of the UFS Device
+ * One logic unit controls one or more disk partitions
+ */
+ class UFSSCSIDevice: SimObject
+ {
+ public:
+ /**
+ * Constructor and destructor
+ */
+ UFSSCSIDevice(const UFSHostDeviceParams* p, uint32_t lun_id, Callback*
+ transfer_cb, Callback *read_cb);
+ ~UFSSCSIDevice();
+
+ /**
+ * SCSI command handle function; determines what the command is and
+ * returns a reply structure that allows the host device to continue
+ * with the transfer.
+ */
+ struct SCSIReply SCSICMDHandle(uint32_t* SCSI_msg);
+
+ /**
+ * Disk access functions. These will transfer the data from/to the disk
+ */
+
+ /**
+ * Read flash. read the data from the disk image. This function
+ * doesn't model timing behaviour
+ */
+ void readFlash(uint8_t* readaddr, uint64_t offset, uint32_t size);
+
+ /**
+ * Write flash. write the data to the disk image. This function
+ * doesn't model timing behaviour
+ */
+ void writeFlash(uint8_t* writeaddr, uint64_t offset, uint32_t size);
+
+ /**
+ * finished command. Probe to find out wether this logic unit
+ * finished its transfer and triggered the callback; The host needs
+ * this to handle the final part of the transaction.
+ */
+ bool finishedCommand() const {return transferCompleted;};
+
+ /**
+ * Clear signal. Handle for the host to clear the transfer complete
+ * signal.
+ */
+ void clearSignal() {transferCompleted = false;};
+
+ /**
+ * Finished read. Probe to find out which logic unit finished its
+ * read. This is needed, because multiple units can do transactions
+ * at the same time, and need to push back data at the right time in
+ * the right order. (because writes work the other way round, they do
+ * not need this mechanism)
+ */
+ bool finishedRead() const {return readCompleted;};
+
+ /**
+ * Clear signal. Handle for the host to clear the read complete
+ * signal.
+ */
+ void clearReadSignal() {readCompleted = false;};
+
+ /**
+ * Start the transactions to (and from) the disk
+ * The host will queue all the transactions. Once the next phase
+ * commences, this function should be started.
+ */
+ void SSDReadStart(uint32_t total_read);
+ void SSDWriteStart();
+
+ /**
+ * Sets total amount of write transactions that needs to be made.
+ * First they need to be fetched via DMA, so this value is needed in
+ * a later stage.
+ */
+ void setTotalWrite(uint32_t total_write) {totalWrite = total_write;};
+
+ /**
+ * End of transfer information
+ */
+ transferDoneInfo transferInfo;
+
+ /**
+ * Information message queues, as there can be multiple messages
+ * queued for handling in this system. These are the main
+ * communication interfaces between the Host and the device layers
+ */
+
+ /**
+ * SCSIInfoQueue: each LU handles its own SCSI commands.
+ */
+ std::deque<struct SCSIResumeInfo> SCSIInfoQueue;
+
+ /**
+ * SSDReadInfo: Structure from disk to dma, that contains data, and
+ * helper info to get it to the right place in the memory.
+ */
+ std::deque<struct transferInfo> SSDReadInfo;
+
+ /**
+ * SSDWriteDoneInfo: Structure from dma to disk, that contains data,
+ * and helper info to get it to the right place in the memory.
+ * The done is added because it is going to the last phase of the
+ * write transfer.
+ */
+ std::deque<struct transferInfo> SSDWriteDoneInfo;
+
+ private:
+ /**
+ * Functions to indicate that the action to the SSD has completed.
+ */
+ /**
+ * Read Call back; This is the callback function for the memory model
+ */
+ void readCallback();
+
+ /**
+ * SSD Read done; Determines if the final callback of the transaction
+ * should be made at the end of a read transfer.
+ */
+ void SSDReadDone();
+
+ /**
+ * SSD Write Done; This is the callback function for the memory model.
+ */
+ void SSDWriteDone();
+
+ /**
+ * Status of SCSI. This may be linked to a status check in the future.
+ * For now it (mainly) fills a data structure with sense information
+ * for a successfull transaction
+ */
+ void statusCheck(uint8_t status, uint8_t* sensecodelist);
+
+ /**
+ * set signal to indicate that the transaction has been completed.
+ */
+ void setSignal() {transferCompleted = true;};
+
+ /**
+ * set signal to indicate that the read action has been completed
+ */
+ void setReadSignal() {readCompleted = true;};
+
+ /**
+ * The objects this model links to.
+ * 1: the disk data model
+ * 2: the memory timing model
+ */
+ DiskImage* flashDisk;
+ AbstractNVM* flashDevice;
+
+ /**
+ * Logic unit dimensions
+ */
+ const uint32_t blkSize;
+ const uint32_t lunAvail;
+ const uint64_t diskSize;
+ const uint32_t capacityLower;
+ const uint32_t capacityUpper;
+
+ /**
+ * Logic unit info; needed for SCSI Info messages and LU
+ * identification
+ */
+ struct LUNInfo lunInfo;
+ const uint32_t lunID;
+
+ /**
+ * Signals to Host layer
+ * 1: signal for transaction completion
+ * 2: signal for read action completion
+ */
+ bool transferCompleted;
+ bool readCompleted;
+
+ /**
+ * Total amount transactions that need to be made
+ */
+ uint32_t totalRead;
+ uint32_t totalWrite;
+
+ /**
+ * transaction progress tracking
+ */
+ uint32_t amountOfWriteTransfers;
+ uint32_t amountOfReadTransfers;
+
+ /**
+ * Callbacks between Host and Device
+ */
+ Callback* signalDone;
+ Callback* deviceReadCallback;
+
+ /**
+ * Callbacks between Device and Memory
+ */
+ Callback* memReadCallback;
+ Callback* memWriteCallback;
+
+ /*
+ * Default response header layout. For more information refer to
+ * chapter 7 http://www.jedec.org/standards-documents/results/jesd220
+ */
+ static const unsigned int UPIUHeaderDataIndWord0 = 0x0000C022;
+ static const unsigned int UPIUHeaderDataIndWord1 = 0x00000000;
+ static const unsigned int UPIUHeaderDataIndWord2 = 0x40000000;
+
+ /*
+ * SCSI mode pages values assigned in ufs_device.cc
+ * The mode pages give device specific information via the SCSI
+ * protocol. They are defined in
+ * http://www.jedec.org/standards-documents/results/jesd220
+ */
+ static const unsigned int controlPage[3];
+ static const unsigned int recoveryPage[3];
+ static const unsigned int cachingPage[5];
+
+ /*
+ * SCSI command set; defined in
+ * http://www.jedec.org/standards-documents/results/jesd220
+ */
+ enum SCSICommandSet {
+ SCSIInquiry = 0x12,
+ SCSIRead6 = 0x08,
+ SCSIRead10 = 0x28,
+ SCSIRead16 = 0x88,
+ SCSIReadCapacity10 = 0x25,
+ SCSIReadCapacity16 = 0x9E,
+ SCSIReportLUNs = 0xA0,
+ SCSIStartStop = 0x1B,
+ SCSITestUnitReady = 0x00,
+ SCSIVerify10 = 0x2F,
+ SCSIWrite6 = 0x0A,
+ SCSIWrite10 = 0x2A,
+ SCSIWrite16 = 0x8A,
+ SCSIFormatUnit = 0x04,
+ SCSISendDiagnostic = 0x1D,
+ SCSISynchronizeCache = 0x35,
+ //UFS SCSI additional command set for full functionality
+ SCSIModeSelect10 = 0x55,
+ SCSIModeSense6 = 0x1A,
+ SCSIModeSense10 = 0x5A,
+ SCSIRequestSense = 0x03,
+ SCSIUnmap = 0x42,
+ SCSIWriteBuffer = 0x3B,
+ SCSIReadBuffer = 0x3C,
+ //SCSI commands not supported by UFS; but Linux send them anyway
+ SCSIMaintenanceIn = 0xA3
+ };
+
+ /*
+ * SCSI status codes; defined in
+ * http://www.jedec.org/standards-documents/results/jesd220
+ */
+ enum SCSIStatusCodes {
+ SCSIGood = 0x00,
+ SCSICheckCondition = 0x02,
+ SCSIConditionGood = 0x04,
+ SCSIBusy = 0x08,
+ SCSIIntermediateGood = 0x10,
+ SCSIIntermediatCGood = 0x14,
+ SCSIReservationConflict = 0x18,
+ SCSICommandTerminated = 0x22,
+ SCSITaskSetFull = 0x28,
+ SCSIACAActive = 0x30,
+ SCSITaskAborted = 0x40
+ };
+
+ /*
+ * SCSI sense codes; defined in
+ * http://www.jedec.org/standards-documents/results/jesd220
+ */
+ enum SCSISenseCodes {
+ SCSINoSense = 0x00,
+ SCSIRecoverdError = 0x01,
+ SCSINotReady = 0x02,
+ SCSIMediumError = 0x03,
+ SCSIHardwareError = 0x04,
+ SCSIIllegalRequest = 0x05,
+ SCSIUnitAttention = 0x06,
+ SCSIDataProtect = 0x07,
+ SCSIBlankCheck = 0x08,
+ SCSIAbortedCommand = 0x0B,
+ SCSIVolumeOverflow = 0x0D,
+ SCSIMisCompare = 0x0E
+ };
+
+ };
+
+ //All access functions are inherrited; no need to make them public
+ /**
+ * Address range functions
+ */
+ AddrRangeList getAddrRanges() const;
+
+ /**
+ * register access functions
+ */
+ Tick read(PacketPtr pkt);
+ Tick write(PacketPtr pkt);
+ // end of access functions
+
+ /**
+ * Initialization function. Sets the sefault HCI register values
+ */
+ void setValues();
+
+ /**
+ * Handler functions. Each function handles a different stage of the
+ * transfer. Note that the UFS protocol specifies three types of messages
+ * to the host (and devices):
+ * 1: Command (to Host specifically)
+ * 2: Task (to device; to control flow, not for data)
+ * 3: Transfer (to device; to transfer data)
+ */
+ /**
+ * request handler. This function finds the cause of the request and
+ * triggers the right follow-up action (command handler, task handler,
+ * or transferhandler)
+ */
+ void requestHandler();
+
+ /**
+ * Command handler function. Handles the command send to the Host
+ * controller
+ */
+ void commandHandler();
+
+ /**
+ * Task Start function. Starts the task handler once the task data
+ * structure has arrived
+ */
+ void taskStart();
+
+ /**
+ * Task handler function. Handles the tasks send to the devices
+ * because there are not many tasks implemented yet this is kept in the
+ * Host controller layer
+ */
+ void taskHandler(struct UTPUPIUTaskReq* request_in,
+ uint32_t req_pos, Addr finaladdress, uint32_t finalsize);
+
+ /**
+ * Transfer Start function. Starts the transfer handler once the transfer
+ * data structure has arrived
+ */
+ void transferStart();
+
+ /**
+ * Transfer handler function. Handles the transfers send to the devices
+ * Important to understand here is that a Logic unit is not a device (a
+ * device can contain multiple logic units). This function analyses the
+ * first data structure that has been transfered. Which will tell the
+ * host to expect SCSI frames for the rest of the transaction. Note that
+ * the host has no indication whatsoever which LU to address. That will
+ * follow in the next transaction.
+ */
+ void transferHandler(struct UTPTransferReqDesc*
+ request_in, int req_pos, Addr finaladdress,
+ uint32_t finalsize, uint32_t done);
+
+ /**
+ * Transfer SCSI function. Determines which Logic unit to address and
+ * starts the SCSI resume function
+ */
+ void SCSIStart();
+
+ /**
+ * Starts the scsi handling function in the apropriate Logic unit,
+ * prepares the right data transfer scheme and kicks it off.
+ */
+ void SCSIResume(uint32_t lun_id);
+
+ /**
+ * LU callback function to indicate that the action has completed.
+ */
+ void LUNSignal();
+
+ /**
+ * transfer done, the beginning of the final stage of the transfer.
+ * Acknowledges UPIU frame and prepares the UTP response frame
+ */
+ void 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);
+ /**
+ * final UTP, sends the last acknowledge data structure to the system;
+ * prepares the clean up functions.
+ */
+ void finalUTP();
+
+ /**
+ * Interrupt control functions
+ */
+ void clearInterrupt();
+ void generateInterrupt();
+
+ /**
+ * DMA transfer functions
+ * These allow the host to push/pull the data to the memory
+ * The provided event indicates what the next phase it that will handle
+ * the obtained data, or what the follow up action is once the data has
+ * been pushed to the memory
+ */
+ void writeDevice(Event* additional_action, bool toDisk, Addr start,
+ int size, uint8_t* destination, uint64_t SCSIDiskOffset,
+ uint32_t lun_id);
+ void readDevice(bool lastTransfer, Addr SCSIStart, uint32_t SCSISize,
+ uint8_t* SCSIDestination, bool no_cache,
+ Event* additional_action);
+
+ /**
+ * Disk transfer management functions
+ * these set up the queues, and initiated them, leading to the data
+ * transaction timing model based on the scatter gather list constructed
+ * in SCSIresume.
+ */
+ void manageWriteTransfer(uint8_t LUN, uint64_t offset, uint32_t
+ sg_table_length, struct UFSHCDSGEntry* sglist);
+ void manageReadTransfer(uint32_t size, uint32_t LUN, uint64_t offset,
+ uint32_t sg_table_length,
+ struct UFSHCDSGEntry* sglist);
+
+ /**
+ * Read done
+ * Started at the end of a transaction after the last read action. Cleans
+ * up UTP descriptor and other remaining data structures. It also raises
+ * the interrupt.
+ */
+ void readDone();
+
+ /**
+ * Write done
+ * After a DMA write with data intended for the disk, this function is
+ * called. It ensures that the disk image is modified, and that the
+ * correct timing function is triggered.
+ */
+ void writeDone();
+
+ /**
+ * Read callback
+ * Call back function for the logic units to indicate the completion of
+ * a read action. Note that this is needed because the read functionality
+ * needs to push data structures back to the memory.
+ */
+ void readCallback();
+
+ /**
+ * Read garbage
+ * A read from disk data structure can vary in size and is therefor
+ * allocated on creation. It can only be destroyed once that particular
+ * read action has completed. This function is called on completion of a
+ * read from disk action to handle this.
+ */
+ void readGarbage();
+
+ /**register statistics*/
+ void regStats();
+
+ /**
+ * Host controller information
+ */
+ const Addr pioAddr;
+ const Addr pioSize;
+ const Tick pioDelay;
+ const int intNum;
+ BaseGic* gic;
+ const uint32_t lunAvail;
+ const uint8_t UFSSlots;
+
+ /**
+ * Host controller memory
+ */
+ HCIMem UFSHCIMem;
+
+ /**
+ * Track number of DMA transactions in progress
+ */
+ int readPendingNum;
+ int writePendingNum;
+
+ /**
+ * Statistics helper variables
+ * Active doorbells indicates how many doorbells are in teh process of
+ * being handled.
+ * Pending doorbells have been handled and are waiting to be acknowledged
+ * by the host system.
+ * The doorbell register is 32 bits wide, so one byte is enough to keep
+ * track of the numbers
+ */
+ uint8_t activeDoorbells;
+ uint8_t pendingDoorbells;
+
+ /**
+ * interrupt verification
+ * This keeps track of the number of interrupts generated. It is usefull
+ * for debug purposes. Make sure that the implemented driver prints the
+ * number of interrupts it has handled so far to fully benefit from this
+ * feature.
+ */
+ uint32_t countInt;
+
+ /**
+ * Track the transfer
+ * This is allows the driver to "group" certain transfers together by
+ * using a tag in the UPIU. The messages with the same tag should be
+ * handled together, i.e. their doorbells should be cleared when they are
+ * all done. but we need to keep track of the ones we already handled,
+ * this integer shadows the doorbells to allow this behaviour.
+ */
+ uint32_t transferTrack;
+ uint32_t taskCommandTrack;
+
+ /**
+ * Helper for latency stats
+ * These variables keep track of the latency for every doorbell.
+ * Eventually the latenmcies will be put in a histogram.
+ */
+ Tick transactionStart[32];
+ Tick idlePhaseStart;
+
+ /**
+ * drain manager
+ * Needed to be able to implement checkpoint functionality
+ */
+
+ DrainManager *drainManager;
+
+ /**
+ * logic units connected to the UFS Host device
+ * Note again that the "device" as such is represented by one or multiple
+ * logic units.
+ */
+ std::vector<UFSSCSIDevice*> UFSDevice;
+
+ /**
+ * SCSI reply structure, used for direct answering. Might be refered to
+ * during the assembly of the reply (data, and response; e.g. if
+ * something goes wrong along the way, the reply will be different)
+ */
+ struct SCSIReply request_out_datain;
+
+ /**
+ * SCSI resume info
+ * information structure for SCSI resume. it keeps track of all the
+ * information that is needed to successfully complete the transaction
+ * (response addresses, communicated information so far, etc.).
+ */
+ struct SCSIResumeInfo SCSIInfo;
+
+ /**
+ * To finish the transaction one needs information about the original
+ * message. This is stored in this queue
+ * transferEnd uses the same structure as transferStartInfo, because all
+ * the information it needs is in there. It improves readability in the
+ * cc file.
+ */
+ std::deque<struct transferStart> transferEnd;
+
+ /**
+ * When a task/transfer is started it needs information about the
+ * task/transfer it is about to perform. This is defined in these
+ * structures. If multiple tasks/transfers are issued at the same time,
+ * they still need to be fetched one by one. They then need to be
+ * executed in the order specified by the UFS standard (least significant
+ * doorbell first). The tasks/transfers are placed in the queue in that
+ * specific order.
+ */
+ std::deque<struct taskStart> taskInfo;
+ std::deque<struct transferStart> transferStartInfo;
+
+ /**
+ * Information to get a DMA transaction
+ */
+ std::deque<struct writeToDiskBurst> dmaWriteInfo;
+
+ /**
+ * Information from DMA transaction to disk
+ */
+ std::deque<struct transferInfo> SSDWriteinfo;
+
+ /**
+ * Information from the Disk, waiting to be pushed to the DMA
+ */
+ std::deque<struct transferInfo> SSDReadPending;
+
+ /**
+ * garbage queue, ensure clearing of the allocated memory
+ */
+ std::deque<struct UTPTransferReqDesc*> garbage;
+
+ /**
+ * RequestHandler stats
+ */
+ struct UFSHostDeviceStats stats;
+
+ /**
+ * Transfer flow events
+ * Basically these events form two queues, one from memory to UFS device
+ * (DMA) and one from device to flash (SSD). The SSD "queue" is
+ * maintained by the flash and the lun classes and does not form a queue
+ * of events as such, but rather a queue of information. This can be done
+ * because the flow of the events is completely in the control of these
+ * classes. (Whereas in the DMA case we rely on an external class)
+ */
+ std::deque<EventWrapper<UFSHostDevice, &UFSHostDevice::readDone> >
+ readDoneEvent;
+ std::deque<EventWrapper<UFSHostDevice, &UFSHostDevice::writeDone> >
+ writeDoneEvent;
+
+ /**
+ * Callbacks for the logic units. One to indicate the completion of a
+ * transaction, the other one to indicate the completion of a read
+ * action.
+ */
+ Callback* transferDoneCallback;
+ Callback* memReadCallback;
+
+ /**
+ * The events that control the functionality.
+ * After a doorbell has been set, either a taskevent or a transfer event
+ * is scheduled. A transfer event might schedule a SCSI event, all events
+ * sequences end with an UTP event, which can be considered as the event
+ * which answers the doorbell.
+ */
+ /**
+ * Wait for the SCSI specific data to arive
+ */
+ EventWrapper<UFSHostDevice, &UFSHostDevice::SCSIStart> SCSIResumeEvent;
+
+ /**
+ * Wait for the moment where we can send the last frame
+ */
+ EventWrapper<UFSHostDevice, &UFSHostDevice::finalUTP> UTPEvent;
+
+ /**
+ * Event after a read to clean up the UTP data structures
+ */
+ std::deque<EventWrapper<UFSHostDevice, &UFSHostDevice::readGarbage> >
+ readGarbageEventQueue;
+
+ /**
+ * Multiple tasks transfers can be scheduled at once for the device, the
+ * only thing we know for sure about them is that they will happen in a
+ * first come first serve order; hence we need to queue.
+ */
+ std::deque<EventWrapper<UFSHostDevice, &UFSHostDevice::taskStart> >
+ taskEventQueue;
+ std::deque<EventWrapper<UFSHostDevice, &UFSHostDevice::transferStart> >
+ transferEventQueue;
+
+ /**
+ * Bits of interest within UFS data packages
+ */
+ static const unsigned int UTPTransferREQCOMPL = 0x01;//UFS_BIT(0)
+ static const unsigned int UTPTaskREQCOMPL = 0x200;//UFS_BIT(9)
+ static const unsigned int UICCommandCOMPL = 0x400;//UFS_BIT(10)
+ static const unsigned int UICCommandReady = 0x08;//UFS_BIT(3)
+
+ /*
+ * UFSHCI Registers; please refer to
+ * http://www.jedec.org/standards-documents/results/jesd223
+ * for their definition.
+ */
+ enum UFSHCIRegisters {
+ regControllerCapabilities = 0x00,
+ regUFSVersion = 0x08,
+ regControllerDEVID = 0x10,
+ regControllerPRODID = 0x14,
+ regInterruptStatus = 0x20,
+ regInterruptEnable = 0x24,
+ regControllerStatus = 0x30,
+ regControllerEnable = 0x34,
+ regUICErrorCodePHYAdapterLayer = 0x38,
+ regUICErrorCodeDataLinkLayer = 0x3C,
+ regUICErrorCodeNetworkLayer = 0x40,
+ regUICErrorCodeTransportLayer = 0x44,
+ regUICErrorCodeDME = 0x48,
+ regUTPTransferREQINTAGGControl = 0x4C,
+ regUTPTransferREQListBaseL = 0x50,
+ regUTPTransferREQListBaseH = 0x54,
+ regUTPTransferREQDoorbell = 0x58,
+ regUTPTransferREQListClear = 0x5C,
+ regUTPTransferREQListRunStop = 0x60,
+ regUTPTaskREQListBaseL = 0x70,
+ regUTPTaskREQListBaseH = 0x74,
+ regUTPTaskREQDoorbell = 0x78,
+ regUTPTaskREQListClear = 0x7C,
+ regUTPTaskREQListRunStop = 0x80,
+ regUICCommand = 0x90,
+ regUICCommandArg1 = 0x94,
+ regUICCommandArg2 = 0x98,
+ regUICCommandArg3 = 0x9C
+ };
+};
+
+#endif //__DEV_ARM_UFS_DEVICE_HH__