/* * fusb302 usb phy driver for type-c and PD * * Copyright (C) 2015, 2016 Fairchild Semiconductor Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Seee the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * */ #include "PDProtocol.h" #include "PDPolicy.h" #include "TypeC.h" #include "fusb30X.h" #include "platform.h" #include "PD_Types.h" #ifdef FSC_HAVE_VDM #include "vdm/vdm_callbacks.h" #include "vdm/vdm_callbacks_defs.h" #include "vdm/vdm.h" #include "vdm/vdm_types.h" #include "vdm/bitfield_translators.h" #endif // FSC_HAVE_VDM ///////////////////////////////////////////////////////////////////////////// // Variables for use with the USB PD state machine ///////////////////////////////////////////////////////////////////////////// #define FSC_PROTOCOL_BUFFER_SIZE 64 // Number of bytes in the Rx/Tx FIFO protocol buffers extern FSC_BOOL g_Idle; // Puts state machine into Idle state extern FSC_U32 PolicyStateTimer; // Multi-function timer for the different policy states #ifdef FSC_DEBUG // Debugging Variables extern volatile FSC_U16 Timer_S; // Tracks seconds elapsed for log timestamp extern volatile FSC_U16 Timer_tms; // Tracks tenths of milliseconds elapsed for log timestamp extern StateLog PDStateLog; // Log for tracking state transitions and times static FSC_U8 USBPDBuf[PDBUFSIZE]; // Circular buffer of all USB PD messages transferred static FSC_U8 USBPDBufStart; // Pointer to the first byte of the first message static FSC_U8 USBPDBufEnd; // Pointer to the last byte of the last message static FSC_BOOL USBPDBufOverflow; // Flag to indicate that there was a buffer overflow since last read #ifdef FM150911A FSC_U8 manualRetries = 1; // Set to 1 to enable manual retries (instead of automatic) #else FSC_U8 manualRetries = 0; // Set to 1 to enable manual retries (instead of automatic) #endif // (FM150911A && FSC_DEBUG) elif FSC_DEBUG FSC_U8 nTries = 4; // Number of tries (1 + 3 retries) #endif // FSC_DEBUG // Protocol Variables ProtocolState_t ProtocolState; // State variable for Protocol Layer PDTxStatus_t PDTxStatus; // Status variable for current transmission static FSC_U8 MessageIDCounter; // Current Tx message ID counter for SOP static FSC_U8 MessageID; // Last received message ID FSC_BOOL ProtocolMsgRx; // Flag to indicate if we have received a packet SopType ProtocolMsgRxSop; // SOP type of message received static FSC_U8 ProtocolTxBytes; // Number of bytes for the Tx FIFO static FSC_U8 ProtocolTxBuffer[FSC_PROTOCOL_BUFFER_SIZE]; // Buffer for device Tx FIFO static FSC_U8 ProtocolRxBuffer[FSC_PROTOCOL_BUFFER_SIZE]; // Buffer for device Rx FIFO static FSC_U16 ProtocolTimer; // Multi-function timer for the different protocol states static FSC_U8 ProtocolCRC[4]; FSC_BOOL ProtocolCheckRxBeforeTx; ///////////////////////////////////////////////////////////////////////////// // Timer Interrupt service routine ///////////////////////////////////////////////////////////////////////////// void ProtocolTick( void ) { if( !USBPDActive ) return; if (ProtocolTimer) // If the Protocol timer is greater than zero... ProtocolTimer--; // Decrement it } void InitializePDProtocolVariables(void) { } // ##################### USB PD Protocol Layer Routines ##################### // void USBPDProtocol(void) { #ifdef FSC_INTERRUPT_TRIGGERED if(g_Idle == TRUE) // Go into active mode { g_Idle = FALSE; platform_enable_timer(TRUE); } #endif if (Registers.Status.I_HARDRST) { ResetProtocolLayer(TRUE); // Reset the protocol layer if (PolicyIsSource) // If we are the source... { PolicyStateTimer = tPSHardReset; PolicyState = peSourceTransitionDefault; // set the source transition to default } else // Otherwise we are the sink... PolicyState = peSinkTransitionDefault; // so set the sink transition to default PolicySubIndex = 0; #ifdef FSC_DEBUG StoreUSBPDToken(FALSE, pdtHardReset); // Store the hard reset #endif // FSC_DEBUG } else { switch (ProtocolState) { case PRLReset: ProtocolSendHardReset(); // Send a Hard Reset sequence PDTxStatus = txWait; // Set the transmission status to wait to signal the policy engine ProtocolState = PRLResetWait; // Go to the next state to wait for the reset signaling to complete ProtocolTimer = tBMCTimeout; // Set a timeout so that we don't hang waiting for the reset to complete break; case PRLResetWait: // Wait for the reset signaling to complete ProtocolResetWait(); break; case PRLIdle: // Waiting to send or receive a message ProtocolIdle(); break; case PRLTxSendingMessage: // We have attempted to transmit and are waiting for it to complete or detect a collision ProtocolSendingMessage(); // Determine which state we should go to next break; case PRLTxVerifyGoodCRC: // Wait for message to be received and handle... ProtocolVerifyGoodCRC(); break; case PRL_BIST_Rx_Reset_Counter: // Reset BISTErrorCounter and preload PRBS protocolBISTRxResetCounter(); break; case PRL_BIST_Rx_Test_Frame: // Wait for test Frame form PHY protocolBISTRxTestFrame(); break; case PRL_BIST_Rx_Error_Count: // Construct and send BIST error count message to PHY protocolBISTRxErrorCount(); break; case PRL_BIST_Rx_Inform_Policy: // Inform policy engine error count has been sent protocolBISTRxInformPolicy(); break; case PRLDisabled: // In the disabled state, don't do anything break; default: break; } } } void ProtocolIdle(void) { if (PDTxStatus == txReset) // If we need to send a hard reset... ProtocolState = PRLReset; // Set the protocol state to send it #ifndef FSC_INTERRUPT_TRIGGERED else if (Registers.Status.I_GCRCSENT) // Otherwise check to see if we have received a message and sent a GoodCRC in response #else else if (!Registers.Status.RX_EMPTY) // Otherwise check to see if we have received a message and sent a GoodCRC in response #endif { ProtocolGetRxPacket(); // Grab the received message to pass up to the policy engine PDTxStatus = txIdle; // Reset the transmitter status if we are receiving data (discard any packet to send) Registers.Status.I_GCRCSENT = 0; } else if (PDTxStatus == txSend) // Otherwise check to see if there has been a request to send data... { ProtocolTransmitMessage(); // If so, send the message } } void ProtocolResetWait(void) { if (Registers.Status.I_HARDSENT) // Wait for the reset sequence to complete { ProtocolState = PRLIdle; // Have the protocol state go to idle PDTxStatus = txSuccess; // Alert the policy engine that the reset signaling has completed } else if (ProtocolTimer == 0) // Wait for the BMCTimeout period before stating success in case the interrupts don't line up { ProtocolState = PRLIdle; // Have the protocol state go to idle PDTxStatus = txSuccess; // Assume that we have successfully sent a hard reset for now (may change in future) } } void ProtocolGetRxPacket(void) { FSC_U32 i, j; FSC_U8 data[3]; SopType rx_sop; #ifdef FSC_DEBUG FSC_U8 sop_token = 0; #endif // FSC_DEBUG DeviceRead(regFIFO, 3, &data[0]); // Read the Rx token and two header bytes PolicyRxHeader.byte[0] = data[1]; PolicyRxHeader.byte[1] = data[2]; // Only setting the Tx header here so that we can store what we expect was sent in our PD buffer for the GUI PolicyTxHeader.word = 0; // Clear the word to initialize for each transaction PolicyTxHeader.NumDataObjects = 0; // Clear the number of objects since this is a command PolicyTxHeader.MessageType = CMTGoodCRC; // Sets the message type to GoodCRC PolicyTxHeader.PortDataRole = PolicyIsDFP; // Set whether the port is acting as a DFP or UFP PolicyTxHeader.PortPowerRole = PolicyIsSource; // Set whether the port is serving as a power source or sink PolicyTxHeader.SpecRevision = USBPDSPECREV; // Set the spec revision PolicyTxHeader.MessageID = PolicyRxHeader.MessageID; // Update the message ID for the return packet // figure out what SOP* the data came in on rx_sop = TokenToSopType(data[0]); if ((PolicyRxHeader.NumDataObjects == 0) && (PolicyRxHeader.MessageType == CMTSoftReset)) { MessageIDCounter = 0; // Clear the message ID counter for tx MessageID = 0xFF; // Reset the message ID (always alert policy engine of soft reset) ProtocolMsgRxSop = rx_sop; ProtocolMsgRx = TRUE; // Set the flag to pass the message to the policy engine #ifdef FSC_DEBUG SourceCapsUpdated = TRUE; // Set the source caps updated flag to indicate to the GUI to update the display #endif // FSC_DEBUG } else if (PolicyRxHeader.MessageID != MessageID) // If the message ID does not match the stored... { MessageID = PolicyRxHeader.MessageID; // Update the stored message ID ProtocolMsgRxSop = rx_sop; ProtocolMsgRx = TRUE; // Set the flag to pass the message to the policy engine } if (PolicyRxHeader.NumDataObjects > 0) // Did we receive a data message? If so, we want to retrieve the data { DeviceRead(regFIFO, ((PolicyRxHeader.NumDataObjects<<2)), &ProtocolRxBuffer[0]); // Grab the data from the FIFO for (i=0; i 0) // If this is a data object... { for (i=0; i 0) // If this is a data message, grab the data objects { DeviceRead(regFIFO, PolicyRxHeader.NumDataObjects<<2, &ProtocolRxBuffer[0]); // Grab the data from the FIFO for (i=0; i>= 5; // Shift the token into place USBPDBuf[USBPDBufEnd++] = SOPToken; // Set the second header byte (PD message type) USBPDBufEnd %= PDBUFSIZE; // Wrap the pointer if it is too large USBPDBuf[USBPDBufEnd++] = Header.byte[0]; // Set the first byte and increment the pointer USBPDBufEnd %= PDBUFSIZE; // Wrap the pointer if it is too large USBPDBuf[USBPDBufEnd++] = Header.byte[1]; // Set the second byte and increment the pointer USBPDBufEnd %= PDBUFSIZE; // Wrap the pointer if it is too large for (i=0; i USBPDBufStart) // If the buffer hasn't wrapped... bytes = USBPDBufEnd - USBPDBufStart; // simply subtract the end from the beginning else // Otherwise it has wrapped... bytes = USBPDBufEnd + (PDBUFSIZE - USBPDBufStart); // calculate the available this way return bytes; } FSC_BOOL ClaimBufferSpace(FSC_S32 intReqSize) { FSC_S32 available; FSC_U8 numBytes; if (intReqSize >= PDBUFSIZE) // If we cannot claim enough space... return FALSE; // Get out of here if (USBPDBufStart == USBPDBufEnd) // If the buffer is empty (using the keep one slot open approach) available = PDBUFSIZE; // Buffer is empty... else if (USBPDBufStart > USBPDBufEnd) // If the buffer has wrapped... available = USBPDBufStart - USBPDBufEnd; // calculate this way else // Otherwise available = PDBUFSIZE - (USBPDBufEnd - USBPDBufStart); // calculate the available this way do { if (intReqSize >= available) // If we don't have enough room in the buffer, we need to make room (always keep 1 spot open) { USBPDBufOverflow = TRUE; // Set the overflow flag to alert the GUI that we are overwriting data numBytes = GetNextUSBPDMessageSize(); // Get the size of the next USB PD message in the buffer if (numBytes == 0) // If the buffer is empty... return FALSE; // Return FALSE since the data cannot fit in the available buffer size (nothing written) available += numBytes; // Add the recovered bytes to the number available USBPDBufStart += numBytes; // Adjust the pointer to the new starting address USBPDBufStart %= PDBUFSIZE; // Wrap the pointer if necessary } else break; } while (1); // Loop until we have enough bytes return TRUE; } // ##################### USB HID Commmunication Routines #################### // void GetUSBPDStatus(FSC_U8 abytData[]) { FSC_U32 i, j; FSC_U32 intIndex = 0; abytData[intIndex++] = GetUSBPDStatusOverview(); // Grab a snapshot of the top level status abytData[intIndex++] = GetUSBPDBufferNumBytes(); // Get the number of bytes in the PD buffer abytData[intIndex++] = PolicyState; // Get the current policy engine state abytData[intIndex++] = PolicySubIndex; // Get the current policy sub index abytData[intIndex++] = (ProtocolState << 4) | PDTxStatus; // Get the protocol state and transmitter status for (i=0;i<4;i++) abytData[intIndex++] = USBPDContract.byte[i]; // Get each byte of the current contract if (PolicyIsSource) { #ifdef FSC_HAVE_SRC abytData[intIndex++] = CapsHeaderSource.byte[0]; // Get the first byte of the received capabilities header abytData[intIndex++] = CapsHeaderSource.byte[1]; // Get the second byte of the received capabilities header for (i=0;i<7;i++) // Loop through each data object { for (j=0;j<4;j++) // Loop through each byte of the data object abytData[intIndex++] = CapsSource[i].byte[j]; // Get each byte of each power data object } #endif // FSC_HAVE_SRC } else { #ifdef FSC_HAVE_SNK abytData[intIndex++] = CapsHeaderReceived.byte[0]; // Get the first byte of the received capabilities header abytData[intIndex++] = CapsHeaderReceived.byte[1]; // Get the second byte of the received capabilities header for (i=0;i<7;i++) // Loop through each data object { for (j=0;j<4;j++) // Loop through each byte of the data object abytData[intIndex++] = CapsReceived[i].byte[j]; // Get each byte of each power data object } #endif // FSC_HAVE_SNK } // We are going to return the Registers for now for debugging purposes // These will be removed eventually and a new command will likely be added // For now, offset the registers by 16 from the beginning to get them out of the way intIndex = 44; abytData[intIndex++] = Registers.DeviceID.byte; // 52 abytData[intIndex++] = Registers.Switches.byte[0]; // 53 abytData[intIndex++] = Registers.Switches.byte[1]; abytData[intIndex++] = Registers.Measure.byte; abytData[intIndex++] = Registers.Slice.byte; abytData[intIndex++] = Registers.Control.byte[0]; // 57 abytData[intIndex++] = Registers.Control.byte[1]; abytData[intIndex++] = Registers.Mask.byte; abytData[intIndex++] = Registers.Power.byte; abytData[intIndex++] = Registers.Status.byte[4]; // Status0 - 61 abytData[intIndex++] = Registers.Status.byte[5]; // Status1 - 62 abytData[intIndex++] = Registers.Status.byte[6]; // Interrupt1 - 63 } FSC_U8 GetUSBPDStatusOverview(void) { FSC_U8 status = 0; if (USBPDEnabled) status |= 0x01; if (USBPDActive) status |= 0x02; if (PolicyIsSource) status |= 0x04; if (PolicyIsDFP) status |= 0x08; if (PolicyHasContract) status |= 0x10; if (SourceCapsUpdated) status |= 0x20; SourceCapsUpdated = FALSE; if (USBPDBufOverflow) status |= 0x80; return status; } FSC_U8 ReadUSBPDBuffer(FSC_U8* pData, FSC_U8 bytesAvail) { FSC_U8 i, msgSize, bytesRead; bytesRead = 0; do { msgSize = GetNextUSBPDMessageSize(); // Grab the next message size if ((msgSize != 0) && (msgSize <= bytesAvail)) // If there is data and the message will fit... { for (i=0; i