aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-bcm63xx.c134
1 files changed, 106 insertions, 28 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 27667c17ce90..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
40struct bcm63xx_spi { 42struct bcm63xx_spi {
41 struct completion done; 43 struct completion done;
42 44
@@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
169 return 0; 171 return 0;
170} 172}
171 173
172static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) 174static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
175 unsigned int num_transfers)
173{ 176{
174 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 177 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
175 u16 msg_ctl; 178 u16 msg_ctl;
176 u16 cmd; 179 u16 cmd;
177 u8 rx_tail; 180 u8 rx_tail;
178 unsigned int timeout = 0; 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;
179 185
180 /* Disable the CMD_DONE interrupt */ 186 /* Disable the CMD_DONE interrupt */
181 bcm_spi_writeb(bs, 0, SPI_INT_MASK); 187 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
@@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
183 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",
184 t->tx_buf, t->rx_buf, t->len); 190 t->tx_buf, t->rx_buf, t->len);
185 191
186 if (t->tx_buf) 192 if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
187 memcpy_toio(bs->tx_io, t->tx_buf, t->len); 193 prepend_len = t->len;
194
195 /* prepare the buffer */
196 for (i = 0; i < num_transfers; i++) {
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);
217 }
218
219 len -= prepend_len;
188 220
189 init_completion(&bs->done); 221 init_completion(&bs->done);
190 222
191 /* Fill in the Message control register */ 223 /* Fill in the Message control register */
192 msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); 224 msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
193 225
194 if (t->rx_buf && t->tx_buf) 226 if (do_rx && do_tx && prepend_len == 0)
195 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 227 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
196 else if (t->rx_buf) 228 else if (do_rx)
197 msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 229 msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
198 else if (t->tx_buf) 230 else if (do_tx)
199 msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 231 msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
200 232
201 switch (bs->msg_ctl_width) { 233 switch (bs->msg_ctl_width) {
@@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
209 241
210 /* Issue the transfer */ 242 /* Issue the transfer */
211 cmd = SPI_CMD_START_IMMEDIATE; 243 cmd = SPI_CMD_START_IMMEDIATE;
212 cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 244 cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
213 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); 245 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
214 bcm_spi_writew(bs, cmd, SPI_CMD); 246 bcm_spi_writew(bs, cmd, SPI_CMD);
215 247
@@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
223 /* read out all data */ 255 /* read out all data */
224 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); 256 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
225 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;
226 /* Read out all the data */ 266 /* Read out all the data */
227 if (rx_tail) 267 for (i = 0; i < num_transfers; i++) {
228 memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail); 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 }
229 277
230 return 0; 278 return 0;
231} 279}
@@ -252,46 +300,76 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
252 struct spi_message *m) 300 struct spi_message *m)
253{ 301{
254 struct bcm63xx_spi *bs = spi_master_get_devdata(master); 302 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
255 struct spi_transfer *t; 303 struct spi_transfer *t, *first = NULL;
256 struct spi_device *spi = m->spi; 304 struct spi_device *spi = m->spi;
257 int status = 0; 305 int status = 0;
258 306 unsigned int n_transfers = 0, total_len = 0;
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 */
259 list_for_each_entry(t, &m->transfers, transfer_list) { 315 list_for_each_entry(t, &m->transfers, transfer_list) {
260 status = bcm63xx_spi_check_transfer(spi, t); 316 status = bcm63xx_spi_check_transfer(spi, t);
261 if (status < 0) 317 if (status < 0)
262 goto exit; 318 goto exit;
263 319
320 if (!first)
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
264 /* we can only transfer one fifo worth of data */ 332 /* we can only transfer one fifo worth of data */
265 if (t->len > bs->fifo_size) { 333 if ((can_use_prepend &&
334 total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
335 (!can_use_prepend && total_len > bs->fifo_size)) {
266 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 336 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
267 t->len, bs->fifo_size); 337 total_len, bs->fifo_size);
268 status = -EINVAL; 338 status = -EINVAL;
269 goto exit; 339 goto exit;
270 } 340 }
271 341
272 /* CS will be deasserted directly after transfer */ 342 /* all combined transfers have to have the same speed */
273 if (t->delay_usecs) { 343 if (t->speed_hz != first->speed_hz) {
274 dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 344 dev_err(&spi->dev, "unable to change speed between transfers\n");
275 status = -EINVAL; 345 status = -EINVAL;
276 goto exit; 346 goto exit;
277 } 347 }
278 348
279 if (!t->cs_change && 349 /* CS will be deasserted directly after transfer */
280 !list_is_last(&t->transfer_list, &m->transfers)) { 350 if (t->delay_usecs) {
281 dev_err(&spi->dev, "unable to keep CS asserted between transfers\n"); 351 dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
282 status = -EINVAL; 352 status = -EINVAL;
283 goto exit; 353 goto exit;
284 } 354 }
285 355
286 /* configure adapter for a new transfer */ 356 if (t->cs_change ||
287 bcm63xx_spi_setup_transfer(spi, t); 357 list_is_last(&t->transfer_list, &m->transfers)) {
358 /* configure adapter for a new transfer */
359 bcm63xx_spi_setup_transfer(spi, first);
288 360
289 /* send the data */ 361 /* send the data */
290 status = bcm63xx_txrx_bufs(spi, t); 362 status = bcm63xx_txrx_bufs(spi, first, n_transfers);
291 if (status) 363 if (status)
292 goto exit; 364 goto exit;
365
366 m->actual_length += total_len;
293 367
294 m->actual_length += t->len; 368 first = NULL;
369 n_transfers = 0;
370 total_len = 0;
371 can_use_prepend = false;
372 }
295 } 373 }
296exit: 374exit:
297 m->status = status; 375 m->status = status;