/*
* 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