Commit f77f469117ab3184ac45683a50dc446265be28cc
1 parent
ab92224f45
Exists in
master
and in
50 other branches
sf: Add dual memories support - DUAL_STACKED
This patch added support for accessing dual memories in stacked connection with single chipselect line from controller. For more info - see doc/SPI/README.dual-flash Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
Showing 6 changed files with 138 additions and 11 deletions Side-by-side Diff
doc/SPI/README.dual-flash
1 | +SPI/QSPI Dual flash connection modes: | |
2 | +===================================== | |
3 | + | |
4 | +This describes how SPI/QSPI flash memories are connected to a given | |
5 | +controller in a single chip select line. | |
6 | + | |
7 | +Current spi_flash framework supports, single flash memory connected | |
8 | +to a given controller with single chip select line, but there are some | |
9 | +hw logics(ex: xilinx zynq qspi) that describes two/dual memories are | |
10 | +connected with a single chip select line from a controller. | |
11 | + | |
12 | +"dual_flash" from include/spi.h describes these types of connection mode | |
13 | + | |
14 | +Possible connections: | |
15 | +-------------------- | |
16 | +SF_SINGLE_FLASH: | |
17 | + - single spi flash memory connected with single chip select line. | |
18 | + | |
19 | + +------------+ CS +---------------+ | |
20 | + | |----------------------->| | | |
21 | + | Controller | I0[3:0] | Flash memory | | |
22 | + | SPI/QSPI |<======================>| (SPI/QSPI) | | |
23 | + | | CLK | | | |
24 | + | |----------------------->| | | |
25 | + +------------+ +---------------+ | |
26 | + | |
27 | +SF_DUAL_STACKED_FLASH: | |
28 | + - dual spi/qspi flash memories are connected with a single chipselect | |
29 | + line and these two memories are operating stacked fasion with shared buses. | |
30 | + - xilinx zynq qspi controller has implemented this feature [1] | |
31 | + | |
32 | + +------------+ CS +---------------+ | |
33 | + | |---------------------->| | | |
34 | + | | I0[3:0] | Upper Flash | | |
35 | + | | +=========>| memory | | |
36 | + | | | CLK | (SPI/QSPI) | | |
37 | + | | | +---->| | | |
38 | + | Controller | CS | | +---------------+ | |
39 | + | SPI/QSPI |------------|----|---->| | | |
40 | + | | I0[3:0] | | | Lower Flash | | |
41 | + | |<===========+====|====>| memory | | |
42 | + | | CLK | | (SPI/QSPI) | | |
43 | + | |-----------------+---->| | | |
44 | + +------------+ +---------------+ | |
45 | + | |
46 | + - two memory flash devices should has same hw part attributes (like size, | |
47 | + vendor..etc) | |
48 | + - Configurations: | |
49 | + on LQSPI_CFG register, Enable TWO_MEM[BIT:30] on LQSPI_CFG | |
50 | + Enable U_PAGE[BIT:28] if U_PAGE flag set - upper memory | |
51 | + Disable U_PAGE[BIT:28] if U_PAGE flag unset - lower memory | |
52 | + - Operation: | |
53 | + accessing memories serially like one after another. | |
54 | + by default, if U_PAGE is unset lower memory should accessible, | |
55 | + once user wants to access upper memory need to set U_PAGE. | |
56 | + | |
57 | +Note: Technically there is only one CS line from the controller, but | |
58 | +zynq qspi controller has an internal hw logic to enable additional CS | |
59 | +when controller is configured for dual memories. | |
60 | + | |
61 | +[1] http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf | |
62 | + | |
63 | +-- | |
64 | +Jagannadha Sutradharudu Teki <jaganna@xilinx.com> | |
65 | +05-01-2014. |
drivers/mtd/spi/sf.c
drivers/mtd/spi/sf_ops.c
... | ... | @@ -131,10 +131,28 @@ |
131 | 131 | } |
132 | 132 | #endif |
133 | 133 | |
134 | +static void spi_flash_dual_flash(struct spi_flash *flash, u32 *addr) | |
135 | +{ | |
136 | + switch (flash->dual_flash) { | |
137 | + case SF_DUAL_STACKED_FLASH: | |
138 | + if (*addr >= (flash->size >> 1)) { | |
139 | + *addr -= flash->size >> 1; | |
140 | + flash->spi->flags |= SPI_XFER_U_PAGE; | |
141 | + } else { | |
142 | + flash->spi->flags &= ~SPI_XFER_U_PAGE; | |
143 | + } | |
144 | + break; | |
145 | + default: | |
146 | + debug("SF: Unsupported dual_flash=%d\n", flash->dual_flash); | |
147 | + break; | |
148 | + } | |
149 | +} | |
150 | + | |
134 | 151 | int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) |
135 | 152 | { |
136 | 153 | struct spi_slave *spi = flash->spi; |
137 | 154 | unsigned long timebase; |
155 | + unsigned long flags = SPI_XFER_BEGIN; | |
138 | 156 | int ret; |
139 | 157 | u8 status; |
140 | 158 | u8 check_status = 0x0; |
... | ... | @@ -146,7 +164,10 @@ |
146 | 164 | check_status = poll_bit; |
147 | 165 | } |
148 | 166 | |
149 | - ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); | |
167 | + if (spi->flags & SPI_XFER_U_PAGE) | |
168 | + flags |= SPI_XFER_U_PAGE; | |
169 | + | |
170 | + ret = spi_xfer(spi, 8, &cmd, NULL, flags); | |
150 | 171 | if (ret) { |
151 | 172 | debug("SF: fail to read %s status register\n", |
152 | 173 | cmd == CMD_READ_STATUS ? "read" : "flag"); |
... | ... | @@ -219,7 +240,7 @@ |
219 | 240 | |
220 | 241 | int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) |
221 | 242 | { |
222 | - u32 erase_size; | |
243 | + u32 erase_size, erase_addr; | |
223 | 244 | u8 cmd[SPI_FLASH_CMD_LEN]; |
224 | 245 | int ret = -1; |
225 | 246 | |
226 | 247 | |
227 | 248 | |
228 | 249 | |
... | ... | @@ -231,15 +252,20 @@ |
231 | 252 | |
232 | 253 | cmd[0] = flash->erase_cmd; |
233 | 254 | while (len) { |
255 | + erase_addr = offset; | |
256 | + | |
257 | + if (flash->dual_flash > SF_SINGLE_FLASH) | |
258 | + spi_flash_dual_flash(flash, &erase_addr); | |
259 | + | |
234 | 260 | #ifdef CONFIG_SPI_FLASH_BAR |
235 | - ret = spi_flash_bank(flash, offset); | |
261 | + ret = spi_flash_bank(flash, erase_addr); | |
236 | 262 | if (ret < 0) |
237 | 263 | return ret; |
238 | 264 | #endif |
239 | - spi_flash_addr(offset, cmd); | |
265 | + spi_flash_addr(erase_addr, cmd); | |
240 | 266 | |
241 | 267 | debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], |
242 | - cmd[2], cmd[3], offset); | |
268 | + cmd[2], cmd[3], erase_addr); | |
243 | 269 | |
244 | 270 | ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); |
245 | 271 | if (ret < 0) { |
... | ... | @@ -258,6 +284,7 @@ |
258 | 284 | size_t len, const void *buf) |
259 | 285 | { |
260 | 286 | unsigned long byte_addr, page_size; |
287 | + u32 write_addr; | |
261 | 288 | size_t chunk_len, actual; |
262 | 289 | u8 cmd[SPI_FLASH_CMD_LEN]; |
263 | 290 | int ret = -1; |
264 | 291 | |
... | ... | @@ -266,8 +293,13 @@ |
266 | 293 | |
267 | 294 | cmd[0] = flash->write_cmd; |
268 | 295 | for (actual = 0; actual < len; actual += chunk_len) { |
296 | + write_addr = offset; | |
297 | + | |
298 | + if (flash->dual_flash > SF_SINGLE_FLASH) | |
299 | + spi_flash_dual_flash(flash, &write_addr); | |
300 | + | |
269 | 301 | #ifdef CONFIG_SPI_FLASH_BAR |
270 | - ret = spi_flash_bank(flash, offset); | |
302 | + ret = spi_flash_bank(flash, write_addr); | |
271 | 303 | if (ret < 0) |
272 | 304 | return ret; |
273 | 305 | #endif |
... | ... | @@ -277,7 +309,7 @@ |
277 | 309 | if (flash->spi->max_write_size) |
278 | 310 | chunk_len = min(chunk_len, flash->spi->max_write_size); |
279 | 311 | |
280 | - spi_flash_addr(offset, cmd); | |
312 | + spi_flash_addr(write_addr, cmd); | |
281 | 313 | |
282 | 314 | debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", |
283 | 315 | buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); |
... | ... | @@ -322,7 +354,7 @@ |
322 | 354 | size_t len, void *data) |
323 | 355 | { |
324 | 356 | u8 *cmd, cmdsz; |
325 | - u32 remain_len, read_len; | |
357 | + u32 remain_len, read_len, read_addr; | |
326 | 358 | int bank_sel = 0; |
327 | 359 | int ret = -1; |
328 | 360 | |
329 | 361 | |
... | ... | @@ -346,8 +378,13 @@ |
346 | 378 | |
347 | 379 | cmd[0] = flash->read_cmd; |
348 | 380 | while (len) { |
381 | + read_addr = offset; | |
382 | + | |
383 | + if (flash->dual_flash > SF_SINGLE_FLASH) | |
384 | + spi_flash_dual_flash(flash, &read_addr); | |
385 | + | |
349 | 386 | #ifdef CONFIG_SPI_FLASH_BAR |
350 | - bank_sel = spi_flash_bank(flash, offset); | |
387 | + bank_sel = spi_flash_bank(flash, read_addr); | |
351 | 388 | if (bank_sel < 0) |
352 | 389 | return ret; |
353 | 390 | #endif |
... | ... | @@ -357,7 +394,7 @@ |
357 | 394 | else |
358 | 395 | read_len = remain_len; |
359 | 396 | |
360 | - spi_flash_addr(offset, cmd); | |
397 | + spi_flash_addr(read_addr, cmd); | |
361 | 398 | |
362 | 399 | ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); |
363 | 400 | if (ret < 0) { |
drivers/mtd/spi/sf_probe.c
... | ... | @@ -134,6 +134,7 @@ |
134 | 134 | flash->spi = spi; |
135 | 135 | flash->name = params->name; |
136 | 136 | flash->memory_map = spi->memory_map; |
137 | + flash->dual_flash = flash->spi->option; | |
137 | 138 | |
138 | 139 | /* Assign spi_flash ops */ |
139 | 140 | flash->write = spi_flash_cmd_write_ops; |
... | ... | @@ -148,6 +149,8 @@ |
148 | 149 | flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256; |
149 | 150 | flash->sector_size = params->sector_size; |
150 | 151 | flash->size = flash->sector_size * params->nr_sectors; |
152 | + if (flash->dual_flash & SF_DUAL_STACKED_FLASH) | |
153 | + flash->size <<= 1; | |
151 | 154 | |
152 | 155 | /* Compute erase sector and command */ |
153 | 156 | if (params->flags & SECT_4K) { |
... | ... | @@ -324,7 +327,10 @@ |
324 | 327 | puts("\n"); |
325 | 328 | #endif |
326 | 329 | #ifndef CONFIG_SPI_FLASH_BAR |
327 | - if (flash->size > SPI_FLASH_16MB_BOUN) { | |
330 | + if (((flash->dual_flash == SF_SINGLE_FLASH) && | |
331 | + (flash->size > SPI_FLASH_16MB_BOUN)) || | |
332 | + ((flash->dual_flash > SF_SINGLE_FLASH) && | |
333 | + (flash->size > SPI_FLASH_16MB_BOUN << 1))) { | |
328 | 334 | puts("SF: Warning - Only lower 16MiB accessible,"); |
329 | 335 | puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); |
330 | 336 | } |
include/spi.h
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | #define SPI_XFER_MMAP 0x08 /* Memory Mapped start */ |
31 | 31 | #define SPI_XFER_MMAP_END 0x10 /* Memory Mapped End */ |
32 | 32 | #define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) |
33 | +#define SPI_XFER_U_PAGE (1 << 5) | |
33 | 34 | |
34 | 35 | /* SPI TX operation modes */ |
35 | 36 | #define SPI_OPM_TX_QPP 1 << 0 |
... | ... | @@ -44,6 +45,9 @@ |
44 | 45 | SPI_OPM_RX_DIO | SPI_OPM_RX_QOF | \ |
45 | 46 | SPI_OPM_RX_QIOF |
46 | 47 | |
48 | +/* SPI bus connection options */ | |
49 | +#define SPI_CONN_DUAL_SHARED 1 << 0 | |
50 | + | |
47 | 51 | /* Header byte that marks the start of the message */ |
48 | 52 | #define SPI_PREAMBLE_END_BYTE 0xec |
49 | 53 | |
... | ... | @@ -62,6 +66,8 @@ |
62 | 66 | * @max_write_size: If non-zero, the maximum number of bytes which can |
63 | 67 | * be written at once, excluding command bytes. |
64 | 68 | * @memory_map: Address of read-only SPI flash access. |
69 | + * @option: Varies SPI bus options - separate bus. | |
70 | + * @flags: Indication of SPI flags. | |
65 | 71 | */ |
66 | 72 | struct spi_slave { |
67 | 73 | unsigned int bus; |
... | ... | @@ -71,6 +77,8 @@ |
71 | 77 | unsigned int wordlen; |
72 | 78 | unsigned int max_write_size; |
73 | 79 | void *memory_map; |
80 | + u8 option; | |
81 | + u8 flags; | |
74 | 82 | }; |
75 | 83 | |
76 | 84 | /** |
include/spi_flash.h
... | ... | @@ -36,6 +36,12 @@ |
36 | 36 | #define RD_EXTN ARRAY_SLOW | DUAL_OUTPUT_FAST | DUAL_IO_FAST |
37 | 37 | #define RD_FULL RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST |
38 | 38 | |
39 | +/* Dual SPI flash memories */ | |
40 | +enum spi_dual_flash { | |
41 | + SF_SINGLE_FLASH = 0, | |
42 | + SF_DUAL_STACKED_FLASH = 1 << 0, | |
43 | +}; | |
44 | + | |
39 | 45 | /** |
40 | 46 | * struct spi_flash_params - SPI/QSPI flash device params structure |
41 | 47 | * |
... | ... | @@ -64,6 +70,7 @@ |
64 | 70 | * |
65 | 71 | * @spi: SPI slave |
66 | 72 | * @name: Name of SPI flash |
73 | + * @dual_flash: Indicates dual flash memories - dual stacked | |
67 | 74 | * @size: Total flash size |
68 | 75 | * @page_size: Write (page) size |
69 | 76 | * @sector_size: Sector size |
... | ... | @@ -88,6 +95,7 @@ |
88 | 95 | struct spi_flash { |
89 | 96 | struct spi_slave *spi; |
90 | 97 | const char *name; |
98 | + u8 dual_flash; | |
91 | 99 | |
92 | 100 | u32 size; |
93 | 101 | u32 page_size; |