diff options
Diffstat (limited to 'drivers/spi/spi-bcm63xx.c')
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 179 |
1 files changed, 125 insertions, 54 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f44ab5508535..9578af782a77 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c | |||
@@ -37,6 +37,8 @@ | |||
37 | 37 | ||
38 | #define PFX KBUILD_MODNAME | 38 | #define PFX KBUILD_MODNAME |
39 | 39 | ||
40 | #define BCM63XX_SPI_MAX_PREPEND 15 | ||
41 | |||
40 | struct bcm63xx_spi { | 42 | struct bcm63xx_spi { |
41 | struct completion done; | 43 | struct completion done; |
42 | 44 | ||
@@ -49,16 +51,10 @@ struct bcm63xx_spi { | |||
49 | unsigned int msg_type_shift; | 51 | unsigned int msg_type_shift; |
50 | unsigned int msg_ctl_width; | 52 | unsigned int msg_ctl_width; |
51 | 53 | ||
52 | /* Data buffers */ | ||
53 | const unsigned char *tx_ptr; | ||
54 | unsigned char *rx_ptr; | ||
55 | |||
56 | /* data iomem */ | 54 | /* data iomem */ |
57 | u8 __iomem *tx_io; | 55 | u8 __iomem *tx_io; |
58 | const u8 __iomem *rx_io; | 56 | const u8 __iomem *rx_io; |
59 | 57 | ||
60 | int remaining_bytes; | ||
61 | |||
62 | struct clk *clk; | 58 | struct clk *clk; |
63 | struct platform_device *pdev; | 59 | struct platform_device *pdev; |
64 | }; | 60 | }; |
@@ -175,24 +171,17 @@ static int bcm63xx_spi_setup(struct spi_device *spi) | |||
175 | return 0; | 171 | return 0; |
176 | } | 172 | } |
177 | 173 | ||
178 | /* Fill the TX FIFO with as many bytes as possible */ | 174 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, |
179 | static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) | 175 | unsigned int num_transfers) |
180 | { | ||
181 | u8 size; | ||
182 | |||
183 | /* Fill the Tx FIFO with as many bytes as possible */ | ||
184 | size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes : | ||
185 | bs->fifo_size; | ||
186 | memcpy_toio(bs->tx_io, bs->tx_ptr, size); | ||
187 | bs->remaining_bytes -= size; | ||
188 | } | ||
189 | |||
190 | static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, | ||
191 | struct spi_transfer *t) | ||
192 | { | 176 | { |
193 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 177 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); |
194 | u16 msg_ctl; | 178 | u16 msg_ctl; |
195 | u16 cmd; | 179 | u16 cmd; |
180 | u8 rx_tail; | ||
181 | unsigned int i, timeout = 0, prepend_len = 0, len = 0; | ||
182 | struct spi_transfer *t = first; | ||
183 | bool do_rx = false; | ||
184 | bool do_tx = false; | ||
196 | 185 | ||
197 | /* Disable the CMD_DONE interrupt */ | 186 | /* Disable the CMD_DONE interrupt */ |
198 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 187 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
@@ -200,25 +189,45 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, | |||
200 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", | 189 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", |
201 | t->tx_buf, t->rx_buf, t->len); | 190 | t->tx_buf, t->rx_buf, t->len); |
202 | 191 | ||
203 | /* Transmitter is inhibited */ | 192 | if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) |
204 | bs->tx_ptr = t->tx_buf; | 193 | prepend_len = t->len; |
205 | bs->rx_ptr = t->rx_buf; | ||
206 | 194 | ||
207 | if (t->tx_buf) { | 195 | /* prepare the buffer */ |
208 | bs->remaining_bytes = t->len; | 196 | for (i = 0; i < num_transfers; i++) { |
209 | bcm63xx_spi_fill_tx_fifo(bs); | 197 | if (t->tx_buf) { |
198 | do_tx = true; | ||
199 | memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); | ||
200 | |||
201 | /* don't prepend more than one tx */ | ||
202 | if (t != first) | ||
203 | prepend_len = 0; | ||
204 | } | ||
205 | |||
206 | if (t->rx_buf) { | ||
207 | do_rx = true; | ||
208 | /* prepend is half-duplex write only */ | ||
209 | if (t == first) | ||
210 | prepend_len = 0; | ||
211 | } | ||
212 | |||
213 | len += t->len; | ||
214 | |||
215 | t = list_entry(t->transfer_list.next, struct spi_transfer, | ||
216 | transfer_list); | ||
210 | } | 217 | } |
211 | 218 | ||
219 | len -= prepend_len; | ||
220 | |||
212 | init_completion(&bs->done); | 221 | init_completion(&bs->done); |
213 | 222 | ||
214 | /* Fill in the Message control register */ | 223 | /* Fill in the Message control register */ |
215 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); | 224 | msg_ctl = (len << SPI_BYTE_CNT_SHIFT); |
216 | 225 | ||
217 | if (t->rx_buf && t->tx_buf) | 226 | if (do_rx && do_tx && prepend_len == 0) |
218 | msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); | 227 | msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); |
219 | else if (t->rx_buf) | 228 | else if (do_rx) |
220 | msg_ctl |= (SPI_HD_R << bs->msg_type_shift); | 229 | msg_ctl |= (SPI_HD_R << bs->msg_type_shift); |
221 | else if (t->tx_buf) | 230 | else if (do_tx) |
222 | msg_ctl |= (SPI_HD_W << bs->msg_type_shift); | 231 | msg_ctl |= (SPI_HD_W << bs->msg_type_shift); |
223 | 232 | ||
224 | switch (bs->msg_ctl_width) { | 233 | switch (bs->msg_ctl_width) { |
@@ -232,14 +241,41 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, | |||
232 | 241 | ||
233 | /* Issue the transfer */ | 242 | /* Issue the transfer */ |
234 | cmd = SPI_CMD_START_IMMEDIATE; | 243 | cmd = SPI_CMD_START_IMMEDIATE; |
235 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | 244 | cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); |
236 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); | 245 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); |
237 | bcm_spi_writew(bs, cmd, SPI_CMD); | 246 | bcm_spi_writew(bs, cmd, SPI_CMD); |
238 | 247 | ||
239 | /* Enable the CMD_DONE interrupt */ | 248 | /* Enable the CMD_DONE interrupt */ |
240 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | 249 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); |
241 | 250 | ||
242 | return t->len - bs->remaining_bytes; | 251 | timeout = wait_for_completion_timeout(&bs->done, HZ); |
252 | if (!timeout) | ||
253 | return -ETIMEDOUT; | ||
254 | |||
255 | /* read out all data */ | ||
256 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
257 | |||
258 | if (do_rx && rx_tail != len) | ||
259 | return -EIO; | ||
260 | |||
261 | if (!rx_tail) | ||
262 | return 0; | ||
263 | |||
264 | len = 0; | ||
265 | t = first; | ||
266 | /* Read out all the data */ | ||
267 | for (i = 0; i < num_transfers; i++) { | ||
268 | if (t->rx_buf) | ||
269 | memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); | ||
270 | |||
271 | if (t != first || prepend_len == 0) | ||
272 | len += t->len; | ||
273 | |||
274 | t = list_entry(t->transfer_list.next, struct spi_transfer, | ||
275 | transfer_list); | ||
276 | } | ||
277 | |||
278 | return 0; | ||
243 | } | 279 | } |
244 | 280 | ||
245 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) | 281 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) |
@@ -264,41 +300,76 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, | |||
264 | struct spi_message *m) | 300 | struct spi_message *m) |
265 | { | 301 | { |
266 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 302 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
267 | struct spi_transfer *t; | 303 | struct spi_transfer *t, *first = NULL; |
268 | struct spi_device *spi = m->spi; | 304 | struct spi_device *spi = m->spi; |
269 | int status = 0; | 305 | int status = 0; |
270 | unsigned int timeout = 0; | 306 | unsigned int n_transfers = 0, total_len = 0; |
271 | 307 | bool can_use_prepend = false; | |
308 | |||
309 | /* | ||
310 | * This SPI controller does not support keeping CS active after a | ||
311 | * transfer. | ||
312 | * Work around this by merging as many transfers we can into one big | ||
313 | * full-duplex transfers. | ||
314 | */ | ||
272 | list_for_each_entry(t, &m->transfers, transfer_list) { | 315 | list_for_each_entry(t, &m->transfers, transfer_list) { |
273 | unsigned int len = t->len; | ||
274 | u8 rx_tail; | ||
275 | |||
276 | status = bcm63xx_spi_check_transfer(spi, t); | 316 | status = bcm63xx_spi_check_transfer(spi, t); |
277 | if (status < 0) | 317 | if (status < 0) |
278 | goto exit; | 318 | goto exit; |
279 | 319 | ||
280 | /* configure adapter for a new transfer */ | 320 | if (!first) |
281 | bcm63xx_spi_setup_transfer(spi, t); | 321 | first = t; |
322 | |||
323 | n_transfers++; | ||
324 | total_len += t->len; | ||
325 | |||
326 | if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && | ||
327 | first->len <= BCM63XX_SPI_MAX_PREPEND) | ||
328 | can_use_prepend = true; | ||
329 | else if (can_use_prepend && t->tx_buf) | ||
330 | can_use_prepend = false; | ||
331 | |||
332 | /* we can only transfer one fifo worth of data */ | ||
333 | if ((can_use_prepend && | ||
334 | total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || | ||
335 | (!can_use_prepend && total_len > bs->fifo_size)) { | ||
336 | dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", | ||
337 | total_len, bs->fifo_size); | ||
338 | status = -EINVAL; | ||
339 | goto exit; | ||
340 | } | ||
341 | |||
342 | /* all combined transfers have to have the same speed */ | ||
343 | if (t->speed_hz != first->speed_hz) { | ||
344 | dev_err(&spi->dev, "unable to change speed between transfers\n"); | ||
345 | status = -EINVAL; | ||
346 | goto exit; | ||
347 | } | ||
282 | 348 | ||
283 | while (len) { | 349 | /* CS will be deasserted directly after transfer */ |
284 | /* send the data */ | 350 | if (t->delay_usecs) { |
285 | len -= bcm63xx_txrx_bufs(spi, t); | 351 | dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); |
352 | status = -EINVAL; | ||
353 | goto exit; | ||
354 | } | ||
355 | |||
356 | if (t->cs_change || | ||
357 | list_is_last(&t->transfer_list, &m->transfers)) { | ||
358 | /* configure adapter for a new transfer */ | ||
359 | bcm63xx_spi_setup_transfer(spi, first); | ||
286 | 360 | ||
287 | timeout = wait_for_completion_timeout(&bs->done, HZ); | 361 | /* send the data */ |
288 | if (!timeout) { | 362 | status = bcm63xx_txrx_bufs(spi, first, n_transfers); |
289 | status = -ETIMEDOUT; | 363 | if (status) |
290 | goto exit; | 364 | goto exit; |
291 | } | ||
292 | 365 | ||
293 | /* read out all data */ | 366 | m->actual_length += total_len; |
294 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
295 | 367 | ||
296 | /* Read out all the data */ | 368 | first = NULL; |
297 | if (rx_tail) | 369 | n_transfers = 0; |
298 | memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); | 370 | total_len = 0; |
371 | can_use_prepend = false; | ||
299 | } | 372 | } |
300 | |||
301 | m->actual_length += t->len; | ||
302 | } | 373 | } |
303 | exit: | 374 | exit: |
304 | m->status = status; | 375 | m->status = status; |