diff options
author | Mark Brown <broonie@linaro.org> | 2013-09-01 08:49:01 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-09-01 08:49:01 -0400 |
commit | 4374f332d9fec26a1b377981f39881abff54941a (patch) | |
tree | 938f015e05045ee39747eb7ea585f71944270e45 | |
parent | 45bb5065e11cd770fcf4afe9714ccea671333766 (diff) | |
parent | 2d0c6148e381b81f8e6783d24b724963a86364cc (diff) |
Merge remote-tracking branch 'spi/topic/omap-100k' into spi-next
-rw-r--r-- | drivers/spi/spi-omap-100k.c | 276 |
1 files changed, 78 insertions, 198 deletions
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index ee25670f8cfd..cdcc72c5d380 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c | |||
@@ -83,11 +83,6 @@ | |||
83 | #define SPI_SHUTDOWN 1 | 83 | #define SPI_SHUTDOWN 1 |
84 | 84 | ||
85 | struct omap1_spi100k { | 85 | struct omap1_spi100k { |
86 | struct work_struct work; | ||
87 | |||
88 | /* lock protects queue and registers */ | ||
89 | spinlock_t lock; | ||
90 | struct list_head msg_queue; | ||
91 | struct spi_master *master; | 86 | struct spi_master *master; |
92 | struct clk *ick; | 87 | struct clk *ick; |
93 | struct clk *fck; | 88 | struct clk *fck; |
@@ -104,8 +99,6 @@ struct omap1_spi100k_cs { | |||
104 | int word_len; | 99 | int word_len; |
105 | }; | 100 | }; |
106 | 101 | ||
107 | static struct workqueue_struct *omap1_spi100k_wq; | ||
108 | |||
109 | #define MOD_REG_BIT(val, mask, set) do { \ | 102 | #define MOD_REG_BIT(val, mask, set) do { \ |
110 | if (set) \ | 103 | if (set) \ |
111 | val |= mask; \ | 104 | val |= mask; \ |
@@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi) | |||
310 | 303 | ||
311 | spi100k_open(spi->master); | 304 | spi100k_open(spi->master); |
312 | 305 | ||
313 | clk_enable(spi100k->ick); | 306 | clk_prepare_enable(spi100k->ick); |
314 | clk_enable(spi100k->fck); | 307 | clk_prepare_enable(spi100k->fck); |
315 | 308 | ||
316 | ret = omap1_spi100k_setup_transfer(spi, NULL); | 309 | ret = omap1_spi100k_setup_transfer(spi, NULL); |
317 | 310 | ||
318 | clk_disable(spi100k->ick); | 311 | clk_disable_unprepare(spi100k->ick); |
319 | clk_disable(spi100k->fck); | 312 | clk_disable_unprepare(spi100k->fck); |
320 | 313 | ||
321 | return ret; | 314 | return ret; |
322 | } | 315 | } |
323 | 316 | ||
324 | static void omap1_spi100k_work(struct work_struct *work) | 317 | static int omap1_spi100k_prepare_hardware(struct spi_master *master) |
325 | { | 318 | { |
326 | struct omap1_spi100k *spi100k; | 319 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
327 | int status = 0; | ||
328 | 320 | ||
329 | spi100k = container_of(work, struct omap1_spi100k, work); | 321 | clk_prepare_enable(spi100k->ick); |
330 | spin_lock_irq(&spi100k->lock); | 322 | clk_prepare_enable(spi100k->fck); |
331 | 323 | ||
332 | clk_enable(spi100k->ick); | 324 | return 0; |
333 | clk_enable(spi100k->fck); | 325 | } |
334 | 326 | ||
335 | /* We only enable one channel at a time -- the one whose message is | 327 | static int omap1_spi100k_transfer_one_message(struct spi_master *master, |
336 | * at the head of the queue -- although this controller would gladly | 328 | struct spi_message *m) |
337 | * arbitrate among multiple channels. This corresponds to "single | 329 | { |
338 | * channel" master mode. As a side effect, we need to manage the | 330 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
339 | * chipselect with the FORCE bit ... CS != channel enable. | 331 | struct spi_device *spi = m->spi; |
340 | */ | 332 | struct spi_transfer *t = NULL; |
341 | while (!list_empty(&spi100k->msg_queue)) { | 333 | int cs_active = 0; |
342 | struct spi_message *m; | 334 | int par_override = 0; |
343 | struct spi_device *spi; | 335 | int status = 0; |
344 | struct spi_transfer *t = NULL; | 336 | |
345 | int cs_active = 0; | 337 | list_for_each_entry(t, &m->transfers, transfer_list) { |
346 | struct omap1_spi100k_cs *cs; | 338 | if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { |
347 | int par_override = 0; | 339 | status = -EINVAL; |
348 | 340 | break; | |
349 | m = container_of(spi100k->msg_queue.next, struct spi_message, | 341 | } |
350 | queue); | 342 | if (par_override || t->speed_hz || t->bits_per_word) { |
351 | 343 | par_override = 1; | |
352 | list_del_init(&m->queue); | 344 | status = omap1_spi100k_setup_transfer(spi, t); |
353 | spin_unlock_irq(&spi100k->lock); | 345 | if (status < 0) |
354 | |||
355 | spi = m->spi; | ||
356 | cs = spi->controller_state; | ||
357 | |||
358 | list_for_each_entry(t, &m->transfers, transfer_list) { | ||
359 | if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { | ||
360 | status = -EINVAL; | ||
361 | break; | 346 | break; |
362 | } | 347 | if (!t->speed_hz && !t->bits_per_word) |
363 | if (par_override || t->speed_hz || t->bits_per_word) { | 348 | par_override = 0; |
364 | par_override = 1; | 349 | } |
365 | status = omap1_spi100k_setup_transfer(spi, t); | ||
366 | if (status < 0) | ||
367 | break; | ||
368 | if (!t->speed_hz && !t->bits_per_word) | ||
369 | par_override = 0; | ||
370 | } | ||
371 | 350 | ||
372 | if (!cs_active) { | 351 | if (!cs_active) { |
373 | omap1_spi100k_force_cs(spi100k, 1); | 352 | omap1_spi100k_force_cs(spi100k, 1); |
374 | cs_active = 1; | 353 | cs_active = 1; |
375 | } | 354 | } |
376 | 355 | ||
377 | if (t->len) { | 356 | if (t->len) { |
378 | unsigned count; | 357 | unsigned count; |
379 | 358 | ||
380 | count = omap1_spi100k_txrx_pio(spi, t); | 359 | count = omap1_spi100k_txrx_pio(spi, t); |
381 | m->actual_length += count; | 360 | m->actual_length += count; |
382 | 361 | ||
383 | if (count != t->len) { | 362 | if (count != t->len) { |
384 | status = -EIO; | 363 | status = -EIO; |
385 | break; | 364 | break; |
386 | } | ||
387 | } | 365 | } |
366 | } | ||
388 | 367 | ||
389 | if (t->delay_usecs) | 368 | if (t->delay_usecs) |
390 | udelay(t->delay_usecs); | 369 | udelay(t->delay_usecs); |
391 | 370 | ||
392 | /* ignore the "leave it on after last xfer" hint */ | 371 | /* ignore the "leave it on after last xfer" hint */ |
393 | 372 | ||
394 | if (t->cs_change) { | 373 | if (t->cs_change) { |
395 | omap1_spi100k_force_cs(spi100k, 0); | 374 | omap1_spi100k_force_cs(spi100k, 0); |
396 | cs_active = 0; | 375 | cs_active = 0; |
397 | } | ||
398 | } | ||
399 | |||
400 | /* Restore defaults if they were overriden */ | ||
401 | if (par_override) { | ||
402 | par_override = 0; | ||
403 | status = omap1_spi100k_setup_transfer(spi, NULL); | ||
404 | } | 376 | } |
377 | } | ||
405 | 378 | ||
406 | if (cs_active) | 379 | /* Restore defaults if they were overriden */ |
407 | omap1_spi100k_force_cs(spi100k, 0); | 380 | if (par_override) { |
381 | par_override = 0; | ||
382 | status = omap1_spi100k_setup_transfer(spi, NULL); | ||
383 | } | ||
408 | 384 | ||
409 | m->status = status; | 385 | if (cs_active) |
410 | m->complete(m->context); | 386 | omap1_spi100k_force_cs(spi100k, 0); |
411 | 387 | ||
412 | spin_lock_irq(&spi100k->lock); | 388 | m->status = status; |
413 | } | ||
414 | 389 | ||
415 | clk_disable(spi100k->ick); | 390 | spi_finalize_current_message(master); |
416 | clk_disable(spi100k->fck); | ||
417 | spin_unlock_irq(&spi100k->lock); | ||
418 | 391 | ||
419 | if (status < 0) | 392 | return status; |
420 | printk(KERN_WARNING "spi transfer failed with %d\n", status); | ||
421 | } | 393 | } |
422 | 394 | ||
423 | static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) | 395 | static int omap1_spi100k_unprepare_hardware(struct spi_master *master) |
424 | { | 396 | { |
425 | struct omap1_spi100k *spi100k; | 397 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
426 | unsigned long flags; | ||
427 | struct spi_transfer *t; | ||
428 | |||
429 | m->actual_length = 0; | ||
430 | m->status = -EINPROGRESS; | ||
431 | |||
432 | spi100k = spi_master_get_devdata(spi->master); | ||
433 | |||
434 | /* Don't accept new work if we're shutting down */ | ||
435 | if (spi100k->state == SPI_SHUTDOWN) | ||
436 | return -ESHUTDOWN; | ||
437 | |||
438 | /* reject invalid messages and transfers */ | ||
439 | if (list_empty(&m->transfers) || !m->complete) | ||
440 | return -EINVAL; | ||
441 | |||
442 | list_for_each_entry(t, &m->transfers, transfer_list) { | ||
443 | const void *tx_buf = t->tx_buf; | ||
444 | void *rx_buf = t->rx_buf; | ||
445 | unsigned len = t->len; | ||
446 | |||
447 | if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ | ||
448 | || (len && !(rx_buf || tx_buf))) { | ||
449 | dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", | ||
450 | t->speed_hz, | ||
451 | len, | ||
452 | tx_buf ? "tx" : "", | ||
453 | rx_buf ? "rx" : "", | ||
454 | t->bits_per_word); | ||
455 | return -EINVAL; | ||
456 | } | ||
457 | |||
458 | if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) { | ||
459 | dev_dbg(&spi->dev, "%d Hz max exceeds %d\n", | ||
460 | t->speed_hz, | ||
461 | OMAP1_SPI100K_MAX_FREQ/(1<<16)); | ||
462 | return -EINVAL; | ||
463 | } | ||
464 | |||
465 | } | ||
466 | |||
467 | spin_lock_irqsave(&spi100k->lock, flags); | ||
468 | list_add_tail(&m->queue, &spi100k->msg_queue); | ||
469 | queue_work(omap1_spi100k_wq, &spi100k->work); | ||
470 | spin_unlock_irqrestore(&spi100k->lock, flags); | ||
471 | 398 | ||
472 | return 0; | 399 | clk_disable_unprepare(spi100k->ick); |
473 | } | 400 | clk_disable_unprepare(spi100k->fck); |
474 | 401 | ||
475 | static int omap1_spi100k_reset(struct omap1_spi100k *spi100k) | ||
476 | { | ||
477 | return 0; | 402 | return 0; |
478 | } | 403 | } |
479 | 404 | ||
@@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev) | |||
496 | master->bus_num = pdev->id; | 421 | master->bus_num = pdev->id; |
497 | 422 | ||
498 | master->setup = omap1_spi100k_setup; | 423 | master->setup = omap1_spi100k_setup; |
499 | master->transfer = omap1_spi100k_transfer; | 424 | master->transfer_one_message = omap1_spi100k_transfer_one_message; |
425 | master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; | ||
426 | master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; | ||
500 | master->cleanup = NULL; | 427 | master->cleanup = NULL; |
501 | master->num_chipselect = 2; | 428 | master->num_chipselect = 2; |
502 | master->mode_bits = MODEBITS; | 429 | master->mode_bits = MODEBITS; |
503 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); | 430 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); |
431 | master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); | ||
432 | master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ; | ||
504 | 433 | ||
505 | platform_set_drvdata(pdev, master); | 434 | platform_set_drvdata(pdev, master); |
506 | 435 | ||
@@ -514,40 +443,29 @@ static int omap1_spi100k_probe(struct platform_device *pdev) | |||
514 | */ | 443 | */ |
515 | spi100k->base = (void __iomem *) pdev->dev.platform_data; | 444 | spi100k->base = (void __iomem *) pdev->dev.platform_data; |
516 | 445 | ||
517 | INIT_WORK(&spi100k->work, omap1_spi100k_work); | 446 | spi100k->ick = devm_clk_get(&pdev->dev, "ick"); |
518 | |||
519 | spin_lock_init(&spi100k->lock); | ||
520 | INIT_LIST_HEAD(&spi100k->msg_queue); | ||
521 | spi100k->ick = clk_get(&pdev->dev, "ick"); | ||
522 | if (IS_ERR(spi100k->ick)) { | 447 | if (IS_ERR(spi100k->ick)) { |
523 | dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); | 448 | dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); |
524 | status = PTR_ERR(spi100k->ick); | 449 | status = PTR_ERR(spi100k->ick); |
525 | goto err1; | 450 | goto err; |
526 | } | 451 | } |
527 | 452 | ||
528 | spi100k->fck = clk_get(&pdev->dev, "fck"); | 453 | spi100k->fck = devm_clk_get(&pdev->dev, "fck"); |
529 | if (IS_ERR(spi100k->fck)) { | 454 | if (IS_ERR(spi100k->fck)) { |
530 | dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); | 455 | dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); |
531 | status = PTR_ERR(spi100k->fck); | 456 | status = PTR_ERR(spi100k->fck); |
532 | goto err2; | 457 | goto err; |
533 | } | 458 | } |
534 | 459 | ||
535 | if (omap1_spi100k_reset(spi100k) < 0) | ||
536 | goto err3; | ||
537 | |||
538 | status = spi_register_master(master); | 460 | status = spi_register_master(master); |
539 | if (status < 0) | 461 | if (status < 0) |
540 | goto err3; | 462 | goto err; |
541 | 463 | ||
542 | spi100k->state = SPI_RUNNING; | 464 | spi100k->state = SPI_RUNNING; |
543 | 465 | ||
544 | return status; | 466 | return status; |
545 | 467 | ||
546 | err3: | 468 | err: |
547 | clk_put(spi100k->fck); | ||
548 | err2: | ||
549 | clk_put(spi100k->ick); | ||
550 | err1: | ||
551 | spi_master_put(master); | 469 | spi_master_put(master); |
552 | return status; | 470 | return status; |
553 | } | 471 | } |
@@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev) | |||
557 | struct spi_master *master; | 475 | struct spi_master *master; |
558 | struct omap1_spi100k *spi100k; | 476 | struct omap1_spi100k *spi100k; |
559 | struct resource *r; | 477 | struct resource *r; |
560 | unsigned limit = 500; | ||
561 | unsigned long flags; | ||
562 | int status = 0; | 478 | int status = 0; |
563 | 479 | ||
564 | master = platform_get_drvdata(pdev); | 480 | master = platform_get_drvdata(pdev); |
565 | spi100k = spi_master_get_devdata(master); | 481 | spi100k = spi_master_get_devdata(master); |
566 | 482 | ||
567 | spin_lock_irqsave(&spi100k->lock, flags); | ||
568 | |||
569 | spi100k->state = SPI_SHUTDOWN; | ||
570 | while (!list_empty(&spi100k->msg_queue) && limit--) { | ||
571 | spin_unlock_irqrestore(&spi100k->lock, flags); | ||
572 | msleep(10); | ||
573 | spin_lock_irqsave(&spi100k->lock, flags); | ||
574 | } | ||
575 | |||
576 | if (!list_empty(&spi100k->msg_queue)) | ||
577 | status = -EBUSY; | ||
578 | |||
579 | spin_unlock_irqrestore(&spi100k->lock, flags); | ||
580 | |||
581 | if (status != 0) | 483 | if (status != 0) |
582 | return status; | 484 | return status; |
583 | 485 | ||
584 | clk_put(spi100k->fck); | ||
585 | clk_put(spi100k->ick); | ||
586 | |||
587 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 486 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
588 | 487 | ||
589 | spi_unregister_master(master); | 488 | spi_unregister_master(master); |
@@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = { | |||
596 | .name = "omap1_spi100k", | 495 | .name = "omap1_spi100k", |
597 | .owner = THIS_MODULE, | 496 | .owner = THIS_MODULE, |
598 | }, | 497 | }, |
498 | .probe = omap1_spi100k_probe, | ||
599 | .remove = omap1_spi100k_remove, | 499 | .remove = omap1_spi100k_remove, |
600 | }; | 500 | }; |
601 | 501 | ||
602 | 502 | module_platform_driver(omap1_spi100k_driver); | |
603 | static int __init omap1_spi100k_init(void) | ||
604 | { | ||
605 | omap1_spi100k_wq = create_singlethread_workqueue( | ||
606 | omap1_spi100k_driver.driver.name); | ||
607 | |||
608 | if (omap1_spi100k_wq == NULL) | ||
609 | return -1; | ||
610 | |||
611 | return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); | ||
612 | } | ||
613 | |||
614 | static void __exit omap1_spi100k_exit(void) | ||
615 | { | ||
616 | platform_driver_unregister(&omap1_spi100k_driver); | ||
617 | |||
618 | destroy_workqueue(omap1_spi100k_wq); | ||
619 | } | ||
620 | |||
621 | module_init(omap1_spi100k_init); | ||
622 | module_exit(omap1_spi100k_exit); | ||
623 | 503 | ||
624 | MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); | 504 | MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); |
625 | MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); | 505 | MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); |