diff options
Diffstat (limited to 'shared/opensource/spi')
-rwxr-xr-x | shared/opensource/spi/Makefile | 30 | ||||
-rwxr-xr-x | shared/opensource/spi/bcmHsSpi.c | 898 | ||||
-rwxr-xr-x | shared/opensource/spi/bcmLegSpi.c | 774 | ||||
-rwxr-xr-x | shared/opensource/spi/bcmSpiRes.c | 660 |
4 files changed, 2362 insertions, 0 deletions
diff --git a/shared/opensource/spi/Makefile b/shared/opensource/spi/Makefile new file mode 100755 index 0000000..b3eb2b1 --- /dev/null +++ b/shared/opensource/spi/Makefile @@ -0,0 +1,30 @@ + +ifeq ($(CONFIG_MIPS_BRCM),y) + +ifeq ($(strip $(BRCM_CHIP)),6362) +obj-y += \ + bcmHsSpi.o +else +ifeq ($(strip $(BRCM_CHIP)),6816) +obj-y += \ + bcmHsSpi.o +else +ifeq ($(strip $(BRCM_CHIP)),6328) +obj-y += \ + bcmHsSpi.o +endif +endif +endif + +ifneq ($(strip $(BRCM_CHIP)),6328) +obj-y += \ + bcmLegSpi.o +endif + +obj-y += \ + bcmSpiRes.o + +EXTRA_CFLAGS += -DCONFIG_BCM9$(BRCM_CHIP) -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) + +endif + diff --git a/shared/opensource/spi/bcmHsSpi.c b/shared/opensource/spi/bcmHsSpi.c new file mode 100755 index 0000000..b32e9fc --- /dev/null +++ b/shared/opensource/spi/bcmHsSpi.c @@ -0,0 +1,898 @@ +/* + Copyright 2000-2010 Broadcom Corporation + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and to + copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Notwithstanding the above, under no circumstances may you combine this + software in any way with any other Broadcom software provided under a + license other than the GPL, without Broadcom's express prior written + consent. +*/ + +#ifdef _CFE_ +#include "lib_types.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "bcm_map.h" +#define printk printf +#else +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> + +#include <bcm_map_part.h> +#include <bcm_intr.h> +#endif + +/* if HS_SPI is defined then the HS SPI controller is available, otherwise do not compile this code */ + +#ifdef HS_SPI + +#include "bcmSpiRes.h" +#include "bcmSpi.h" + +int BcmHsSpiRead(unsigned char * msg_buf, int prependcnt, int nbytes, int devId, int freqHz); +int BcmHsSpiWrite(unsigned char * msg_buf, int nbytes, int devId, int freqHz); + +#define HS_SPI_STATE_CLOCK_POLARITY (1 << 31) +#define HS_SPI_STATE_GATE_CLOCK_SSOFF (1 << 30) +#define HS_SPI_STATE_LAUNCH_RISING (1 << 29) +#define HS_SPI_STATE_LATCH_RISING (1 << 28) +#define HS_SPI_STATE_ASYNC_CLOCK (1 << 27) +#if defined(_BCM96816_) || defined(CONFIG_BCM96816) +#define HS_SPI_CONTROLLER_STATE_DEF (HS_SPI_STATE_GATE_CLOCK_SSOFF) +#endif +#if defined(_BCM96328_) || defined(CONFIG_BCM96328) +#define HS_SPI_CONTROLLER_STATE_DEF (HS_SPI_STATE_GATE_CLOCK_SSOFF | HS_SPI_STATE_LATCH_RISING) +#endif +#if defined(_BCM96362_) || defined(CONFIG_BCM96362) +#define HS_SPI_CONTROLLER_STATE_DEF (HS_SPI_STATE_GATE_CLOCK_SSOFF | HS_SPI_STATE_LATCH_RISING | HS_SPI_STATE_ASYNC_CLOCK) +#endif + + +#ifndef _CFE_ +//#define HS_SPI_USE_INTERRUPTS /* define this to use interrupts instead of polling */ +static struct bcmspi BcmHsSpi = { SPIN_LOCK_UNLOCKED, + "bcmHsSpiDev", + }; +#else +#define udelay(X) \ + do { { int i; for( i = 0; i < (X) * 500; i++ ) ; } } while(0) +#endif + +static int hsSpiRead( unsigned char *pRxBuf, int prependcnt, int nbytes, int devId ) +{ + uint16 msgCtrl; + + HS_SPI_PROFILES[devId].mode_ctrl = prependcnt<<HS_SPI_PREPENDBYTE_CNT | 0<<HS_SPI_MODE_ONE_WIRE | + 0<<HS_SPI_MULTIDATA_WR_SIZE | 0<<HS_SPI_MULTIDATA_RD_SIZE | 2<<HS_SPI_MULTIDATA_WR_STRT | + 2<<HS_SPI_MULTIDATA_RD_STRT | 0xff<<HS_SPI_FILLBYTE; + + msgCtrl = (HS_SPI_OP_READ<<HS_SPI_OP_CODE) | nbytes; + memcpy((char *)HS_SPI_FIFO0, (void *)(&msgCtrl), 2); + + if ( 0 != prependcnt ) + { + memcpy((char *)(HS_SPI_FIFO0+2), (char *)pRxBuf, prependcnt); + } + + HS_SPI_PINGPONG0->command = devId<<HS_SPI_SS_NUM | devId<<HS_SPI_PROFILE_NUM | 0<<HS_SPI_TRIGGER_NUM | + HS_SPI_COMMAND_START_NOW<<HS_SPI_COMMAND_VALUE; + + return SPI_STATUS_OK; + +} + +static int hsSpiWriteFull( unsigned char *pTxBuf, int nbytes, int devId, int opcode ) +{ + uint16 msgCtrl; + + HS_SPI_PROFILES[devId].mode_ctrl = 0<<HS_SPI_PREPENDBYTE_CNT | 0<<HS_SPI_MODE_ONE_WIRE | + 0<<HS_SPI_MULTIDATA_WR_SIZE | 0<<HS_SPI_MULTIDATA_RD_SIZE | 2<<HS_SPI_MULTIDATA_WR_STRT | + 2<<HS_SPI_MULTIDATA_RD_STRT | 0xff<<HS_SPI_FILLBYTE; + + if (BCM_SPI_FULL == opcode) + { + msgCtrl = (HS_SPI_OP_READ_WRITE<<HS_SPI_OP_CODE) | nbytes; + } + else + { + msgCtrl = (HS_SPI_OP_WRITE<<HS_SPI_OP_CODE) | nbytes; + } + memcpy((char *)HS_SPI_FIFO0, (void *)(&msgCtrl), 2); + memcpy((char *)(HS_SPI_FIFO0+2), (void *)pTxBuf, nbytes); + + HS_SPI_PINGPONG0->command = devId<<HS_SPI_SS_NUM | devId<<HS_SPI_PROFILE_NUM | 0<<HS_SPI_TRIGGER_NUM | + HS_SPI_COMMAND_START_NOW<<HS_SPI_COMMAND_VALUE; + + return SPI_STATUS_OK; + +} + +static int hsSpiTransEnd( unsigned char *rxBuf, int nbytes ) +{ + if ( NULL != rxBuf ) + { + memcpy((char *)rxBuf, (void *)HS_SPI_FIFO0, nbytes); + } + + return SPI_STATUS_OK; + +} + +static int hsSpiTransPoll(void) +{ + unsigned int wait; + + for (wait = (100*1000); wait>0; wait--) + { + if (!(HS_SPI_PINGPONG0->status & 1<<HS_SPI_SOURCE_BUSY )) + { + break; + } + udelay(1); + } + + if (wait == 0) + { + return SPI_STATUS_ERR; + } + + return SPI_STATUS_OK; +} + + +static void hsSpiClearIntStatus(void) +{ + HS_SPI->hs_spiIntStatus = HS_SPI_INTR_CLEAR_ALL; +} + +#ifdef HS_SPI_USE_INTERRUPTS +static void hsSpiEnableInt(bool bEnable) +{ + if ( bEnable ) + { + HS_SPI->hs_spiIntMask = HS_SPI_IRQ_PING0_CMD_DONE; + } + else + { + HS_SPI->hs_spiIntMask = 0; + } +} +#endif + +#ifndef _CFE_ +static int hsSpiSetClock( int clockHz, int profile ) +{ + int clock; + + clock = HS_SPI_PLL_FREQ/clockHz; + if (HS_SPI_PLL_FREQ%HS_SPI_CLOCK_DEF) + clock++; + + clock = 2048/clock; + if (2048%(clock)) + clock++; + + HS_SPI_PROFILES[profile].clk_ctrl = 1<<HS_SPI_ACCUM_RST_ON_LOOP | 0<<HS_SPI_SPI_CLK_2X_SEL | clock<<HS_SPI_FREQ_CTRL_WORD; + + return SPI_STATUS_OK; +} + +static void hsSpiSetControllerState(unsigned int ctrlState, unsigned char devId) +{ + unsigned int temp32; + + temp32 = HS_SPI->hs_spiGlobalCtrl; + if ( 0 == (ctrlState & HS_SPI_STATE_GATE_CLOCK_SSOFF) ) + { + temp32 &= ~HS_SPI_CLK_GATE_SSOFF; + } + else + { + temp32 |= HS_SPI_CLK_GATE_SSOFF; + } +#if defined(_BCM96328_) || defined(CONFIG_BCM96328) || defined(_BCM96362_) || defined(CONFIG_BCM96362) + if ( 0 == (ctrlState & HS_SPI_STATE_CLOCK_POLARITY) ) + { + temp32 &= ~HS_SPI_CLK_POLARITY; + } + else + { + temp32 |= HS_SPI_CLK_POLARITY; + } +#endif + /* write value if required */ + if ( temp32 != HS_SPI->hs_spiGlobalCtrl ) + { + HS_SPI->hs_spiGlobalCtrl = temp32; + } + + temp32 = HS_SPI_PROFILES[devId].signal_ctrl; + if ( 0 == (ctrlState & HS_SPI_STATE_LATCH_RISING) ) + { + temp32 &= ~HS_SPI_LATCH_RISING; + } + else + { + temp32 |= HS_SPI_LATCH_RISING; + } + if ( 0 == (ctrlState & HS_SPI_STATE_LAUNCH_RISING) ) + { + temp32 &= ~HS_SPI_LAUNCH_RISING; + } + else + { + temp32 |= HS_SPI_LAUNCH_RISING; + } + +#if defined(_BCM96328_) || defined(CONFIG_BCM96328) || defined(_BCM96362_) || defined(CONFIG_BCM96362) + if ( 0 == (ctrlState & HS_SPI_STATE_ASYNC_CLOCK) ) + { + temp32 &= ~HS_SPI_ASYNC_INPUT_PATH; + } + else + { + temp32 |= HS_SPI_ASYNC_INPUT_PATH; + } +#endif + + /* write value if required */ + if ( temp32 != HS_SPI_PROFILES[devId].signal_ctrl ) + { + HS_SPI_PROFILES[devId].signal_ctrl = temp32; + } + +} +#endif + +/* these interfaces are availble for the CFE and spi flash driver only + all modules must use the linux kernel framework + if this is called by a module and interrupts are being used there will + be a problem */ +int BcmHsSpiRead( unsigned char *msg_buf, int prependcnt, int nbytes, int devId, int freqHz ) +{ +#ifndef _CFE_ + struct bcmspi *pBcmSpi = &BcmHsSpi; + + if ( pBcmSpi->irq ) + { + printk("BcmHsSpiRead error - Interrupts are enabled\n"); + return( SPI_STATUS_ERR ); + } + + spin_lock(&pBcmSpi->lock); + hsSpiSetControllerState(HS_SPI_CONTROLLER_STATE_DEF, devId); + hsSpiSetClock(freqHz, devId); +#endif + hsSpiClearIntStatus(); + hsSpiRead(msg_buf, prependcnt, nbytes, devId); + hsSpiTransPoll(); + hsSpiTransEnd(msg_buf, nbytes); + hsSpiClearIntStatus(); +#ifndef _CFE_ + spin_unlock(&pBcmSpi->lock); +#endif + + return( SPI_STATUS_OK ); +} + +int BcmHsSpiWrite( unsigned char *msg_buf, int nbytes, int devId, int freqHz ) +{ +#ifndef _CFE_ + struct bcmspi *pBcmSpi = &BcmHsSpi; + + if ( pBcmSpi->irq ) + { + printk("BcmHsSpiWrite error - Interrupts are enabled\n"); + return( SPI_STATUS_ERR ); + } + + spin_lock(&pBcmSpi->lock); + hsSpiSetControllerState(HS_SPI_CONTROLLER_STATE_DEF, devId); + hsSpiSetClock(freqHz, devId); +#endif + hsSpiClearIntStatus(); + hsSpiWriteFull(msg_buf, nbytes, devId, BCM_SPI_WRITE); + hsSpiTransPoll(); + hsSpiTransEnd(msg_buf, nbytes); + hsSpiClearIntStatus(); +#ifndef _CFE_ + spin_unlock(&pBcmSpi->lock); +#endif + + return( SPI_STATUS_OK ); +} + + +#ifndef _CFE_ +static void hsSpiNextMessage(struct bcmspi *pBcmSpi); + +static void hsSpiMsgDone(struct bcmspi *pBcmSpi, struct spi_message *msg, int status) +{ + list_del(&msg->queue); + msg->status = status; + + spin_unlock(&pBcmSpi->lock); + msg->complete(msg->context); + spin_lock(&pBcmSpi->lock); + + pBcmSpi->curTrans = NULL; + + /* continue if needed */ + if (list_empty(&pBcmSpi->queue) || pBcmSpi->stopping) + { + // disable controler ... + } + else + { + hsSpiNextMessage(pBcmSpi); + } +} + +#ifdef HS_SPI_USE_INTERRUPTS +static void hsSpiIntXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int length; + int prependCnt; + char *pTxBuf; + char *pRxBuf; + int opCode; + + xfer = pBcmSpi->curTrans; + if ( NULL == xfer) + { + xfer = list_entry(msg->transfers.next, struct spi_transfer, transfer_list); + } + else + { + xfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + } + pBcmSpi->curTrans = xfer; + + length = xfer->len; + prependCnt = 0; + pRxBuf = xfer->rx_buf; + pTxBuf = (unsigned char *)xfer->tx_buf; + + if ( (NULL != pRxBuf) && (NULL != pTxBuf) ) + { + opCode = BCM_SPI_FULL; + } + else if ( NULL != pRxBuf ) + { + opCode = BCM_SPI_READ; + } + else + { + opCode = BCM_SPI_WRITE; + } + + if ( msg->state ) + { + /* this controller does not support keeping the chip select active for all transfers + non NULL state indicates that we need to combine the transfers */ + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + prependCnt = length; + length = nextXfer->len; + pRxBuf = nextXfer->rx_buf; + opCode = BCM_SPI_READ; + pBcmSpi->curTrans = nextXfer; + } + + hsSpiSetClock(xfer->speed_hz, msg->spi->chip_select); + + hsSpiClearIntStatus(); + hsSpiEnableInt(TRUE); + if ( BCM_SPI_READ == opCode ) + { + hsSpiRead(pTxBuf, prependCnt, length, msg->spi->chip_select); + } + else + { + hsSpiWriteFull(pTxBuf, length, msg->spi->chip_select, opCode); + } + + return; + +} +#endif + +static void hsSpiPollXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int length; + int prependCnt; + char *pTxBuf; + char *pRxBuf; + int opCode; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) + { + pBcmSpi->curTrans = xfer; + length = xfer->len; + prependCnt = 0; + pRxBuf = xfer->rx_buf; + pTxBuf = (unsigned char *)xfer->tx_buf; + + if ( (NULL != pRxBuf) && (NULL != pTxBuf) ) + { + opCode = BCM_SPI_FULL; + } + else if ( NULL != pRxBuf ) + { + opCode = BCM_SPI_READ; + } + else + { + opCode = BCM_SPI_WRITE; + } + + if ( msg->state ) + { + /* this controller does not support keeping the chip select active for all transfers + non NULL state indicates that we need to combine the transfers */ + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + prependCnt = length; + length = nextXfer->len; + pRxBuf = nextXfer->rx_buf; + opCode = BCM_SPI_READ; + xfer = nextXfer; + } + + hsSpiSetClock(xfer->speed_hz, msg->spi->chip_select); + + hsSpiClearIntStatus(); + if ( BCM_SPI_READ == opCode ) + { + hsSpiRead(pTxBuf, prependCnt, length, msg->spi->chip_select); + } + else + { + hsSpiWriteFull(pTxBuf, length, msg->spi->chip_select, opCode); + } + + hsSpiTransPoll(); + hsSpiTransEnd(pRxBuf, length); + hsSpiClearIntStatus(); + + if (xfer->delay_usecs) + { + udelay(xfer->delay_usecs); + } + + msg->actual_length += length; + } + + hsSpiMsgDone(pBcmSpi, msg, SPI_STATUS_OK); + +} + + +static void hsSpiNextXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ +#ifdef HS_SPI_USE_INTERRUPTS + if (pBcmSpi->irq) + hsSpiIntXfer(pBcmSpi, msg); + else +#endif + hsSpiPollXfer(pBcmSpi, msg); +} + +static void hsSpiNextMessage(struct bcmspi *pBcmSpi) +{ + struct spi_message *msg; + unsigned int ctrlState; + + BUG_ON(pBcmSpi->curTrans); + + msg = list_entry(pBcmSpi->queue.next, struct spi_message, queue); + + /* set the controller state for this message */ + ctrlState = (unsigned int)spi_get_ctldata(msg->spi); + hsSpiSetControllerState(ctrlState, msg->spi->chip_select); + + /* there will always be one transfer in a given message */ + hsSpiNextXfer(pBcmSpi, msg); + +} + + +static int hsSpiSetup(struct spi_device *spi) +{ + struct bcmspi *pBcmSpi; + unsigned int spiCtrlData; + unsigned int spiCtrlState = 0; + + pBcmSpi = spi_master_get_devdata(spi->master); + + if (pBcmSpi->stopping) + return -ESHUTDOWN; + + spiCtrlData = (unsigned int)spi->controller_data; + if ( 0 == spiCtrlData ) + { + spiCtrlState = HS_SPI_CONTROLLER_STATE_DEF; + } + else + { + spiCtrlState = 0; + /* note that in HW, the meaning of latch and launch bits changes when CPOl = 1 */ + if ( (0 == (spi->mode & SPI_CPHA)) && + (0 == (spiCtrlData & SPI_CONTROLLER_STATE_CPHA_EXT)) ) + { + /* latch rising, launch falling */ + spiCtrlState = HS_SPI_STATE_LATCH_RISING; + } + else if ( (0 == (spi->mode & SPI_CPHA)) && + (0 != (spiCtrlData & SPI_CONTROLLER_STATE_CPHA_EXT)) ) + { + /* latch rising, launch rising */ + spiCtrlState = HS_SPI_STATE_LATCH_RISING | HS_SPI_STATE_LAUNCH_RISING; + } + else if ( (0 != (spi->mode & SPI_CPHA)) && + (0 == (spiCtrlData & SPI_CONTROLLER_STATE_CPHA_EXT)) ) + { + /* latch falling, launch rising */ + spiCtrlState = HS_SPI_STATE_LAUNCH_RISING; + } + // else - both set to 0, latch falling, launch falling + + if ( 0 != (spi->mode & SPI_CPOL) ) + { + spiCtrlState |= HS_SPI_STATE_CLOCK_POLARITY; + } + + if ( spiCtrlData & SPI_CONTROLLER_STATE_GATE_CLK_SSOFF ) + { + spiCtrlState |= HS_SPI_STATE_GATE_CLOCK_SSOFF; + } + + if ( spiCtrlData & SPI_CONTROLLER_STATE_ASYNC_CLOCK ) + { + spiCtrlState |= HS_SPI_STATE_ASYNC_CLOCK; + } + } + + spi_set_ctldata(spi, (void *)spiCtrlState); + + return 0; +} + + +static int hsSpiTransfer(struct spi_device *spi, struct spi_message *msg) +{ + struct bcmspi *pBcmSpi = &BcmHsSpi; + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int xferCnt; + int bCsChange; + int xferLen; + + if (unlikely(list_empty(&msg->transfers))) + return -EINVAL; + + if (pBcmSpi->stopping) + return -ESHUTDOWN; + + /* make sure a completion callback is set */ + if ( NULL == msg->complete ) + { + return -EINVAL; + } + + xferCnt = 0; + bCsChange = 0; + xferLen = 0; + list_for_each_entry(xfer, &msg->transfers, transfer_list) + { + /* check transfer parameters */ + if (!(xfer->tx_buf || xfer->rx_buf)) + { + return -EINVAL; + } + + /* check the clock setting - if it is 0 then set to max clock of the device */ + if ( 0 == xfer->speed_hz ) + { + if ( 0 == spi->max_speed_hz ) + { + return -EINVAL; + } + xfer->speed_hz = spi->max_speed_hz; + } + + xferCnt++; + xferLen += xfer->len; + bCsChange |= xfer->cs_change; + + if ( xfer->len > HS_SPI_BUFFER_LEN ) + { + return -EINVAL; + } + } + + /* this controller does not support keeping the chip select active between + transfers. If a message is detected with a write transfer followed by a + read transfer and cs_change is set to 0 then the two transfers need to be + combined. The message state is used to indicate that the transfers + need to be combined */ + msg->state = NULL; + if ( (2 == xferCnt) && (0 == bCsChange) ) + { + xfer = list_entry(msg->transfers.next, struct spi_transfer, transfer_list); + if ( (NULL != xfer->tx_buf) && (NULL == xfer->rx_buf)) + { + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list);; + if ( (NULL == nextXfer->tx_buf) && (NULL != nextXfer->rx_buf)) + { + msg->state = (void *)1; + } + } + } + + msg->status = -EINPROGRESS; + msg->actual_length = 0; + +#ifdef HS_SPI_USE_INTERRUPTS + /* disable interrupts for the SPI controller + using spin_lock_irqsave would disable all interrupts */ + if ( pBcmSpi->irq ) + hsSpiEnableInt(FALSE); +#endif + spin_lock(&pBcmSpi->lock); + + list_add_tail(&msg->queue, &pBcmSpi->queue); + if (NULL == pBcmSpi->curTrans) + { + hsSpiNextMessage(pBcmSpi); + } + + spin_unlock(&pBcmSpi->lock); +#ifdef HS_SPI_USE_INTERRUPTS + if ( pBcmSpi->irq ) + hsSpiEnableInt(TRUE); +#endif + + return 0; +} + + +#ifdef HS_SPI_USE_INTERRUPTS +static irqreturn_t hsSpiIntHandler(int irq, void *dev_id) +{ + struct bcmspi *pBcmSpi = dev_id; + struct spi_message *msg; + struct spi_transfer *xfer; + + if ( 0 == HS_SPI->hs_spiIntStatusMasked ) + { + return ( IRQ_NONE ); + } + + hsSpiClearIntStatus(); + hsSpiEnableInt(FALSE); + + spin_lock(&pBcmSpi->lock); + if ( NULL == pBcmSpi->curTrans ) + { + spin_unlock(&pBcmSpi->lock); + return IRQ_HANDLED; + } + + xfer = pBcmSpi->curTrans; + msg = list_entry(pBcmSpi->queue.next, struct spi_message, queue); + + hsSpiTransEnd(xfer->rx_buf, xfer->len); + + /* xfer can specify a delay before the next transfer is started + this delay would be processed here normally. However, delay in the + interrupt handler is bad so it is ignored. It is used for polling + mode */ + + /* check to see if this is the last transfer in the message */ + if (msg->transfers.prev == &xfer->transfer_list) + { + /* report completed message */ + hsSpiMsgDone(pBcmSpi, msg, SPI_STATUS_OK); + } + else + { + /* Submit the next transfer */ + hsSpiNextXfer(pBcmSpi, msg); + } + + spin_unlock(&pBcmSpi->lock); + + return IRQ_HANDLED; + +} + +int __init hsSpiIntrInit( void ) +{ + int ret = 0; + struct bcmspi *pBcmSpi = &BcmHsSpi; + + hsSpiEnableInt(FALSE); + ret = request_irq(INTERRUPT_ID_SPI, hsSpiIntHandler, (IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED), pBcmSpi->devName, pBcmSpi); + + spin_lock(&pBcmSpi->lock); + pBcmSpi->irq = INTERRUPT_ID_SPI; + spin_unlock(&pBcmSpi->lock); + + BcmHalInterruptEnable(pBcmSpi->irq); + + return( 0 ); + +} +/* we cannot initialize interrupts early + The flash module is intialized before an interrupt handler can be installed + and before the Linux framework can be used. This means it needs direct access + to the controller initially. This conflicts with the interrupt handling so we + need to wait for all modules to intialize */ +late_initcall(hsSpiIntrInit); +#endif + +static void hsSpiCleanup(struct spi_device *spi) +{ + /* would free spi_controller memory here if any was allocated */ + +} + +static int __init hsSpiProbe(struct platform_device *pdev) +{ + int ret; + struct spi_master *master; + struct bcmspi *pBcmSpi; + + ret = -ENOMEM; + master = spi_alloc_master(&pdev->dev, 0); + if (!master) + goto out_free; + + master->bus_num = pdev->id; + master->num_chipselect = 8; + master->setup = hsSpiSetup; + master->transfer = hsSpiTransfer; + master->cleanup = hsSpiCleanup; + platform_set_drvdata(pdev, master); + + spi_master_set_devdata(master, (void *)&BcmHsSpi); + pBcmSpi = spi_master_get_devdata(master); + + INIT_LIST_HEAD(&pBcmSpi->queue); + + pBcmSpi->pdev = pdev; + pBcmSpi->bus_num = HS_SPI_BUS_NUM; + pBcmSpi->num_chipselect = 8; + pBcmSpi->curTrans = NULL; + + /* make sure irq is 0 here + since this is used to identify when interrupts are enabled + the IRQ is initialized in hsSpiIntrInit */ + pBcmSpi->irq = 0; + + /* Initialize the hardware */ + + /* register and we are done */ + ret = spi_register_master(master); + if (ret) + goto out_free; + + return 0; + +out_free: + spi_master_put(master); + + return ret; +} + + +static int __exit hsSpiRemove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + struct spi_message *msg; + + /* reset the hardware and block queue progress */ +#ifdef HS_SPI_USE_INTERRUPTS + hsSpiEnableInt(FALSE); +#endif + spin_lock(&pBcmSpi->lock); + pBcmSpi->stopping = 1; + + /* HW shutdown */ + + spin_unlock(&pBcmSpi->lock); + + /* Terminate remaining queued transfers */ + list_for_each_entry(msg, &pBcmSpi->queue, queue) + { + msg->status = -ESHUTDOWN; + msg->complete(msg->context); + } + +#ifdef HS_SPI_USE_INTERRUPTS + if ( pBcmSpi->irq ) + { + free_irq(pBcmSpi->irq, master); + } +#endif + spi_unregister_master(master); + + return 0; +} + +//#ifdef CONFIG_PM +#if 0 +static int hsSpiSuspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + + return 0; +} + +static int hsSpiResume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + + return 0; +} +#else +#define hsSpiSuspend NULL +#define hsSpiResume NULL +#endif + +static struct platform_device bcm_hsspi_device = { + .name = "bcmhs_spi", + .id = HS_SPI_BUS_NUM, +}; + +static struct platform_driver bcm_hsspi_driver = { + .driver = + { + .name = "bcmhs_spi", + .owner = THIS_MODULE, + }, + .suspend = hsSpiSuspend, + .resume = hsSpiResume, + .remove = __exit_p(hsSpiRemove), +}; + +int __init hsSpiModInit( void ) +{ + platform_device_register(&bcm_hsspi_device); + return platform_driver_probe(&bcm_hsspi_driver, hsSpiProbe); + +} +subsys_initcall(hsSpiModInit); +#endif + +#endif /* HS_SPI */ + diff --git a/shared/opensource/spi/bcmLegSpi.c b/shared/opensource/spi/bcmLegSpi.c new file mode 100755 index 0000000..6d1a391 --- /dev/null +++ b/shared/opensource/spi/bcmLegSpi.c @@ -0,0 +1,774 @@ +/* + Copyright 2000-2010 Broadcom Corporation + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and to + copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Notwithstanding the above, under no circumstances may you combine this + software in any way with any other Broadcom software provided under a + license other than the GPL, without Broadcom's express prior written + consent. +*/ + +#ifdef _CFE_ +#include "lib_types.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "bcm_map.h" +#define printk printf +#else +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> + +#include <bcm_map_part.h> +#include <bcm_intr.h> +#endif + +/* if SPI is defined then the legacy SPI controller is available, otherwise do not compile this code */ +#ifdef SPI + +#include "bcmSpiRes.h" +#include "bcmSpi.h" + +int BcmLegSpiRead(unsigned char * msg_buf, int prependcnt, int nbytes, int devId, int freqHz); +int BcmLegSpiWrite(unsigned char * msg_buf, int nbytes, int devId, int freqHz); + +#ifndef _CFE_ +//#define LEG_SPI_USE_INTERRUPTS /* define this to use interrupts instead of polling */ +static struct bcmspi BcmLegSpi = { SPIN_LOCK_UNLOCKED, + "bcmLegSpiDev", + }; +#endif + +/* following are the frequency tables for the SPI controllers + they are ordered by frequency in descending order with column + 2 represetning the register value */ +#define LEG_SPI_FREQ_TABLE_SIZE 7 +int legSpiClockFreq[LEG_SPI_FREQ_TABLE_SIZE][2] = { + { 20000000, 0}, + { 12500000, 6}, + { 6250000, 5}, + { 3125000, 4}, + { 1563000, 3}, + { 781000, 2}, + { 391000, 1} }; + +static int legSpiRead( unsigned char *pRxBuf, int prependcnt, int nbytes, int devId ) +{ + int i; + + SPI->spiMsgCtl = (HALF_DUPLEX_R << SPI_MSG_TYPE_SHIFT) | (nbytes << SPI_BYTE_CNT_SHIFT); + + for (i = 0; i < prependcnt; i++) + { + SPI->spiMsgData[i] = pRxBuf[i]; + } + + SPI->spiCmd = (SPI_CMD_START_IMMEDIATE << SPI_CMD_COMMAND_SHIFT | + devId << SPI_CMD_DEVICE_ID_SHIFT | + prependcnt << SPI_CMD_PREPEND_BYTE_CNT_SHIFT | + 0 << SPI_CMD_ONE_BYTE_SHIFT); + + return SPI_STATUS_OK; + +} + +static int legSpiWriteFull( unsigned char *pTxBuf, int nbytes, int devId, int opcode ) +{ + int i; + + if ( opcode == BCM_SPI_FULL ) + { + SPI->spiMsgCtl = (FULL_DUPLEX_RW << SPI_MSG_TYPE_SHIFT) | (nbytes << SPI_BYTE_CNT_SHIFT); + } + else + { + SPI->spiMsgCtl = (HALF_DUPLEX_W << SPI_MSG_TYPE_SHIFT) | (nbytes << SPI_BYTE_CNT_SHIFT); + } + + for (i = 0; i < nbytes; i++) + { + SPI->spiMsgData[i] = pTxBuf[i]; + } + + SPI->spiCmd = (SPI_CMD_START_IMMEDIATE << SPI_CMD_COMMAND_SHIFT | + devId << SPI_CMD_DEVICE_ID_SHIFT | + 0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT | + 0 << SPI_CMD_ONE_BYTE_SHIFT); + + return SPI_STATUS_OK; + +} + + +static int legSpiTransEnd( unsigned char *rxBuf, int nbytes ) +{ + int i; + if ( NULL != rxBuf ) + { + for (i = 0; i < nbytes; i++) + { + rxBuf[i] = SPI->spiRxDataFifo[i]; + } + } + + return SPI_STATUS_OK; + +} + +static int legSpiTransPoll(void) +{ + while ( 1 ) + { + if ( SPI->spiIntStatus & SPI_INTR_CMD_DONE ) + { + break; + } + } + + return SPI_STATUS_OK; +} + +static void legSpiClearIntStatus(void) +{ + SPI->spiIntStatus = SPI_INTR_CLEAR_ALL; +} + +#ifdef LEG_SPI_USE_INTERRUPTS +static void legSpiEnableInt(bool bEnable) +{ + if ( bEnable ) + { + SPI->spiIntMask = SPI_INTR_CMD_DONE; + } + else + { + SPI->spiIntMask = 0; + } +} +#endif + +#ifndef _CFE_ +static int legSpiSetClock( int clockHz ) +{ + int i; + int clock = -1; + + for( i = 0; i < LEG_SPI_FREQ_TABLE_SIZE; i++ ) + { + /* look for the closest frequency that is less than the frequency passed in */ + if ( legSpiClockFreq[i][0] <= clockHz ) + { + clock = legSpiClockFreq[i][1]; + break; + } + } + /* if no clock was found set to default */ + if ( -1 == clock ) + { + clock = LEG_SPI_CLOCK_DEF; + } + SPI->spiClkCfg = (SPI->spiClkCfg & ~SPI_CLK_MASK) | clock; + + return SPI_STATUS_OK; +} +#endif + +/* these interfaces are availble for the CFE and spi flash driver only + all modules must use the linux kernel framework + if this is called by a module and interrupts are being used there will + be a problem */ +int BcmLegSpiRead( unsigned char *msg_buf, int prependcnt, int nbytes, int devId, int freqHz ) +{ +#ifndef _CFE_ + struct bcmspi *pBcmSpi = &BcmLegSpi; + + if ( pBcmSpi->irq ) + { + printk("BcmLegSpiRead error - SPI Interrupts are enabled\n"); + return( SPI_STATUS_ERR ); + } + + spin_lock(&pBcmSpi->lock); + legSpiSetClock(freqHz); +#endif + legSpiClearIntStatus(); + legSpiRead(msg_buf, prependcnt, nbytes, devId); + legSpiTransPoll(); + legSpiTransEnd(msg_buf, nbytes); + legSpiClearIntStatus(); +#ifndef _CFE_ + spin_unlock(&pBcmSpi->lock); +#endif + + return( SPI_STATUS_OK ); +} + +int BcmLegSpiWrite( unsigned char *msg_buf, int nbytes, int devId, int freqHz ) +{ +#ifndef _CFE_ + struct bcmspi *pBcmSpi = &BcmLegSpi; + + if ( pBcmSpi->irq ) + { + printk("BcmLegSpiWrite error - SPI Interrupts are enabled\n"); + return( SPI_STATUS_ERR ); + } + + spin_lock(&pBcmSpi->lock); + legSpiSetClock(freqHz); +#endif + legSpiClearIntStatus(); + legSpiWriteFull(msg_buf, nbytes, devId, BCM_SPI_WRITE); + legSpiTransPoll(); + legSpiTransEnd(msg_buf, nbytes); + legSpiClearIntStatus(); +#ifndef _CFE_ + spin_unlock(&pBcmSpi->lock); +#endif + + return( SPI_STATUS_OK ); +} + + +#ifndef _CFE_ +static void legSpiNextMessage(struct bcmspi *pBcmSpi); + +static void legSpiMsgDone(struct bcmspi *pBcmSpi, struct spi_message *msg, int status) +{ + list_del(&msg->queue); + msg->status = status; + + spin_unlock(&pBcmSpi->lock); + msg->complete(msg->context); + spin_lock(&pBcmSpi->lock); + + pBcmSpi->curTrans = NULL; + + /* continue if needed */ + if (list_empty(&pBcmSpi->queue) || pBcmSpi->stopping) + { + // disable controler ... + } + else + { + legSpiNextMessage(pBcmSpi); + } +} + +#ifdef LEG_SPI_USE_INTERRUPTS +static void legSpiIntXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int length; + int prependCnt; + char *pTxBuf; + char *pRxBuf; + int opCode; + + xfer = pBcmSpi->curTrans; + if ( NULL == xfer) + { + xfer = list_entry(msg->transfers.next, struct spi_transfer, transfer_list); + } + else + { + xfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + } + pBcmSpi->curTrans = xfer; + + length = xfer->len; + prependCnt = 0; + pRxBuf = xfer->rx_buf; + pTxBuf = (unsigned char *)xfer->tx_buf; + + if ( (NULL != pRxBuf) && (NULL != pTxBuf) ) + { + opCode = BCM_SPI_FULL; + } + else if ( NULL != pRxBuf ) + { + opCode = BCM_SPI_READ; + } + else + { + opCode = BCM_SPI_WRITE; + } + + if ( msg->state ) + { + /* this controller does not support keeping the chip select active for all transfers + non NULL state indicates that we need to combine the transfers */ + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + prependCnt = length; + length = nextXfer->len; + pRxBuf = nextXfer->rx_buf; + opCode = BCM_SPI_READ; + pBcmSpi->curTrans = nextXfer; + } + + legSpiSetClock(xfer->speed_hz); + + legSpiClearIntStatus(); + legSpiEnableInt(TRUE); + if ( BCM_SPI_READ == opCode ) + { + legSpiRead(pTxBuf, prependCnt, length, msg->spi->chip_select); + } + else + { + legSpiWriteFull(pTxBuf, length, msg->spi->chip_select, opCode); + } + + return; + +} +#endif + +static void legSpiPollXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int length; + int prependCnt; + char *pTxBuf; + char *pRxBuf; + int opCode; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) + { + pBcmSpi->curTrans = xfer; + length = xfer->len; + prependCnt = 0; + pRxBuf = xfer->rx_buf; + pTxBuf = (unsigned char *)xfer->tx_buf; + + if ( (NULL != pRxBuf) && (NULL != pTxBuf) ) + { + opCode = BCM_SPI_FULL; + } + else if ( NULL != pRxBuf ) + { + opCode = BCM_SPI_READ; + } + else + { + opCode = BCM_SPI_WRITE; + } + + if ( msg->state ) + { + /* this controller does not support keeping the chip select active for all transfers + non NULL state indicates that we need to combine the transfers */ + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list); + prependCnt = length; + length = nextXfer->len; + pRxBuf = nextXfer->rx_buf; + opCode = BCM_SPI_READ; + xfer = nextXfer; + } + + legSpiSetClock(xfer->speed_hz); + + legSpiClearIntStatus(); + if ( BCM_SPI_READ == opCode ) + { + legSpiRead(pTxBuf, prependCnt, length, msg->spi->chip_select); + } + else + { + legSpiWriteFull(pTxBuf, length, msg->spi->chip_select, opCode); + } + + legSpiTransPoll(); + legSpiTransEnd(pRxBuf, length); + legSpiClearIntStatus(); + + if (xfer->delay_usecs) + { + udelay(xfer->delay_usecs); + } + + msg->actual_length += length; + } + + legSpiMsgDone(pBcmSpi, msg, SPI_STATUS_OK); + +} + + +static void legSpiNextXfer(struct bcmspi *pBcmSpi, struct spi_message *msg) +{ +#ifdef LEG_SPI_USE_INTERRUPTS + if (pBcmSpi->irq) + legSpiIntXfer(pBcmSpi, msg); + else +#endif + legSpiPollXfer(pBcmSpi, msg); + +} + + +static void legSpiNextMessage(struct bcmspi *pBcmSpi) +{ + struct spi_message *msg; + + BUG_ON(pBcmSpi->curTrans); + + msg = list_entry(pBcmSpi->queue.next, struct spi_message, queue); + + /* there will always be one transfer in a given message */ + legSpiNextXfer(pBcmSpi, msg); + +} + + +static int legSpiSetup(struct spi_device *spi) +{ + struct bcmspi *pBcmSpi; + + pBcmSpi = spi_master_get_devdata(spi->master); + + if (pBcmSpi->stopping) + return -ESHUTDOWN; + + /* there is nothing to setup */ + + return 0; +} + + +int legSpiTransfer(struct spi_device *spi, struct spi_message *msg) +{ + struct bcmspi *pBcmSpi = &BcmLegSpi; + struct spi_transfer *xfer; + struct spi_transfer *nextXfer; + int xferCnt; + int bCsChange; + int xferLen; + + if (unlikely(list_empty(&msg->transfers))) + return -EINVAL; + + if (pBcmSpi->stopping) + return -ESHUTDOWN; + + /* make sure a completion callback is set */ + if ( NULL == msg->complete ) + { + return -EINVAL; + } + + xferCnt = 0; + bCsChange = 0; + xferLen = 0; + list_for_each_entry(xfer, &msg->transfers, transfer_list) + { + /* check transfer parameters */ + if (!(xfer->tx_buf || xfer->rx_buf)) + { + return -EINVAL; + } + + /* check the clock setting - if it is 0 then set to max clock of the device */ + if ( 0 == xfer->speed_hz ) + { + if ( 0 == spi->max_speed_hz ) + { + return -EINVAL; + } + xfer->speed_hz = spi->max_speed_hz; + } + + xferCnt++; + xferLen += xfer->len; + bCsChange |= xfer->cs_change; + + if ( xfer->len > (sizeof(SPI->spiMsgData) & ~0x3) ) + { + return -EINVAL; + } + } + + /* this controller does not support keeping the chip select active between + transfers. If a message is detected with a write transfer followed by a + read transfer and cs_change is set to 0 then the two transfers need to be + combined. The message state is used to indicate that the transfers + need to be combined */ + msg->state = NULL; + if ( (2 == xferCnt) && (0 == bCsChange) ) + { + xfer = list_entry(msg->transfers.next, struct spi_transfer, transfer_list); + if ( (NULL != xfer->tx_buf) && (NULL == xfer->rx_buf)) + { + nextXfer = list_entry(xfer->transfer_list.next, struct spi_transfer, transfer_list);; + if ( (NULL == nextXfer->tx_buf) && (NULL != nextXfer->rx_buf)) + { + msg->state = (void *)1; + } + } + } + + msg->status = -EINPROGRESS; + msg->actual_length = 0; + +#ifdef LEG_SPI_USE_INTERRUPTS + /* disable interrupts for the SPI controller + using spin_lock_irqsave would disable all interrupts */ + if ( pBcmSpi->irq ) + legSpiEnableInt(FALSE); +#endif + spin_lock(&pBcmSpi->lock); + + list_add_tail(&msg->queue, &pBcmSpi->queue); + if (NULL == pBcmSpi->curTrans) + { + legSpiNextMessage(pBcmSpi); + } + + spin_unlock(&pBcmSpi->lock); +#ifdef LEG_SPI_USE_INTERRUPTS + if ( pBcmSpi->irq ) + legSpiEnableInt(TRUE); +#endif + + return 0; +} + + +#ifdef LEG_SPI_USE_INTERRUPTS +static irqreturn_t legSpiIntHandler(int irq, void *dev_id) +{ + struct bcmspi *pBcmSpi = dev_id; + struct spi_message *msg; + struct spi_transfer *xfer; + + if ( 0 == SPI->spiMaskIntStatus ) + { + return ( IRQ_NONE ); + } + + legSpiClearIntStatus(); + legSpiEnableInt(FALSE); + + spin_lock(&pBcmSpi->lock); + if ( NULL == pBcmSpi->curTrans ) + { + spin_unlock(&pBcmSpi->lock); + return IRQ_HANDLED; + } + + xfer = pBcmSpi->curTrans; + msg = list_entry(pBcmSpi->queue.next, struct spi_message, queue); + + legSpiTransEnd(xfer->rx_buf, xfer->len); + + /* xfer can specify a delay before the next transfer is started + this is only used for polling mode */ + + /* check to see if this is the last transfer in the message */ + if (msg->transfers.prev == &xfer->transfer_list) + { + /* report completed message */ + legSpiMsgDone(pBcmSpi, msg, SPI_STATUS_OK); + } + else + { + /* Submit the next transfer */ + legSpiNextXfer(pBcmSpi, msg); + } + + spin_unlock(&pBcmSpi->lock); + + return IRQ_HANDLED; + +} + +int __init legSpiIntrInit( void ) +{ + int ret = 0; + struct bcmspi *pBcmSpi = &BcmLegSpi; + + legSpiEnableInt(FALSE); + ret = request_irq(INTERRUPT_ID_SPI, legSpiIntHandler, (IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED), pBcmSpi->devName, pBcmSpi); + + spin_lock(&pBcmSpi->lock); + pBcmSpi->irq = INTERRUPT_ID_SPI; + spin_unlock(&pBcmSpi->lock); + + BcmHalInterruptEnable(pBcmSpi->irq); + + return( 0 ); + +} +/* we cannot initialize interrupts early + The flash module is intialized before an interrupt handler can be installed + and before the Linux framework can be used. This means it needs direct access + to the controller initially. This conflicts with the interrupt handling so we + need to wait for all modules to intialize */ +late_initcall(legSpiIntrInit); +#endif + +static void legSpiCleanup(struct spi_device *spi) +{ + /* would free spi_controller memory here if any was allocated */ + +} + +static int __init legSpiProbe(struct platform_device *pdev) +{ + int ret; + struct spi_master *master; + struct bcmspi *pBcmSpi; + + ret = -ENOMEM; + master = spi_alloc_master(&pdev->dev, 0); + if (!master) + goto out_free; + + master->bus_num = pdev->id; + master->num_chipselect = 8; + master->setup = legSpiSetup; + master->transfer = legSpiTransfer; + master->cleanup = legSpiCleanup; + platform_set_drvdata(pdev, master); + + spi_master_set_devdata(master, (void *)&BcmLegSpi); + pBcmSpi = spi_master_get_devdata(master); + + INIT_LIST_HEAD(&pBcmSpi->queue); + + pBcmSpi->pdev = pdev; + pBcmSpi->bus_num = LEG_SPI_BUS_NUM; + pBcmSpi->num_chipselect = 8; + pBcmSpi->curTrans = NULL; + + /* make sure irq is 0 here + since this is used to identify when interrupts are enabled + the IRQ is initialized in legSpiIntrInit */ + pBcmSpi->irq = 0; + + /* Initialize the hardware */ + + /* register and we are done */ + ret = spi_register_master(master); + if (ret) + goto out_free; + + return 0; + +out_free: + spi_master_put(master); + + return ret; +} + + +static int __exit legSpiRemove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + struct spi_message *msg; + + /* reset the hardware and block queue progress */ +#ifdef LEG_SPI_USE_INTERRUPTS + legSpiEnableInt(FALSE); +#endif + spin_lock(&pBcmSpi->lock); + pBcmSpi->stopping = 1; + + /* HW shutdown */ + + spin_unlock(&pBcmSpi->lock); + + /* Terminate remaining queued transfers */ + list_for_each_entry(msg, &pBcmSpi->queue, queue) + { + msg->status = -ESHUTDOWN; + msg->complete(msg->context); + } + +#ifdef LEG_SPI_USE_INTERRUPTS + if ( pBcmSpi->irq ) + { + free_irq(pBcmSpi->irq, master); + } +#endif + spi_unregister_master(master); + + return 0; +} + +//#ifdef CONFIG_PM +#if 0 +static int legSpiSuspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + + return 0; +} + +static int legSpiResume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcmspi *pBcmSpi = spi_master_get_devdata(master); + + return 0; +} +#else +#define legSpiSuspend NULL +#define legSpiResume NULL +#endif + + +static struct platform_device bcm_legacyspi_device = { + .name = "bcmleg_spi", + .id = LEG_SPI_BUS_NUM, +}; + +static struct platform_driver bcm_legspi_driver = { + .driver = + { + .name = "bcmleg_spi", + .owner = THIS_MODULE, + }, + .suspend = legSpiSuspend, + .resume = legSpiResume, + .remove = __exit_p(legSpiRemove), +}; + + +int __init legSpiModInit( void ) +{ + platform_device_register(&bcm_legacyspi_device); + return platform_driver_probe(&bcm_legspi_driver, legSpiProbe); + +} +subsys_initcall(legSpiModInit); +#endif + +#endif /* SPI */ + diff --git a/shared/opensource/spi/bcmSpiRes.c b/shared/opensource/spi/bcmSpiRes.c new file mode 100755 index 0000000..ed16575 --- /dev/null +++ b/shared/opensource/spi/bcmSpiRes.c @@ -0,0 +1,660 @@ +/* + Copyright 2000-2010 Broadcom Corporation + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and to + copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Notwithstanding the above, under no circumstances may you combine this + software in any way with any other Broadcom software provided under a + license other than the GPL, without Broadcom's express prior written + consent. +*/ + +#ifdef _CFE_ +#include "lib_types.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "bcm_map.h" +#define printk printf +#else +#include <linux/version.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) +#include <linux/semaphore.h> +#endif +#include <linux/spi/spi.h> +#include <linux/autoconf.h> + +#include <bcm_map_part.h> +#endif +#include "bcmSpiRes.h" + +extern int BcmLegSpiRead(unsigned char * msg_buf, int prependcnt, int nbytes, int devId, int freqHz); +extern int BcmLegSpiWrite(unsigned char * msg_buf, int nbytes, int devId, int freqHz); +extern int BcmHsSpiRead(unsigned char * msg_buf, int prependcnt, int nbytes, int devId, int freqHz); +extern int BcmHsSpiWrite(unsigned char * msg_buf, int nbytes, int devId, int freqHz); + +#ifndef _CFE_ +#ifdef SPI +/* the BCM legacy controller supports up to 8 devices */ +static struct spi_board_info bcmLegSpiDevInfo[8] = +{ + { + .modalias = "bcm_LegSpiDev0", + .chip_select = 0, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev1", + .chip_select = 1, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev2", + .chip_select = 2, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev3", + .chip_select = 3, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev4", + .chip_select = 4, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev5", + .chip_select = 5, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev6", + .chip_select = 6, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, + { + .modalias = "bcm_LegSpiDev7", + .chip_select = 7, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, + }, +}; + +static struct spi_driver bcmLegSpiDevDrv[8] = +{ + { + .driver = + { + .name = "bcm_LegSpiDev0", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev1", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev2", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev3", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev4", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev5", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev6", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_LegSpiDev7", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, +}; + +static struct spi_device * bcmLegSpiDevices[8]; +#endif + +#ifdef HS_SPI +/* the BCM HS controller supports up to 8 devices */ +static struct spi_board_info bcmHSSpiDevInfo[8] = +{ + { + .modalias = "bcm_HSSpiDev0", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 0, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev1", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 1, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev2", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 2, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev3", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 3, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev4", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 4, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev5", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 5, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev6", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 6, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, + { + .modalias = "bcm_HSSpiDev7", + .controller_data = (void *)SPI_CONTROLLER_STATE_DEFAULT, + .chip_select = 7, + .max_speed_hz = 781000, + .bus_num = HS_SPI_BUS_NUM, + .mode = SPI_MODE_DEFAULT, + }, +}; + +static struct spi_driver bcmHSSpiDevDrv[8] = +{ + { + .driver = + { + .name = "bcm_HSSpiDev0", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev1", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev2", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev3", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev4", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev5", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev6", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, + { + .driver = + { + .name = "bcm_HSSpiDev7", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + }, +}; + +static struct spi_device * bcmHSSpiDevices[8]; +#endif + + +int BcmSpiReserveSlave2(int busNum, int slaveId, int maxFreq, int spiMode, int ctrlState) +{ + struct spi_master * pSpiMaster; + struct spi_driver * pSpiDriver; + + if ( slaveId > 7 ) + { + return SPI_STATUS_ERR; + } + + if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL != bcmLegSpiDevices[slaveId] ) + { + printk(KERN_ERR "BcmSpiReserveSlave - slaveId %d, already registerd\n", slaveId); + return( SPI_STATUS_ERR ); + } + + bcmLegSpiDevInfo[slaveId].max_speed_hz = maxFreq; + bcmLegSpiDevInfo[slaveId].controller_data = (void *)ctrlState; + bcmLegSpiDevInfo[slaveId].mode = spiMode; + + pSpiMaster = spi_busnum_to_master( busNum ); + bcmLegSpiDevices[slaveId] = spi_new_device(pSpiMaster, &bcmLegSpiDevInfo[slaveId]); + pSpiDriver = &bcmLegSpiDevDrv[slaveId]; +#endif + } + else if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL != bcmHSSpiDevices[slaveId] ) + { + printk(KERN_ERR "BcmSpiReserveSlave - slaveId %d, already registerd\n", slaveId); + return( SPI_STATUS_ERR ); + } + + bcmHSSpiDevInfo[slaveId].max_speed_hz = maxFreq; + bcmHSSpiDevInfo[slaveId].controller_data = (void *)ctrlState; + bcmHSSpiDevInfo[slaveId].mode = spiMode; + + pSpiMaster = spi_busnum_to_master( busNum ); + bcmHSSpiDevices[slaveId] = spi_new_device(pSpiMaster, &bcmHSSpiDevInfo[slaveId]); + pSpiDriver = &bcmHSSpiDevDrv[slaveId]; +#endif + } + else + return( SPI_STATUS_ERR ); + + /* register the SPI driver */ + spi_register_driver(pSpiDriver); + + return 0; + +} +EXPORT_SYMBOL(BcmSpiReserveSlave2); + +int BcmSpiReserveSlave(int busNum, int slaveId, int maxFreq) +{ + return( BcmSpiReserveSlave2(busNum, slaveId, maxFreq, SPI_MODE_DEFAULT, SPI_CONTROLLER_STATE_DEFAULT) ); +} +EXPORT_SYMBOL(BcmSpiReserveSlave); + +int BcmSpiReleaseSlave(int busNum, int slaveId) +{ + if ( slaveId > 7 ) + { + return SPI_STATUS_ERR; + } + + if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL == bcmLegSpiDevices[slaveId] ) + { + printk(KERN_ERR "BcmSpiReleaseSlave - slaveId %d, already released\n", slaveId); + return( SPI_STATUS_ERR ); + } + + bcmLegSpiDevInfo[slaveId].max_speed_hz = 781000; + spi_unregister_driver(&bcmLegSpiDevDrv[slaveId]); + spi_unregister_device(bcmLegSpiDevices[slaveId]); + bcmLegSpiDevices[slaveId] = 0; +#endif + } + else if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL == bcmHSSpiDevices[slaveId] ) + { + printk(KERN_ERR "BcmSpiReleaseSlave - slaveId %d, already released\n", slaveId); + return( SPI_STATUS_ERR ); + } + + bcmHSSpiDevInfo[slaveId].max_speed_hz = 781000; + spi_unregister_driver(&bcmHSSpiDevDrv[slaveId]); + spi_unregister_device(bcmHSSpiDevices[slaveId]); + bcmHSSpiDevices[slaveId] = 0; +#endif + } + else + return( SPI_STATUS_ERR ); + + return 0; + +} +EXPORT_SYMBOL(BcmSpiReleaseSlave); + + +int BcmSpiSyncTrans(unsigned char *txBuf, unsigned char *rxBuf, int prependcnt, int nbytes, int busNum, int slaveId) +{ + struct spi_message msg; + struct spi_transfer xfer[2]; + int status; + int maxLength; + struct spi_device *pSpiDevice; + + maxLength = BcmSpi_GetMaxRWSize(busNum); + if ( (nbytes > maxLength) || (prependcnt > maxLength) ) + { + printk(KERN_ERR "ERROR BcmSpiSyncTrans: invalid length len %d, pre %d, max %d\n", nbytes, prependcnt, maxLength); + return SPI_STATUS_ERR; + } + + if ( slaveId > 7 ) + { + printk(KERN_ERR "ERROR BcmSpiSyncTrans: invalid slave id %d\n", slaveId); + return SPI_STATUS_ERR; + } + + if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL == bcmLegSpiDevices[slaveId] ) + { + printk(KERN_ERR "ERROR BcmSpiSyncTrans: device not registered\n"); + return SPI_STATUS_ERR; + } + pSpiDevice = bcmLegSpiDevices[slaveId]; +#endif + } + else if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return( SPI_STATUS_ERR ); +#else + if ( NULL == bcmHSSpiDevices[slaveId] ) + { + printk(KERN_ERR "ERROR BcmSpiSyncTrans: device not registered\n"); + return SPI_STATUS_ERR; + } + pSpiDevice = bcmHSSpiDevices[slaveId]; +#endif + } + else + return( SPI_STATUS_ERR ); + + spi_message_init(&msg); + memset(xfer, 0, (sizeof xfer)); + + if ( prependcnt ) + { + xfer[0].len = prependcnt; + xfer[0].speed_hz = pSpiDevice->max_speed_hz; + if ( txBuf ) + { + xfer[0].tx_buf = txBuf; + } + else + { + xfer[0].tx_buf = rxBuf; + } + spi_message_add_tail(&xfer[0], &msg); + } + + xfer[1].len = nbytes; + xfer[1].speed_hz = pSpiDevice->max_speed_hz; + xfer[1].rx_buf = rxBuf; + + /* for the controller to use the prepend count correctly the first operation must be a read and the second a write + make sure tx is NULL for second transaction */ + if ( 0 == prependcnt ) + { + xfer[1].tx_buf = txBuf; + } + spi_message_add_tail(&xfer[1], &msg); + + status = spi_sync(pSpiDevice, &msg); + if (status >= 0) + { + status = SPI_STATUS_OK; + } + else + { + status = SPI_STATUS_ERR; + } + + return( status ); + +} +EXPORT_SYMBOL(BcmSpiSyncTrans); +#endif + +int BcmSpi_SetFlashCtrl( int opCode, int addrBytes, int dummyBytes, int busNum, int devId ) +{ + if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return SPI_STATUS_ERR; +#else + int clock; + + clock = HS_SPI_PLL_FREQ/HS_SPI_CLOCK_DEF; + if (HS_SPI_PLL_FREQ%HS_SPI_CLOCK_DEF) + clock++; + + clock = 2048/clock; + if (2048%(clock)) + clock++; + + HS_SPI_PROFILES[0].clk_ctrl = 1<<HS_SPI_ACCUM_RST_ON_LOOP | 0<<HS_SPI_SPI_CLK_2X_SEL | clock<<HS_SPI_FREQ_CTRL_WORD; + HS_SPI->hs_spiFlashCtrl = devId<<HS_SPI_FCTRL_SS_NUM | 0<<HS_SPI_FCTRL_PROFILE_NUM | dummyBytes<<HS_SPI_FCTRL_DUMMY_BYTES | + addrBytes<<HS_SPI_FCTRL_ADDR_BYTES | opCode<<HS_SPI_FCTRL_READ_OPCODE; +#endif + } + else if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return SPI_STATUS_ERR; +#endif + } + else + return SPI_STATUS_ERR; + + return SPI_STATUS_OK; + +} + + +int BcmSpi_GetMaxRWSize( int busNum ) +{ + int maxRWSize = 0; + + if ( HS_SPI_BUS_NUM == busNum ) + { +#ifdef HS_SPI + maxRWSize = HS_SPI_BUFFER_LEN; +#endif + } + else if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifdef SPI + maxRWSize = sizeof(SPI->spiMsgData); +#endif + } + + maxRWSize &= ~0x3; + + return(maxRWSize); + +} + + +/* The interface bcmSpi_Read and bcmSpi_Write provide direct access to the SPI controller. + these interfaces should only be called by CFE and early spi flash code */ +int BcmSpi_Read( unsigned char *msg_buf, int prependcnt, int nbytes, int busNum, int devId, int freqHz ) +{ + if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return SPI_STATUS_ERR; +#else + return BcmLegSpiRead( msg_buf, prependcnt, nbytes, devId, freqHz ); +#endif + } + else if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return SPI_STATUS_ERR; +#else + return BcmHsSpiRead( msg_buf, prependcnt, nbytes, devId, freqHz ); +#endif + } + else + { + return SPI_STATUS_ERR; + } + +} + +int BcmSpi_Write( unsigned char *msg_buf, int nbytes, int busNum, int devId, int freqHz ) +{ + if ( LEG_SPI_BUS_NUM == busNum ) + { +#ifndef SPI + return SPI_STATUS_ERR; +#else + return BcmLegSpiWrite( msg_buf, nbytes, devId, freqHz ); +#endif + } + else if ( HS_SPI_BUS_NUM == busNum ) + { +#ifndef HS_SPI + return SPI_STATUS_ERR; +#else + return BcmHsSpiWrite( msg_buf, nbytes, devId, freqHz ); +#endif + } + else + { + return SPI_STATUS_ERR; + } +} + +#ifndef _CFE_ +EXPORT_SYMBOL(BcmSpi_SetFlashCtrl); +EXPORT_SYMBOL(BcmSpi_GetMaxRWSize); +#endif + |