You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1637 lines
43 KiB
1637 lines
43 KiB
/* vxbFtI2c.c - I2C Controller hardware driver */
|
|
|
|
/*
|
|
*
|
|
* This program is OPEN SOURCE software: you can redistribute it and/or modify it;
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <vxWorks.h>
|
|
#include <vxBusLib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <semLib.h>
|
|
#include <vxbTimerLib.h>
|
|
#include <hwif/util/hwMemLib.h>
|
|
#include <hwif/vxbus/vxBus.h>
|
|
#include <hwif/vxbus/vxbPlbLib.h>
|
|
#include <hwif/vxbus/hwConf.h>
|
|
#include <hwif/vxbus/vxbI2cLib.h>
|
|
#include <spinLockLib.h>
|
|
|
|
#include "vxbFtI2c.h"
|
|
#include "ft2000-4.h"
|
|
|
|
/* defines */
|
|
|
|
#undef I2C_DBG_ON
|
|
#ifdef I2C_DBG_ON
|
|
#define I2C_DBG_IRQ 0x00000001
|
|
#define I2C_DBG_RW 0x00000002
|
|
#define I2C_DBG_ERR 0x00000004
|
|
#define I2C_DBG_ALL 0xffffffff
|
|
#define I2C_DBG_OFF 0x00000000
|
|
LOCAL UINT32 i2cDbgMask = I2C_DBG_ALL;
|
|
IMPORT FUNCPTR _func_logMsg;
|
|
|
|
#define I2C_DBG(mask, string, a, b, c, d, e, f) \
|
|
if ((i2cDbgMask & mask) || (mask == I2C_DBG_ALL)) \
|
|
if (_func_logMsg != NULL) \
|
|
(* _func_logMsg)(string, a, b, c, d, e, f)
|
|
#else
|
|
#define I2C_DBG(mask, string, a, b, c, d, e, f)
|
|
#endif /* I2C_DBG_ON */
|
|
|
|
#undef CSR_READ_4
|
|
#define CSR_READ_4(pCtrl, addr) \
|
|
vxbRead32 (FT_I2C_HANDLE (pCtrl), \
|
|
(UINT32 *) ((char *) FT_I2C_BAR (pCtrl) + addr))
|
|
|
|
#undef CSR_WRITE_4
|
|
#define CSR_WRITE_4(pCtrl, addr, data) \
|
|
vxbWrite32 (FT_I2C_HANDLE (pCtrl), \
|
|
(UINT32 *) ((char *) FT_I2C_BAR (pCtrl) + addr), data)
|
|
|
|
/* 8bit data + 1bit ack */
|
|
|
|
#define I2C_BYTE_LEN 9
|
|
#define I2C_BYTE_EXTRA I2C_BYTE_LEN
|
|
|
|
#ifdef I2C_DBG_ON
|
|
|
|
/* may print lots of information when I2C_DBG_ON is defined */
|
|
|
|
#define I2C_TOTAL_EXTRA_TICK(len) (2 * len)
|
|
#else
|
|
#define I2C_TOTAL_EXTRA_TICK(len) 2
|
|
#endif /* I2C_DBG_ON */
|
|
/* forward declarations */
|
|
|
|
void vxbFtI2cRegister (void);
|
|
LOCAL void vxbFtI2cInstInit (VXB_DEVICE_ID);
|
|
LOCAL void vxbFtI2cInstInit2 (VXB_DEVICE_ID);
|
|
LOCAL VXB_I2C_BUS_CTRL * vxbFtI2cCtrlGet (VXB_DEVICE_ID pDev);
|
|
LOCAL void vxbFtI2cInstConnect (VXB_DEVICE_ID pDev);
|
|
LOCAL STATUS vxbFtI2cInstUnlink (VXB_DEVICE_ID pDev, void * unused);
|
|
LOCAL STATUS vxbFtI2cIsr (VXB_DEVICE_ID pDev);
|
|
STATUS vxbFtI2cShow (VXB_DEVICE_ID pDevice, int verbose);
|
|
LOCAL void vxbFtI2cReadProcess (I2C_DRV_CTRL * pDrvCtrl);
|
|
LOCAL void vxbFtI2cWriteProcess (I2C_DRV_CTRL * pDrvCtrl);
|
|
LOCAL STATUS vxbFtI2cTransfer (VXB_DEVICE_ID pDev, I2C_MSG * msgs, int num);
|
|
LOCAL STATUS vxbFtI2cRead(VXB_DEVICE_ID, UINT8, UINT32, UINT8 *, UINT32);
|
|
LOCAL STATUS vxbFtI2cWrite(VXB_DEVICE_ID, UINT8, UINT32, UINT8 *, UINT32);
|
|
|
|
/* structure to store the driver functions for vxBus */
|
|
|
|
LOCAL struct drvBusFuncs ftI2cDrvFuncs =
|
|
{
|
|
vxbFtI2cInstInit, /* devInstanceInit */
|
|
vxbFtI2cInstInit2, /* devInstanceInit2 */
|
|
vxbFtI2cInstConnect /* devConnect */
|
|
};
|
|
|
|
LOCAL device_method_t ftI2cDrv_methods[] =
|
|
{
|
|
DEVMETHOD (vxbI2cControlGet, vxbFtI2cCtrlGet),
|
|
DEVMETHOD (busDevShow, vxbFtI2cShow),
|
|
DEVMETHOD (vxbDrvUnlink, vxbFtI2cInstUnlink),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
/* I2C VxBus registration info */
|
|
|
|
LOCAL struct vxbDevRegInfo ftI2cDrvRegistration =
|
|
{
|
|
NULL, /* pNext */
|
|
VXB_DEVID_DEVICE, /* devID */
|
|
VXB_BUSID_PLB, /* busID = PLB */
|
|
VXB_VER_4_0_0, /* busVer */
|
|
FT_I2C_DRIVER_NAME, /* drvName */
|
|
&ftI2cDrvFuncs, /* pDrvBusFuncs */
|
|
&ftI2cDrv_methods[0], /* pMethods */
|
|
NULL /* devProbe */
|
|
};
|
|
|
|
LOCAL struct vxbI2cBusCtrl ftI2cCtrl = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
vxbFtI2cRead,
|
|
vxbFtI2cWrite,
|
|
vxbFtI2cTransfer,
|
|
};
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* vxbFtI2cInit - initialize the I2C module
|
|
*
|
|
* This routine initializes the I2C module
|
|
*
|
|
* RETURNS: OK, or ERROR if fail
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cInit
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
|
|
UINT16 sclHi = 0;
|
|
UINT16 sclLo = 0;
|
|
UINT32 val = 0;
|
|
UINT32 timeout = 0;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = pDev->pDrvCtrl;
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_ENABLE, 0x0);
|
|
while (timeout++ < I2C_TIMEOUT)
|
|
{
|
|
if (0 ==
|
|
(IC_ENABLE_STATUS_IC_EN & CSR_READ_4 (pDrvCtrl, I2C_ENABLE_STATUS)))
|
|
{
|
|
break;
|
|
}
|
|
vxbUsDelay (I2C_DELAY);
|
|
|
|
if (timeout == I2C_TIMEOUT)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR,
|
|
"vxbFtI2cInit disable I2C_ENABLE timeout\n",1,2,3,4,5,6);
|
|
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_CON, 0x0);
|
|
|
|
/* update fifo size information */
|
|
|
|
val = CSR_READ_4 (pDrvCtrl, I2C_COMP_PARAM_1);
|
|
pDrvCtrl->txFifoSize = ((val >> I2C_COMP_PARAM_1_TX_SHIFT) &
|
|
I2C_COMP_PARAM_1_TX_MASK) + 1;
|
|
pDrvCtrl->rxFifoSize = ((val >> I2C_COMP_PARAM_1_RX_SHIFT) &
|
|
I2C_COMP_PARAM_1_RX_MASK) + 1;
|
|
|
|
/* setting SCL clock */
|
|
|
|
val = pDrvCtrl->clkFrequency / pDrvCtrl->busSpeed;
|
|
|
|
sclHi = (UINT16) (val / 2);
|
|
|
|
/* compensate side effects of integer calculations */
|
|
|
|
sclLo = (UINT16) (val - sclHi);
|
|
|
|
if (sclHi < 29)
|
|
{
|
|
sclLo = sclLo - (29-sclHi);
|
|
sclHi = 29;
|
|
}
|
|
|
|
sclLo = sclLo - 8;
|
|
sclHi = sclHi - 15;
|
|
|
|
if (pDrvCtrl->busSpeed > I2C_STANDARD_SPEED)
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_FS_SCL_LCNT, sclLo);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_FS_SCL_HCNT, sclHi);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_CON, I2C_CTR_DEFAULT | I2C_CON_MS_FS);
|
|
}
|
|
else
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_SS_SCL_LCNT, sclLo);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_SS_SCL_HCNT, sclHi);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_CON, I2C_CTR_DEFAULT | I2C_CON_MS_SS);
|
|
}
|
|
|
|
/* clear Interrupt Status Register */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_STAT, CSR_READ_4 (pDrvCtrl, I2C_INTR_STAT));
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_INTR);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RX_UNDER);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RX_OVER);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_TX_OVER);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RD_REQ);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_TX_ABRT);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RX_DONE);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_ACTIVITY);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_STOP_DET);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_START_DET);
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_GEN_CALL);
|
|
|
|
/* disable all IRQs */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, I2C_IRQ_NONE);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* vxbFtI2cDrvRegister - register FT I2c driver
|
|
*
|
|
* This routine registers the Altera I2c driver with the vxBus subsystem.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
void vxbFtI2cRegister (void)
|
|
{
|
|
|
|
/* call the vxBus routine to register the I2c driver */
|
|
|
|
vxbDevRegister (&ftI2cDrvRegistration);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cInstInit - first level initialization routine of I2c device
|
|
*
|
|
* This routine performs the first level initialization of the I2c device.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cInstInit
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
struct hcfDevice * pHcf;
|
|
UINT32 muxRegBase, muxRegBaseEx;
|
|
UINT32 muxBitValue, muxBitValueEx;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
/* create controller driver context structure for core */
|
|
|
|
pDrvCtrl = (I2C_DRV_CTRL *) hwMemAlloc (sizeof (I2C_DRV_CTRL));
|
|
|
|
if (pDrvCtrl == NULL)
|
|
return;
|
|
|
|
/* save instance ID */
|
|
|
|
pDev->pDrvCtrl = pDrvCtrl;
|
|
pDrvCtrl->i2cDev = pDev;
|
|
|
|
/* override intCtlr device reg base from device scratch registers 0 */
|
|
|
|
vxbRegMap (pDev, 0, &pDrvCtrl->i2cHandle);
|
|
|
|
pHcf = (struct hcfDevice *) hcfDeviceGet (pDev);
|
|
if (pHcf == NULL)
|
|
return;
|
|
|
|
/*
|
|
* resourceDesc {
|
|
* The clkFreq resource specifies clock frequency of I2C function clock. }
|
|
*/
|
|
|
|
if (devResourceGet (pHcf, "clkFreq", HCF_RES_INT,
|
|
(void *) &pDrvCtrl->clkFrequency) != OK)
|
|
{
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree ((char *) pDrvCtrl);
|
|
#endif /* _VXBUS_BASIC_HWMEMLIB */
|
|
pDev->pDrvCtrl = NULL;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* resourceDesc {
|
|
* The busSpeed resource specifies the BUS Speed. }
|
|
*/
|
|
|
|
if (devResourceGet (pHcf, "busSpeed", HCF_RES_INT,
|
|
(void *) &pDrvCtrl->busSpeed) != OK)
|
|
{
|
|
pDrvCtrl->busSpeed = I2C_FAST_SPEED;
|
|
}
|
|
pDrvCtrl->defBusSpeed = pDrvCtrl->busSpeed;
|
|
|
|
/* choose I2C function pin for multi-function pad pin. D2000,FT2000A4... */
|
|
/* I2C Controller 1,2,3 */
|
|
|
|
if(pDev->unitNumber > 0)
|
|
{
|
|
if (devResourceGet (pHcf, "pinDemuxReg", HCF_RES_INT,
|
|
(void *) &muxRegBase) == OK)
|
|
{
|
|
|
|
if (devResourceGet (pHcf, "pinDemuxBit", HCF_RES_INT,
|
|
(void *) &muxBitValue) == OK)
|
|
{
|
|
muxBitValue |= readl(muxRegBase);
|
|
writel(muxBitValue, muxRegBase);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* only I2C Controller 3 */
|
|
|
|
if(3 == pDev->unitNumber)
|
|
{
|
|
if (devResourceGet (pHcf, "pinDemuxRegEx", HCF_RES_INT,
|
|
(void *) &muxRegBaseEx) == OK)
|
|
{
|
|
|
|
if (devResourceGet (pHcf, "pinDemuxBitEx", HCF_RES_INT,
|
|
(void *) &muxBitValueEx) == OK)
|
|
{
|
|
muxBitValueEx |= readl(muxRegBaseEx);
|
|
writel(muxBitValueEx, muxRegBaseEx);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* reset I2C Controller */
|
|
|
|
vxbFtI2cInit (pDev);
|
|
|
|
/* announce that there's an I2C bus */
|
|
|
|
(void) vxbBusAnnounce (pDev, VXB_BUSID_I2C);
|
|
|
|
i2cBusAnnounceDevices (pDev);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cInstInit2 - second level initialization routine of I2c modules
|
|
*
|
|
* This routine performs the second level initialization of the I2c modules.
|
|
*
|
|
* This routine is called later during system initialization. OS features
|
|
* such as memory allocation are available at this time.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cInstInit2
|
|
(
|
|
VXB_DEVICE_ID pDev /* The device */
|
|
)
|
|
{
|
|
|
|
I2C_DRV_CTRL * pDrvCtrl = NULL;
|
|
struct hcfDevice * pHcf;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = (I2C_DRV_CTRL *) pDev->pDrvCtrl;
|
|
|
|
pHcf = (struct hcfDevice *) hcfDeviceGet (pDev);
|
|
if (pHcf == NULL)
|
|
return;
|
|
|
|
/*
|
|
* resourceDesc {
|
|
* The polling resource specifies whether
|
|
* the driver uses polling mode or not.
|
|
* If this property is not explicitly
|
|
* specified, the driver uses interrupt
|
|
* by default. }
|
|
*/
|
|
|
|
if (devResourceGet (pHcf, "polling", HCF_RES_INT,
|
|
(void *) &pDrvCtrl->polling) != OK)
|
|
{
|
|
pDrvCtrl->polling = TRUE;
|
|
}
|
|
|
|
pDrvCtrl->i2cDevSem = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE |
|
|
SEM_INVERSION_SAFE);
|
|
if (pDrvCtrl->i2cDevSem == SEM_ID_NULL)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cInstInit2:i2cDevSem create failed\n",1,2,3,4,5,6);
|
|
return;
|
|
}
|
|
|
|
pDrvCtrl->i2cDataSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
|
|
if (pDrvCtrl->i2cDataSem == SEM_ID_NULL)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cInstInit2:i2cDataSem Ceate failed\n",1,2,3,4,5,6);
|
|
return;
|
|
}
|
|
|
|
SPIN_LOCK_ISR_INIT (&pDrvCtrl->lock, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cIsr - interrupt service routine
|
|
*
|
|
* This routine handles interrupts of I2C.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cIsr
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
UINT32 status = 0;
|
|
UINT32 enabled = 0;
|
|
UINT32 giveSem = FALSE;
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
|
|
pDrvCtrl = pDev->pDrvCtrl;
|
|
|
|
I2C_DBG (I2C_DBG_IRQ, "vxbFtI2cIsr enter \n ",1,2,3,4,5,6);
|
|
|
|
/* check if i2c controller is enabled */
|
|
|
|
enabled = CSR_READ_4 (pDrvCtrl, I2C_ENABLE);
|
|
|
|
if (enabled == 0)
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "Fatal error statue 0x%x \n ",
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_STAT),2,3,4,5,6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/* check there is no interrupt */
|
|
|
|
status = CSR_READ_4 (pDrvCtrl, I2C_RAW_INTR_STAT);
|
|
|
|
if (0 == (status & ((UINT32) ~I2C_IRQ_ACTIVITY)))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "None IRQ\n",1,2,3,4,5,6);
|
|
return ERROR;
|
|
}
|
|
|
|
/* read interrupt status */
|
|
|
|
status = CSR_READ_4 (pDrvCtrl, I2C_INTR_STAT);
|
|
|
|
if (I2C_IRQ_STOP_DET == (status & I2C_IRQ_STOP_DET))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_STOP_DET\n",1,2,3,4,5,6);
|
|
|
|
/* clear interrupt bit */
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_STOP_DET);
|
|
giveSem = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (I2C_INTR_START_DET == (status & I2C_INTR_START_DET))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_START_DET\n",1,2,3,4,5,6);
|
|
|
|
/* clear interrupt bit */
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_START_DET);
|
|
}
|
|
|
|
if (I2C_IRQ_RX_UNDER == (status & I2C_IRQ_RX_UNDER))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_RX_UNDER\n",1,2,3,4,5,6);
|
|
|
|
/* clear interrupt bit */
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RX_UNDER);
|
|
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_RX_UNDER;
|
|
giveSem = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (I2C_IRQ_RX_OVER == (status & I2C_IRQ_RX_OVER))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_RX_OVER\n",1,2,3,4,5,6);
|
|
|
|
/* clear interrupt bit */
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_RX_OVER);
|
|
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_RX_OVER;
|
|
giveSem = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (I2C_IRQ_TX_ABRT == (status & I2C_IRQ_TX_ABRT))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_TX_ABRT Source 0x%x \n",
|
|
CSR_READ_4 (pDrvCtrl, I2C_TX_ABRT_SOURCE),2,3,4,5,6);
|
|
|
|
/* clear interrupt bit */
|
|
|
|
(void) CSR_READ_4 (pDrvCtrl, I2C_CLR_TX_ABRT);
|
|
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_TX_ABRT;
|
|
giveSem = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
/* Reading Process */
|
|
|
|
if ((I2C_IRQ_RX_FULL == (status & I2C_IRQ_RX_FULL))
|
|
)
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_RX_FULL\n",1,2,3,4,5,6);
|
|
|
|
/* Mask interrupt */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) &
|
|
(UINT32) ~I2C_IRQ_RX_FULL);
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
|
|
isrDeferJobAdd (pDrvCtrl->trxQueueId, &pDrvCtrl->rxIsrDef);
|
|
}
|
|
|
|
/* Writing Process */
|
|
|
|
if (I2C_IRQ_TX_EMPTY == (status & I2C_IRQ_TX_EMPTY))
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_TX_EMPTY\n",1,2,3,4,5,6);
|
|
|
|
/* Mask interrupt */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) &
|
|
(UINT32) ~I2C_IRQ_TX_EMPTY);
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
|
|
isrDeferJobAdd (pDrvCtrl->trxQueueId, &pDrvCtrl->txIsrDef);
|
|
}
|
|
|
|
done:
|
|
|
|
if (giveSem)
|
|
{
|
|
if (semGive (pDrvCtrl->i2cDataSem) == ERROR)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "semGive error\n",1,2,3,4,5,6);
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cCtrlGet - get the callback functions
|
|
*
|
|
* This routine installs the callback functions to I2C libary.
|
|
*
|
|
* RETURNS: the address of callback functions
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL VXB_I2C_BUS_CTRL * vxbFtI2cCtrlGet
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
return (&ftI2cCtrl);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cInstConnect - third level initialization
|
|
*
|
|
* This routine performs the third level initialization of the i2c controller
|
|
* driver. Nothing to be done in this routine.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO : none
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cInstConnect
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
|
|
I2C_DRV_CTRL * pDrvCtrl = NULL;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = (I2C_DRV_CTRL *) pDev->pDrvCtrl;
|
|
|
|
if (pDrvCtrl->polling == FALSE)
|
|
{
|
|
|
|
pDrvCtrl->txIsrDef.func = (void (*)(void *)) vxbFtI2cWriteProcess;
|
|
pDrvCtrl->txIsrDef.pData = (void *) pDrvCtrl;
|
|
pDrvCtrl->rxIsrDef.func = (void (*)(void *)) vxbFtI2cReadProcess;
|
|
pDrvCtrl->rxIsrDef.pData = (void *) pDrvCtrl;
|
|
|
|
|
|
pDrvCtrl->trxQueueId = isrDeferQueueGet (NULL, 0, 0, 0);
|
|
|
|
if(pDrvCtrl->trxQueueId == NULL)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cInstConnect:DeferQueue Ceate failed\n",1,2,3,4,5,6);
|
|
return;
|
|
}
|
|
|
|
/* connect and enable interrupt */
|
|
|
|
vxbIntConnect (pDev, 0, (VOIDFUNCPTR) vxbFtI2cIsr, pDev);
|
|
vxbIntEnable (pDev, 0, (VOIDFUNCPTR) vxbFtI2cIsr, pDev);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cInstUnlink - VxBus unlink handler
|
|
*
|
|
* This function shuts down a I2C controller instance in response to an
|
|
* an unlink event from VxBus. This may occur if our VxBus instance has
|
|
* been terminated, or if the FslI2c driver has been unloaded.
|
|
*
|
|
* RETURNS: OK if device was successfully destroyed, otherwise ERROR
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cInstUnlink
|
|
(
|
|
VXB_DEVICE_ID pDev,
|
|
void * unused
|
|
)
|
|
{
|
|
I2C_DRV_CTRL * pDrvCtrl = NULL;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = (I2C_DRV_CTRL *) pDev->pDrvCtrl;
|
|
|
|
if (semTake (pDrvCtrl->i2cDevSem, WAIT_FOREVER) == OK)
|
|
{
|
|
if (semDelete (pDrvCtrl->i2cDevSem) != OK)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR,
|
|
"vxbFtI2cInstUnlink:i2cDevSem delete failed\n",
|
|
0, 0, 0, 0, 0, 0);
|
|
return (ERROR);
|
|
}
|
|
}
|
|
|
|
if (semTake (pDrvCtrl->i2cDataSem, WAIT_FOREVER) == OK)
|
|
{
|
|
if (semDelete (pDrvCtrl->i2cDataSem) != OK)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR,
|
|
"vxbFtI2cInstUnlink:i2cDevSem delete failed\n",
|
|
0, 0, 0, 0, 0, 0);
|
|
return (ERROR);
|
|
}
|
|
}
|
|
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree ((char *) pDrvCtrl);
|
|
#endif /* _VXBUS_BASIC_HWMEMLIB */
|
|
pDev->pDrvCtrl = NULL;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cPoll - poll service function
|
|
*
|
|
* This function provides poll service of I2C controller.
|
|
*
|
|
* RETURNS: OK, or ERROR if failed.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cPoll
|
|
(
|
|
I2C_DRV_CTRL * pDrvCtrl
|
|
)
|
|
{
|
|
UINT32 status = 0;
|
|
UINT64 timeout = 0;
|
|
|
|
pDrvCtrl->pollIrqMask = (I2C_IRQ_DEFAULT_MASK & ~I2C_IRQ_STOP_DET);
|
|
|
|
/* calculate time out to us */
|
|
|
|
timeout = pDrvCtrl->timeout;
|
|
I2C_DBG (I2C_DBG_RW, "vxbFtI2cPoll timeout tick is %llu\n", timeout,2,3,4,5,6);
|
|
|
|
while (timeout-- > 0)
|
|
{
|
|
status = CSR_READ_4 (pDrvCtrl, I2C_RAW_INTR_STAT);
|
|
status &= pDrvCtrl->pollIrqMask;
|
|
|
|
if (I2C_IRQ_TX_ABRT == (status & I2C_IRQ_TX_ABRT))
|
|
{
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_TX_ABRT;
|
|
I2C_DBG (I2C_DBG_IRQ, "I2C_IRQ_TX_ABRT Source 0x%x \n",
|
|
CSR_READ_4 (pDrvCtrl, I2C_TX_ABRT_SOURCE),2,3,4,5,6);
|
|
return ERROR;
|
|
}
|
|
if (I2C_IRQ_RX_UNDER == (status & I2C_IRQ_RX_UNDER))
|
|
{
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_RX_UNDER;
|
|
return ERROR;
|
|
}
|
|
if (I2C_IRQ_RX_OVER == (status & I2C_IRQ_RX_OVER))
|
|
{
|
|
pDrvCtrl->errorStatus = I2C_ERR_STATUS_RX_OVER;
|
|
return ERROR;
|
|
}
|
|
|
|
if (I2C_IRQ_STOP_DET == (status & I2C_IRQ_STOP_DET))
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
/* Reading process */
|
|
|
|
if (I2C_IRQ_RX_FULL == (status & I2C_IRQ_RX_FULL))
|
|
{
|
|
vxbFtI2cReadProcess (pDrvCtrl);
|
|
}
|
|
|
|
/* Writing process */
|
|
|
|
if (I2C_IRQ_TX_EMPTY == (status & I2C_IRQ_TX_EMPTY))
|
|
{
|
|
vxbFtI2cWriteProcess (pDrvCtrl);
|
|
}
|
|
|
|
vxbUsDelay (1);
|
|
}
|
|
|
|
/* time out */
|
|
|
|
I2C_DBG (I2C_DBG_RW, "time out\n",1,2,3,4,5,6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cWaitBusFree - wait for I2C bus free
|
|
*
|
|
* This function waits for I2C bus free
|
|
*
|
|
* RETURNS: OK, or ERROR if timeout.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cWaitBusFree
|
|
(
|
|
I2C_DRV_CTRL * pDrvCtrl
|
|
)
|
|
{
|
|
#ifdef I2C_DBG_ON
|
|
UINT32 status;
|
|
#endif /* I2C_DBG_ON */
|
|
|
|
UINT32 timeout = 0;
|
|
|
|
/* Wait until Activity Status Bit in I2C_STATUS is IDLE = 0 */
|
|
|
|
#ifdef I2C_DBG_ON
|
|
while (((status =
|
|
CSR_READ_4 (pDrvCtrl, I2C_STATUS)) & I2C_STATUS_ACTIVITY) &&
|
|
#else
|
|
while ((CSR_READ_4 (pDrvCtrl, I2C_STATUS) & I2C_STATUS_ACTIVITY) &&
|
|
#endif /* I2C_DBG_ON */
|
|
(++timeout < I2C_TIMEOUT))
|
|
vxbUsDelay (I2C_DELAY);
|
|
|
|
if (timeout == I2C_TIMEOUT)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR,
|
|
"vxbFtI2cWaitBusFree:timeout status 0x%x\n", status,2,3,4,5,6);
|
|
|
|
return (ERROR);
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cReadProcess - read process
|
|
*
|
|
* This function is the read process of I2C controller.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cReadProcess
|
|
(
|
|
I2C_DRV_CTRL * pDrvCtrl
|
|
)
|
|
{
|
|
I2C_MSG * pMsg;
|
|
UINT8 * buf;
|
|
UINT32 bufLen = 0;
|
|
UINT32 rxfifoAvLen;
|
|
|
|
for (; pDrvCtrl->msgsRxIdx < pDrvCtrl->num; pDrvCtrl->msgsRxIdx++)
|
|
{
|
|
pMsg = &pDrvCtrl->msgs[pDrvCtrl->msgsRxIdx];
|
|
|
|
if (pDrvCtrl->errorStatus != OK)
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "vxbFtI2cReadProcess errorStatus detected\n",1,2,3,4,5,6);
|
|
return;
|
|
}
|
|
|
|
/* if this msg is not I2C_M_RD, continue next msg */
|
|
|
|
if (0 == (pMsg->flags & I2C_M_RD))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pDrvCtrl->msgRxLen == 0)
|
|
{
|
|
bufLen = pMsg->len;
|
|
buf = pMsg->buf;
|
|
}
|
|
else
|
|
{
|
|
bufLen = pDrvCtrl->msgRxLen;
|
|
buf = pDrvCtrl->msgRxBuf;
|
|
}
|
|
|
|
rxfifoAvLen = CSR_READ_4 (pDrvCtrl, I2C_RXFLR);
|
|
I2C_DBG (I2C_DBG_IRQ, "vxbFtI2cReadProcess msgs[%d] recv len %d\n",
|
|
pDrvCtrl->msgsRxIdx, rxfifoAvLen,3,4,5,6);
|
|
|
|
for (; ((bufLen > 0) && (rxfifoAvLen > 0)); bufLen--, rxfifoAvLen--)
|
|
{
|
|
*buf++ = CSR_READ_4 (pDrvCtrl, I2C_DATA_CMD) & 0xff;
|
|
if (pDrvCtrl->fifoRdLvl > 0)
|
|
{
|
|
pDrvCtrl->fifoRdLvl--;
|
|
}
|
|
}
|
|
|
|
pDrvCtrl->msgRxLen = bufLen;
|
|
pDrvCtrl->msgRxBuf = buf;
|
|
|
|
I2C_DBG (I2C_DBG_IRQ, "vxbFtI2cReadProcess bufLen %d rxfifoAvLen %d\n",
|
|
bufLen,
|
|
rxfifoAvLen,3,4,5,6);
|
|
|
|
if (bufLen > 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pDrvCtrl->polling)
|
|
{
|
|
if ((pDrvCtrl->msgsRxIdx == pDrvCtrl->num) && (bufLen == 0))
|
|
{
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
|
|
/* after read all the msgs, disable rx full interrupt */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) &
|
|
(UINT32) ~I2C_IRQ_RX_FULL);
|
|
|
|
if (0 != (pDrvCtrl->msgs[pDrvCtrl->msgsRxIdx-1].flags & I2C_M_RD))
|
|
{
|
|
/* If the last msg is I2C_M_RD, enable stop interrupt. */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) |
|
|
I2C_IRQ_STOP_DET);
|
|
}
|
|
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
}
|
|
else
|
|
{
|
|
/* If it is not the last msg, enable rx full interrupt again. */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) |
|
|
I2C_IRQ_RX_FULL);
|
|
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* In polling mode, only need set stop mask if it is the
|
|
* last msg and the last msg is I2C_M_RD.
|
|
*/
|
|
|
|
if (((pDrvCtrl->msgsRxIdx == pDrvCtrl->num) && (bufLen == 0)) &&
|
|
(0 != (pDrvCtrl->msgs[pDrvCtrl->msgsRxIdx-1].flags & I2C_M_RD)))
|
|
{
|
|
pDrvCtrl->pollIrqMask = (pDrvCtrl->pollIrqMask &
|
|
(UINT32) ~I2C_IRQ_RX_FULL) |
|
|
I2C_IRQ_STOP_DET;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cWriteProcess - write process
|
|
*
|
|
* This function is the write process of I2C controller. Both datas which will
|
|
* be transmited or received should be processed in this function.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cWriteProcess
|
|
(
|
|
I2C_DRV_CTRL * pDrvCtrl
|
|
)
|
|
{
|
|
UINT8 * buf;
|
|
UINT32 bufLen;
|
|
UINT32 txFifoAvLen;
|
|
UINT32 rxFifoAvLen;
|
|
UINT32 cmd = 0;
|
|
UINT32 readLen = 0;
|
|
I2C_MSG * pMsg;
|
|
BOOL isRead;
|
|
BOOL restartSign = FALSE;
|
|
|
|
for (; pDrvCtrl->msgsTxIdx < pDrvCtrl->num; pDrvCtrl->msgsTxIdx++)
|
|
{
|
|
pMsg = &pDrvCtrl->msgs[pDrvCtrl->msgsTxIdx];
|
|
|
|
readLen = 0;
|
|
|
|
if (pDrvCtrl->errorStatus != OK)
|
|
{
|
|
I2C_DBG (I2C_DBG_IRQ, "vxbFtI2cWriteProcess errorStatus detected\n",1,2,3,4,5,6);
|
|
return;
|
|
}
|
|
|
|
if (0 == (pMsg->flags & I2C_M_RD))
|
|
{
|
|
isRead = FALSE;
|
|
}
|
|
else
|
|
{
|
|
isRead = TRUE;
|
|
}
|
|
|
|
if (pDrvCtrl->msgTxLen == 0)
|
|
{
|
|
bufLen = pMsg->len;
|
|
buf = pMsg->buf;
|
|
if (pDrvCtrl->msgsTxIdx > 0)
|
|
restartSign = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bufLen = pDrvCtrl->msgTxLen;
|
|
buf = pDrvCtrl->msgTxBuf;
|
|
}
|
|
|
|
txFifoAvLen = pDrvCtrl->txFifoSize - CSR_READ_4 (pDrvCtrl, I2C_TXFLR);
|
|
rxFifoAvLen = pDrvCtrl->rxFifoSize - CSR_READ_4 (pDrvCtrl, I2C_RXFLR);
|
|
|
|
I2C_DBG (I2C_DBG_IRQ,
|
|
"vxbFtI2cWriteProcess msgs[%d] send %s len %d txavl %d rxavl %d\n",
|
|
pDrvCtrl->msgsTxIdx,
|
|
(isRead) ? "read" : "write",
|
|
bufLen,
|
|
txFifoAvLen,
|
|
rxFifoAvLen,6);
|
|
|
|
for (;
|
|
((bufLen > 0) && (txFifoAvLen > 0) &&
|
|
((pDrvCtrl->rxFifoSize - CSR_READ_4 (pDrvCtrl, I2C_RXFLR)) > 0));
|
|
bufLen--, txFifoAvLen--, rxFifoAvLen--
|
|
)
|
|
{
|
|
cmd = 0;
|
|
if (restartSign)
|
|
{
|
|
/* need set restart at the first data of new msg */
|
|
|
|
cmd |= I2C_DATA_CMD_RESTART;
|
|
restartSign = FALSE;
|
|
I2C_DBG (I2C_DBG_IRQ, "restart\n",1,2,3,4,5,6);
|
|
}
|
|
if ((pDrvCtrl->msgsTxIdx == (pDrvCtrl->num - 1)) &&
|
|
(bufLen == 1))
|
|
{
|
|
/* need set stop at the last data of the last msg */
|
|
|
|
cmd |= I2C_DATA_CMD_STOP;
|
|
I2C_DBG (I2C_DBG_IRQ, "stop\n",1,2,3,4,5,6);
|
|
}
|
|
if (FALSE == isRead)
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_DATA_CMD,
|
|
*buf | cmd | I2C_DATA_CMD_WR);
|
|
buf++;
|
|
}
|
|
else
|
|
{
|
|
if (pDrvCtrl->fifoRdLvl >= pDrvCtrl->rxFifoSize)
|
|
{
|
|
break;
|
|
}
|
|
pDrvCtrl->fifoRdLvl++;
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_DATA_CMD, cmd | I2C_DATA_CMD_RD);
|
|
readLen++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If write read decriptor, set RX_TL according to real write number,
|
|
* this can reduce rx full interrupt number.
|
|
*/
|
|
|
|
if (isRead)
|
|
{
|
|
if (readLen > 0)
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_RX_TL, readLen - 1);
|
|
}
|
|
else
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_RX_TL, 0);
|
|
}
|
|
}
|
|
|
|
pDrvCtrl->msgTxLen = bufLen;
|
|
pDrvCtrl->msgTxBuf = buf;
|
|
|
|
I2C_DBG (I2C_DBG_IRQ,
|
|
"vxbFtI2cWriteProcess bufLen %d txFifoAvLen %d rxFifoAvLen %d\n",
|
|
bufLen,
|
|
txFifoAvLen,
|
|
rxFifoAvLen,4,5,6);
|
|
|
|
if (bufLen > 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pDrvCtrl->polling)
|
|
{
|
|
if ((pDrvCtrl->msgsTxIdx == pDrvCtrl->num) && (bufLen == 0))
|
|
{
|
|
/*
|
|
* After write all the rx descriptors, set I2C_RX_TL to zero
|
|
* to ensure receive all the left rx packets.
|
|
*/
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_RX_TL, 0);
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
|
|
/* after write all the msgs, disable tx empty interrupt */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) &
|
|
(UINT32) ~I2C_IRQ_TX_EMPTY);
|
|
|
|
if (0 == (pDrvCtrl->msgs[pDrvCtrl->msgsTxIdx - 1].flags & I2C_M_RD))
|
|
{
|
|
/* If the last msg is I2C_M_WR, enable stop interrupt. */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) |
|
|
I2C_IRQ_STOP_DET);
|
|
}
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
}
|
|
else
|
|
{
|
|
/* If it is not the last msg, enable tx empty interrupt again. */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pDrvCtrl->lock);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK,
|
|
CSR_READ_4 (pDrvCtrl, I2C_INTR_MASK) |
|
|
I2C_IRQ_TX_EMPTY);
|
|
SPIN_LOCK_ISR_GIVE (&pDrvCtrl->lock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* In polling mode, only need to set stop mask if it is the
|
|
* last msg and the last msg is I2C_M_WR, also set I2C_RX_TL zero
|
|
* to ensure receive all the left rx packets.
|
|
*/
|
|
|
|
if ((pDrvCtrl->msgsTxIdx == pDrvCtrl->num) && (bufLen == 0))
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_RX_TL, 0);
|
|
if (0 == (pDrvCtrl->msgs[pDrvCtrl->msgsTxIdx-1].flags & I2C_M_RD))
|
|
pDrvCtrl->pollIrqMask = (pDrvCtrl->pollIrqMask &
|
|
(UINT32) ~I2C_IRQ_TX_EMPTY) |
|
|
I2C_IRQ_STOP_DET;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cMasterInit - initialize the I2C master module
|
|
*
|
|
* This function initializes the I2C master module, control data and sets some
|
|
* initial values.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL void vxbFtI2cMasterInit
|
|
(
|
|
I2C_DRV_CTRL * pDrvCtrl,
|
|
I2C_MSG * msgs,
|
|
int num
|
|
)
|
|
{
|
|
UINT32 i;
|
|
UINT32 len = 0;
|
|
UINT64 timeout;
|
|
|
|
/* disable I2C Controller */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_ENABLE, 0x0);
|
|
|
|
/* set target address */
|
|
|
|
if (msgs->flags & I2C_M_TEN)
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_TAR, msgs->addr | I2C_TAR_ADR_10BIT);
|
|
else
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_TAR, msgs->addr);
|
|
|
|
/* it's the combined transfer, set restart enable */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_CON,
|
|
CSR_READ_4 (pDrvCtrl, I2C_CON) | I2C_CON_RESTART_EN);
|
|
|
|
/* disable interrupt */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, I2C_IRQ_NONE);
|
|
|
|
/* set interrupt trigger level */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_TX_TL, pDrvCtrl->txFifoSize/2);
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_RX_TL, 0);
|
|
|
|
/* enable I2C Controller */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_ENABLE, 0x1);
|
|
|
|
/* init tranfer control data */
|
|
|
|
pDrvCtrl->msgs = msgs;
|
|
pDrvCtrl->num = (UINT32) num;
|
|
pDrvCtrl->msgsRxIdx = 0;
|
|
pDrvCtrl->msgRxLen = 0;
|
|
pDrvCtrl->msgRxBuf = NULL;
|
|
pDrvCtrl->fifoRdLvl = 0;
|
|
pDrvCtrl->msgsTxIdx = 0;
|
|
pDrvCtrl->msgTxLen = 0;
|
|
pDrvCtrl->msgTxBuf = NULL;
|
|
pDrvCtrl->errorStatus = OK;
|
|
|
|
/* calculate total wait time */
|
|
|
|
for (i = 0; i < (UINT32) num; i++)
|
|
{
|
|
len += msgs[i].len;
|
|
I2C_DBG (I2C_DBG_RW, "msg [%d]\n", i,2,3,4,5,6);
|
|
}
|
|
pDrvCtrl->totalLen = len;
|
|
|
|
timeout = (UINT64) I2C_S_TO_US * (I2C_BYTE_LEN + I2C_BYTE_EXTRA) * len;
|
|
timeout /= pDrvCtrl->busSpeed;
|
|
pDrvCtrl->timeout = timeout;
|
|
|
|
I2C_DBG (I2C_DBG_RW,
|
|
"vxbFtI2cMasterInit: byte lenght %d, timeout is %llu (us)\n",
|
|
len, timeout,3,4,5,6);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cTransfer - VxBus I2C device generic write support function
|
|
*
|
|
* This function receive msg from upper layer and perform I2C transfer.
|
|
*
|
|
* RETURNS: OK, or ERROR if failed.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS vxbFtI2cTransfer
|
|
(
|
|
VXB_DEVICE_ID pDev,
|
|
I2C_MSG * msgs,
|
|
int num
|
|
)
|
|
{
|
|
int clkRate;
|
|
_Vx_ticks_t timeout;
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
|
|
VXB_ASSERT (pDevice != NULL, ERROR)
|
|
|
|
pDrvCtrl = pDev->pDrvCtrl;
|
|
|
|
if (pDev == NULL || msgs == NULL || num == 0)
|
|
return ERROR;
|
|
|
|
if (pDrvCtrl == NULL)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
if (msgs->scl == 0)
|
|
{
|
|
msgs->scl = pDrvCtrl->defBusSpeed;
|
|
}
|
|
|
|
if (msgs->scl != pDrvCtrl->busSpeed)
|
|
{
|
|
pDrvCtrl->busSpeed = msgs->scl;
|
|
vxbFtI2cInit (pDev);
|
|
}
|
|
|
|
(void) semTake (pDrvCtrl->i2cDevSem, WAIT_FOREVER);
|
|
|
|
/* lock the I2C controller */
|
|
|
|
if (vxbFtI2cWaitBusFree (pDrvCtrl) == ERROR)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cTransfer: WaitBusFree error\n",1,2,3,4,5,6);
|
|
|
|
goto xfailed;
|
|
}
|
|
|
|
vxbFtI2cMasterInit (pDrvCtrl, msgs, num);
|
|
|
|
if (!pDrvCtrl->polling)
|
|
{
|
|
clkRate = vxbSysClkRateGet ();
|
|
timeout = (_Vx_ticks_t) (pDrvCtrl->timeout * (UINT64) clkRate) /
|
|
I2C_S_TO_US;
|
|
timeout += I2C_TOTAL_EXTRA_TICK (pDrvCtrl->totalLen);
|
|
|
|
/* I2C_IRQ_STOP_DET will be enabled after last byte */
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, (I2C_IRQ_DEFAULT_MASK &
|
|
~I2C_IRQ_STOP_DET));
|
|
|
|
if (semTake (pDrvCtrl->i2cDataSem, timeout) == ERROR)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cTransfer: semTake error\n",1,2,3,4,5,6);
|
|
goto xfailed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, I2C_IRQ_NONE);
|
|
|
|
if (OK != vxbFtI2cPoll (pDrvCtrl))
|
|
goto xfailed;
|
|
}
|
|
|
|
if (vxbFtI2cWaitBusFree (pDrvCtrl) == ERROR)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "vxbFtI2cTransfer: WaitBusFree error\n",1,2,3,4,5,6);
|
|
|
|
goto xfailed;
|
|
}
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, I2C_IRQ_NONE);
|
|
if (pDrvCtrl->errorStatus != OK)
|
|
{
|
|
I2C_DBG (I2C_DBG_ERR, "errorStatus == %d\n", pDrvCtrl->errorStatus,2,3,4,5,6);
|
|
goto xfailed;
|
|
}
|
|
|
|
(void) semGive (pDrvCtrl->i2cDevSem);
|
|
|
|
return OK;
|
|
|
|
xfailed:
|
|
|
|
I2C_DBG (I2C_DBG_ERR, "transfer failed \n",1,2,3,4,5,6);
|
|
|
|
CSR_WRITE_4 (pDrvCtrl, I2C_INTR_MASK, I2C_IRQ_NONE);
|
|
vxbFtI2cInit (pDev);
|
|
|
|
(void) semGive (pDrvCtrl->i2cDevSem);
|
|
|
|
return ERROR;
|
|
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cRead - issue the command to read bytes from a device
|
|
*
|
|
* This function reads <length> bytes from the I2C device and stores them
|
|
* in the buffer specified by <pDataBuf>. The read starts at an offset
|
|
* specified previously in the transaction. If too many bytes are requested,
|
|
* the read will return ERROR, though any successfully read bytes will be
|
|
* returned in <pDataBuf>.
|
|
*
|
|
* RETURNS: OK if read completed successfully, otherwise ERROR.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
STATUS vxbFtI2cRead
|
|
(
|
|
VXB_DEVICE_ID pDevice,
|
|
UINT8 devAddr,
|
|
UINT32 startAddr,
|
|
UINT8 * pDataBuf,
|
|
UINT32 length
|
|
)
|
|
{
|
|
I2C_HARDWARE * pI2cDev;
|
|
VXB_DEVICE_ID pDev;
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
I2C_MSG msg[2];
|
|
UINT8 msgbuf[2];
|
|
int i;
|
|
|
|
I2C_DBG (I2C_DBG_RW, "vxbFtI2cRead\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
if ((pDevice == NULL) || (length == 0) || (pDataBuf == NULL))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
pI2cDev = (I2C_HARDWARE *) pDevice->pBusSpecificDevInfo;
|
|
if ((pI2cDev == NULL) || (pI2cDev->pCtlr == NULL) ||
|
|
(pI2cDev->pCtlr->pDrvCtrl == NULL))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
pDev = pI2cDev->pCtlr;
|
|
pDrvCtrl = (I2C_DRV_CTRL *)pDev->pDrvCtrl;
|
|
memset (msg, 0, sizeof(I2C_MSG) * 2);
|
|
memset (msgbuf, 0, sizeof(msgbuf));
|
|
i = 0;
|
|
if ((pI2cDev->flag & I2C_WORDADDR) != 0)
|
|
{
|
|
msgbuf[i++] = (UINT8)(startAddr >> 8);
|
|
}
|
|
msgbuf[i++] = (UINT8)startAddr;
|
|
|
|
/* write start address */
|
|
|
|
msg[0].addr = devAddr;
|
|
msg[0].scl = pDrvCtrl->busSpeed;
|
|
msg[0].buf = msgbuf;
|
|
msg[0].len = i;
|
|
|
|
/* read data */
|
|
|
|
msg[1].addr = devAddr;
|
|
msg[1].scl = pDrvCtrl->busSpeed;
|
|
msg[1].flags = I2C_M_RD;
|
|
msg[1].buf = pDataBuf;
|
|
msg[1].len = length;
|
|
|
|
return vxbFtI2cTransfer (pDev, msg, 2);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cWrite - write a sequence of bytes to a device
|
|
*
|
|
* This function writes <length> bytes from the I2C device read from the
|
|
* buffer specified by <pDataBuf>. The write starts at an offset specified
|
|
* previously in the transaction. If too many bytes are requested, the write
|
|
* will return ERROR.
|
|
*
|
|
* RETURNS: OK if write completed successfully, otherwise ERROR.
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
STATUS vxbFtI2cWrite
|
|
(
|
|
VXB_DEVICE_ID pDevice,
|
|
UINT8 devAddr,
|
|
UINT32 startAddr,
|
|
UINT8 * pDataBuf,
|
|
UINT32 length
|
|
)
|
|
{
|
|
I2C_HARDWARE * pI2cDev;
|
|
VXB_DEVICE_ID pDev;
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
I2C_MSG msg;
|
|
UINT8 * msgbuf;
|
|
int i;
|
|
STATUS stat;
|
|
|
|
I2C_DBG (I2C_DBG_RW, "vxbFtI2cWrite\n", 1, 2, 3, 4, 5, 6);
|
|
|
|
/* length == 0 is OK for I2C switch which only writes info in startAddr */
|
|
|
|
if (pDevice == NULL)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
pI2cDev = (I2C_HARDWARE *) pDevice->pBusSpecificDevInfo;
|
|
if ((pI2cDev == NULL) || (pI2cDev->pCtlr == NULL) ||
|
|
(pI2cDev->pCtlr->pDrvCtrl == NULL) ||
|
|
((pI2cDev->flag & I2C_RDONLY) != 0))
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
msgbuf = malloc (length + 2);
|
|
if (msgbuf == NULL)
|
|
{
|
|
return ERROR;
|
|
}
|
|
pDev = pI2cDev->pCtlr;
|
|
pDrvCtrl = (I2C_DRV_CTRL *)pDev->pDrvCtrl;
|
|
memset (&msg, 0, sizeof(I2C_MSG));
|
|
i = 0;
|
|
if ((pI2cDev->flag & I2C_WORDADDR) != 0)
|
|
{
|
|
msgbuf[i++] = (UINT8)(startAddr >> 8);
|
|
}
|
|
msgbuf[i++] = (UINT8)startAddr;
|
|
|
|
msg.addr = devAddr;
|
|
msg.scl = pDrvCtrl->busSpeed;
|
|
msg.buf = msgbuf;
|
|
msg.len = i + length;
|
|
|
|
if ((length != 0) && (pDataBuf != NULL))
|
|
{
|
|
memcpy (&msgbuf[i], pDataBuf, length);
|
|
}
|
|
|
|
stat = vxbFtI2cTransfer (pDev, &msg, 1);
|
|
free (msgbuf);
|
|
|
|
return stat;
|
|
}
|
|
|
|
|
|
#define I2C_DBG_ON
|
|
#ifdef I2C_DBG_ON
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* vxbFtI2cDbgShow - show I2C transfer control data
|
|
*
|
|
* This function shows I2C transfer control data.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
void vxbFtI2cDbgShow
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
I2C_DRV_CTRL * pDrvCtrl = pDev->pDrvCtrl;
|
|
if (pDrvCtrl == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
printf ("pDrvCtrl->msgs 0x%x\n", pDrvCtrl->msgs );
|
|
printf ("pDrvCtrl->num %d\n", pDrvCtrl->num );
|
|
printf ("pDrvCtrl->msgsRxIdx %d\n", pDrvCtrl->msgsRxIdx );
|
|
printf ("pDrvCtrl->msgRxLen %d\n", pDrvCtrl->msgRxLen );
|
|
printf ("pDrvCtrl->msgRxBuf 0x%x\n", pDrvCtrl->msgRxBuf );
|
|
printf ("pDrvCtrl->fifoRdLvl %d\n", pDrvCtrl->fifoRdLvl );
|
|
printf ("pDrvCtrl->msgsTxIdx %d\n", pDrvCtrl->msgsTxIdx );
|
|
printf ("pDrvCtrl->msgTxLen %d\n", pDrvCtrl->msgTxLen );
|
|
printf ("pDrvCtrl->msgTxBuf 0x%x\n", pDrvCtrl->msgTxBuf );
|
|
printf ("pDrvCtrl->errorStatus %d\n", pDrvCtrl->errorStatus );
|
|
}
|
|
#endif /* I2C_DBG_ON */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* vxbFtI2cShow - show the valid device address on the bus
|
|
*
|
|
* This function scans the device address from 0 to 0x7f
|
|
* (only 7 bit address supported) on each I2C bus,
|
|
* if we can receipt the ACK signal from the device, means this address is valid
|
|
* and have the device connect to the BUS. If no ACK received, go on to the next
|
|
* address.
|
|
*
|
|
* RETURNS: OK if write completed successfully, otherwise ERROR
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*
|
|
*/
|
|
|
|
STATUS vxbFtI2cShow
|
|
(
|
|
VXB_DEVICE_ID pDev,
|
|
int verbose
|
|
)
|
|
{
|
|
I2C_DRV_CTRL * pDrvCtrl;
|
|
|
|
VXB_ASSERT (pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = pDev->pDrvCtrl;
|
|
|
|
printf (" %s unit %d on %s @ 0x%08x",
|
|
pDev->pName,
|
|
pDev->unitNumber,
|
|
vxbBusTypeString (pDev->busID),
|
|
pDev);
|
|
printf (" with busInfo %p\n", pDev->u.pSubordinateBus);
|
|
|
|
if (verbose >= 1)
|
|
{
|
|
printf (" BAR0 @ 0x%08x (memory mapped)\n",
|
|
pDev->pRegBase[0]);
|
|
printf (" pDrvCtrl @ 0x%08x\n", pDev->pDrvCtrl);
|
|
}
|
|
|
|
if (verbose > 1000)
|
|
{
|
|
printf(" clkFrequency: %dMhz\n", pDrvCtrl->clkFrequency/1000000);
|
|
printf(" busSpeed : %dKbps\n",pDrvCtrl->busSpeed/1000);
|
|
printf(" interrupt : %s\n",pDrvCtrl->polling?"FALSE":"TRUE");
|
|
|
|
printf("\n");
|
|
}
|
|
return OK;
|
|
}
|
|
|