Commit c4a796329d00ce46de6b5afeb1fdabec82830677
Committed by
Jagannadha Sutradharudu Teki
1 parent
120af1572a
Exists in
master
and in
53 other branches
spi: exynos: Support word transfers
Since SPI register access is so expensive, it is worth transferring data a word at a time if we can. This complicates the driver unfortunately. Use the byte-swapping feature to avoid having to convert to/from big endian in software. This change increases speed from about 2MB/s to about 4.5MB/s. Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: Rajeshwari S Shinde <rajeshwari.s@samsung.com> Reviewed-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
Showing 2 changed files with 71 additions and 16 deletions Side-by-side Diff
arch/arm/include/asm/arch-exynos/spi.h
... | ... | @@ -22,7 +22,7 @@ |
22 | 22 | unsigned int rx_data; /* 0x1c */ |
23 | 23 | unsigned int pkt_cnt; /* 0x20 */ |
24 | 24 | unsigned char reserved2[4]; |
25 | - unsigned char reserved3[4]; | |
25 | + unsigned int swap_cfg; /* 0x28 */ | |
26 | 26 | unsigned int fb_clk; /* 0x2c */ |
27 | 27 | unsigned char padding[0xffd0]; |
28 | 28 | }; |
... | ... | @@ -61,6 +61,15 @@ |
61 | 61 | |
62 | 62 | /* Packet Count */ |
63 | 63 | #define SPI_PACKET_CNT_EN (1 << 16) |
64 | + | |
65 | +/* Swap config */ | |
66 | +#define SPI_TX_SWAP_EN (1 << 0) | |
67 | +#define SPI_TX_BYTE_SWAP (1 << 2) | |
68 | +#define SPI_TX_HWORD_SWAP (1 << 3) | |
69 | +#define SPI_TX_BYTE_SWAP (1 << 2) | |
70 | +#define SPI_RX_SWAP_EN (1 << 4) | |
71 | +#define SPI_RX_BYTE_SWAP (1 << 6) | |
72 | +#define SPI_RX_HWORD_SWAP (1 << 7) | |
64 | 73 | |
65 | 74 | #endif /* __ASSEMBLY__ */ |
66 | 75 | #endif |
drivers/spi/exynos_spi.c
... | ... | @@ -204,12 +204,29 @@ |
204 | 204 | * |
205 | 205 | * @param regs SPI peripheral registers |
206 | 206 | * @param count Number of bytes to transfer |
207 | + * @param step Number of bytes to transfer in each packet (1 or 4) | |
207 | 208 | */ |
208 | -static void spi_request_bytes(struct exynos_spi *regs, int count) | |
209 | +static void spi_request_bytes(struct exynos_spi *regs, int count, int step) | |
209 | 210 | { |
211 | + /* For word address we need to swap bytes */ | |
212 | + if (step == 4) { | |
213 | + setbits_le32(®s->mode_cfg, | |
214 | + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); | |
215 | + count /= 4; | |
216 | + setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | | |
217 | + SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | | |
218 | + SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); | |
219 | + } else { | |
220 | + /* Select byte access and clear the swap configuration */ | |
221 | + clrbits_le32(®s->mode_cfg, | |
222 | + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); | |
223 | + writel(0, ®s->swap_cfg); | |
224 | + } | |
225 | + | |
210 | 226 | assert(count && count < (1 << 16)); |
211 | 227 | setbits_le32(®s->ch_cfg, SPI_CH_RST); |
212 | 228 | clrbits_le32(®s->ch_cfg, SPI_CH_RST); |
229 | + | |
213 | 230 | writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); |
214 | 231 | } |
215 | 232 | |
... | ... | @@ -224,6 +241,7 @@ |
224 | 241 | int toread; |
225 | 242 | unsigned start = get_timer(0); |
226 | 243 | int stopping; |
244 | + int step; | |
227 | 245 | |
228 | 246 | out_bytes = in_bytes = todo; |
229 | 247 | |
230 | 248 | |
... | ... | @@ -231,10 +249,19 @@ |
231 | 249 | !(spi_slave->mode & SPI_SLAVE); |
232 | 250 | |
233 | 251 | /* |
252 | + * Try to transfer words if we can. This helps read performance at | |
253 | + * SPI clock speeds above about 20MHz. | |
254 | + */ | |
255 | + step = 1; | |
256 | + if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && | |
257 | + !spi_slave->skip_preamble) | |
258 | + step = 4; | |
259 | + | |
260 | + /* | |
234 | 261 | * If there's something to send, do a software reset and set a |
235 | 262 | * transaction size. |
236 | 263 | */ |
237 | - spi_request_bytes(regs, todo); | |
264 | + spi_request_bytes(regs, todo, step); | |
238 | 265 | |
239 | 266 | /* |
240 | 267 | * Bytes are transmitted/received in pairs. Wait to receive all the |
241 | 268 | |
242 | 269 | |
243 | 270 | |
... | ... | @@ -247,14 +274,26 @@ |
247 | 274 | |
248 | 275 | /* Keep the fifos full/empty. */ |
249 | 276 | spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); |
277 | + | |
278 | + /* | |
279 | + * Don't completely fill the txfifo, since we don't want our | |
280 | + * rxfifo to overflow, and it may already contain data. | |
281 | + */ | |
250 | 282 | while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { |
251 | - temp = txp ? *txp++ : 0xff; | |
283 | + if (!txp) | |
284 | + temp = -1; | |
285 | + else if (step == 4) | |
286 | + temp = *(uint32_t *)txp; | |
287 | + else | |
288 | + temp = *txp; | |
252 | 289 | writel(temp, ®s->tx_data); |
253 | - out_bytes--; | |
254 | - tx_lvl++; | |
290 | + out_bytes -= step; | |
291 | + if (txp) | |
292 | + txp += step; | |
293 | + tx_lvl += step; | |
255 | 294 | } |
256 | - if (rx_lvl > 0) { | |
257 | - while (rx_lvl > 0) { | |
295 | + if (rx_lvl >= step) { | |
296 | + while (rx_lvl >= step) { | |
258 | 297 | temp = readl(®s->rx_data); |
259 | 298 | if (spi_slave->skip_preamble) { |
260 | 299 | if (temp == SPI_PREAMBLE_END_BYTE) { |
261 | 300 | |
... | ... | @@ -262,12 +301,15 @@ |
262 | 301 | stopping = 0; |
263 | 302 | } |
264 | 303 | } else { |
265 | - if (rxp || stopping) | |
266 | - *rxp++ = temp; | |
267 | - in_bytes--; | |
304 | + if (rxp || stopping) { | |
305 | + *rxp = temp; | |
306 | + rxp += step; | |
307 | + } | |
308 | + in_bytes -= step; | |
268 | 309 | } |
269 | - toread--; | |
270 | - rx_lvl--; | |
310 | + toread -= step; | |
311 | + rx_lvl -= step; | |
312 | + } | |
271 | 313 | } else if (!toread) { |
272 | 314 | /* |
273 | 315 | * We have run out of input data, but haven't read |
... | ... | @@ -279,7 +321,7 @@ |
279 | 321 | out_bytes = in_bytes; |
280 | 322 | toread = in_bytes; |
281 | 323 | txp = NULL; |
282 | - spi_request_bytes(regs, toread); | |
324 | + spi_request_bytes(regs, toread, step); | |
283 | 325 | } |
284 | 326 | if (spi_slave->skip_preamble && get_timer(start) > 100) { |
285 | 327 | printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", |
286 | 328 | |
... | ... | @@ -323,10 +365,14 @@ |
323 | 365 | if ((flags & SPI_XFER_BEGIN)) |
324 | 366 | spi_cs_activate(slave); |
325 | 367 | |
326 | - /* Exynos SPI limits each transfer to 65535 bytes */ | |
368 | + /* | |
369 | + * Exynos SPI limits each transfer to 65535 transfers. To keep | |
370 | + * things simple, allow a maximum of 65532 bytes. We could allow | |
371 | + * more in word mode, but the performance difference is small. | |
372 | + */ | |
327 | 373 | bytelen = bitlen / 8; |
328 | 374 | for (upto = 0; !ret && upto < bytelen; upto += todo) { |
329 | - todo = min(bytelen - upto, (1 << 16) - 1); | |
375 | + todo = min(bytelen - upto, (1 << 16) - 4); | |
330 | 376 | ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); |
331 | 377 | if (ret) |
332 | 378 | break; |