SPI NAND flash added
[keystone-rtos/ibl.git] / src / hw / spi / spi.c
1 /**********************************************************************************************
2  * FILE PURPOSE: The SPI boot driver
3  **********************************************************************************************
4  * FILE NAME: spi.c
5  *
6  * DESCRIPTION: Implements an SPI eeprom read.
7  *
8  **********************************************************************************************/
9 #include "types.h"
10 #include "device.h"
11 #include "spi_api.h"
12 #include "spi_loc.h"
14 typedef struct spimcb_s
15 {
16     spiConfig_t spiCfg;
18 } spimcb_t;
20 spimcb_t spimcb;
23 /**********************************************************************************************
24  * FUNCTION PURPOSE: Initialize the SPI interface
25  **********************************************************************************************
26  * DESCRIPTION: The SPI interface is setup. Only format register 0 is configured
27  **********************************************************************************************/
28 SINT16 hwSpiConfig (spiConfig_t *cfg)
29 {
30     UINT32 v;
32     if ((cfg->addrWidth != 24) && (cfg->addrWidth != 16))
33         return (SPI_INVALID_ADDR_WIDTH);
35     if ((cfg->npin != 4) && (cfg->npin != 5))
36         return (SPI_INVALID_NPIN);
38     /* Store the configuration for subsequent reads */
39     spimcb.spiCfg = *cfg;
40     
42     /* Reset */
43     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR0, SPI_REG_VAL_SPIGCR0_RESET);
45     /* Release Reset */
46     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR0, SPI_REG_VAL_SPIGCR0_ENABLE);
48     /* Master mode */
49     if (cfg->npin == 4)  
50         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIPC0, SPI_REG_VAL_SPIPC0_4PIN);
51     else
52         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIPC0, SPI_REG_VAL_SPIPC0_5PIN);
54     /* Format 0 register */
55     v = 0;
56     if (cfg->clkdiv > 0)
57         SPI_REG_SPIFMT_SET_PRESCALE(v, cfg->clkdiv - 1);
58     else
59         SPI_REG_SPIFMT_SET_PRESCALE(v, 0);
61     SPI_REG_SPIFMT_SET_CHARLEN(v, 8);
62     SPI_REG_SPIFMT_SET_MODE(v, cfg->mode);
63     SPI_REG_SPIFMT_SET_SHIFTDIR(v, SPI_REG_VAL_SPIFMT_SHIFT_MSB_FIRST);
64     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFMT(0), v);
66     /* Chip select to transmit delay */
67     v = 0;
68     SPI_REG_SPIDELAY_SET_C2T(v, cfg->c2tdelay);
69     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIDELAY, v);
72     return (0);
74 } /* hwSpiConfig */
77 /*************************************************************************************************
78  * FUNCTION PURPOSE: Perform an SPI transfer
79  *************************************************************************************************
80  * DESCRIPTION: A bi-directional transfer is done
81  *************************************************************************************************/
82 SINT16 hw_spi_xfer (UINT16 nbytes, UINT8 *dataOut, UINT8 *dataIn, spiConfig_t *cfg, BOOL terminate)
83 {
84     UINT32 v;
85     UINT32 i;
86     UINT32 tcount;
87     UINT32 timeout;
88     UINT32 spidat1;
91     /* The SPIDAT1 upper 16 bits */
92     spidat1 = 0;
93     SPI_REG_SPIDAT1_SET_CSHOLD(spidat1, 1);
94     SPI_REG_SPIDAT1_SET_WDELAY(spidat1, 1);
95     SPI_REG_SPIDAT1_SET_CSNR(spidat1, cfg->csel);
98     /* The timeout is used to prevent traps. 
99      *
100      * Each bit on the SPI bus will clock in at MOD_DIVIDER * cfg->clkdiv
101      * cpu cycles. So the timeout is based on the MOD_DIVIDER * cfg->clkdiv
102      * product. The actual number of cycles the loop takes isnt known
103      * with too much accuracy since the code is in C, so assume the
104      * compiler is spectacular and condenses each loop into just one
105      * cpu cycle. In that case it would take MOD_DIVIDER * cfg->clkdiv * 8
106      * passes through the loop to get the data byte. Since the compiler
107      * is slower then that the timeout value is good. But an extra
108      * x20 is thrown in just to be safe. */
110     timeout = (cfg->clkdiv + 1) * 8 * 20 * DEVICE_SPI_MOD_DIVIDER;
113     /* Clear out any pending read data */
114     do  {
115         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIBUF);
116         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
117     }  while (SPI_REG_SPIFLG_RX_DATA(v));
120     /* Perform the transfer */
121     for (i = 0; i < nbytes; i++)  {
124         /* For the last byte release the hold */
125         if ((terminate == TRUE) && (i == (nbytes - 1)))  {
126             SPI_REG_SPIDAT1_SET_CSHOLD(spidat1, 0);
127         }
129         tcount = 0;
131         do  {
133             v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
134             tcount += 1;
136         } while ( (SPI_REG_SPIFLG_TX_EMPTY(v) == 0) && (tcount < timeout) );
138         if (tcount >= timeout)  {
140             /* Disable transfer */
141             DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
143             return (SPI_TIMEOUT);
144         }
145         
146         if (dataOut != NULL)
147             v = dataOut[i];
148         else
149             v = 0;
152         /* Send the data */
153         SPI_REG_SPIDAT1_SET_DATA(spidat1, v);
154         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIDAT1, spidat1);
156         /* Receive the data */
157         tcount = 0;
159         do  {
161             v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
162             tcount += 1;
164         } while ( (SPI_REG_SPIFLG_RX_DATA(v) == 0) && (tcount < timeout) );
166         if (tcount >= timeout)  {
168             /* Disable transfer */
169             DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
171             return (SPI_TIMEOUT);
172         }
175         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIBUF);
177         if (dataIn != NULL)
178             dataIn[i] = v & 0xff;
181     }
183     return (0);
190 /*************************************************************************************************
191  * FUNCTION PURPOSE: Read a block of data
192  *************************************************************************************************
193  * DESCRIPTION: A single data block of a fixed size is read
194  *************************************************************************************************/
195 SINT16 hwSpiRead (UINT32 addr, UINT16 sizeBytes, UINT8 *data)
197     UINT32 n;
198     UINT8  command[4];
199     UINT16 ret;
201     /* Do nothing for a read of 0 bytes */
202     if (sizeBytes == 0)
203         return (0);
205     /* Format the read command and address */
206     command[0] = SPI_COMMAND_READ;
207     if (spimcb.spiCfg.addrWidth == 24)  {
208         n = 4;
209         command[1] = (addr >> 16) & 0xff;
210         command[2] = (addr >>  8) & 0xff;
211         command[3] = addr & 0xff;
213     }  else if (spimcb.spiCfg.addrWidth == 16)  {
214         n = 3;
215         command[1] = (addr >> 8) & 0xff;
216         command[2] = addr & 0xff;
218     }  else  {
219         return (SPI_INVALID_ADDR_WIDTH);
220     }
223     /* Enable the device for transfer */
224     DEVICE_REG32_W (DEVICE_SPI_BASE(spimcb.spiCfg.port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER);
226     /* Send the command and address */
227     ret = hw_spi_xfer (n, command, NULL, &spimcb.spiCfg, FALSE);
228     if (ret != 0)
229         return (ret);
232     /* Read the data */
233     ret = hw_spi_xfer (sizeBytes, NULL, data, &spimcb.spiCfg, TRUE);
234     if (ret != 0)
235         return (ret);
238     DEVICE_REG32_W (DEVICE_SPI_BASE(spimcb.spifg.port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
239     return (0);
244     
248