summaryrefslogtreecommitdiffstats
path: root/shared/opensource/spi/bcmLegSpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/opensource/spi/bcmLegSpi.c')
-rwxr-xr-xshared/opensource/spi/bcmLegSpi.c774
1 files changed, 774 insertions, 0 deletions
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 */
+