• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2019-2020, STMicroelectronics - All Rights Reserved
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <stddef.h>
10 
11 #include <common/debug.h>
12 #include <drivers/delay_timer.h>
13 #include <drivers/spi_nor.h>
14 #include <lib/utils.h>
15 
16 #define SR_WIP			BIT(0)	/* Write in progress */
17 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
18 #define SR_QUAD_EN_MX		BIT(6)	/* Macronix Quad I/O */
19 #define FSR_READY		BIT(7)	/* Device status, 0 = Busy, 1 = Ready */
20 
21 /* Defined IDs for supported memories */
22 #define SPANSION_ID		0x01U
23 #define MACRONIX_ID		0xC2U
24 #define MICRON_ID		0x2CU
25 
26 #define BANK_SIZE		0x1000000U
27 
28 #define SPI_READY_TIMEOUT_US	40000U
29 
30 static struct nor_device nor_dev;
31 
32 #pragma weak plat_get_nor_data
plat_get_nor_data(struct nor_device * device)33 int plat_get_nor_data(struct nor_device *device)
34 {
35 	return 0;
36 }
37 
spi_nor_reg(uint8_t reg,uint8_t * buf,size_t len,enum spi_mem_data_dir dir)38 static int spi_nor_reg(uint8_t reg, uint8_t *buf, size_t len,
39 		       enum spi_mem_data_dir dir)
40 {
41 	struct spi_mem_op op;
42 
43 	zeromem(&op, sizeof(struct spi_mem_op));
44 	op.cmd.opcode = reg;
45 	op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
46 	op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
47 	op.data.dir = dir;
48 	op.data.nbytes = len;
49 	op.data.buf = buf;
50 
51 	return spi_mem_exec_op(&op);
52 }
53 
spi_nor_read_id(uint8_t * id)54 static inline int spi_nor_read_id(uint8_t *id)
55 {
56 	return spi_nor_reg(SPI_NOR_OP_READ_ID, id, 1U, SPI_MEM_DATA_IN);
57 }
58 
spi_nor_read_cr(uint8_t * cr)59 static inline int spi_nor_read_cr(uint8_t *cr)
60 {
61 	return spi_nor_reg(SPI_NOR_OP_READ_CR, cr, 1U, SPI_MEM_DATA_IN);
62 }
63 
spi_nor_read_sr(uint8_t * sr)64 static inline int spi_nor_read_sr(uint8_t *sr)
65 {
66 	return spi_nor_reg(SPI_NOR_OP_READ_SR, sr, 1U, SPI_MEM_DATA_IN);
67 }
68 
spi_nor_read_fsr(uint8_t * fsr)69 static inline int spi_nor_read_fsr(uint8_t *fsr)
70 {
71 	return spi_nor_reg(SPI_NOR_OP_READ_FSR, fsr, 1U, SPI_MEM_DATA_IN);
72 }
73 
spi_nor_write_en(void)74 static inline int spi_nor_write_en(void)
75 {
76 	return spi_nor_reg(SPI_NOR_OP_WREN, NULL, 0U, SPI_MEM_DATA_OUT);
77 }
78 
79 /*
80  * Check if device is ready.
81  *
82  * Return 0 if ready, 1 if busy or a negative error code otherwise
83  */
spi_nor_ready(void)84 static int spi_nor_ready(void)
85 {
86 	uint8_t sr;
87 	int ret;
88 
89 	ret = spi_nor_read_sr(&sr);
90 	if (ret != 0) {
91 		return ret;
92 	}
93 
94 	if ((nor_dev.flags & SPI_NOR_USE_FSR) != 0U) {
95 		uint8_t fsr;
96 
97 		ret = spi_nor_read_fsr(&fsr);
98 		if (ret != 0) {
99 			return ret;
100 		}
101 
102 		return (((fsr & FSR_READY) != 0U) && ((sr & SR_WIP) == 0U)) ?
103 			0 : 1;
104 	}
105 
106 	return (((sr & SR_WIP) != 0U) ? 1 : 0);
107 }
108 
spi_nor_wait_ready(void)109 static int spi_nor_wait_ready(void)
110 {
111 	int ret;
112 	uint64_t timeout = timeout_init_us(SPI_READY_TIMEOUT_US);
113 
114 	while (!timeout_elapsed(timeout)) {
115 		ret = spi_nor_ready();
116 		if (ret <= 0) {
117 			return ret;
118 		}
119 	}
120 
121 	return -ETIMEDOUT;
122 }
123 
spi_nor_macronix_quad_enable(void)124 static int spi_nor_macronix_quad_enable(void)
125 {
126 	uint8_t sr;
127 	int ret;
128 
129 	ret = spi_nor_read_sr(&sr);
130 	if (ret != 0) {
131 		return ret;
132 	}
133 
134 	if ((sr & SR_QUAD_EN_MX) == 0U) {
135 		return 0;
136 	}
137 
138 	ret = spi_nor_write_en();
139 	if (ret != 0) {
140 		return ret;
141 	}
142 
143 	sr |= SR_QUAD_EN_MX;
144 	ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1, SPI_MEM_DATA_OUT);
145 	if (ret != 0) {
146 		return ret;
147 	}
148 
149 	ret = spi_nor_wait_ready();
150 	if (ret != 0) {
151 		return ret;
152 	}
153 
154 	ret = spi_nor_read_sr(&sr);
155 	if ((ret != 0) || ((sr & SR_QUAD_EN_MX) == 0U)) {
156 		return -EINVAL;
157 	}
158 
159 	return 0;
160 }
161 
spi_nor_write_sr_cr(uint8_t * sr_cr)162 static int spi_nor_write_sr_cr(uint8_t *sr_cr)
163 {
164 	int ret;
165 
166 	ret = spi_nor_write_en();
167 	if (ret != 0) {
168 		return ret;
169 	}
170 
171 	ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2, SPI_MEM_DATA_OUT);
172 	if (ret != 0) {
173 		return -EINVAL;
174 	}
175 
176 	ret = spi_nor_wait_ready();
177 	if (ret != 0) {
178 		return ret;
179 	}
180 
181 	return 0;
182 }
183 
spi_nor_quad_enable(void)184 static int spi_nor_quad_enable(void)
185 {
186 	uint8_t sr_cr[2];
187 	int ret;
188 
189 	ret = spi_nor_read_cr(&sr_cr[1]);
190 	if (ret != 0) {
191 		return ret;
192 	}
193 
194 	if ((sr_cr[1] & CR_QUAD_EN_SPAN) != 0U) {
195 		return 0;
196 	}
197 
198 	sr_cr[1] |= CR_QUAD_EN_SPAN;
199 	ret = spi_nor_read_sr(&sr_cr[0]);
200 	if (ret != 0) {
201 		return ret;
202 	}
203 
204 	ret = spi_nor_write_sr_cr(sr_cr);
205 	if (ret != 0) {
206 		return ret;
207 	}
208 
209 	ret = spi_nor_read_cr(&sr_cr[1]);
210 	if ((ret != 0) || ((sr_cr[1] & CR_QUAD_EN_SPAN) == 0U)) {
211 		return -EINVAL;
212 	}
213 
214 	return 0;
215 }
216 
spi_nor_clean_bar(void)217 static int spi_nor_clean_bar(void)
218 {
219 	int ret;
220 
221 	if (nor_dev.selected_bank == 0U) {
222 		return 0;
223 	}
224 
225 	nor_dev.selected_bank = 0U;
226 
227 	ret = spi_nor_write_en();
228 	if (ret != 0) {
229 		return ret;
230 	}
231 
232 	return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank,
233 			   1, SPI_MEM_DATA_OUT);
234 }
235 
spi_nor_write_bar(uint32_t offset)236 static int spi_nor_write_bar(uint32_t offset)
237 {
238 	uint8_t selected_bank = offset / BANK_SIZE;
239 	int ret;
240 
241 	if (selected_bank == nor_dev.selected_bank) {
242 		return 0;
243 	}
244 
245 	ret = spi_nor_write_en();
246 	if (ret != 0) {
247 		return ret;
248 	}
249 
250 	ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank,
251 			  1, SPI_MEM_DATA_OUT);
252 	if (ret != 0) {
253 		return ret;
254 	}
255 
256 	nor_dev.selected_bank = selected_bank;
257 
258 	return 0;
259 }
260 
spi_nor_read_bar(void)261 static int spi_nor_read_bar(void)
262 {
263 	uint8_t selected_bank = 0;
264 	int ret;
265 
266 	ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank,
267 			  1, SPI_MEM_DATA_IN);
268 	if (ret != 0) {
269 		return ret;
270 	}
271 
272 	nor_dev.selected_bank = selected_bank;
273 
274 	return 0;
275 }
276 
spi_nor_read(unsigned int offset,uintptr_t buffer,size_t length,size_t * length_read)277 int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length,
278 		 size_t *length_read)
279 {
280 	size_t remain_len;
281 	int ret;
282 
283 	*length_read = 0;
284 	nor_dev.read_op.addr.val = offset;
285 	nor_dev.read_op.data.buf = (void *)buffer;
286 
287 	VERBOSE("%s offset %i length %zu\n", __func__, offset, length);
288 
289 	while (length != 0U) {
290 		if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
291 			ret = spi_nor_write_bar(nor_dev.read_op.addr.val);
292 			if (ret != 0) {
293 				return ret;
294 			}
295 
296 			remain_len = (BANK_SIZE * (nor_dev.selected_bank + 1)) -
297 				nor_dev.read_op.addr.val;
298 			nor_dev.read_op.data.nbytes = MIN(length, remain_len);
299 		} else {
300 			nor_dev.read_op.data.nbytes = length;
301 		}
302 
303 		ret = spi_mem_exec_op(&nor_dev.read_op);
304 		if (ret != 0) {
305 			spi_nor_clean_bar();
306 			return ret;
307 		}
308 
309 		length -= nor_dev.read_op.data.nbytes;
310 		nor_dev.read_op.addr.val += nor_dev.read_op.data.nbytes;
311 		nor_dev.read_op.data.buf += nor_dev.read_op.data.nbytes;
312 		*length_read += nor_dev.read_op.data.nbytes;
313 	}
314 
315 	if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
316 		ret = spi_nor_clean_bar();
317 		if (ret != 0) {
318 			return ret;
319 		}
320 	}
321 
322 	return 0;
323 }
324 
spi_nor_init(unsigned long long * size,unsigned int * erase_size)325 int spi_nor_init(unsigned long long *size, unsigned int *erase_size)
326 {
327 	int ret = 0;
328 	uint8_t id;
329 
330 	/* Default read command used */
331 	nor_dev.read_op.cmd.opcode = SPI_NOR_OP_READ;
332 	nor_dev.read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
333 	nor_dev.read_op.addr.nbytes = 3U;
334 	nor_dev.read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
335 	nor_dev.read_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE;
336 	nor_dev.read_op.data.dir = SPI_MEM_DATA_IN;
337 
338 	if (plat_get_nor_data(&nor_dev) != 0) {
339 		return -EINVAL;
340 	}
341 
342 	assert(nor_dev.size != 0);
343 
344 	if (nor_dev.size > BANK_SIZE) {
345 		nor_dev.flags |= SPI_NOR_USE_BANK;
346 	}
347 
348 	*size = nor_dev.size;
349 
350 	ret = spi_nor_read_id(&id);
351 	if (ret != 0) {
352 		return ret;
353 	}
354 
355 	if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) {
356 		switch (id) {
357 		case SPANSION_ID:
358 			nor_dev.bank_read_cmd = SPINOR_OP_BRRD;
359 			nor_dev.bank_write_cmd = SPINOR_OP_BRWR;
360 			break;
361 		default:
362 			nor_dev.bank_read_cmd = SPINOR_OP_RDEAR;
363 			nor_dev.bank_write_cmd = SPINOR_OP_WREAR;
364 			break;
365 		}
366 	}
367 
368 	if (nor_dev.read_op.data.buswidth == 4U) {
369 		switch (id) {
370 		case MACRONIX_ID:
371 			INFO("Enable Macronix quad support\n");
372 			ret = spi_nor_macronix_quad_enable();
373 			break;
374 		case MICRON_ID:
375 			break;
376 		default:
377 			ret = spi_nor_quad_enable();
378 			break;
379 		}
380 	}
381 
382 	if ((ret == 0) && ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U)) {
383 		ret = spi_nor_read_bar();
384 	}
385 
386 	return ret;
387 }
388