diff options
Diffstat (limited to 'src/dev/arm/ufs_device.hh')
-rw-r--r-- | src/dev/arm/ufs_device.hh | 1230 |
1 files changed, 1230 insertions, 0 deletions
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 §ion); + + 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__ |