/*
 * @brief LPC8XX I2C slave driver
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2014
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

#ifndef __I2CS_8XX_H_
#define __I2CS_8XX_H_

#include "i2c_common_8xx.h"

#ifdef __cplusplus
extern "C" {
#endif

/** @defgroup I2CS_8XX CHIP: LPC8XX I2C slave-only driver
 * @ingroup I2C_8XX
 * This driver only works in slave mode.
 * @{
 */

/** @brief I2C slave service start callback
 * This callback is called from the I2C slave handler when an I2C slave address is
 * received and needs servicing. It's used to indicate the start of a slave transfer
 * that will happen on the slave bus.
 */
typedef void (*I2CSlaveXferStart)(uint8_t addr);

/** @brief I2C slave send data callback
 * This callback is called from the I2C slave handler when an I2C slave address needs
 * data to send. Return 0 to NACK the master and terminate the transfer, or return
 * a non-0 value with the value to send in *data.
 */
typedef uint8_t (*I2CSlaveXferSend)(uint8_t *data);

/** @brief I2C slave receive data callback
 * This callback is called from the I2C slave handler when an I2C slave address has
 * receive data. Return 0 to NACK the master and terminate the transfer, or return
 * a non-0 value to continue the transfer.
 */
typedef uint8_t (*I2CSlaveXferRecv)(uint8_t data);

/** @brief I2C slave service done callback
 * This callback is called from the I2C slave handler when an I2C slave transfer is
 * completed. It's used to indicate the end of a slave transfer.
 */
typedef void (*I2CSlaveXferDone)(void);

/**
 * Slave transfer are performed using 3 callbacks. These 3 callbacks handle most I2C
 * slave transfer cases. When the slave is setup and a slave interrupt is receive
 * and processed with the Chip_I2CS_XferHandler() function in the I2C interrupt handler,
 * one of these 3 callbacks is called. The callbacks can be used for unsized transfers
 * from the master.
 *
 * When an address is received, the SlaveXferAddr() callback is called with the
 * received address. Only addresses enabled in the slave controller will be handled.
 * The slave controller can support up to 4 slave addresses.
 *
 * If the master is going to perform a read operation, the SlaveXferSend() callback
 * is called. Place the data byte to send in *data and return a non-0 value to the
 * caller, or return 0 to NACK the master. (Note the master ACKS/NACKS to slave
 * on reads, so this won't necessarily stop the slave transfer.)<br>
 *
 * If the master performs a write operation, the SlaveXferRecv() callback is called
 * with the received data. Return a non-0 value to the caller, or return 0 to NACK
 * the master.<br>
 *
 * Once the transfer completes, the SlaveXferDone() callback will be called.<br>
 */
typedef struct {
	I2CSlaveXferStart slaveStart;	/*!< Called when an matching I2C slave address is received */
	I2CSlaveXferSend slaveSend;		/*!< Called when a byte is needed to send to master */
	I2CSlaveXferRecv slaveRecv;		/*!< Called when a byte is received from master */
	I2CSlaveXferDone slaveDone;		/*!< Called when a slave transfer is complete */
} I2CS_XFER_T;

/**
 * @brief	Enable I2C slave interface
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Nothing
 * @note	Do not call this function until the slave interface is fully configured.
 */
STATIC INLINE void Chip_I2CS_Enable(LPC_I2C_T *pI2C)
{
	pI2C->CFG = (pI2C->CFG & I2C_CFG_MASK) | I2C_CFG_SLVEN;
}

/**
 * @brief	Disable I2C slave interface
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Nothing
 */
STATIC INLINE void Chip_I2CS_Disable(LPC_I2C_T *pI2C)
{
	pI2C->CFG = (pI2C->CFG & I2C_CFG_MASK) & ~I2C_CFG_SLVEN;
}

/**
 * @brief	Get I2C Status
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	I2C Status register value
 * @note	This function returns the value of the status register.
 */
STATIC INLINE uint32_t Chip_I2CS_GetStatus(LPC_I2C_T *pI2C)
{
	return pI2C->STAT & ~I2C_STAT_RESERVED;
}

/**
 * @brief	Clear I2C status bits (slave)
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param clrStatus : Status bit to clear, must be I2C_STAT_SLVDESEL
 * @return	Nothing
 * @note	This function clears selected status flags.
 */
STATIC INLINE void Chip_I2CS_ClearStatus(LPC_I2C_T *pI2C, uint32_t clrStatus)
{
	pI2C->STAT = clrStatus & I2C_STAT_SLVDESEL;
}

/**
 * @brief	Check if I2C slave is pending
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Returns TRUE if the slave is pending else returns FALSE
 * @note
 */
STATIC INLINE bool Chip_I2CS_IsSlavePending(LPC_I2C_T *pI2C)
{
	return (pI2C->STAT & I2C_STAT_SLVPENDING) != 0;
}

/**
 * @brief	Check if I2C slave is selected
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Returns TRUE if the slave is is selected, otherwise FALSE
 * @note
 */
STATIC INLINE bool Chip_I2CS_IsSlaveSelected(LPC_I2C_T *pI2C)
{
	return (pI2C->STAT & I2C_STAT_SLVSEL) != 0;
}

/**
 * @brief	Check if I2C slave is deselected
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Returns TRUE if the slave is is deselected, otherwise FALSE
 * @note
 */
STATIC INLINE bool Chip_I2CS_IsSlaveDeSelected(LPC_I2C_T *pI2C)
{
	return (pI2C->STAT & I2C_STAT_SLVDESEL) != 0;
}

/**
 * @brief	Get current state of the I2C slave
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	slave State Code, a value of type I2C_STAT_SLVCODE_*
 * @note	After the slave is pending this state code tells the reason
 *        for slave pending.
 */
STATIC INLINE uint32_t Chip_I2CS_GetSlaveState(LPC_I2C_T *pI2C)
{
	return (pI2C->STAT & I2C_STAT_SLVSTATE) >> 9;
}

/**
 * @brief	Returns the current slave address match index
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	slave match index, 0 - 3
 */
STATIC INLINE uint32_t Chip_I2CS_GetSlaveMatchIndex(LPC_I2C_T *pI2C)
{
	return (pI2C->STAT & I2C_STAT_SLVIDX) >> 12;
}

/**
 * @brief	Slave Continue transfer operation (ACK)
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Nothing
 * @note	This function sets the slave controller to continue transmission.
 *				This should be called only when slave is pending. The function writes a
 *				complete value to slave Control register, ORing is not advised.
 */
STATIC INLINE void Chip_I2CS_SlaveContinue(LPC_I2C_T *pI2C)
{
	pI2C->SLVCTL = I2C_SLVCTL_SLVCONTINUE;
}

/**
 * @brief	Slave NACK operation
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	Nothing
 * @note	This function sets the slave controller to NAK the master.
 */
STATIC INLINE void Chip_I2CS_SlaveNACK(LPC_I2C_T *pI2C)
{
	pI2C->SLVCTL = I2C_SLVCTL_SLVNACK;
}

/**
 * @brief	Transmit a single data byte through the I2C peripheral (slave)
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	data	: Byte to transmit
 * @return	Nothing
 * @note	This function attempts to place a byte into the I2C slave
 *			Data Register
 *
 */
STATIC INLINE void Chip_I2CS_WriteByte(LPC_I2C_T *pI2C, uint8_t data)
{
	pI2C->SLVDAT = (uint32_t) data;
}

/**
 * @brief	Read a single byte data from the I2C peripheral (slave)
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @return	A single byte of data read
 * @note	This function reads a byte from the I2C receive hold register
 *			regardless of I2C state.
 */
STATIC INLINE uint8_t Chip_I2CS_ReadByte(LPC_I2C_T *pI2C)
{
	return (uint8_t) (pI2C->SLVDAT & I2C_SLVDAT_DATAMASK);
}

/**
 * @brief	Set a I2C slave address for slave operation
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	slvNum	: Possible slave address number, between 0 - 3
 * @param	slvAddr	: Slave Address for the index (7-bits, bit 7 = 0)
 * @return	Nothing
 * @note	Setting a slave address also enables the slave address. Do
 * not 'pre-shift' the slave address.
 */
STATIC INLINE void Chip_I2CS_SetSlaveAddr(LPC_I2C_T *pI2C, uint8_t slvNum, uint8_t slvAddr)
{
	pI2C->SLVADR[slvNum] = (uint32_t) (slvAddr << 1);
}

/**
 * @brief	Return a I2C programmed slave address
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	slvNum	: Possible slave address number, between 0 - 3
 * @return	Nothing
 */
STATIC INLINE uint8_t Chip_I2CS_GetSlaveAddr(LPC_I2C_T *pI2C, uint8_t slvNum)
{
	return (pI2C->SLVADR[slvNum] >> 1) & 0x7F;
}

/**
 * @brief	Enable a I2C address
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	slvNum	: Possible slave address number, between 0 - 3
 * @return	Nothing
 */
STATIC INLINE void Chip_I2CS_EnableSlaveAddr(LPC_I2C_T *pI2C, uint8_t slvNum)
{
	pI2C->SLVADR[slvNum] = (pI2C->SLVADR[slvNum] & I2C_SLVADR_MASK) & ~I2C_SLVADR_SADISABLE;
}

/**
 * @brief	Disable a I2C address
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	slvNum	: Possible slave address number, between 0 - 3
 * @return	Nothing
 */
STATIC INLINE void Chip_I2CS_DisableSlaveAddr(LPC_I2C_T *pI2C, uint8_t slvNum)
{
	pI2C->SLVADR[slvNum] = (pI2C->SLVADR[slvNum] & I2C_SLVADR_MASK) | I2C_SLVADR_SADISABLE;
}

/**
 * @brief	Setup slave qialifier address
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	extend	: true to extend I2C slave detect address 0 range, or false to match to corresponding bits
 * @param	slvNum	: Slave address qualifier, see SLVQUAL0 register in User Manual
 * @return	Nothing
 * @note	Do not 'pre-shift' the slave address.
 */
STATIC INLINE void Chip_I2CS_SetSlaveQual0(LPC_I2C_T *pI2C, bool extend, uint8_t slvNum)
{
	slvNum = slvNum << 1;
	if (extend) {
		slvNum |= I2C_SLVQUAL_QUALMODE0;
	}

	pI2C->SLVQUAL0 = slvNum;
}

/**
 * @brief	Slave transfer state change handler
 * @param	pI2C	: Pointer to selected I2C peripheral
 * @param	xfers	: Pointer to a I2CS_MULTI_XFER_T structure see notes below
 * @return	Returns non-zero value on completion of transfer
 * @note	See @ref I2CS_XFER_T for more information on this function. When using
 * this function, the I2C_INTENSET_SLVPENDING and I2C_INTENSET_SLVDESEL interrupts
 * should be enabled and setup in the I2C interrupt handler to call this function
 * when they fire.
 */
uint32_t Chip_I2CS_XferHandler(LPC_I2C_T *pI2C, const I2CS_XFER_T *xfers);

/**
 * @}
 */

 #ifdef __cplusplus
}
#endif

#endif /* __I2CS_8XX_H_ */
