diff options
-rw-r--r-- | drivers/spi/spi-orion.c | 209 |
1 files changed, 61 insertions, 148 deletions
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 9b0caddce503..b17c09cf0a05 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c | |||
@@ -36,12 +36,6 @@ | |||
36 | #define ORION_SPI_CLK_PRESCALE_MASK 0x1F | 36 | #define ORION_SPI_CLK_PRESCALE_MASK 0x1F |
37 | 37 | ||
38 | struct orion_spi { | 38 | struct orion_spi { |
39 | struct work_struct work; | ||
40 | |||
41 | /* Lock access to transfer list. */ | ||
42 | spinlock_t lock; | ||
43 | |||
44 | struct list_head msg_queue; | ||
45 | struct spi_master *master; | 39 | struct spi_master *master; |
46 | void __iomem *base; | 40 | void __iomem *base; |
47 | unsigned int max_speed; | 41 | unsigned int max_speed; |
@@ -49,8 +43,6 @@ struct orion_spi { | |||
49 | struct clk *clk; | 43 | struct clk *clk; |
50 | }; | 44 | }; |
51 | 45 | ||
52 | static struct workqueue_struct *orion_spi_wq; | ||
53 | |||
54 | static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) | 46 | static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) |
55 | { | 47 | { |
56 | return orion_spi->base + reg; | 48 | return orion_spi->base + reg; |
@@ -277,73 +269,78 @@ out: | |||
277 | } | 269 | } |
278 | 270 | ||
279 | 271 | ||
280 | static void orion_spi_work(struct work_struct *work) | 272 | static int orion_spi_transfer_one_message(struct spi_master *master, |
273 | struct spi_message *m) | ||
281 | { | 274 | { |
282 | struct orion_spi *orion_spi = | 275 | struct orion_spi *orion_spi = spi_master_get_devdata(master); |
283 | container_of(work, struct orion_spi, work); | 276 | struct spi_device *spi = m->spi; |
284 | 277 | struct spi_transfer *t = NULL; | |
285 | spin_lock_irq(&orion_spi->lock); | 278 | int par_override = 0; |
286 | while (!list_empty(&orion_spi->msg_queue)) { | 279 | int status = 0; |
287 | struct spi_message *m; | 280 | int cs_active = 0; |
288 | struct spi_device *spi; | ||
289 | struct spi_transfer *t = NULL; | ||
290 | int par_override = 0; | ||
291 | int status = 0; | ||
292 | int cs_active = 0; | ||
293 | |||
294 | m = container_of(orion_spi->msg_queue.next, struct spi_message, | ||
295 | queue); | ||
296 | 281 | ||
297 | list_del_init(&m->queue); | 282 | /* Load defaults */ |
298 | spin_unlock_irq(&orion_spi->lock); | 283 | status = orion_spi_setup_transfer(spi, NULL); |
299 | 284 | ||
300 | spi = m->spi; | 285 | if (status < 0) |
286 | goto msg_done; | ||
301 | 287 | ||
302 | /* Load defaults */ | 288 | list_for_each_entry(t, &m->transfers, transfer_list) { |
303 | status = orion_spi_setup_transfer(spi, NULL); | 289 | /* make sure buffer length is even when working in 16 |
290 | * bit mode*/ | ||
291 | if ((t->bits_per_word == 16) && (t->len & 1)) { | ||
292 | dev_err(&spi->dev, | ||
293 | "message rejected : " | ||
294 | "odd data length %d while in 16 bit mode\n", | ||
295 | t->len); | ||
296 | status = -EIO; | ||
297 | goto msg_done; | ||
298 | } | ||
304 | 299 | ||
305 | if (status < 0) | 300 | if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { |
301 | dev_err(&spi->dev, | ||
302 | "message rejected : " | ||
303 | "device min speed (%d Hz) exceeds " | ||
304 | "required transfer speed (%d Hz)\n", | ||
305 | orion_spi->min_speed, t->speed_hz); | ||
306 | status = -EIO; | ||
306 | goto msg_done; | 307 | goto msg_done; |
308 | } | ||
307 | 309 | ||
308 | list_for_each_entry(t, &m->transfers, transfer_list) { | 310 | if (par_override || t->speed_hz || t->bits_per_word) { |
309 | if (par_override || t->speed_hz || t->bits_per_word) { | 311 | par_override = 1; |
310 | par_override = 1; | 312 | status = orion_spi_setup_transfer(spi, t); |
311 | status = orion_spi_setup_transfer(spi, t); | 313 | if (status < 0) |
312 | if (status < 0) | 314 | break; |
313 | break; | 315 | if (!t->speed_hz && !t->bits_per_word) |
314 | if (!t->speed_hz && !t->bits_per_word) | 316 | par_override = 0; |
315 | par_override = 0; | ||
316 | } | ||
317 | |||
318 | if (!cs_active) { | ||
319 | orion_spi_set_cs(orion_spi, 1); | ||
320 | cs_active = 1; | ||
321 | } | ||
322 | |||
323 | if (t->len) | ||
324 | m->actual_length += | ||
325 | orion_spi_write_read(spi, t); | ||
326 | |||
327 | if (t->delay_usecs) | ||
328 | udelay(t->delay_usecs); | ||
329 | |||
330 | if (t->cs_change) { | ||
331 | orion_spi_set_cs(orion_spi, 0); | ||
332 | cs_active = 0; | ||
333 | } | ||
334 | } | 317 | } |
335 | 318 | ||
336 | msg_done: | 319 | if (!cs_active) { |
337 | if (cs_active) | 320 | orion_spi_set_cs(orion_spi, 1); |
338 | orion_spi_set_cs(orion_spi, 0); | 321 | cs_active = 1; |
322 | } | ||
339 | 323 | ||
340 | m->status = status; | 324 | if (t->len) |
341 | m->complete(m->context); | 325 | m->actual_length += orion_spi_write_read(spi, t); |
342 | 326 | ||
343 | spin_lock_irq(&orion_spi->lock); | 327 | if (t->delay_usecs) |
328 | udelay(t->delay_usecs); | ||
329 | |||
330 | if (t->cs_change) { | ||
331 | orion_spi_set_cs(orion_spi, 0); | ||
332 | cs_active = 0; | ||
333 | } | ||
344 | } | 334 | } |
345 | 335 | ||
346 | spin_unlock_irq(&orion_spi->lock); | 336 | msg_done: |
337 | if (cs_active) | ||
338 | orion_spi_set_cs(orion_spi, 0); | ||
339 | |||
340 | m->status = status; | ||
341 | spi_finalize_current_message(master); | ||
342 | |||
343 | return 0; | ||
347 | } | 344 | } |
348 | 345 | ||
349 | static int __init orion_spi_reset(struct orion_spi *orion_spi) | 346 | static int __init orion_spi_reset(struct orion_spi *orion_spi) |
@@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi) | |||
376 | return 0; | 373 | return 0; |
377 | } | 374 | } |
378 | 375 | ||
379 | static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) | ||
380 | { | ||
381 | struct orion_spi *orion_spi; | ||
382 | struct spi_transfer *t = NULL; | ||
383 | unsigned long flags; | ||
384 | |||
385 | m->actual_length = 0; | ||
386 | m->status = 0; | ||
387 | |||
388 | /* reject invalid messages and transfers */ | ||
389 | if (list_empty(&m->transfers) || !m->complete) | ||
390 | return -EINVAL; | ||
391 | |||
392 | orion_spi = spi_master_get_devdata(spi->master); | ||
393 | |||
394 | list_for_each_entry(t, &m->transfers, transfer_list) { | ||
395 | unsigned int bits_per_word = spi->bits_per_word; | ||
396 | |||
397 | if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { | ||
398 | dev_err(&spi->dev, | ||
399 | "message rejected : " | ||
400 | "invalid transfer data buffers\n"); | ||
401 | goto msg_rejected; | ||
402 | } | ||
403 | |||
404 | if (t->bits_per_word) | ||
405 | bits_per_word = t->bits_per_word; | ||
406 | |||
407 | if ((bits_per_word != 8) && (bits_per_word != 16)) { | ||
408 | dev_err(&spi->dev, | ||
409 | "message rejected : " | ||
410 | "invalid transfer bits_per_word (%d bits)\n", | ||
411 | bits_per_word); | ||
412 | goto msg_rejected; | ||
413 | } | ||
414 | /*make sure buffer length is even when working in 16 bit mode*/ | ||
415 | if ((t->bits_per_word == 16) && (t->len & 1)) { | ||
416 | dev_err(&spi->dev, | ||
417 | "message rejected : " | ||
418 | "odd data length (%d) while in 16 bit mode\n", | ||
419 | t->len); | ||
420 | goto msg_rejected; | ||
421 | } | ||
422 | |||
423 | if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { | ||
424 | dev_err(&spi->dev, | ||
425 | "message rejected : " | ||
426 | "device min speed (%d Hz) exceeds " | ||
427 | "required transfer speed (%d Hz)\n", | ||
428 | orion_spi->min_speed, t->speed_hz); | ||
429 | goto msg_rejected; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | |||
434 | spin_lock_irqsave(&orion_spi->lock, flags); | ||
435 | list_add_tail(&m->queue, &orion_spi->msg_queue); | ||
436 | queue_work(orion_spi_wq, &orion_spi->work); | ||
437 | spin_unlock_irqrestore(&orion_spi->lock, flags); | ||
438 | |||
439 | return 0; | ||
440 | msg_rejected: | ||
441 | /* Message rejected and not queued */ | ||
442 | m->status = -EINVAL; | ||
443 | if (m->complete) | ||
444 | m->complete(m->context); | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | static int __init orion_spi_probe(struct platform_device *pdev) | 376 | static int __init orion_spi_probe(struct platform_device *pdev) |
449 | { | 377 | { |
450 | struct spi_master *master; | 378 | struct spi_master *master; |
@@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) | |||
474 | master->mode_bits = 0; | 402 | master->mode_bits = 0; |
475 | 403 | ||
476 | master->setup = orion_spi_setup; | 404 | master->setup = orion_spi_setup; |
477 | master->transfer = orion_spi_transfer; | 405 | master->transfer_one_message = orion_spi_transfer_one_message; |
478 | master->num_chipselect = ORION_NUM_CHIPSELECTS; | 406 | master->num_chipselect = ORION_NUM_CHIPSELECTS; |
479 | 407 | ||
480 | dev_set_drvdata(&pdev->dev, master); | 408 | dev_set_drvdata(&pdev->dev, master); |
@@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev) | |||
507 | } | 435 | } |
508 | spi->base = ioremap(r->start, SZ_1K); | 436 | spi->base = ioremap(r->start, SZ_1K); |
509 | 437 | ||
510 | INIT_WORK(&spi->work, orion_spi_work); | ||
511 | |||
512 | spin_lock_init(&spi->lock); | ||
513 | INIT_LIST_HEAD(&spi->msg_queue); | ||
514 | |||
515 | if (orion_spi_reset(spi) < 0) | 438 | if (orion_spi_reset(spi) < 0) |
516 | goto out_rel_mem; | 439 | goto out_rel_mem; |
517 | 440 | ||
@@ -536,14 +459,12 @@ out: | |||
536 | static int __exit orion_spi_remove(struct platform_device *pdev) | 459 | static int __exit orion_spi_remove(struct platform_device *pdev) |
537 | { | 460 | { |
538 | struct spi_master *master; | 461 | struct spi_master *master; |
539 | struct orion_spi *spi; | ||
540 | struct resource *r; | 462 | struct resource *r; |
463 | struct orion_spi *spi; | ||
541 | 464 | ||
542 | master = dev_get_drvdata(&pdev->dev); | 465 | master = dev_get_drvdata(&pdev->dev); |
543 | spi = spi_master_get_devdata(master); | 466 | spi = spi_master_get_devdata(master); |
544 | 467 | ||
545 | cancel_work_sync(&spi->work); | ||
546 | |||
547 | clk_disable_unprepare(spi->clk); | 468 | clk_disable_unprepare(spi->clk); |
548 | clk_put(spi->clk); | 469 | clk_put(spi->clk); |
549 | 470 | ||
@@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = { | |||
574 | 495 | ||
575 | static int __init orion_spi_init(void) | 496 | static int __init orion_spi_init(void) |
576 | { | 497 | { |
577 | orion_spi_wq = create_singlethread_workqueue( | ||
578 | orion_spi_driver.driver.name); | ||
579 | if (orion_spi_wq == NULL) | ||
580 | return -ENOMEM; | ||
581 | |||
582 | return platform_driver_probe(&orion_spi_driver, orion_spi_probe); | 498 | return platform_driver_probe(&orion_spi_driver, orion_spi_probe); |
583 | } | 499 | } |
584 | module_init(orion_spi_init); | 500 | module_init(orion_spi_init); |
585 | 501 | ||
586 | static void __exit orion_spi_exit(void) | 502 | static void __exit orion_spi_exit(void) |
587 | { | 503 | { |
588 | flush_workqueue(orion_spi_wq); | ||
589 | platform_driver_unregister(&orion_spi_driver); | 504 | platform_driver_unregister(&orion_spi_driver); |
590 | |||
591 | destroy_workqueue(orion_spi_wq); | ||
592 | } | 505 | } |
593 | module_exit(orion_spi_exit); | 506 | module_exit(orion_spi_exit); |
594 | 507 | ||