blob: 074b5ea2e0ced8b0a7af9dcc2fbd23ec064e488d [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010 - 2015 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xiicps_slave.c
* @addtogroup iicps_v3_0
* @{
*
* Handles slave transfers
*
* <pre> MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- -- -------- ---------------------------------------------
* 1.00a jz 01/30/10 First release
* 1.04a kpc 08/30/13 Avoid buffer overwrite in SlaveRecvData function
* 3.00 sk 01/31/15 Modified the code according to MISRAC 2012 Compliant.
*
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xiicps.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
extern s32 TransmitFifoFill(XIicPs *InstancePtr);
static s32 SlaveRecvData(XIicPs *InstancePtr);
/************************* Variable Definitions *****************************/
/*****************************************************************************/
/**
* This function sets up the device to be a slave.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param SlaveAddr is the address of the slave we are receiving from.
*
* @return None.
*
* @note
* Interrupt is always enabled no matter the tranfer is interrupt-
* driven or polled mode. Whether device will be interrupted or not
* depends on whether the device is connected to an interrupt
* controller and interrupt for the device is enabled.
*
****************************************************************************/
void XIicPs_SetupSlave(XIicPs *InstancePtr, u16 SlaveAddr)
{
u32 ControlReg;
u32 BaseAddr;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertVoid(XIICPS_ADDR_MASK >= SlaveAddr);
BaseAddr = InstancePtr->Config.BaseAddress;
ControlReg = XIicPs_In32(BaseAddr + XIICPS_CR_OFFSET);
/*
* Set up master, AckEn, nea and also clear fifo.
*/
ControlReg |= (u32)XIICPS_CR_ACKEN_MASK | (u32)XIICPS_CR_CLR_FIFO_MASK;
ControlReg |= (u32)XIICPS_CR_NEA_MASK;
ControlReg &= (u32)(~XIICPS_CR_MS_MASK);
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
ControlReg);
XIicPs_DisableAllInterrupts(BaseAddr);
XIicPs_WriteReg(InstancePtr->Config.BaseAddress,
XIICPS_ADDR_OFFSET, (u32)SlaveAddr);
return;
}
/*****************************************************************************/
/**
* This function setup a slave interrupt-driven send. It set the repeated
* start for the device is the tranfer size is larger than FIFO depth.
* Data processing for the send is initiated by the interrupt handler.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the send buffer.
* @param ByteCount is the number of bytes to be sent.
*
* @return None.
*
* @note This send routine is for interrupt-driven transfer only.
*
****************************************************************************/
void XIicPs_SlaveSend(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount)
{
u32 BaseAddr;
/*
* Assert validates the input arguments
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(MsgPtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->SendBufferPtr = MsgPtr;
InstancePtr->SendByteCount = ByteCount;
InstancePtr->RecvBufferPtr = NULL;
XIicPs_EnableInterrupts(BaseAddr,
(u32)XIICPS_IXR_DATA_MASK | (u32)XIICPS_IXR_COMP_MASK |
(u32)XIICPS_IXR_TO_MASK | (u32)XIICPS_IXR_NACK_MASK |
(u32)XIICPS_IXR_TX_OVR_MASK);
}
/*****************************************************************************/
/**
* This function setup a slave interrupt-driven receive.
* Data processing for the receive is handled by the interrupt handler.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the receive buffer.
* @param ByteCount is the number of bytes to be received.
*
* @return None.
*
* @note This routine is for interrupt-driven transfer only.
*
****************************************************************************/
void XIicPs_SlaveRecv(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount)
{
/*
* Assert validates the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(MsgPtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
InstancePtr->RecvBufferPtr = MsgPtr;
InstancePtr->RecvByteCount = ByteCount;
InstancePtr->SendBufferPtr = NULL;
XIicPs_EnableInterrupts(InstancePtr->Config.BaseAddress,
(u32)XIICPS_IXR_DATA_MASK | (u32)XIICPS_IXR_COMP_MASK |
(u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_TO_MASK |
(u32)XIICPS_IXR_RX_OVR_MASK | (u32)XIICPS_IXR_RX_UNF_MASK);
}
/*****************************************************************************/
/**
* This function sends a buffer in polled mode as a slave.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the send buffer.
* @param ByteCount is the number of bytes to be sent.
*
* @return
* - XST_SUCCESS if everything went well.
* - XST_FAILURE if master sends us data or master terminates the
* transfer before all data has sent out.
*
* @note This send routine is for polled mode transfer only.
*
****************************************************************************/
s32 XIicPs_SlaveSendPolled(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount)
{
u32 IntrStatusReg;
u32 StatusReg;
u32 BaseAddr;
s32 Tmp;
s32 BytesToSend;
s32 Error = 0;
s32 Status = (s32)XST_SUCCESS;
u32 Value;
/*
* Assert validates the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(MsgPtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->SendBufferPtr = MsgPtr;
InstancePtr->SendByteCount = ByteCount;
/*
* Use RXRW bit in status register to wait master to start a read.
*/
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
while (((StatusReg & XIICPS_SR_RXRW_MASK) == 0U) &&
((!Error) != 0)) {
/*
* If master tries to send us data, it is an error.
*/
if ((StatusReg & XIICPS_SR_RXDV_MASK) != 0x0U) {
Error = 1;
}
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
}
if (Error != 0) {
Status = (s32)XST_FAILURE;
} else {
/*
* Clear the interrupt status register.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
/*
* Send data as long as there is more data to send and
* there are no errors.
*/
Value = (InstancePtr->SendByteCount > (s32)0) &&
((!Error) != 0);
while (Value != (u32)0x00U) {
/*
* Find out how many can be sent.
*/
BytesToSend = InstancePtr->SendByteCount;
if (BytesToSend > (s32)(XIICPS_FIFO_DEPTH)) {
BytesToSend = (s32)(XIICPS_FIFO_DEPTH);
}
for(Tmp = 0; Tmp < BytesToSend; Tmp ++) {
XIicPs_SendByte(InstancePtr);
}
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
/*
* Wait for master to read the data out of fifo.
*/
while (((StatusReg & XIICPS_SR_TXDV_MASK) != (u32)0x00U) &&
((!Error) != 0)) {
/*
* If master terminates the transfer before all data is
* sent, it is an error.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr,
XIICPS_ISR_OFFSET);
if ((IntrStatusReg & XIICPS_IXR_NACK_MASK) != 0x0U) {
Error = 1;
}
/* Clear ISR.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET,
IntrStatusReg);
StatusReg = XIicPs_ReadReg(BaseAddr,
XIICPS_SR_OFFSET);
}
Value = (InstancePtr->SendByteCount > (s32)0U) &&
((!Error) != 0);
}
}
if (Error != 0) {
Status = (s32)XST_FAILURE;
}
return Status;
}
/*****************************************************************************/
/**
* This function receives a buffer in polled mode as a slave.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the receive buffer.
* @param ByteCount is the number of bytes to be received.
*
* @return
* - XST_SUCCESS if everything went well.
* - XST_FAILURE if timed out.
*
* @note This receive routine is for polled mode transfer only.
*
****************************************************************************/
s32 XIicPs_SlaveRecvPolled(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount)
{
u32 IntrStatusReg;
u32 StatusReg;
u32 BaseAddr;
s32 Count;
/*
* Assert validates the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(MsgPtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->RecvBufferPtr = MsgPtr;
InstancePtr->RecvByteCount = ByteCount;
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
/*
* Clear the interrupt status register.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
/*
* Clear the status register.
*/
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_SR_OFFSET, StatusReg);
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
Count = InstancePtr->RecvByteCount;
while (Count > (s32)0) {
/* Wait for master to put data */
while ((StatusReg & XIICPS_SR_RXDV_MASK) == 0U) {
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
/*
* If master terminates the transfer before we get all
* the data or the master tries to read from us,
* it is an error.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr,
XIICPS_ISR_OFFSET);
if (((IntrStatusReg & (XIICPS_IXR_DATA_MASK |
XIICPS_IXR_COMP_MASK))!=0x0U) &&
((StatusReg & XIICPS_SR_RXDV_MASK) == 0U) &&
((InstancePtr->RecvByteCount > 0) != 0x0U)) {
return (s32)XST_FAILURE;
}
/*
* Clear the interrupt status register.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET,
IntrStatusReg);
}
/*
* Read all data from FIFO.
*/
while (((StatusReg & XIICPS_SR_RXDV_MASK)!=0x0U) &&
((InstancePtr->RecvByteCount > 0) != 0x0U)){
XIicPs_RecvByte(InstancePtr);
StatusReg = XIicPs_ReadReg(BaseAddr,
XIICPS_SR_OFFSET);
}
Count = InstancePtr->RecvByteCount;
}
return (s32)XST_SUCCESS;
}
/*****************************************************************************/
/**
* The interrupt handler for slave mode. It does the protocol handling for
* the interrupt-driven transfers.
*
* Completion events and errors are signaled to upper layer for proper
* handling.
*
* <pre>
*
* The interrupts that are handled are:
* - DATA
* If the instance is sending, it means that the master wants to read more
* data from us. Send more data, and check whether we are done with this
* send.
*
* If the instance is receiving, it means that the master has writen
* more data to us. Receive more data, and check whether we are done with
* with this receive.
*
* - COMP
* This marks that stop sequence has been sent from the master, transfer
* is about to terminate. However, for receiving, the master may have
* written us some data, so receive that first.
*
* It is an error if the amount of transfered data is less than expected.
*
* - NAK
* This marks that master does not want our data. It is for send only.
*
* - Other interrupts
* These interrupts are marked as error.
*
* </pre>
*
* @param InstancePtr is a pointer to the XIicPs instance.
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XIicPs_SlaveInterruptHandler(XIicPs *InstancePtr)
{
u32 IntrStatusReg;
u32 IsSend = 0U;
u32 StatusEvent = 0U;
s32 LeftOver;
u32 BaseAddr;
/*
* Assert validates the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
BaseAddr = InstancePtr->Config.BaseAddress;
/*
* Read the Interrupt status register.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
/*
* Write the status back to clear the interrupts so no events are missed
* while processing this interrupt.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
/*
* Use the Mask register AND with the Interrupt Status register so
* disabled interrupts are not processed.
*/
IntrStatusReg &= ~(XIicPs_ReadReg(BaseAddr, XIICPS_IMR_OFFSET));
/*
* Determine whether the device is sending.
*/
if (InstancePtr->RecvBufferPtr == NULL) {
IsSend = 1U;
}
/* Data interrupt
*
* This means master wants to do more data transfers.
* Also check for completion of transfer, signal upper layer if done.
*/
if ((u32)0U != (IntrStatusReg & XIICPS_IXR_DATA_MASK)) {
if (IsSend != 0x0U) {
LeftOver = TransmitFifoFill(InstancePtr);
/*
* We may finish send here
*/
if (LeftOver == 0) {
StatusEvent |=
XIICPS_EVENT_COMPLETE_SEND;
}
} else {
LeftOver = SlaveRecvData(InstancePtr);
/* We may finish the receive here */
if (LeftOver == 0) {
StatusEvent |= XIICPS_EVENT_COMPLETE_RECV;
}
}
}
/*
* Complete interrupt.
*
* In slave mode, it means the master has done with this transfer, so
* we signal the application using completion event.
*/
if (0U != (IntrStatusReg & XIICPS_IXR_COMP_MASK)) {
if (IsSend != 0x0U) {
if (InstancePtr->SendByteCount > 0) {
StatusEvent |= XIICPS_EVENT_ERROR;
}else {
StatusEvent |= XIICPS_EVENT_COMPLETE_SEND;
}
} else {
LeftOver = SlaveRecvData(InstancePtr);
if (LeftOver > 0) {
StatusEvent |= XIICPS_EVENT_ERROR;
} else {
StatusEvent |= XIICPS_EVENT_COMPLETE_RECV;
}
}
}
/*
* Nack interrupt, pass this information to application.
*/
if (0U != (IntrStatusReg & XIICPS_IXR_NACK_MASK)) {
StatusEvent |= XIICPS_EVENT_NACK;
}
/*
* All other interrupts are treated as error.
*/
if (0U != (IntrStatusReg & (XIICPS_IXR_TO_MASK |
XIICPS_IXR_RX_UNF_MASK |
XIICPS_IXR_TX_OVR_MASK |
XIICPS_IXR_RX_OVR_MASK))){
StatusEvent |= XIICPS_EVENT_ERROR;
}
/*
* Signal application if there are any events.
*/
if (0U != StatusEvent) {
InstancePtr->StatusHandler(InstancePtr->CallBackRef,
StatusEvent);
}
}
/*****************************************************************************/
/*
*
* This function handles continuation of receiving data. It is invoked
* from interrupt handler.
*
* @param InstancePtr is a pointer to the XIicPs instance.
*
* @return Number of bytes still expected by the instance.
*
* @note None.
*
****************************************************************************/
static s32 SlaveRecvData(XIicPs *InstancePtr)
{
u32 StatusReg;
u32 BaseAddr;
BaseAddr = InstancePtr->Config.BaseAddress;
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
while (((StatusReg & XIICPS_SR_RXDV_MASK)!=0x0U) &&
((InstancePtr->RecvByteCount > 0) != 0x0U)) {
XIicPs_RecvByte(InstancePtr);
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
}
return InstancePtr->RecvByteCount;
}
/** @} */