]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - keystone-rtos/ibl.git/blob - src/hw/spi/spi.c
First version working on 6608 EVM over SPI boot
[keystone-rtos/ibl.git] / src / hw / spi / spi.c
1 /*
2  *
3  * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 
4  * 
5  * 
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions 
8  *  are met:
9  *
10  *    Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  *    Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the   
16  *    distribution.
17  *
18  *    Neither the name of Texas Instruments Incorporated nor the names of
19  *    its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
23  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
24  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
26  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
27  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
28  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
31  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
32  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34 */
36 /**********************************************************************************************
37  * FILE PURPOSE: The SPI boot driver
38  **********************************************************************************************
39  * FILE NAME: spi.c
40  *
41  * DESCRIPTION: Implements an SPI eeprom read.
42  *
43  **********************************************************************************************/
44 #include "types.h"
45 #include "device.h"
46 #include "spi_api.h"
47 #include "spi_loc.h"
49 typedef struct spimcb_s
50 {
51     spiConfig_t spiCfg;
53 } spimcb_t;
55 spimcb_t spimcb;
58 /**********************************************************************************************
59  * FUNCTION PURPOSE: Initialize the SPI interface
60  **********************************************************************************************
61  * DESCRIPTION: The SPI interface is setup. Only format register 0 is configured
62  **********************************************************************************************/
63 SINT16 hwSpiConfig (spiConfig_t *cfg)
64 {
65     UINT32 v;
67     if ((cfg->addrWidth != 24) && (cfg->addrWidth != 16))
68         return (SPI_INVALID_ADDR_WIDTH);
70     if ((cfg->npin != 4) && (cfg->npin != 5))
71         return (SPI_INVALID_NPIN);
73     /* Store the configuration for subsequent reads */
74     spimcb.spiCfg = *cfg;
75     
77     /* Reset */
78     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR0, SPI_REG_VAL_SPIGCR0_RESET);
80     /* Release Reset */
81     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR0, SPI_REG_VAL_SPIGCR0_ENABLE);
83     /* Master mode */
84     if (cfg->npin == 4)  
85         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIPC0, SPI_REG_VAL_SPIPC0_4PIN);
86     else
87         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIPC0, SPI_REG_VAL_SPIPC0_5PIN);
89     /* Format 0 register */
90     v = 0;
91     if (cfg->clkdiv > 0)
92         SPI_REG_SPIFMT_SET_PRESCALE(v, cfg->clkdiv - 1);
93     else
94         SPI_REG_SPIFMT_SET_PRESCALE(v, 0);
96     SPI_REG_SPIFMT_SET_CHARLEN(v, 8);
97     SPI_REG_SPIFMT_SET_MODE(v, cfg->mode);
98     SPI_REG_SPIFMT_SET_SHIFTDIR(v, SPI_REG_VAL_SPIFMT_SHIFT_MSB_FIRST);
99     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFMT(0), v);
101     /* Chip select to transmit delay */
102     v = 0;
103     SPI_REG_SPIDELAY_SET_C2T(v, cfg->c2tdelay);
104     DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIDELAY, v);
107     return (0);
109 } /* hwSpiConfig */
112 /*************************************************************************************************
113  * FUNCTION PURPOSE: Perform an SPI transfer
114  *************************************************************************************************
115  * DESCRIPTION: A bi-directional transfer is done
116  *************************************************************************************************/
117 SINT16 hw_spi_xfer (UINT16 nbytes, UINT8 *dataOut, UINT8 *dataIn, spiConfig_t *cfg, BOOL terminate)
119     UINT32 v;
120     UINT32 i;
121     UINT32 tcount;
122     UINT32 timeout;
123     UINT32 spidat1;
126     /* The SPIDAT1 upper 16 bits */
127     spidat1 = 0;
128     SPI_REG_SPIDAT1_SET_CSHOLD(spidat1, 1);
129     SPI_REG_SPIDAT1_SET_WDELAY(spidat1, 1);
130     SPI_REG_SPIDAT1_SET_CSNR(spidat1, cfg->csel);
133     /* The timeout is used to prevent traps. 
134      *
135      * Each bit on the SPI bus will clock in at MOD_DIVIDER * cfg->clkdiv
136      * cpu cycles. So the timeout is based on the MOD_DIVIDER * cfg->clkdiv
137      * product. The actual number of cycles the loop takes isnt known
138      * with too much accuracy since the code is in C, so assume the
139      * compiler is spectacular and condenses each loop into just one
140      * cpu cycle. In that case it would take MOD_DIVIDER * cfg->clkdiv * 8
141      * passes through the loop to get the data byte. Since the compiler
142      * is slower then that the timeout value is good. But an extra
143      * x20 is thrown in just to be safe. */
145     timeout = (cfg->clkdiv + 1) * 8 * 20 * DEVICE_SPI_MOD_DIVIDER;
148     /* Clear out any pending read data */
149     do  {
150         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIBUF);
151         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
152     }  while (SPI_REG_SPIFLG_RX_DATA(v));
155     /* Perform the transfer */
156     for (i = 0; i < nbytes; i++)  {
159         /* For the last byte release the hold */
160         if ((terminate == TRUE) && (i == (nbytes - 1)))  {
161             SPI_REG_SPIDAT1_SET_CSHOLD(spidat1, 0);
162         }
164         tcount = 0;
166         do  {
168             v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
169             tcount += 1;
171         } while ( (SPI_REG_SPIFLG_TX_EMPTY(v) == 0) && (tcount < timeout) );
173         if (tcount >= timeout)  {
175             /* Disable transfer */
176             DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
178             return (SPI_TIMEOUT);
179         }
180         
181         if (dataOut != NULL)
182             v = dataOut[i];
183         else
184             v = 0;
187         /* Send the data */
188         SPI_REG_SPIDAT1_SET_DATA(spidat1, v);
189         DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIDAT1, spidat1);
191         /* Receive the data */
192         tcount = 0;
194         do  {
196             v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIFLG);
197             tcount += 1;
199         } while ( (SPI_REG_SPIFLG_RX_DATA(v) == 0) && (tcount < timeout) );
201         if (tcount >= timeout)  {
203             /* Disable transfer */
204             DEVICE_REG32_W (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
206             return (SPI_TIMEOUT);
207         }
210         v = DEVICE_REG32_R (DEVICE_SPI_BASE(cfg->port) + SPI_REG_SPIBUF);
212         if (dataIn != NULL)
213             dataIn[i] = v & 0xff;
216     }
218     return (0);
225 /*************************************************************************************************
226  * FUNCTION PURPOSE: Read a block of data
227  *************************************************************************************************
228  * DESCRIPTION: A single data block of a fixed size is read
229  *************************************************************************************************/
230 SINT16 hwSpiRead (UINT32 addr, UINT16 sizeBytes, UINT8 *data)
232     UINT32 n;
233     UINT8  command[4];
234     UINT16 ret;
236     /* Do nothing for a read of 0 bytes */
237     if (sizeBytes == 0)
238         return (0);
240     /* Format the read command and address */
241     command[0] = SPI_COMMAND_READ;
242     if (spimcb.spiCfg.addrWidth == 24)  {
243         n = 4;
244         command[1] = (addr >> 16) & 0xff;
245         command[2] = (addr >>  8) & 0xff;
246         command[3] = addr & 0xff;
248     }  else if (spimcb.spiCfg.addrWidth == 16)  {
249         n = 3;
250         command[1] = (addr >> 8) & 0xff;
251         command[2] = addr & 0xff;
253     }  else  {
254         return (SPI_INVALID_ADDR_WIDTH);
255     }
258     /* Enable the device for transfer */
259     DEVICE_REG32_W (DEVICE_SPI_BASE(spimcb.spiCfg.port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER);
261     /* Send the command and address */
262     ret = hw_spi_xfer (n, command, NULL, &spimcb.spiCfg, FALSE);
263     if (ret != 0)
264         return (ret);
267     /* Read the data */
268     ret = hw_spi_xfer (sizeBytes, NULL, data, &spimcb.spiCfg, TRUE);
269     if (ret != 0)
270         return (ret);
273     DEVICE_REG32_W (DEVICE_SPI_BASE(spimcb.spifg.port) + SPI_REG_SPIGCR1, SPI_REG_VAL_SPIGCR1_XFER_DISABLE);
274     return (0);
279     
283