/* * 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 #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/storage/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); DrainState drain() override; void checkDrain(); void serialize(CheckpointOut &cp) const override; void unserialize(CheckpointIn &cp) override; 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 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 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 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 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 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 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 override; /** * register access functions */ Tick read(PacketPtr pkt) override; Tick write(PacketPtr pkt) override; // 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() override; /** * 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; /** * 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 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 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 taskInfo; std::deque transferStartInfo; /** * Information to get a DMA transaction */ std::deque dmaWriteInfo; /** * Information from DMA transaction to disk */ std::deque SSDWriteinfo; /** * Information from the Disk, waiting to be pushed to the DMA */ std::deque SSDReadPending; /** * garbage queue, ensure clearing of the allocated memory */ std::deque 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 readDoneEvent; std::deque 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 */ EventFunctionWrapper SCSIResumeEvent; /** * Wait for the moment where we can send the last frame */ EventFunctionWrapper UTPEvent; /** * Event after a read to clean up the UTP data structures */ std::deque 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 taskEventQueue; std::deque 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__