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;
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)
118 {
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 }
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);
220 }
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)
231 {
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);
276 }