drv_spi.cΒΆ

/*
 drv_spi.c
 LP2378 SPI driver
 Paul Lever
 Dec. 2007

 */

#include "includes.h"  // OS includes
#include "init.h"
#include "sys.h"
#include "driver.h"
#include "drv_spi.h"

#define SPI_DEFAULT_CLOCK 1000000

// we are supporting two SSP devices
#define SPI_DEVICE_COUNT 2

// define data for each supported device
typedef struct _SPI_DEVICE
{
    INT32U Clock; // clock rate
    INT8U OpenCount; // track opens handles to this device
    INT16U* pSSPnCR0; // SSP register set
    INT8U* pSSPnCR1;
    INT16U* pSSPnDR;
    INT8U* pSSPnSR;
    INT8U* pSSPnCPSR;
    INT8U* pSSPnIMSC;
    INT8U* pSSPnRIS;
    INT8U* pSSPnMIS;
    INT8U* pSSPnICR;
    INT16U* pSSPnDMACR;
    INT32U PinSck; // I/O pins
    INT32U PinMISO;
    INT32U PinMOSI;
} SPI_DEVICE, *PSPI_DEVICE;

// define a data extension to hold data required by this driver
typedef struct _SPI_EXT
{
    SPI_DEVICE Device[SPI_DEVICE_COUNT]; // each supported device
} SPI_EXT, *PSPI_EXT;

#define SPI0_SCK (1<<15)       /*       -  sck0  P0.15*/
#define SPI0_MISO (1<<17)      /*       -  MISO0 P0.17*/
#define SPI0_MOSI (1<<18)      /*       -  MOSI0 P0.18*/
#define SPI1_SCK (1<<7)        /* UEXT9 -  sck  P0.7*/
#define SPI1_MISO (1<<8)       /* UEXT7 -  MISO P0.8*/
#define SPI1_MOSI (1<<9)       /* UEXT8 -  MOSI P0.9*/

//status register
#define SSP_TNF   0x2
#define SSP_RNE   0x4
//control register0
#define SSP_SPIMODE  0x07
//control register1
#define SSP_ENABLE 0x02

// prototypes
INT8U Spi_Open(INT8U DeviceIndex, PVOID pContext, INT8U Flags);
INT8U Spi_Close(INT8U DeviceIndex, PVOID pContext);
INT8U Spi_Write(INT8U DeviceIndex, PVOID pContext, PVOID pBuffer,
        INT32U* pCount);
INT8U Spi_Ioctl(INT8U DeviceIndex, PVOID pContext, INT8U Control,
        PVOID pBuffer, INT32U* pCount);
static void SpiSetClock(PSPI_DEVICE pDevice, INT32U ClockRate);
static INT8U SpiInit(PSPI_DEVICE pDevice, INT32U ClockRate);

/*
 Spi_Init - SPI driver initialization routine
 */
INT8U Spi_Init(POSDRV_DISPATCH pDispatch, PVOID *ppContext,
        POSDRV_CONFIG pConfig)
{
    PSPI_DEVICE pDevice;

    // allocate the data extension for use to save data for this driver
    *ppContext = DRV_MEMALLOC(sizeof(SPI_EXT));
    if (*ppContext == NULL)
    {
        return OS_DRV_BUFFER_SIZE;
    }

    // zero the data extension
    memset(*ppContext, 0, sizeof(SPI_EXT));

    // initialize the addresses for the supported devices
    pDevice = &((PSPI_EXT) *ppContext)->Device[0];
    pDevice->pSSPnCR0 = (INT16U*)&SSP0CR0;
    pDevice->pSSPnCR1 = (INT8U*)&SSP0CR1;
    pDevice->pSSPnDR = (INT16U*)&SSP0DR;
    pDevice->pSSPnSR = (INT8U*)&SSP0SR;
    pDevice->pSSPnCPSR = (INT8U*)&SSP0CPSR;
    pDevice->pSSPnIMSC = (INT8U*)&SSP0IMSC;
    pDevice->pSSPnRIS = (INT8U*)&SSP0RIS;
    pDevice->pSSPnMIS = (INT8U*)&SSP0MIS;
    pDevice->pSSPnICR = (INT8U*)&SSP0ICR;
    pDevice->pSSPnDMACR = (INT16U*)&SSP0DMACR;

    //set the pins
    pDevice->PinSck = SPI0_SCK;
    pDevice->PinMISO = SPI0_MISO;
    pDevice->PinMOSI = SPI0_MOSI;

    // create a name for each device that we support
    OSDRV_AddName(pConfig, "SPI0");

    // now do the second device
    pDevice = &((PSPI_EXT) *ppContext)->Device[1];
    pDevice->pSSPnCR0 = (INT16U*)&SSP1CR0;
    pDevice->pSSPnCR1 = (INT8U*)&SSP1CR1;
    pDevice->pSSPnDR = (INT16U*)&SSP1DR;
    pDevice->pSSPnSR = (INT8U*)&SSP1SR;
    pDevice->pSSPnCPSR = (INT8U*)&SSP1CPSR;
    pDevice->pSSPnIMSC = (INT8U*)&SSP1IMSC;
    pDevice->pSSPnRIS = (INT8U*)&SSP1RIS;
    pDevice->pSSPnMIS = (INT8U*)&SSP1MIS;
    pDevice->pSSPnICR = (INT8U*)&SSP1ICR;
    pDevice->pSSPnDMACR = (INT16U*)&SSP1DMACR;

    //set the pins
    pDevice->PinSck = SPI1_SCK;
    pDevice->PinMISO = SPI1_MISO;
    pDevice->PinMOSI = SPI1_MOSI;

    // create a name for each device that we support
    OSDRV_AddName(pConfig, "SPI1");

    // setup the supported entry points for this driver
    pDispatch->pOpen = Spi_Open; // required
    pDispatch->pClose = Spi_Close; // required
    pDispatch->pRead = NULL; // optional, not currently supported in this driver
    pDispatch->pWrite = Spi_Write; // optional
    pDispatch->pIoctl = Spi_Ioctl; // optional

    // set this driver to accept non-serialized requests on each handle
    // any synchronization needs to be handled by the driver or caller above us
    pConfig->Exclusive = FALSE;

    // return success
    return OS_DRV_NO_ERR;
}

/*
 Spi_Open - open function for driver
 */
INT8U Spi_Open(INT8U DeviceIndex, PVOID pContext, INT8U Flags)
{
    PSPI_DEVICE pDevice = &((PSPI_EXT) pContext)->Device[DeviceIndex];
    INT8U err;

    if (DeviceIndex >= SPI_DEVICE_COUNT)
    {
        // someone passed us a bogus device
        DEBUGMSG(TRUE, ("SPI_DRV attempt to open bad device index: 0x%X\n\r", DeviceIndex));
        return OS_DRV_NO_DEVICE;
    }

    // we only allow one handle to the device at a time in this driver
    if (((PSPI_EXT) pContext)->Device[DeviceIndex].OpenCount > 0)
    {
        DEBUGMSG(TRUE, ("SPI_DRV attempt to open too many handles\n\r"));
        return OS_DRV_NO_DEVICE;
    }

    //initialize the SPI port
    err = SpiInit(pDevice, SPI_DEFAULT_CLOCK);

    if (err != OS_DRV_NO_ERR)
    {
        return err;
    }

    // count the open handles
    ((PSPI_EXT) pContext)->Device[DeviceIndex].OpenCount++;

    // return success
    return OS_DRV_NO_ERR;
}

/*
 Spi_Close - close function for driver
 */
INT8U Spi_Close(INT8U DeviceIndex, PVOID pContext)
{
    if (DeviceIndex >= SPI_DEVICE_COUNT)
    {
        // someone passed us a bogus device
        DEBUGMSG(TRUE, ("SPI_DRV attempt to close bad device index: 0x%X\n\r", DeviceIndex));
        return OS_DRV_NO_DEVICE;
    }

    // keep count of the open handles
    ((PSPI_EXT) pContext)->Device[DeviceIndex].OpenCount--;

    // return success
    return OS_DRV_NO_ERR;
}

/*
 Spi_Write - write function for driver
 */
INT8U Spi_Write(INT8U DeviceIndex, PVOID pContext, PVOID pBuffer,
        INT32U* pCount)
{
    INT32U length = *pCount;
    INT32U ii;
    PSPI_DEVICE pDevice = &((PSPI_EXT) pContext)->Device[DeviceIndex];
    INT8U tmp;

    *pCount = 0;

    if (DeviceIndex >= SPI_DEVICE_COUNT)
    {
        // someone passed us a bogus device
        DEBUGMSG(TRUE, ("SPI_DRV attempt to Write bad device index: 0x%X\n\r", DeviceIndex));
        return OS_DRV_NO_DEVICE;
    }

    //write out the bytes
    for (ii = 0; ii < length; ii++)
    {
        while (!((tmp = READREG8(pDevice->pSSPnSR)) & SSP_TNF))
        {
            // DEBUGMSG(TRUE, ("SPI_DRV SPSR: 0x%X\n\r", tmp));
        }
        WRITEREG16(pDevice->pSSPnDR, (INT16U)((INT8U*)pBuffer)[ii]);
        while (!((tmp = READREG8(pDevice->pSSPnSR)) & SSP_RNE))
        {
            // DEBUGMSG(TRUE, ("SPI_DRV SPSR: 0x%X\n\r", tmp));
        }

        //read back any input data
        ((INT8U*) pBuffer)[ii] = (INT8U) READREG16(pDevice->pSSPnDR);
    }
    *pCount = length;

    return OS_DRV_NO_ERR;
}

/*
 Spi_Ioctl - Ioctl function for driver
 */
INT8U Spi_Ioctl(INT8U DeviceIndex, PVOID pContext, INT8U Control,
        PVOID pBuffer, INT32U* pCount)
{
    PSPI_DEVICE pDevice = &((PSPI_EXT) pContext)->Device[DeviceIndex];

    if (DeviceIndex >= SPI_DEVICE_COUNT)
    {
        // someone passed us a bogus device
        DEBUGMSG(TRUE, ("SPI_DRV attempt to Ioctl bad device index: 0x%X\n\r", DeviceIndex));
        return OS_DRV_NO_DEVICE;
    }

    // determine which Control code is being used
    switch (Control)
    {
    // set master SPI clock
    case IOCTL_SPI_SET_CLOCK:
        // make sure we have enough room in the return buffer
        if (*pCount != sizeof(INT32U))
        {
            return OS_DRV_BUFFER_SIZE;
        }
        SpiSetClock(pDevice, *((INT32U *) pBuffer));

        // set the return length equal to the amount of data returned
        if (pCount != NULL)
        {
            *pCount = 0;
        }
        break;

        // return the current master clock
    case IOCTL_SPI_GET_CLOCK:
        // make sure we have enough room in the return buffer
        if (*pCount != sizeof(INT32U))
        {
            return OS_DRV_BUFFER_SIZE;
        }
        // return the current open count for this device
        *((INT32U *) pBuffer) = (long) pDevice->Clock;
        // set the return length equal to the amount of data returned
        if (pCount != NULL)
        {
            *pCount = sizeof(INT32U);
        }
        break;

    default:
        // we don't know this control code
        // set the return length equal to the amount of data returned
        if (pCount != NULL)
        {
            *pCount = 0;
        }
        return OS_DRV_UNSUPPORTED;
    }

    return OS_DRV_NO_ERR;
}

/*
 SpiInit - initialize the SPI port
 */
static INT8U SpiInit(PSPI_DEVICE pDevice, INT32U ClockRate)
{
    // pinsel0_P0 must be previously setup for SSP use.
    // set their direction
    FIO0DIR = (FIO0DIR | pDevice->PinSck | pDevice->PinMOSI) & ~pDevice->PinMISO;

    // set master mode, 8-bits, sample on first clock edge, no ints
    WRITEREG16(pDevice->pSSPnCR0, SSP_SPIMODE);

    SpiSetClock(pDevice, SPI_DEFAULT_CLOCK);
    //enable
    WRITEREG8(pDevice->pSSPnCR1, SSP_ENABLE);

    return OS_DRV_NO_ERR;
}
/*
 SpiSetClock - set the SPI master clock
 */
static void SpiSetClock(PSPI_DEVICE pDevice, INT32U ClockRate)
{
    //assumes SPI PCLK set to divide by 1
    INT32U clk = ((SYS_GetFsclk()) / ClockRate);
    clk = (clk < 2) ? 2 : clk;
    WRITEREG8(pDevice->pSSPnCPSR, (clk & 0xFE));

    pDevice->Clock = ClockRate;
}

Previous topic

driver.c

Next topic

drv_mp3.c

This Page