/* vim: set ts=4 sw=4 et fdm=marker: */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <../src/hwif/h/vxbus/vxbAccess.h> #include #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); }