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.
1152 lines
34 KiB
1152 lines
34 KiB
/* vim: set ts=4 sw=4 et fdm=marker: */
|
|
#include <vxWorks.h>
|
|
#include <vsbConfig.h>
|
|
#include <intLib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <semLib.h>
|
|
#include <taskLib.h>
|
|
#include <stdio.h>
|
|
#include <vxbTimerLib.h>
|
|
#include <cacheLib.h>
|
|
#include <hwif/util/hwMemLib.h>
|
|
#include <hwif/util/vxbParamSys.h>
|
|
#include <hwif/vxbus/vxBus.h>
|
|
#include <hwif/vxbus/vxbPlbLib.h>
|
|
#include <../src/hwif/h/vxbus/vxbAccess.h>
|
|
#include <hwif/vxbus/vxbSpiLib.h>
|
|
#include "vxbFtSpi.h"
|
|
#include "ft2000-4.h"
|
|
|
|
/* offset map of SPI register */
|
|
/* clang-format off */
|
|
#define FSPIM_CTRL_R0_OFFSET 0x00 /* Ctrl register 0 */
|
|
#define FSPIM_CTRL_R1_OFFSET 0x04 /* Ctrl register 1 */
|
|
#define FSPIM_SSIENR_OFFSET 0x08 /* SPI enable register */
|
|
#define FSPIM_MWCR_OFFSET 0x0c /* Microwire ctrl register */
|
|
#define FSPIM_SER_OFFSET 0x10 /* Slave enable register */
|
|
#define FSPIM_BAUD_R_OFFSET 0x14 /* Baudrate set register */
|
|
#define FSPIM_TXFTL_R_OFFSET 0x18 /* Tx threshold register */
|
|
#define FSPIM_RXFTL_R_OFFSET 0x1c /* Rx threshold register */
|
|
#define FSPIM_TXFLR_OFFSET 0x20 /* Tx level register */
|
|
#define FSPIM_RXFLR_OFFSET 0x24 /* Rx level register */
|
|
#define FSPIM_SR_OFFSET 0x28 /* Status register */
|
|
#define FSPIM_IMR_OFFSET 0x2c /* Intr mask register */
|
|
#define FSPIM_ISR_OFFSET 0x30 /* Irq Status register */
|
|
#define FSPIM_RIS_R_OFFSET 0x34 /* Intr status register */
|
|
#define FSPIM_TXOI_CR_OFFSET 0x38 /* TX FIFO overflow intr clear register */
|
|
#define FSPIM_RXOI_CR_OFFSET 0x3c /* RX FIFO overflow intr clear register */
|
|
#define FSPIM_RXUI_CR_OFFSET 0x40 /* TX FIFO underflow intr clear register */
|
|
#define FSPIM_MSTI_CR_OFFSET 0x44 /* Multi slave intr clear register */
|
|
#define FSPIM_ICR_OFFSET 0x48 /* Intr clear register */
|
|
#define FSPIM_DMA_CR_OFFSET 0x4c /* DMA ctrl register */
|
|
#define FSPIM_DMA_TDLR_OFFSET 0x50 /* DMA TX Data level register */
|
|
#define FSPIM_DMA_RDLR_OFFSET 0x54 /* DMA RX Data level register */
|
|
#define FSPIM_IDR_OFFSET 0x58 /* Identification register */
|
|
#define FSPIM_DR_OFFSET 0x60 /* Data register */
|
|
#define FSPIM_RX_SAMPLE_DLY_OFFSET 0xfc /* RX Data delay register */
|
|
#define FSPIM_CS_OFFSET 0x100 /* Chip selection register */
|
|
|
|
#define FSPIM_SSIENR_SSI_EN(x) ((x) << 0)
|
|
|
|
#define FSPIM_MIN_FIFO_DEPTH 0
|
|
#define FSPIM_MAX_FIFO_DEPTH 256
|
|
#define FSPIM_TIMEOUT 256
|
|
|
|
#define BITS_PER_LONG 32
|
|
#define BITS_PER_WORD 32
|
|
#define BIT(bitnum) (1 << (bitnum % BITS_PER_WORD))
|
|
|
|
#define min3(x, y, z) min((typeof(x))min(x, y), z)
|
|
|
|
#define FSPIM_CHIP_SEL_EN(cs) BIT((cs) + SPI_MAX_CS_NUM) /* 1: enable chip selection */
|
|
#define FSPIM_CHIP_SEL(cs) BIT(cs)
|
|
/*
|
|
CPOL = 0, CPHA = 0, sample at the first rising edge
|
|
CPOL = 1, CPHA = 1, sample at the second rising edge
|
|
CPOL = 1, CPHA = 0, sample at the second falling edge
|
|
CPOL = 0, CPHA = 1, sample at the first falling edge
|
|
*/
|
|
typedef enum {
|
|
FSPIM_CPOL_LOW = 0, /* pharse 0 CPOL=0 */
|
|
FSPIM_CPOL_HIGH /* pharse 1 CPOL=1 */
|
|
} FSpimCpolType;
|
|
|
|
typedef enum {
|
|
FSPIM_CPHA_1_EDGE = 0, /* sample at the 1st edge, CPHA=0 */
|
|
FSPIM_CPHA_2_EDGE /* sample at the 2nd edge, CPHA=1 */
|
|
} FSpimCphaType;
|
|
|
|
/*
|
|
* Create a contiguous bitmask starting at bit position @l and ending at
|
|
* position @h. For example
|
|
* GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
|
|
*/
|
|
#define GENMASK(h, l) \
|
|
(((~0U) - (1U << (l)) + 1) & (~0U >> (BITS_PER_LONG - 1U - (h))))
|
|
|
|
#define GENMASK_ULL(h, l) \
|
|
(((~0ULL) - (1ULL << (l)) + 1) & \
|
|
(~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
|
|
|
|
/** @name FSPIM_CTRL_R0_OFFSET Register
|
|
*/
|
|
#define FSPIM_CTRL_R0_DFS_MASK GENMASK(3, 0)
|
|
#define FSPIM_CTRL_R0_DFS(x) (FSPIM_CTRL_R0_DFS_MASK & ((x) << 0)) /* data len */
|
|
#define FSPIM_CTRL_R0_FRF(x) (GENMASK(5, 4) & ((x) << 4)) /* transfer mode */
|
|
#define FSPIM_CTRL_R0_SCPHA(x) ((x) << 6) /* phrase & cpol */
|
|
#define FSPIM_CTRL_R0_SCPHA_MASK BIT(6)
|
|
|
|
enum {
|
|
FSPIM_SCPHA_SWITCH_DATA_MID = 0x0,
|
|
FSPIM_SCPHA_SWITCH_DATA_BEG = 0x1
|
|
};
|
|
#define FSPIM_CTRL_R0_SCPOL(x) ((x) << 7) /* cpol */
|
|
#define FSPIM_CTRL_R0_SCPOL_MASK BIT(7)
|
|
|
|
enum {
|
|
FSPIM_SCPOL_INACTIVE_LOW = 0,
|
|
FSPIM_SCPOL_INACTIVE_HIGH = 1
|
|
};
|
|
#define FSPIM_CTRL_R0_TMOD_MASK GENMASK(9, 8)
|
|
#define FSPIM_CTRL_R0_TMOD(x) (FSPIM_CTRL_R0_TMOD_MASK & ((x) << 8)) /* transfer mode bit */
|
|
#define FSPIM_CTRL_R0_TMOD_SHIFT 8
|
|
|
|
enum {
|
|
FSPIM_TMOD_RX_TX = 0b00,
|
|
FSPIM_TMOD_TX_ONLY = 0b01,
|
|
FSPIM_TMOD_RX_ONLY = 0b10,
|
|
FSPIM_TMOD_RD_EEPROM = 0b11
|
|
};
|
|
#define FSPIM_CTRL_R0_SLV_OE(x) ((x) << 10) /* slaver enable */
|
|
#define FSPIM_CTRL_R0_SLV_OE_MASK BIT(10)
|
|
|
|
enum {
|
|
FSPIM_SRL_NORAML = 0,
|
|
FSPIM_SRL_TEST = 1
|
|
};
|
|
#define FSPIM_CTRL_R0_CFS(x) (GENMASK(15, 12) & ((x) << 12)) /* data size in Microwire mode */
|
|
enum {
|
|
FSPIM_SLAVE_TX_ENABLE = 0,
|
|
FSPIM_SLAVE_TX_DISALE = 1
|
|
};
|
|
#define FSPIM_CTRL_R0_SLV_SRL(x) ((x) << 11) /* shift regiter loopback */
|
|
|
|
/** @name FSPIM_IMR_OFFSET Register
|
|
*/
|
|
#define FSPIM_IMR_TXEIS BIT(0) /* tx FIFO empty */
|
|
#define FSPIM_IMR_TXOIS BIT(1) /* tx FIFO overflow */
|
|
#define FSPIM_IMR_RXUIS BIT(2) /* rx FIFO underflow */
|
|
#define FSPIM_IMR_RXOIS BIT(3) /* rx FIFO overflow */
|
|
#define FSPIM_IMR_RXFIS BIT(4) /* rx FIFO full */
|
|
#define FSPIM_IMR_ALL_BITS GENMASK(4, 0)
|
|
|
|
typedef enum {
|
|
FSPIM_TRANS_MODE_RX_TX = 0x0,
|
|
FSPIM_TRANS_MODE_TX_ONLY = 0x1,
|
|
FSPIM_TRANS_MODE_RX_ONLY = 0x2,
|
|
FSPIM_TRANS_MODE_READ_EEPROM = 0x3,
|
|
FSPIM_TRANS_MODE_MAX
|
|
} FSpimTransMode;
|
|
|
|
/** @name FSPIM_SER_OFFSET Register
|
|
*/
|
|
#define FSPIM_SER(x) (GENMASK(3, 0) & ((x) << 0)) /* slave flag */
|
|
/* bit map to SPI slave's cs (ss_x_n]). when bit == 1, start transfering */
|
|
enum {
|
|
FSPIM_SER_UNSELECT = 0x0,
|
|
FSPIM_SER_SELECT = 0x1
|
|
};
|
|
|
|
typedef enum {
|
|
FSPIM_SLAVE_DEV_0 = 0,
|
|
FSPIM_SLAVE_DEV_1,
|
|
FSPIM_SLAVE_DEV_2,
|
|
FSPIM_SLAVE_DEV_3,
|
|
|
|
FSPIM_NUM_OF_SLAVE_DEV
|
|
} FSpimSlaveDevice;
|
|
|
|
/** @name FSPIM_DR_OFFSET Register
|
|
*/
|
|
#define FSPIM_DR(x) (GENMASK(15, 0) & ((x) << 0)) /* data register */
|
|
/* clang-format on */
|
|
|
|
/* debug macro */
|
|
#undef SPI_DBG_ON
|
|
#ifdef SPI_DBG_ON
|
|
|
|
#undef LOCAL
|
|
#define LOCAL
|
|
|
|
#define SPI_DBG_OFF 0x00000000
|
|
#define SPI_DBG_ISR 0x00000001
|
|
#define SPI_DBG_RW 0x00000002
|
|
#define SPI_DBG_ERR 0x00000004
|
|
#define SPI_DBG_RTN 0x00000008
|
|
#define SPI_DBG_INFO 0x00000010
|
|
#define SPI_DBG_ALL 0xffffffff
|
|
|
|
LOCAL UINT32 spiDbgMask = SPI_DBG_ALL;
|
|
IMPORT FUNCPTR _func_logMsg;
|
|
|
|
#define SPI_DBG(mask, string, X1, X2, X3, X4, X5, X6) \
|
|
if ((spiDbgMask & mask) || (mask == SPI_DBG_ALL)) \
|
|
if (_func_logMsg != NULL) \
|
|
(*_func_logMsg)(string, (int)X1, (int)X2, (int)X3, (int)X4, (int)X5, (int)X6)
|
|
#else
|
|
#define SPI_DBG(mask, string, X1, X2, X3, X4, X5, X6)
|
|
#endif /* SPI_DBG_ON */
|
|
|
|
#if _BYTE_ORDER == _BIG_ENDIAN
|
|
#define SPI_REG_HANDLE_SWAP(x) VXB_HANDLE_SWAP(x)
|
|
#else
|
|
#define SPI_REG_HANDLE_SWAP(x) (x)
|
|
#endif /* _BYTE_ORDER == _BIG_ENDIAN */
|
|
|
|
/* read write macros */
|
|
#define VXB_FT_SPI_REG(pChan, reg) (UINT32 *)((UINT32)((pChan)->regBase) + (reg))
|
|
|
|
#define VXB_FT_SPI_REG16(pChan, reg) (UINT16 *)((UINT32)((pChan)->regBase) + (reg))
|
|
|
|
#define VXB_FT_SPI_REG8(pChan, reg) (UINT8 *)((UINT32)((pChan)->regBase) + (reg))
|
|
|
|
#define VXB_FT_SPI_REG_READ(pChan, reg, result) (result) = vxbRead32((pChan)->regHandle, VXB_FT_SPI_REG(pChan, reg))
|
|
|
|
#define VXB_FT_SPI_REG_WRITE(pChan, reg, data) vxbWrite32((pChan)->regHandle, VXB_FT_SPI_REG(pChan, reg), (data))
|
|
|
|
#define VXB_FT_SPI_REG_BIT_SET(pChan, reg, data) \
|
|
VXB_FT_SPI_REG_WRITE(pChan, reg, vxbRead32((pChan)->regHandle, VXB_FT_SPI_REG(pChan, reg)) | (data))
|
|
|
|
#define VXB_FT_SPI_REG_BIT_CLR(pChan, reg, data) \
|
|
VXB_FT_SPI_REG_WRITE(pChan, reg, vxbRead32((pChan)->regHandle, VXB_FT_SPI_REG(pChan, reg)) & (~(data)))
|
|
|
|
/* VxBus methods */
|
|
LOCAL void vxbFtSpiInstInit(VXB_DEVICE_ID pDev);
|
|
LOCAL void vxbFtSpiInstInit2(VXB_DEVICE_ID pDev);
|
|
LOCAL void vxbFtSpiInstConnect(VXB_DEVICE_ID pDev);
|
|
LOCAL VXB_SPI_BUS_CTRL *vxbFtSpiCtrlGet(VXB_DEVICE_ID pDev);
|
|
LOCAL void vxbFtSpiShow(VXB_DEVICE_ID, int);
|
|
LOCAL STATUS vxbFtSpiInstUnlink(VXB_DEVICE_ID pDev, void *unused);
|
|
|
|
/* forward declarations */
|
|
LOCAL void vxbFtSpiCtrlInit(VXB_DEVICE_ID pDev);
|
|
|
|
LOCAL STATUS vxbFtSpiTransfer(VXB_DEVICE_ID pDev, SPI_HARDWARE *pSpiDev, SPI_TRANSFER *pPkg);
|
|
/* locals */
|
|
LOCAL struct drvBusFuncs vxbFtSpiVxbFuncs = {
|
|
vxbFtSpiInstInit, /* devInstanceInit */
|
|
vxbFtSpiInstInit2, /* devInstanceInit2 */
|
|
vxbFtSpiInstConnect /* devConnect */
|
|
};
|
|
|
|
/* clang-format off */
|
|
LOCAL device_method_t vxbFtSpiDeviceMethods[] = {
|
|
DEVMETHOD(vxbSpiControlGet, vxbFtSpiCtrlGet),
|
|
DEVMETHOD(busDevShow, vxbFtSpiShow),
|
|
DEVMETHOD(vxbDrvUnlink, vxbFtSpiInstUnlink),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
LOCAL struct vxbPlbRegister vxbFtSpiDevRegistration = {
|
|
{
|
|
NULL , /* pNext */
|
|
VXB_DEVID_DEVICE , /* devID */
|
|
VXB_BUSID_PLB , /* busID = PLB */
|
|
VXB_VER_4_0_0 , /* vxbVersion */
|
|
FT_SPI_NAME , /* drvName */
|
|
&vxbFtSpiVxbFuncs , /* pDrvBusFuncs */
|
|
&vxbFtSpiDeviceMethods[0] , /* pMethods */
|
|
NULL , /* devProbe */
|
|
NULL , /* pParamDefaults */
|
|
},
|
|
};
|
|
/* clang-format on */
|
|
|
|
/*
|
|
* vxbFtSpiRegister - register with the VxBus subsystem
|
|
*
|
|
* This routine registers the SPI driver with VxBus Systems.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
void vxbFtSpiRegister(void)
|
|
{
|
|
vxbDevRegister((struct vxbDevRegInfo *)&vxbFtSpiDevRegistration);
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiInstInit - initialize SPI controller
|
|
*
|
|
* This function implements the VxBus instInit handler for a SPI controller
|
|
* device instance.
|
|
*
|
|
* Initialize the device by the following:
|
|
*
|
|
* - retrieve the resource from the hwconf
|
|
* - per-device init
|
|
* - announce SPI Bus and create device instance
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL void vxbFtSpiInstInit(VXB_DEVICE_ID pDev)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
struct hcfDevice *pHcf;
|
|
int i;
|
|
|
|
/* check for valid parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
/* create controller driver context structure for core */
|
|
pDrvCtrl = (FT_SPI_CTRL *)hwMemAlloc(sizeof(FT_SPI_CTRL));
|
|
if (pDrvCtrl == NULL) {
|
|
return;
|
|
}
|
|
pDev->pDrvCtrl = pDrvCtrl;
|
|
pDrvCtrl->pDev = pDev;
|
|
|
|
for (i = 0; i < VXB_MAXBARS; i++) {
|
|
if (pDev->regBaseFlags[i] == VXB_REG_MEM) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == VXB_MAXBARS) {
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree((char *)pDrvCtrl);
|
|
#endif
|
|
pDev->pDrvCtrl = NULL;
|
|
return;
|
|
}
|
|
pDrvCtrl->regBase = pDev->pRegBase[i];
|
|
vxbRegMap(pDev, i, &pDrvCtrl->regHandle);
|
|
pDrvCtrl->regHandle = (void *)SPI_REG_HANDLE_SWAP((ULONG)pDrvCtrl->regHandle);
|
|
pHcf = (struct hcfDevice *)hcfDeviceGet(pDev);
|
|
if (pHcf == NULL) {
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree((char *)pDrvCtrl);
|
|
#endif
|
|
pDev->pDrvCtrl = NULL;
|
|
return;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "clkFreq", HCF_RES_INT, (void *)&pDrvCtrl->clkFreq) != OK) {
|
|
pDrvCtrl->clkFreq = FT2000_SPI_CLK;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "speed", HCF_RES_INT, (void *)&pDrvCtrl->speed) != OK) {
|
|
pDrvCtrl->speed = 1000000;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "mode", HCF_RES_INT, (void *)&pDrvCtrl->mode) != OK) {
|
|
pDrvCtrl->mode = 0;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "dataWidth", HCF_RES_INT, (void *)&pDrvCtrl->dataWidth) != OK) {
|
|
pDrvCtrl->dataWidth = 8;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "loopback", HCF_RES_INT, (void *)&pDrvCtrl->en_test) != OK) {
|
|
pDrvCtrl->en_test = 0;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "txFifoLen", HCF_RES_INT, (void *)&pDrvCtrl->tx_fifo_len) != OK) {
|
|
pDrvCtrl->tx_fifo_len = 0;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "rxFifoLen", HCF_RES_INT, (void *)&pDrvCtrl->rx_fifo_len) != OK) {
|
|
pDrvCtrl->rx_fifo_len = 0;
|
|
}
|
|
|
|
if (devResourceGet(pHcf, "spiDevNum", HCF_RES_INT, (void *)&pDrvCtrl->spiDevNum) != OK) {
|
|
pDrvCtrl->spiDevNum = 0;
|
|
}
|
|
|
|
pDrvCtrl->cs = 0;
|
|
|
|
vxbFtSpiCtrlInit(pDev);
|
|
|
|
pDrvCtrl->vxbSpiCtrl.spiTransfer = (void *)vxbFtSpiTransfer;
|
|
|
|
/* announce that there's a SPI bus */
|
|
(void)vxbBusAnnounce(pDev, VXB_BUSID_SPI);
|
|
|
|
/* notify the bus subsystem of all devices on SPI */
|
|
(void)spiBusAnnounceDevices(pDev);
|
|
|
|
pDrvCtrl->initPhase = 1;
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiInstInit2 - second level initialization routine of SPI controller
|
|
*
|
|
* This routine performs the second level initialization of the SPI controller.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL void vxbFtSpiInstInit2(VXB_DEVICE_ID pDev)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
|
|
/* check for valid parameter */
|
|
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
|
|
/* used for mutex accessing of the controller */
|
|
pDrvCtrl->muxSem = semMCreate(SEM_Q_PRIORITY);
|
|
|
|
if (pDrvCtrl->muxSem == NULL) {
|
|
SPI_DBG(SPI_DBG_ERR, "semMCreate failed for muxSem\n", 0, 0, 0, 0, 0, 0);
|
|
return;
|
|
}
|
|
|
|
pDrvCtrl->initPhase = 2;
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiInstConnect - third level initialization
|
|
*
|
|
* This routine performs the third level initialization of the QSPI controller
|
|
* driver.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO : N/A
|
|
*/
|
|
LOCAL void vxbFtSpiInstConnect(VXB_DEVICE_ID pDev)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
|
|
pDrvCtrl = pDev->pDrvCtrl;
|
|
pDrvCtrl->initPhase = 3;
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiCtrlGet - get the SPI controller struct
|
|
*
|
|
* This routine returns the QSPI controller struct pointer (VXB_SPI_BUS_CTRL *)
|
|
* to caller (SPI Lib) by vxbSpiControlGet method. Currently, this struct
|
|
* only contain the spiTransfer routine(eg: vxbFtQspiTransfer) for SPI Lib,
|
|
* other parameters can be easily added in this struct.
|
|
*
|
|
* RETURNS: the pointer of SPI controller struct
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL VXB_SPI_BUS_CTRL *vxbFtSpiCtrlGet(VXB_DEVICE_ID pDev)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
|
|
/* check if the pDev pointer is valid */
|
|
VXB_ASSERT(pDev != NULL, NULL)
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "vxbFtQspiCtrlGet(0x08%x) called\n", (_Vx_usr_arg_t)pDev, 2, 3, 4, 5, 6);
|
|
|
|
return (&(pDrvCtrl->vxbSpiCtrl));
|
|
}
|
|
|
|
/* {{{ SPI functions */
|
|
static inline void FSpimSetEnable(FT_SPI_CTRL *pDrvCtrl, int enable)
|
|
{
|
|
if (enable) {
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_SSIENR_OFFSET, FSPIM_SSIENR_SSI_EN(1));
|
|
} else {
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_SSIENR_OFFSET, FSPIM_SSIENR_SSI_EN(0));
|
|
}
|
|
}
|
|
|
|
static inline void FSpimSetCtrlR0(FT_SPI_CTRL *pDrvCtrl, UINT32 val)
|
|
{
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_CTRL_R0_OFFSET, val);
|
|
}
|
|
|
|
static inline UINT32 FSpimGetCtrlR0(FT_SPI_CTRL *pDrvCtrl)
|
|
{
|
|
UINT32 reg = 0;
|
|
VXB_FT_SPI_REG_READ(pDrvCtrl, FSPIM_CTRL_R0_OFFSET, reg);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static inline void FSpimSetTxFifoThreshold(FT_SPI_CTRL *pDrvCtrl, UINT32 val)
|
|
{
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_TXFTL_R_OFFSET, val);
|
|
}
|
|
|
|
static inline UINT32 FSpimGetTxFifoThreshold(FT_SPI_CTRL *pDrvCtrl)
|
|
{
|
|
UINT32 reg = 0;
|
|
VXB_FT_SPI_REG_READ(pDrvCtrl, FSPIM_TXFTL_R_OFFSET, reg);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static inline void FSpimSetRxFifoThreshold(FT_SPI_CTRL *pDrvCtrl, UINT32 val)
|
|
{
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_RXFTL_R_OFFSET, val);
|
|
}
|
|
|
|
static inline UINT32 FSpimGetRxFifoThreshold(FT_SPI_CTRL *pDrvCtrl)
|
|
{
|
|
UINT32 reg = 0;
|
|
VXB_FT_SPI_REG_READ(pDrvCtrl, FSPIM_RXFTL_R_OFFSET, reg);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static inline void FSpimMaskIrq(FT_SPI_CTRL *pDrvCtrl, UINT32 mask)
|
|
{
|
|
UINT32 curr_mask = 0;
|
|
|
|
VXB_FT_SPI_REG_READ(pDrvCtrl, FSPIM_IMR_OFFSET, curr_mask);
|
|
|
|
curr_mask = curr_mask & ~mask; /* = 0 interrupt inactive */
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_IMR_OFFSET, curr_mask);
|
|
}
|
|
|
|
static inline void FSpimWriteData(FT_SPI_CTRL *pDrvCtrl, UINT16 dat)
|
|
{
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_DR_OFFSET, FSPIM_DR(dat));
|
|
}
|
|
|
|
static inline UINT16 FSpimReadData(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 reg;
|
|
|
|
VXB_FT_SPI_REG_READ(pDrvCtr, FSPIM_DR_OFFSET, reg);
|
|
|
|
return (UINT16)(reg);
|
|
}
|
|
|
|
static void FSpimSetCpha(FT_SPI_CTRL *pDrvCtrl, UINT32 cpha_mode)
|
|
{
|
|
UINT32 reg_val = FSpimGetCtrlR0(pDrvCtrl);
|
|
|
|
reg_val &= ~FSPIM_CTRL_R0_SCPHA_MASK; /* clear bits */
|
|
if (FSPIM_CPHA_1_EDGE == cpha_mode) {
|
|
reg_val |= FSPIM_CTRL_R0_SCPHA(FSPIM_SCPHA_SWITCH_DATA_MID);
|
|
} else if (FSPIM_CPHA_2_EDGE == cpha_mode) {
|
|
reg_val |= FSPIM_CTRL_R0_SCPHA(FSPIM_SCPHA_SWITCH_DATA_BEG);
|
|
} else {
|
|
SPI_DBG(SPI_DBG_RTN, "FSpimSetCpha(0x08%x) Warning chpa\n", (_Vx_usr_arg_t)pDev, 2, 3, 4, 5, 6);
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtrl, reg_val);
|
|
}
|
|
|
|
static inline int FSpimGetEnable(FT_SPI_CTRL *pDrvCtrl)
|
|
{
|
|
UINT32 reg = 0;
|
|
VXB_FT_SPI_REG_READ(pDrvCtrl, FSPIM_SSIENR_OFFSET, reg);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static void FSpimSetCpol(FT_SPI_CTRL *pDrvCtrl, UINT32 cpol_mode)
|
|
{
|
|
UINT32 reg_val = FSpimGetCtrlR0(pDrvCtrl);
|
|
|
|
reg_val &= ~FSPIM_CTRL_R0_SCPOL_MASK; /* clear bits */
|
|
if (FSPIM_CPOL_LOW == cpol_mode) {
|
|
reg_val |= FSPIM_CTRL_R0_SCPOL(FSPIM_SCPOL_INACTIVE_LOW);
|
|
} else if (FSPIM_CPOL_HIGH == cpol_mode) {
|
|
reg_val |= FSPIM_CTRL_R0_SCPOL(FSPIM_SCPOL_INACTIVE_HIGH);
|
|
} else {
|
|
SPI_DBG(SPI_DBG_RTN, "FSpimSetCpol(0x08%x) Warning Cpol\n", (_Vx_usr_arg_t)pDrvCtrl, 2, 3, 4, 5, 6);
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtrl, reg_val);
|
|
}
|
|
|
|
static void FSpimSetSlaveEnable(FT_SPI_CTRL *pDrvCtrl, int enable)
|
|
{
|
|
UINT32 reg_val;
|
|
int enabled = FSpimGetEnable(pDrvCtrl);
|
|
|
|
if (enabled) {
|
|
FSpimSetEnable(pDrvCtrl, FALSE);
|
|
}
|
|
|
|
reg_val = FSpimGetCtrlR0(pDrvCtrl);
|
|
|
|
reg_val &= ~FSPIM_CTRL_R0_SLV_OE_MASK;
|
|
if (enable) {
|
|
reg_val |= FSPIM_CTRL_R0_SLV_OE(FSPIM_SLAVE_TX_ENABLE);
|
|
} else {
|
|
reg_val |= FSPIM_CTRL_R0_SLV_OE(FSPIM_SLAVE_TX_DISALE);
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtrl, reg_val);
|
|
|
|
if (enabled) {
|
|
FSpimSetEnable(pDrvCtrl, TRUE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void FSpimSetTransMode(FT_SPI_CTRL *pDrvCtr, UINT32 trans_mode)
|
|
{
|
|
UINT32 reg_val;
|
|
int enabled = FSpimGetEnable(pDrvCtr);
|
|
|
|
if (trans_mode > FSPIM_TRANS_MODE_MAX) {
|
|
return;
|
|
}
|
|
|
|
if (enabled) {
|
|
FSpimSetEnable(pDrvCtr, FALSE);
|
|
}
|
|
|
|
reg_val = FSpimGetCtrlR0(pDrvCtr);
|
|
reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK; /* clear trans mode bits */
|
|
switch (trans_mode) {
|
|
case FSPIM_TRANS_MODE_RX_TX:
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX);
|
|
break;
|
|
case FSPIM_TRANS_MODE_TX_ONLY:
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_TX_ONLY);
|
|
break;
|
|
case FSPIM_TRANS_MODE_RX_ONLY:
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY);
|
|
break;
|
|
case FSPIM_TRANS_MODE_READ_EEPROM:
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RD_EEPROM);
|
|
break;
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtr, reg_val);
|
|
|
|
if (enabled) {
|
|
FSpimSetEnable(pDrvCtr, TRUE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void FSpimSelSlaveDev(FT_SPI_CTRL *pDrvCtr, UINT32 slave_dev_id)
|
|
{
|
|
UINT32 reg_val;
|
|
|
|
if (slave_dev_id >= SPI_MAX_CS_NUM) {
|
|
return;
|
|
}
|
|
|
|
reg_val = (FSPIM_SER_SELECT << slave_dev_id);
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtr, FSPIM_SER_OFFSET, reg_val);
|
|
}
|
|
|
|
static UINT32 FSpimGetTxFifoDepth(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 fifo_depth;
|
|
|
|
for (fifo_depth = 1; fifo_depth < FSPIM_MAX_FIFO_DEPTH; fifo_depth++) {
|
|
FSpimSetTxFifoThreshold(pDrvCtr, fifo_depth); /* try until failed */
|
|
if (fifo_depth != FSpimGetTxFifoThreshold(pDrvCtr)) {
|
|
SPI_DBG(SPI_DBG_RTN, "The Tx fifo threshold is %d", fifo_depth, 2, 3, 4, 5, 6);
|
|
break;
|
|
}
|
|
}
|
|
|
|
FSpimSetTxFifoThreshold(pDrvCtr, 0);
|
|
return fifo_depth;
|
|
}
|
|
|
|
static UINT32 FSpimGetRxFifoDepth(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 fifo_depth = FSPIM_MIN_FIFO_DEPTH;
|
|
|
|
while (FSPIM_MAX_FIFO_DEPTH >= fifo_depth) {
|
|
FSpimSetRxFifoThreshold(pDrvCtr, fifo_depth);
|
|
if (fifo_depth != FSpimGetRxFifoThreshold(pDrvCtr)) {
|
|
SPI_DBG(SPI_DBG_RTN, "The Rx fifo threshold is %d", fifo_depth, 2, 3, 4, 5, 6);
|
|
break;
|
|
}
|
|
|
|
fifo_depth++;
|
|
}
|
|
|
|
return fifo_depth;
|
|
}
|
|
|
|
static int FSpimSetSpeed(FT_SPI_CTRL *pDrvCtr, UINT32 clk_freq)
|
|
{
|
|
UINT32 clk_div;
|
|
|
|
if (clk_freq == 0) {
|
|
printf("Input spi clock frequency is %d => do not support, this parameter should not be 0.", clk_freq);
|
|
return -1;
|
|
} else {
|
|
clk_div = pDrvCtr->clkFreq / clk_freq;
|
|
}
|
|
|
|
if ((clk_div >= 2) && (clk_div <= 65534)) {
|
|
if ((clk_div % 2) != 0) {
|
|
clk_div += 1;
|
|
}
|
|
|
|
FSpimSetEnable(pDrvCtr, FALSE);
|
|
SPI_DBG(SPI_DBG_RTN, "Set spi clock divider as %d", clk_div, 2, 3, 4, 5, 6);
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtr, FSPIM_BAUD_R_OFFSET, clk_div);
|
|
FSpimSetEnable(pDrvCtr, TRUE);
|
|
return 0;
|
|
} else {
|
|
printf(
|
|
"Input spi clock frequency is %ld, this parameter be set wrong. spi clock divider = %d, this parameter should be an even from 2 to 65534.",
|
|
clk_freq, clk_div);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
void FSpimSetChipSelection(FT_SPI_CTRL *pDrvCtr, int on)
|
|
{
|
|
UINT32 reg_val;
|
|
FSpimSlaveDevice cs_n = pDrvCtr->cs;
|
|
|
|
VXB_FT_SPI_REG_READ(pDrvCtr, FSPIM_CS_OFFSET, reg_val);
|
|
|
|
if (on) {
|
|
reg_val |= FSPIM_CHIP_SEL_EN((UINT32)cs_n);
|
|
reg_val |= FSPIM_CHIP_SEL((UINT32)cs_n);
|
|
} else {
|
|
reg_val &= ~FSPIM_CHIP_SEL_EN((UINT32)cs_n);
|
|
reg_val &= ~FSPIM_CHIP_SEL((UINT32)cs_n);
|
|
}
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtr, FSPIM_CS_OFFSET, reg_val);
|
|
|
|
return;
|
|
}
|
|
|
|
static UINT32 FSpimGetTxRound(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 data_width = pDrvCtr->dataWidth >> 3;
|
|
UINT32 tx_left_round, tx_fifo_room, rx_tx_gap;
|
|
UINT32 lvl = 0;
|
|
|
|
tx_left_round = (UINT32)(pDrvCtr->length - pDrvCtr->tx_count) / data_width;
|
|
|
|
VXB_FT_SPI_REG_READ(pDrvCtr, FSPIM_TXFLR_OFFSET, lvl);
|
|
|
|
tx_fifo_room = pDrvCtr->tx_fifo_len - lvl;
|
|
|
|
rx_tx_gap =
|
|
((UINT32)(pDrvCtr->length - pDrvCtr->rx_count) - (UINT32)(pDrvCtr->length - pDrvCtr->tx_count)) / data_width;
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "tx_left_round: %d, tx_fifo_room: %d, gap: %d, instance_p->tx_count: %ld", tx_left_round,
|
|
tx_fifo_room, ((UINT32)(pDrvCtr->tx_fifo_len) - rx_tx_gap), pDrvCtr->tx_count, 5, 6);
|
|
return min3(tx_left_round, tx_fifo_room, ((UINT32)(pDrvCtr->tx_fifo_len) - rx_tx_gap));
|
|
}
|
|
|
|
static void FSpimFifoTx(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 tx_round = FSpimGetTxRound(pDrvCtr);
|
|
UINT32 data_width = pDrvCtr->dataWidth >> 3;
|
|
UINT16 data = 0;
|
|
|
|
if (2 == data_width) {
|
|
data = 0xffff;
|
|
} else {
|
|
data = 0xff;
|
|
}
|
|
|
|
while (tx_round) {
|
|
if (pDrvCtr->tx_buff) {
|
|
if (1 == data_width) {
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
data = *(UINT8 *)(pDrvCtr->tx_buff);
|
|
} else if (2 == data_width) {
|
|
/*
|
|
* Data Transfer Width is Half Word (16 bit).
|
|
*/
|
|
data = *(UINT16 *)(pDrvCtr->tx_buff);
|
|
}
|
|
|
|
pDrvCtr->tx_buff += data_width;
|
|
}
|
|
pDrvCtr->tx_count += data_width;
|
|
FSpimWriteData(pDrvCtr, data);
|
|
SPI_DBG(SPI_DBG_RTN, " send 0x%x", data, 2, 3, 4, 5, 6);
|
|
tx_round--;
|
|
}
|
|
}
|
|
|
|
static UINT32 FSpimGetRxRound(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 data_width = pDrvCtr->dataWidth >> 3;
|
|
UINT32 lvl = 0;
|
|
UINT32 rx_left_round = (UINT32)(pDrvCtr->length - pDrvCtr->rx_count) / data_width;
|
|
|
|
VXB_FT_SPI_REG_READ(pDrvCtr, FSPIM_RXFLR_OFFSET, lvl);
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "left round %d, rx level %d,instance_p->rx_count %ld", rx_left_round, lvl, pDrvCtr->rx_count,
|
|
4, 5, 6);
|
|
return min(rx_left_round, lvl);
|
|
}
|
|
|
|
static void FSpimFifoRx(FT_SPI_CTRL *pDrvCtr)
|
|
{
|
|
UINT32 rx_round = FSpimGetRxRound(pDrvCtr);
|
|
UINT32 data_width = pDrvCtr->dataWidth >> 3;
|
|
UINT16 data;
|
|
|
|
while (rx_round) {
|
|
data = FSpimReadData(pDrvCtr);
|
|
if (pDrvCtr->rx_buff) {
|
|
if (1 == data_width) {
|
|
/*
|
|
* Data Transfer Width is Byte (8 bit).
|
|
*/
|
|
*(UINT8 *)(pDrvCtr->rx_buff) = (UINT8)data;
|
|
SPI_DBG(SPI_DBG_RTN, " recv 0x%x", *(UINT8 *)(pDrvCtr->rx_buff), 2, 3, 4, 5, 6);
|
|
} else if (2 == data_width) {
|
|
/*
|
|
* Data Transfer Width is Half Word (16 bit).
|
|
*/
|
|
*(UINT16 *)(pDrvCtr->rx_buff) = (UINT16)data;
|
|
SPI_DBG(SPI_DBG_RTN, " recv 0x%x", *(u16 *)(pDrvCtr->rx_buff), 2, 3, 4, 5, 6);
|
|
}
|
|
pDrvCtr->rx_buff += data_width;
|
|
}
|
|
pDrvCtr->rx_count += data_width;
|
|
rx_round--;
|
|
}
|
|
}
|
|
|
|
static void FSpimTransferPollFifo(FT_SPI_CTRL *pDrvCtr, const void *tx_buf, void *rx_buf, UINT32 len)
|
|
{
|
|
UINT32 reg_val;
|
|
UINT32 data_width = pDrvCtr->dataWidth;
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "buff address rx= %x, tx=%x\r\n", rx_buf, tx_buf, 3, 4, 5, 6);
|
|
|
|
FSpimSetEnable(pDrvCtr, FALSE);
|
|
|
|
reg_val = FSpimGetCtrlR0(pDrvCtr);
|
|
|
|
reg_val &= ~FSPIM_CTRL_R0_DFS_MASK;
|
|
reg_val |= FSPIM_CTRL_R0_DFS(data_width - 1);
|
|
|
|
reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK;
|
|
if (tx_buf && rx_buf) {
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX);
|
|
} else if (rx_buf) {
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY);
|
|
} else {
|
|
reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX);
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtr, reg_val);
|
|
|
|
FSpimMaskIrq(pDrvCtr, FSPIM_IMR_ALL_BITS);
|
|
|
|
pDrvCtr->tx_count = 0;
|
|
pDrvCtr->rx_count = 0;
|
|
pDrvCtr->length = len;
|
|
pDrvCtr->tx_buff = tx_buf;
|
|
pDrvCtr->rx_buff = rx_buf;
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "tx buff@%p-%d, rx buff@%p-%d", pDrvCtr->tx_buff, len, pDrvCtr->rx_buff, len, 5, 6);
|
|
|
|
FSpimSetEnable(pDrvCtr, TRUE);
|
|
do {
|
|
FSpimFifoTx(pDrvCtr);
|
|
FSpimFifoRx(pDrvCtr);
|
|
} while (pDrvCtr->tx_count < len || pDrvCtr->rx_count < len);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/*
|
|
* vxbFtSpiCtrlInit - SPI controller initialization
|
|
*
|
|
* This routine performs the SPI controller initialization.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL void vxbFtSpiCtrlInit(VXB_DEVICE_ID pDev)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
UINT32 reg_val, fifo;
|
|
STATUS ret;
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
|
|
/* disable controller */
|
|
FSpimSetEnable(pDrvCtrl, FALSE);
|
|
|
|
/* data frame format */
|
|
reg_val = FSPIM_CTRL_R0_DFS(pDrvCtrl->dataWidth - 1) | FSPIM_CTRL_R0_FRF(FSPIM_DEFAULT_FRF) |
|
|
FSPIM_CTRL_R0_CFS(FSPIM_DEFAULT_CFS);
|
|
|
|
if (pDrvCtrl->en_test) {
|
|
reg_val |= FSPIM_CTRL_R0_SLV_SRL(FSPIM_SRL_TEST); /* loopback */
|
|
} else {
|
|
reg_val |= FSPIM_CTRL_R0_SLV_SRL(FSPIM_SRL_NORAML); /* normal */
|
|
}
|
|
|
|
FSpimSetCtrlR0(pDrvCtrl, reg_val);
|
|
|
|
/* phrase and polarity
|
|
* spi mode mapping:
|
|
*
|
|
* CPOL CPHA
|
|
* 0 0 mode 0
|
|
* 0 1 mode 1
|
|
* 1 0 mode 2
|
|
* 1 1 mode 3
|
|
*/
|
|
FSpimSetCpha(pDrvCtrl, pDrvCtrl->mode & 0x1);
|
|
FSpimSetCpol(pDrvCtrl, (pDrvCtrl->mode & 0x2) >> 1);
|
|
|
|
/* transfer mode */
|
|
FSpimSetTransMode(pDrvCtrl, FSPIM_TRANS_MODE_RX_TX);
|
|
|
|
/* disable slave output */
|
|
FSpimSetSlaveEnable(pDrvCtrl, FALSE);
|
|
|
|
/* disable interrup*/
|
|
FSpimMaskIrq(pDrvCtrl, FSPIM_IMR_ALL_BITS);
|
|
FSpimSelSlaveDev(pDrvCtrl, pDrvCtrl->cs);
|
|
|
|
/* query SPI RX/TX FIFO depth */
|
|
if (0 == pDrvCtrl->tx_fifo_len) {
|
|
fifo = FSpimGetTxFifoDepth(pDrvCtrl); /* fifo depth */
|
|
pDrvCtrl->tx_fifo_len = fifo;
|
|
SPI_DBG(SPI_DBG_RTN, "The fifo depth is %d ,tx effective length bits %d", fifo, pDrvCtrl->tx_fifo_len, 3, 4, 5,
|
|
6);
|
|
}
|
|
|
|
if (0 == pDrvCtrl->rx_fifo_len) {
|
|
fifo = FSpimGetRxFifoDepth(pDrvCtrl);
|
|
pDrvCtrl->rx_fifo_len = fifo;
|
|
SPI_DBG(SPI_DBG_RTN, "The fifo depth is %d ,rx effective length bits %d", fifo, pDrvCtrl->rx_fifo_len, 3, 4, 5,
|
|
6);
|
|
}
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_DMA_CR_OFFSET, 0x0); /* disable ddma */
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_CTRL_R1_OFFSET, 0);
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_DMA_RDLR_OFFSET, 0);
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_DMA_TDLR_OFFSET, 0);
|
|
|
|
FSpimSetRxFifoThreshold(pDrvCtrl, 0);
|
|
FSpimSetTxFifoThreshold(pDrvCtrl, 0);
|
|
|
|
ret = FSpimSetSpeed(pDrvCtrl, pDrvCtrl->speed);
|
|
if (0 != ret) {
|
|
return;
|
|
}
|
|
|
|
VXB_FT_SPI_REG_WRITE(pDrvCtrl, FSPIM_RX_SAMPLE_DLY_OFFSET, 0);
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiTransfer - SPI transfer routine
|
|
*
|
|
* This routine is used to perform one transmission. It is the interface which
|
|
* can be called by SPI device driver to send and receive data via the QSPI
|
|
* controller.
|
|
*
|
|
* RETURNS: OK or ERROR
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL STATUS vxbFtSpiTransfer(VXB_DEVICE_ID pDev, /* controller pDev */
|
|
SPI_HARDWARE *pSpiDev, /* device info */
|
|
SPI_TRANSFER *pPkg /* transfer data info */
|
|
)
|
|
{
|
|
STATUS sts = OK;
|
|
UINT32 alignSize;
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
/* check if the pointers are valid */
|
|
|
|
if (pDev == NULL || pSpiDev == NULL || pPkg == NULL || pSpiDev->devInfo == NULL) {
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer NULL pointer\n", 1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
if (pDrvCtrl == NULL) {
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer pDrvCtrl is NULL\n", 1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
SPI_DBG(SPI_DBG_RTN, "vxbFtQspiTransfer txLen[%d] rxLen[%d]\n", pPkg->txLen, pPkg->rxLen, 3, 4, 5, 6);
|
|
|
|
if (pPkg->txLen == 0 && pPkg->rxLen == 0) {
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer tx and rx both are 0\n", 1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
if ((pPkg->txLen != 0 && pPkg->txBuf == NULL) || (pPkg->rxLen != 0 && pPkg->rxBuf == NULL)) {
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer invalid parameters[%x %x %x %x] \n", pPkg->txBuf, pPkg->txLen,
|
|
pPkg->rxLen, pPkg->rxBuf, 5, 6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
if (pSpiDev->devInfo->bitWidth <= 8) { /* 4 to 8 bits */
|
|
alignSize = sizeof(char);
|
|
} else if (pSpiDev->devInfo->bitWidth <= 16) { /* 9 to 16 bits */
|
|
alignSize = sizeof(UINT16);
|
|
} else { /* 17 to 32 bits */
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer: data word length must between 2-16\n", 1, 2, 3, 4, 5, 6);
|
|
return ERROR;
|
|
}
|
|
|
|
/* check to see if the address is aligned with SPI bit width */
|
|
if ((pPkg->txLen != 0 && (((UINT32)pPkg->txBuf & (alignSize - 1)) != 0 || (pPkg->txLen & (alignSize - 1)) != 0)) ||
|
|
(pPkg->rxLen != 0 && (((UINT32)pPkg->rxBuf & (alignSize - 1)) != 0 || (pPkg->rxLen & (alignSize - 1)) != 0))) {
|
|
SPI_DBG(SPI_DBG_ERR,
|
|
"vxbFtQspiTransfer address or len is not aligned:"
|
|
"[tx:%x-%x rx:%x-%x] with %d \n",
|
|
pPkg->txBuf, pPkg->txLen, pPkg->rxBuf, pPkg->rxLen, alignSize, 6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
if (pSpiDev->devInfo->chipSelect >= SPI_MAX_CS_NUM) {
|
|
SPI_DBG(SPI_DBG_ERR, "vxbFtQspiTransfer invalid channel[%x] \n", pDrvCtrl->channel, 2, 3, 4, 5, 6);
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/*
|
|
* The controller can not be used by multichannel at the same time.
|
|
* If the initPhase < 2, there is no multi-task context. So, the
|
|
* semTake is not needed.
|
|
*/
|
|
if (pDrvCtrl->initPhase >= 2) {
|
|
(void)semTake(pDrvCtrl->muxSem, WAIT_FOREVER);
|
|
}
|
|
|
|
/* reconfigure */
|
|
if ((pSpiDev->devInfo->devFreq != pDrvCtrl->speed) || (pSpiDev->devInfo->mode != pDrvCtrl->mode) ||
|
|
(pSpiDev->devInfo->chipSelect != pDrvCtrl->cs) || (pDrvCtrl->dataWidth != pSpiDev->devInfo->bitWidth)) {
|
|
pDrvCtrl->mode = pSpiDev->devInfo->mode;
|
|
pDrvCtrl->speed = pSpiDev->devInfo->devFreq;
|
|
pDrvCtrl->cs = pSpiDev->devInfo->chipSelect;
|
|
pDrvCtrl->dataWidth = pSpiDev->devInfo->bitWidth;
|
|
vxbFtSpiCtrlInit(pDev);
|
|
}
|
|
|
|
FSpimSetEnable(pDrvCtrl, FALSE);
|
|
|
|
/* FSpimSetChipSelection(pDrvCtrl, TRUE); */
|
|
|
|
FSpimTransferPollFifo(pDrvCtrl, pPkg->txBuf, pPkg->rxBuf, max(pPkg->txLen, pPkg->rxLen));
|
|
|
|
/* FSpimSetChipSelection(pDrvCtrl, FALSE); */
|
|
|
|
SPI_DBG(SPI_DBG_ERR, " xfer : tx %d bytes, rx %d bytes\n", pPkg->txLen, pPkg->rxLen, 3, 4, 5, 6);
|
|
|
|
if (pDrvCtrl->initPhase >= 2) {
|
|
semGive(pDrvCtrl->muxSem);
|
|
}
|
|
|
|
if (pPkg->usDelay > 0) {
|
|
vxbUsDelay(pPkg->usDelay);
|
|
}
|
|
|
|
return sts;
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiShow - show the controller info
|
|
*
|
|
* This function shows the SPI controller's info.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL void vxbFtSpiShow(VXB_DEVICE_ID pDev, int verbose)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
|
|
/* check for valid parameter */
|
|
VXB_ASSERT_NONNULL_V(pDev);
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
|
|
printf(" %s unit %d on %s @ %p with busInfo %08p\n", pDev->pName, pDev->unitNumber,
|
|
vxbBusTypeString(pDev->busID), pDev, pDev->u.pSubordinateBus);
|
|
|
|
if (verbose > 0) {
|
|
printf(" BAR0 @ 0x%08x (memory mapped)\n", pDev->pRegBase[0]);
|
|
printf(" pDrvCtrl @ 0x%08x\n", pDev->pDrvCtrl);
|
|
printf(" initPhase : %u\n", pDrvCtrl->initPhase);
|
|
printf(" clkFreq : %u\n", pDrvCtrl->clkFreq);
|
|
printf(" speed : %u\n", pDrvCtrl->speed);
|
|
printf(" bitWidth : %u\n", pDrvCtrl->dataWidth);
|
|
printf(" mode : %u\n", pDrvCtrl->mode);
|
|
printf(" txFifoLen : %u\n", pDrvCtrl->tx_fifo_len);
|
|
printf(" rxFifoLen : %u\n", pDrvCtrl->rx_fifo_len);
|
|
printf(" spiDevNum : %u\n", pDrvCtrl->spiDevNum);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vxbFtSpiInstUnlink - VxBus unlink handler
|
|
*
|
|
* This function shuts down a SPI controller instance in response to an
|
|
* an unlink event from VxBus. This may occur if our VxBus instance has
|
|
* been terminated, or if the SPI driver has been unloaded.
|
|
*
|
|
* RETURNS: OK if device was successfully destroyed, otherwise ERROR
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
LOCAL STATUS vxbFtSpiInstUnlink(VXB_DEVICE_ID pDev, void *unused)
|
|
{
|
|
FT_SPI_CTRL *pDrvCtrl;
|
|
|
|
/* check if the pDev pointer is valid */
|
|
VXB_ASSERT(pDev != NULL, ERROR)
|
|
|
|
pDrvCtrl = (FT_SPI_CTRL *)pDev->pDrvCtrl;
|
|
|
|
/*
|
|
* The semaphore and interrupt resource are released here . The
|
|
* semaphore was created at phase 2 and interrupt was installed
|
|
* at phase 3.
|
|
*/
|
|
|
|
if (pDrvCtrl->initPhase >= 2) {
|
|
(void)semTake(pDrvCtrl->muxSem, WAIT_FOREVER);
|
|
|
|
(void)semDelete(pDrvCtrl->muxSem);
|
|
pDrvCtrl->muxSem = NULL;
|
|
}
|
|
|
|
#ifndef _VXBUS_BASIC_HWMEMLIB
|
|
hwMemFree((char *)pDrvCtrl);
|
|
#endif /* _VXBUS_BASIC_HWMEMLIB */
|
|
|
|
pDev->pDrvCtrl = NULL;
|
|
pDrvCtrl->initPhase = 0;
|
|
|
|
return (OK);
|
|
}
|
|
|