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