]>
Commit | Line | Data |
---|---|---|
907208c4 CL |
1 | /* |
2 | * Copyright (c) 2001 Navin Boppuri / Prashant Patel | |
3 | * <[email protected]>, | |
4 | * <[email protected]> | |
5 | * Copyright (c) 2001 Gerd Mennchen <[email protected]> | |
6 | * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <[email protected]>. | |
7 | * | |
8 | * SPDX-License-Identifier: GPL-2.0+ | |
9 | */ | |
10 | ||
11 | /* | |
12 | * MPC8xx CPM SPI interface. | |
13 | * | |
14 | * Parts of this code are probably not portable and/or specific to | |
15 | * the board which I used for the tests. Please send fixes/complaints | |
16 | * to [email protected] | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <common.h> | |
21 | #include <mpc8xx.h> | |
22 | #include <commproc.h> | |
23 | #include <linux/ctype.h> | |
24 | #include <malloc.h> | |
25 | #include <post.h> | |
26 | #include <serial.h> | |
27 | ||
28 | #ifdef CONFIG_SPI | |
29 | ||
30 | #define SPI_EEPROM_WREN 0x06 | |
31 | #define SPI_EEPROM_RDSR 0x05 | |
32 | #define SPI_EEPROM_READ 0x03 | |
33 | #define SPI_EEPROM_WRITE 0x02 | |
34 | ||
35 | /* --------------------------------------------------------------- | |
36 | * Offset for initial SPI buffers in DPRAM: | |
37 | * We need a 520 byte scratch DPRAM area to use at an early stage. | |
38 | * It is used between the two initialization calls (spi_init_f() | |
39 | * and spi_init_r()). | |
40 | * The value 0xb00 makes it far enough from the start of the data | |
41 | * area (as well as from the stack pointer). | |
42 | * --------------------------------------------------------------- */ | |
43 | #ifndef CONFIG_SYS_SPI_INIT_OFFSET | |
44 | #define CONFIG_SYS_SPI_INIT_OFFSET 0xB00 | |
45 | #endif | |
46 | ||
47 | /* ------------------- | |
48 | * Function prototypes | |
49 | * ------------------- */ | |
50 | void spi_init (void); | |
51 | ||
52 | ssize_t spi_read (uchar *, int, uchar *, int); | |
53 | ssize_t spi_write (uchar *, int, uchar *, int); | |
54 | ssize_t spi_xfer (size_t); | |
55 | ||
56 | /* ------------------- | |
57 | * Variables | |
58 | * ------------------- */ | |
59 | ||
60 | #define MAX_BUFFER 0x104 | |
61 | ||
62 | /* ---------------------------------------------------------------------- | |
63 | * Initially we place the RX and TX buffers at a fixed location in DPRAM! | |
64 | * ---------------------------------------------------------------------- */ | |
65 | static uchar *rxbuf = | |
66 | (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem | |
67 | [CONFIG_SYS_SPI_INIT_OFFSET]; | |
68 | static uchar *txbuf = | |
69 | (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem | |
70 | [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER]; | |
71 | ||
72 | /* ************************************************************************** | |
73 | * | |
74 | * Function: spi_init_f | |
75 | * | |
76 | * Description: Init SPI-Controller (ROM part) | |
77 | * | |
78 | * return: --- | |
79 | * | |
80 | * *********************************************************************** */ | |
81 | void spi_init_f (void) | |
82 | { | |
83 | unsigned int dpaddr; | |
84 | ||
85 | volatile spi_t *spi; | |
86 | volatile immap_t *immr; | |
87 | volatile cpm8xx_t *cp; | |
88 | volatile cbd_t *tbdf, *rbdf; | |
89 | ||
90 | immr = (immap_t *) CONFIG_SYS_IMMR; | |
91 | cp = (cpm8xx_t *) &immr->im_cpm; | |
92 | ||
93 | spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; | |
94 | /* Disable relocation */ | |
95 | spi->spi_rpbase = 0; | |
96 | ||
97 | /* 1 */ | |
98 | /* ------------------------------------------------ | |
99 | * Initialize Port B SPI pins -> page 34-8 MPC860UM | |
100 | * (we are only in Master Mode !) | |
101 | * ------------------------------------------------ */ | |
102 | ||
103 | /* -------------------------------------------- | |
104 | * GPIO or per. Function | |
105 | * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO) | |
106 | * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI) | |
107 | * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK) | |
108 | * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM) | |
109 | * -------------------------------------------- */ | |
110 | cp->cp_pbpar |= 0x0000000E; /* set bits */ | |
111 | cp->cp_pbpar &= ~0x00000001; /* reset bit */ | |
112 | ||
113 | /* ---------------------------------------------- | |
114 | * In/Out or per. Function 0/1 | |
115 | * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO | |
116 | * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI | |
117 | * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK | |
118 | * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM | |
119 | * ---------------------------------------------- */ | |
120 | cp->cp_pbdir |= 0x0000000F; | |
121 | ||
122 | /* ---------------------------------------------- | |
123 | * open drain or active output | |
124 | * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO | |
125 | * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI | |
126 | * PBODR[30] = 0 [0x00000002] -> active output: SPICLK | |
127 | * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM | |
128 | * ---------------------------------------------- */ | |
129 | ||
130 | cp->cp_pbodr |= 0x00000008; | |
131 | cp->cp_pbodr &= ~0x00000007; | |
132 | ||
133 | /* Initialize the parameter ram. | |
134 | * We need to make sure many things are initialized to zero | |
135 | */ | |
136 | spi->spi_rstate = 0; | |
137 | spi->spi_rdp = 0; | |
138 | spi->spi_rbptr = 0; | |
139 | spi->spi_rbc = 0; | |
140 | spi->spi_rxtmp = 0; | |
141 | spi->spi_tstate = 0; | |
142 | spi->spi_tdp = 0; | |
143 | spi->spi_tbptr = 0; | |
144 | spi->spi_tbc = 0; | |
145 | spi->spi_txtmp = 0; | |
146 | ||
147 | dpaddr = CPM_SPI_BASE; | |
148 | ||
149 | /* 3 */ | |
150 | /* Set up the SPI parameters in the parameter ram */ | |
151 | spi->spi_rbase = dpaddr; | |
152 | spi->spi_tbase = dpaddr + sizeof (cbd_t); | |
153 | ||
154 | /***********IMPORTANT******************/ | |
155 | ||
156 | /* | |
157 | * Setting transmit and receive buffer descriptor pointers | |
158 | * initially to rbase and tbase. Only the microcode patches | |
159 | * documentation talks about initializing this pointer. This | |
160 | * is missing from the sample I2C driver. If you dont | |
161 | * initialize these pointers, the kernel hangs. | |
162 | */ | |
163 | spi->spi_rbptr = spi->spi_rbase; | |
164 | spi->spi_tbptr = spi->spi_tbase; | |
165 | ||
166 | /* 4 */ | |
167 | /* Init SPI Tx + Rx Parameters */ | |
168 | while (cp->cp_cpcr & CPM_CR_FLG) | |
169 | ; | |
170 | cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG; | |
171 | while (cp->cp_cpcr & CPM_CR_FLG) | |
172 | ; | |
173 | ||
174 | /* 5 */ | |
175 | /* Set SDMA configuration register */ | |
176 | immr->im_siu_conf.sc_sdcr = 0x0001; | |
177 | ||
178 | /* 6 */ | |
179 | /* Set to big endian. */ | |
180 | spi->spi_tfcr = SMC_EB; | |
181 | spi->spi_rfcr = SMC_EB; | |
182 | ||
183 | /* 7 */ | |
184 | /* Set maximum receive size. */ | |
185 | spi->spi_mrblr = MAX_BUFFER; | |
186 | ||
187 | /* 8 + 9 */ | |
188 | /* tx and rx buffer descriptors */ | |
189 | tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; | |
190 | rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; | |
191 | ||
192 | tbdf->cbd_sc &= ~BD_SC_READY; | |
193 | rbdf->cbd_sc &= ~BD_SC_EMPTY; | |
194 | ||
195 | /* Set the bd's rx and tx buffer address pointers */ | |
196 | rbdf->cbd_bufaddr = (ulong) rxbuf; | |
197 | tbdf->cbd_bufaddr = (ulong) txbuf; | |
198 | ||
199 | /* 10 + 11 */ | |
200 | cp->cp_spim = 0; /* Mask all SPI events */ | |
201 | cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ | |
202 | ||
203 | return; | |
204 | } | |
205 | ||
206 | /* ************************************************************************** | |
207 | * | |
208 | * Function: spi_init_r | |
209 | * | |
210 | * Description: Init SPI-Controller (RAM part) - | |
211 | * The malloc engine is ready and we can move our buffers to | |
212 | * normal RAM | |
213 | * | |
214 | * return: --- | |
215 | * | |
216 | * *********************************************************************** */ | |
217 | void spi_init_r (void) | |
218 | { | |
219 | volatile cpm8xx_t *cp; | |
220 | volatile spi_t *spi; | |
221 | volatile immap_t *immr; | |
222 | volatile cbd_t *tbdf, *rbdf; | |
223 | ||
224 | immr = (immap_t *) CONFIG_SYS_IMMR; | |
225 | cp = (cpm8xx_t *) &immr->im_cpm; | |
226 | ||
227 | spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; | |
228 | /* Disable relocation */ | |
229 | spi->spi_rpbase = 0; | |
230 | ||
231 | /* tx and rx buffer descriptors */ | |
232 | tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; | |
233 | rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; | |
234 | ||
235 | /* Allocate memory for RX and TX buffers */ | |
236 | rxbuf = (uchar *) malloc (MAX_BUFFER); | |
237 | txbuf = (uchar *) malloc (MAX_BUFFER); | |
238 | ||
239 | rbdf->cbd_bufaddr = (ulong) rxbuf; | |
240 | tbdf->cbd_bufaddr = (ulong) txbuf; | |
241 | ||
242 | return; | |
243 | } | |
244 | ||
245 | /**************************************************************************** | |
246 | * Function: spi_write | |
247 | **************************************************************************** */ | |
248 | ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len) | |
249 | { | |
250 | int i; | |
251 | ||
252 | memset(rxbuf, 0, MAX_BUFFER); | |
253 | memset(txbuf, 0, MAX_BUFFER); | |
254 | *txbuf = SPI_EEPROM_WREN; /* write enable */ | |
255 | spi_xfer(1); | |
256 | memcpy(txbuf, addr, alen); | |
257 | *txbuf = SPI_EEPROM_WRITE; /* WRITE memory array */ | |
258 | memcpy(alen + txbuf, buffer, len); | |
259 | spi_xfer(alen + len); | |
260 | /* ignore received data */ | |
261 | for (i = 0; i < 1000; i++) { | |
262 | *txbuf = SPI_EEPROM_RDSR; /* read status */ | |
263 | txbuf[1] = 0; | |
264 | spi_xfer(2); | |
265 | if (!(rxbuf[1] & 1)) { | |
266 | break; | |
267 | } | |
268 | udelay(1000); | |
269 | } | |
270 | if (i >= 1000) { | |
271 | printf ("*** spi_write: Time out while writing!\n"); | |
272 | } | |
273 | ||
274 | return len; | |
275 | } | |
276 | ||
277 | /**************************************************************************** | |
278 | * Function: spi_read | |
279 | **************************************************************************** */ | |
280 | ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len) | |
281 | { | |
282 | memset(rxbuf, 0, MAX_BUFFER); | |
283 | memset(txbuf, 0, MAX_BUFFER); | |
284 | memcpy(txbuf, addr, alen); | |
285 | *txbuf = SPI_EEPROM_READ; /* READ memory array */ | |
286 | ||
287 | /* | |
288 | * There is a bug in 860T (?) that cuts the last byte of input | |
289 | * if we're reading into DPRAM. The solution we choose here is | |
290 | * to always read len+1 bytes (we have one extra byte at the | |
291 | * end of the buffer). | |
292 | */ | |
293 | spi_xfer(alen + len + 1); | |
294 | memcpy(buffer, alen + rxbuf, len); | |
295 | ||
296 | return len; | |
297 | } | |
298 | ||
299 | /**************************************************************************** | |
300 | * Function: spi_xfer | |
301 | **************************************************************************** */ | |
302 | ssize_t spi_xfer (size_t count) | |
303 | { | |
304 | volatile immap_t *immr; | |
305 | volatile cpm8xx_t *cp; | |
306 | volatile spi_t *spi; | |
307 | cbd_t *tbdf, *rbdf; | |
308 | ushort loop; | |
309 | int tm; | |
310 | ||
311 | immr = (immap_t *) CONFIG_SYS_IMMR; | |
312 | cp = (cpm8xx_t *) &immr->im_cpm; | |
313 | ||
314 | spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; | |
315 | /* Disable relocation */ | |
316 | spi->spi_rpbase = 0; | |
317 | ||
318 | tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase]; | |
319 | rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase]; | |
320 | ||
321 | /* Set CS for device */ | |
322 | cp->cp_pbdat &= ~0x0001; | |
323 | ||
324 | /* Setting tx bd status and data length */ | |
325 | tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; | |
326 | tbdf->cbd_datlen = count; | |
327 | ||
328 | /* Setting rx bd status and data length */ | |
329 | rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; | |
330 | rbdf->cbd_datlen = 0; /* rx length has no significance */ | |
331 | ||
332 | loop = cp->cp_spmode & SPMODE_LOOP; | |
333 | cp->cp_spmode = /*SPMODE_DIV16 |*/ /* BRG/16 mode not used here */ | |
334 | loop | | |
335 | SPMODE_REV | | |
336 | SPMODE_MSTR | | |
337 | SPMODE_EN | | |
338 | SPMODE_LEN(8) | /* 8 Bits per char */ | |
339 | SPMODE_PM(0x8) ; /* medium speed */ | |
340 | cp->cp_spim = 0; /* Mask all SPI events */ | |
341 | cp->cp_spie = SPI_EMASK; /* Clear all SPI events */ | |
342 | ||
343 | /* start spi transfer */ | |
344 | cp->cp_spcom |= SPI_STR; /* Start transmit */ | |
345 | ||
346 | /* -------------------------------- | |
347 | * Wait for SPI transmit to get out | |
348 | * or time out (1 second = 1000 ms) | |
349 | * -------------------------------- */ | |
350 | for (tm=0; tm<1000; ++tm) { | |
351 | if (cp->cp_spie & SPI_TXB) { /* Tx Buffer Empty */ | |
352 | break; | |
353 | } | |
354 | if ((tbdf->cbd_sc & BD_SC_READY) == 0) { | |
355 | break; | |
356 | } | |
357 | udelay (1000); | |
358 | } | |
359 | if (tm >= 1000) { | |
360 | printf ("*** spi_xfer: Time out while xferring to/from SPI!\n"); | |
361 | } | |
362 | ||
363 | /* Clear CS for device */ | |
364 | cp->cp_pbdat |= 0x0001; | |
365 | ||
366 | return count; | |
367 | } | |
368 | #endif /* CONFIG_SPI */ |