aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/atmel-mci.c
diff options
context:
space:
mode:
authorHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2008-07-31 08:49:16 -0400
committerHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2008-10-05 14:39:20 -0400
commitc06ad2580dca4eb14ca07541d4f00a3b7cbcf12f (patch)
treefb3cac8ab80b921b5ea1cfb4bad7f18248fb94ad /drivers/mmc/host/atmel-mci.c
parenta252e3e35ef8144fb772da70bb93c99a1486097a (diff)
atmel-mci: Implement tasklet as a state machine
With the current system of completed/pending events, things may get handled in different order depending on which event triggers first. For example, if the data transfer is complete before the command, the stop command must be sent after the command is complete, not the data. This creates a bit of complexity around the stop command. By having the tasklet go through a sequence of clearly defined states, things always happen in a certain order even if the events come at different times, so the stop command can simply be sent when we exit the "sending data" state because we will never enter that state before the command has been sent successfully. Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com>
Diffstat (limited to 'drivers/mmc/host/atmel-mci.c')
-rw-r--r--drivers/mmc/host/atmel-mci.c249
1 files changed, 145 insertions, 104 deletions
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 00008967ef7a..d834e3756aef 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -36,11 +36,17 @@
36 36
37enum { 37enum {
38 EVENT_CMD_COMPLETE = 0, 38 EVENT_CMD_COMPLETE = 0,
39 EVENT_DATA_ERROR,
40 EVENT_DATA_COMPLETE,
41 EVENT_STOP_SENT,
42 EVENT_STOP_COMPLETE,
43 EVENT_XFER_COMPLETE, 39 EVENT_XFER_COMPLETE,
40 EVENT_DATA_COMPLETE,
41 EVENT_DATA_ERROR,
42};
43
44enum atmel_mci_state {
45 STATE_SENDING_CMD = 0,
46 STATE_SENDING_DATA,
47 STATE_DATA_BUSY,
48 STATE_SENDING_STOP,
49 STATE_DATA_ERROR,
44}; 50};
45 51
46struct atmel_mci { 52struct atmel_mci {
@@ -56,7 +62,6 @@ struct atmel_mci {
56 62
57 u32 cmd_status; 63 u32 cmd_status;
58 u32 data_status; 64 u32 data_status;
59 u32 stop_status;
60 u32 stop_cmdr; 65 u32 stop_cmdr;
61 66
62 u32 mode_reg; 67 u32 mode_reg;
@@ -65,6 +70,7 @@ struct atmel_mci {
65 struct tasklet_struct tasklet; 70 struct tasklet_struct tasklet;
66 unsigned long pending_events; 71 unsigned long pending_events;
67 unsigned long completed_events; 72 unsigned long completed_events;
73 enum atmel_mci_state state;
68 74
69 int present; 75 int present;
70 int detect_pin; 76 int detect_pin;
@@ -79,18 +85,12 @@ struct atmel_mci {
79 struct platform_device *pdev; 85 struct platform_device *pdev;
80}; 86};
81 87
82#define atmci_is_completed(host, event) \
83 test_bit(event, &host->completed_events)
84#define atmci_test_and_clear_pending(host, event) \ 88#define atmci_test_and_clear_pending(host, event) \
85 test_and_clear_bit(event, &host->pending_events) 89 test_and_clear_bit(event, &host->pending_events)
86#define atmci_test_and_set_completed(host, event) \
87 test_and_set_bit(event, &host->completed_events)
88#define atmci_set_completed(host, event) \ 90#define atmci_set_completed(host, event) \
89 set_bit(event, &host->completed_events) 91 set_bit(event, &host->completed_events)
90#define atmci_set_pending(host, event) \ 92#define atmci_set_pending(host, event) \
91 set_bit(event, &host->pending_events) 93 set_bit(event, &host->pending_events)
92#define atmci_clear_pending(host, event) \
93 clear_bit(event, &host->pending_events)
94 94
95/* 95/*
96 * The debugfs stuff below is mostly optimized away when 96 * The debugfs stuff below is mostly optimized away when
@@ -258,6 +258,10 @@ static void atmci_init_debugfs(struct atmel_mci *host)
258 if (!node) 258 if (!node)
259 goto err; 259 goto err;
260 260
261 node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
262 if (!node)
263 goto err;
264
261 node = debugfs_create_x32("pending_events", S_IRUSR, root, 265 node = debugfs_create_x32("pending_events", S_IRUSR, root,
262 (u32 *)&host->pending_events); 266 (u32 *)&host->pending_events);
263 if (!node) 267 if (!node)
@@ -378,8 +382,6 @@ static void atmci_start_command(struct atmel_mci *host,
378 struct mmc_command *cmd, 382 struct mmc_command *cmd,
379 u32 cmd_flags) 383 u32 cmd_flags)
380{ 384{
381 /* Must read host->cmd after testing event flags */
382 smp_rmb();
383 WARN_ON(host->cmd); 385 WARN_ON(host->cmd);
384 host->cmd = cmd; 386 host->cmd = cmd;
385 387
@@ -472,6 +474,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
472 host->mrq = mrq; 474 host->mrq = mrq;
473 host->pending_events = 0; 475 host->pending_events = 0;
474 host->completed_events = 0; 476 host->completed_events = 0;
477 host->state = STATE_SENDING_CMD;
475 478
476 atmci_enable(host); 479 atmci_enable(host);
477 480
@@ -596,8 +599,10 @@ static struct mmc_host_ops atmci_ops = {
596}; 599};
597 600
598static void atmci_command_complete(struct atmel_mci *host, 601static void atmci_command_complete(struct atmel_mci *host,
599 struct mmc_command *cmd, u32 status) 602 struct mmc_command *cmd)
600{ 603{
604 u32 status = host->cmd_status;
605
601 /* Read the response from the card (up to 16 bytes) */ 606 /* Read the response from the card (up to 16 bytes) */
602 cmd->resp[0] = mci_readl(host, RSPR); 607 cmd->resp[0] = mci_readl(host, RSPR);
603 cmd->resp[1] = mci_readl(host, RSPR); 608 cmd->resp[1] = mci_readl(host, RSPR);
@@ -666,20 +671,32 @@ static void atmci_detect_change(unsigned long data)
666 * commands or data transfers. 671 * commands or data transfers.
667 */ 672 */
668 mci_writel(host, CR, MCI_CR_SWRST); 673 mci_writel(host, CR, MCI_CR_SWRST);
674 mci_readl(host, SR);
669 675
670 if (!atmci_is_completed(host, EVENT_CMD_COMPLETE)) 676 host->data = NULL;
671 mrq->cmd->error = -ENOMEDIUM; 677 host->cmd = NULL;
672 678
673 if (mrq->data && !atmci_is_completed(host, 679 switch (host->state) {
674 EVENT_DATA_COMPLETE)) { 680 case STATE_SENDING_CMD:
675 host->data = NULL; 681 mrq->cmd->error = -ENOMEDIUM;
682 if (!mrq->data)
683 break;
684 /* fall through */
685 case STATE_SENDING_DATA:
676 mrq->data->error = -ENOMEDIUM; 686 mrq->data->error = -ENOMEDIUM;
677 } 687 break;
678 if (mrq->stop && !atmci_is_completed(host, 688 case STATE_DATA_BUSY:
679 EVENT_STOP_COMPLETE)) 689 case STATE_DATA_ERROR:
690 if (mrq->data->error == -EINPROGRESS)
691 mrq->data->error = -ENOMEDIUM;
692 if (!mrq->stop)
693 break;
694 /* fall through */
695 case STATE_SENDING_STOP:
680 mrq->stop->error = -ENOMEDIUM; 696 mrq->stop->error = -ENOMEDIUM;
697 break;
698 }
681 699
682 host->cmd = NULL;
683 atmci_request_end(host->mmc, mrq); 700 atmci_request_end(host->mmc, mrq);
684 } 701 }
685 702
@@ -693,81 +710,116 @@ static void atmci_tasklet_func(unsigned long priv)
693 struct atmel_mci *host = mmc_priv(mmc); 710 struct atmel_mci *host = mmc_priv(mmc);
694 struct mmc_request *mrq = host->mrq; 711 struct mmc_request *mrq = host->mrq;
695 struct mmc_data *data = host->data; 712 struct mmc_data *data = host->data;
713 struct mmc_command *cmd = host->cmd;
714 enum atmel_mci_state state = host->state;
715 enum atmel_mci_state prev_state;
716 u32 status;
717
718 state = host->state;
696 719
697 dev_vdbg(&mmc->class_dev, 720 dev_vdbg(&mmc->class_dev,
698 "tasklet: pending/completed/mask %lx/%lx/%x\n", 721 "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
699 host->pending_events, host->completed_events, 722 state, host->pending_events, host->completed_events,
700 mci_readl(host, IMR)); 723 mci_readl(host, IMR));
701 724
702 if (atmci_test_and_clear_pending(host, EVENT_CMD_COMPLETE)) { 725 do {
703 /* 726 prev_state = state;
704 * host->cmd must be set to NULL before the interrupt
705 * handler sees EVENT_CMD_COMPLETE
706 */
707 host->cmd = NULL;
708 smp_wmb();
709 atmci_set_completed(host, EVENT_CMD_COMPLETE);
710 atmci_command_complete(host, mrq->cmd, host->cmd_status);
711
712 if (!mrq->cmd->error && mrq->stop
713 && atmci_is_completed(host, EVENT_XFER_COMPLETE)
714 && !atmci_test_and_set_completed(host,
715 EVENT_STOP_SENT))
716 send_stop_cmd(host->mmc, mrq->data);
717 }
718 if (atmci_test_and_clear_pending(host, EVENT_STOP_COMPLETE)) {
719 /*
720 * host->cmd must be set to NULL before the interrupt
721 * handler sees EVENT_STOP_COMPLETE
722 */
723 host->cmd = NULL;
724 smp_wmb();
725 atmci_set_completed(host, EVENT_STOP_COMPLETE);
726 atmci_command_complete(host, mrq->stop, host->stop_status);
727 }
728 if (atmci_test_and_clear_pending(host, EVENT_DATA_ERROR)) {
729 u32 status = host->data_status;
730 727
731 dev_vdbg(&mmc->class_dev, "data error: status=%08x\n", status); 728 switch (state) {
729 case STATE_SENDING_CMD:
730 if (!atmci_test_and_clear_pending(host,
731 EVENT_CMD_COMPLETE))
732 break;
732 733
733 atmci_set_completed(host, EVENT_DATA_ERROR); 734 host->cmd = NULL;
734 atmci_set_completed(host, EVENT_DATA_COMPLETE); 735 atmci_set_completed(host, EVENT_CMD_COMPLETE);
736 atmci_command_complete(host, mrq->cmd);
737 if (!mrq->data || cmd->error) {
738 atmci_request_end(mmc, host->mrq);
739 break;
740 }
735 741
736 if (status & MCI_DTOE) { 742 prev_state = state = STATE_SENDING_DATA;
737 dev_dbg(&mmc->class_dev, 743 /* fall through */
738 "data timeout error\n");
739 data->error = -ETIMEDOUT;
740 } else if (status & MCI_DCRCE) {
741 dev_dbg(&mmc->class_dev, "data CRC error\n");
742 data->error = -EILSEQ;
743 } else {
744 dev_dbg(&mmc->class_dev,
745 "data FIFO error (status=%08x)\n",
746 status);
747 data->error = -EIO;
748 }
749 744
750 if (host->present && data->stop 745 case STATE_SENDING_DATA:
751 && atmci_is_completed(host, EVENT_CMD_COMPLETE) 746 if (atmci_test_and_clear_pending(host,
752 && !atmci_test_and_set_completed( 747 EVENT_DATA_ERROR)) {
753 host, EVENT_STOP_SENT)) 748 if (data->stop)
754 send_stop_cmd(host->mmc, data); 749 send_stop_cmd(host->mmc, data);
750 state = STATE_DATA_ERROR;
751 break;
752 }
755 753
756 host->data = NULL; 754 if (!atmci_test_and_clear_pending(host,
757 } 755 EVENT_XFER_COMPLETE))
758 if (atmci_test_and_clear_pending(host, EVENT_DATA_COMPLETE)) { 756 break;
759 atmci_set_completed(host, EVENT_DATA_COMPLETE);
760 757
761 if (!atmci_is_completed(host, EVENT_DATA_ERROR)) { 758 atmci_set_completed(host, EVENT_XFER_COMPLETE);
762 data->bytes_xfered = data->blocks * data->blksz; 759 prev_state = state = STATE_DATA_BUSY;
763 data->error = 0; 760 /* fall through */
764 }
765 761
766 host->data = NULL; 762 case STATE_DATA_BUSY:
767 } 763 if (!atmci_test_and_clear_pending(host,
764 EVENT_DATA_COMPLETE))
765 break;
766
767 host->data = NULL;
768 atmci_set_completed(host, EVENT_DATA_COMPLETE);
769 status = host->data_status;
770 if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
771 if (status & MCI_DTOE) {
772 dev_dbg(&mmc->class_dev,
773 "data timeout error\n");
774 data->error = -ETIMEDOUT;
775 } else if (status & MCI_DCRCE) {
776 dev_dbg(&mmc->class_dev,
777 "data CRC error\n");
778 data->error = -EILSEQ;
779 } else {
780 dev_dbg(&mmc->class_dev,
781 "data FIFO error (status=%08x)\n",
782 status);
783 data->error = -EIO;
784 }
785 } else {
786 data->bytes_xfered = data->blocks * data->blksz;
787 data->error = 0;
788 }
789
790 if (!data->stop) {
791 atmci_request_end(mmc, host->mrq);
792 prev_state = state;
793 break;
794 }
768 795
769 if (host->mrq && !host->cmd && !host->data) 796 prev_state = state = STATE_SENDING_STOP;
770 atmci_request_end(mmc, host->mrq); 797 if (!data->error)
798 send_stop_cmd(host->mmc, data);
799 /* fall through */
800
801 case STATE_SENDING_STOP:
802 if (!atmci_test_and_clear_pending(host,
803 EVENT_CMD_COMPLETE))
804 break;
805
806 host->cmd = NULL;
807 atmci_command_complete(host, mrq->stop);
808 atmci_request_end(mmc, host->mrq);
809 prev_state = state;
810 break;
811
812 case STATE_DATA_ERROR:
813 if (!atmci_test_and_clear_pending(host,
814 EVENT_XFER_COMPLETE))
815 break;
816
817 state = STATE_DATA_BUSY;
818 break;
819 }
820 } while (state != prev_state);
821
822 host->state = state;
771} 823}
772 824
773static void atmci_read_data_pio(struct atmel_mci *host) 825static void atmci_read_data_pio(struct atmel_mci *host)
@@ -832,10 +884,7 @@ done:
832 mci_writel(host, IDR, MCI_RXRDY); 884 mci_writel(host, IDR, MCI_RXRDY);
833 mci_writel(host, IER, MCI_NOTBUSY); 885 mci_writel(host, IER, MCI_NOTBUSY);
834 data->bytes_xfered += nbytes; 886 data->bytes_xfered += nbytes;
835 atmci_set_completed(host, EVENT_XFER_COMPLETE); 887 atmci_set_pending(host, EVENT_XFER_COMPLETE);
836 if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
837 && !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
838 send_stop_cmd(host->mmc, data);
839} 888}
840 889
841static void atmci_write_data_pio(struct atmel_mci *host) 890static void atmci_write_data_pio(struct atmel_mci *host)
@@ -903,10 +952,7 @@ done:
903 mci_writel(host, IDR, MCI_TXRDY); 952 mci_writel(host, IDR, MCI_TXRDY);
904 mci_writel(host, IER, MCI_NOTBUSY); 953 mci_writel(host, IER, MCI_NOTBUSY);
905 data->bytes_xfered += nbytes; 954 data->bytes_xfered += nbytes;
906 atmci_set_completed(host, EVENT_XFER_COMPLETE); 955 atmci_set_pending(host, EVENT_XFER_COMPLETE);
907 if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
908 && !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
909 send_stop_cmd(host->mmc, data);
910} 956}
911 957
912static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) 958static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
@@ -915,14 +961,8 @@ static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
915 961
916 mci_writel(host, IDR, MCI_CMDRDY); 962 mci_writel(host, IDR, MCI_CMDRDY);
917 963
918 if (atmci_is_completed(host, EVENT_STOP_SENT)) { 964 host->cmd_status = status;
919 host->stop_status = status; 965 atmci_set_pending(host, EVENT_CMD_COMPLETE);
920 atmci_set_pending(host, EVENT_STOP_COMPLETE);
921 } else {
922 host->cmd_status = status;
923 atmci_set_pending(host, EVENT_CMD_COMPLETE);
924 }
925
926 tasklet_schedule(&host->tasklet); 966 tasklet_schedule(&host->tasklet);
927} 967}
928 968
@@ -951,8 +991,9 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
951 tasklet_schedule(&host->tasklet); 991 tasklet_schedule(&host->tasklet);
952 } 992 }
953 if (pending & MCI_NOTBUSY) { 993 if (pending & MCI_NOTBUSY) {
954 mci_writel(host, IDR, (MCI_NOTBUSY 994 mci_writel(host, IDR,
955 | ATMCI_DATA_ERROR_FLAGS)); 995 ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY);
996 host->data_status = status;
956 atmci_set_pending(host, EVENT_DATA_COMPLETE); 997 atmci_set_pending(host, EVENT_DATA_COMPLETE);
957 tasklet_schedule(&host->tasklet); 998 tasklet_schedule(&host->tasklet);
958 } 999 }