diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2011-04-15 14:30:47 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-05-24 23:53:50 -0400 |
commit | 3b0beafc92406b98cbc2749b8db736f313d390b1 (patch) | |
tree | d782fec514beb5dbb4281adc6d6ec28b7c5915da /drivers | |
parent | 06e8935febe687e2a561707d4c7ca4245d261dbe (diff) |
mmc: sh_mmcif: protect against a theoretical race
The MMC subsystem does not guarantee that host driver .request() and
.set_ios() callbacks are serialised. Such concurrent calls, however,
do not have to be meaningfully supported, drivers just have to make
sure to avoid any severe problems.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Simon Horman <horms@verge.net.au>
Cc: Magnus Damm <damm@opensource.se>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/sh_mmcif.c | 50 |
1 files changed, 42 insertions, 8 deletions
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index af97015a2fc7..d3871b67f77b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/mmc/sh_mmcif.h> | 29 | #include <linux/mmc/sh_mmcif.h> |
30 | #include <linux/pagemap.h> | 30 | #include <linux/pagemap.h> |
31 | #include <linux/platform_device.h> | 31 | #include <linux/platform_device.h> |
32 | #include <linux/spinlock.h> | ||
32 | 33 | ||
33 | #define DRIVER_NAME "sh_mmcif" | 34 | #define DRIVER_NAME "sh_mmcif" |
34 | #define DRIVER_VERSION "2010-04-28" | 35 | #define DRIVER_VERSION "2010-04-28" |
@@ -153,6 +154,12 @@ | |||
153 | #define CLKDEV_MMC_DATA 20000000 /* 20MHz */ | 154 | #define CLKDEV_MMC_DATA 20000000 /* 20MHz */ |
154 | #define CLKDEV_INIT 400000 /* 400 KHz */ | 155 | #define CLKDEV_INIT 400000 /* 400 KHz */ |
155 | 156 | ||
157 | enum mmcif_state { | ||
158 | STATE_IDLE, | ||
159 | STATE_REQUEST, | ||
160 | STATE_IOS, | ||
161 | }; | ||
162 | |||
156 | struct sh_mmcif_host { | 163 | struct sh_mmcif_host { |
157 | struct mmc_host *mmc; | 164 | struct mmc_host *mmc; |
158 | struct mmc_data *data; | 165 | struct mmc_data *data; |
@@ -164,6 +171,8 @@ struct sh_mmcif_host { | |||
164 | long timeout; | 171 | long timeout; |
165 | void __iomem *addr; | 172 | void __iomem *addr; |
166 | struct completion intr_wait; | 173 | struct completion intr_wait; |
174 | enum mmcif_state state; | ||
175 | spinlock_t lock; | ||
167 | 176 | ||
168 | /* DMA support */ | 177 | /* DMA support */ |
169 | struct dma_chan *chan_rx; | 178 | struct dma_chan *chan_rx; |
@@ -798,17 +807,31 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, | |||
798 | static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) | 807 | static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) |
799 | { | 808 | { |
800 | struct sh_mmcif_host *host = mmc_priv(mmc); | 809 | struct sh_mmcif_host *host = mmc_priv(mmc); |
810 | unsigned long flags; | ||
811 | |||
812 | spin_lock_irqsave(&host->lock, flags); | ||
813 | if (host->state != STATE_IDLE) { | ||
814 | spin_unlock_irqrestore(&host->lock, flags); | ||
815 | mrq->cmd->error = -EAGAIN; | ||
816 | mmc_request_done(mmc, mrq); | ||
817 | return; | ||
818 | } | ||
819 | |||
820 | host->state = STATE_REQUEST; | ||
821 | spin_unlock_irqrestore(&host->lock, flags); | ||
801 | 822 | ||
802 | switch (mrq->cmd->opcode) { | 823 | switch (mrq->cmd->opcode) { |
803 | /* MMCIF does not support SD/SDIO command */ | 824 | /* MMCIF does not support SD/SDIO command */ |
804 | case SD_IO_SEND_OP_COND: | 825 | case SD_IO_SEND_OP_COND: |
805 | case MMC_APP_CMD: | 826 | case MMC_APP_CMD: |
827 | host->state = STATE_IDLE; | ||
806 | mrq->cmd->error = -ETIMEDOUT; | 828 | mrq->cmd->error = -ETIMEDOUT; |
807 | mmc_request_done(mmc, mrq); | 829 | mmc_request_done(mmc, mrq); |
808 | return; | 830 | return; |
809 | case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ | 831 | case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ |
810 | if (!mrq->data) { | 832 | if (!mrq->data) { |
811 | /* send_if_cond cmd (not support) */ | 833 | /* send_if_cond cmd (not support) */ |
834 | host->state = STATE_IDLE; | ||
812 | mrq->cmd->error = -ETIMEDOUT; | 835 | mrq->cmd->error = -ETIMEDOUT; |
813 | mmc_request_done(mmc, mrq); | 836 | mmc_request_done(mmc, mrq); |
814 | return; | 837 | return; |
@@ -830,12 +853,9 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
830 | sh_mmcif_start_cmd(host, mrq, mrq->cmd); | 853 | sh_mmcif_start_cmd(host, mrq, mrq->cmd); |
831 | host->data = NULL; | 854 | host->data = NULL; |
832 | 855 | ||
833 | if (mrq->cmd->error != 0) { | 856 | if (!mrq->cmd->error && mrq->stop) |
834 | mmc_request_done(mmc, mrq); | ||
835 | return; | ||
836 | } | ||
837 | if (mrq->stop) | ||
838 | sh_mmcif_stop_cmd(host, mrq, mrq->stop); | 857 | sh_mmcif_stop_cmd(host, mrq, mrq->stop); |
858 | host->state = STATE_IDLE; | ||
839 | mmc_request_done(mmc, mrq); | 859 | mmc_request_done(mmc, mrq); |
840 | } | 860 | } |
841 | 861 | ||
@@ -843,6 +863,16 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
843 | { | 863 | { |
844 | struct sh_mmcif_host *host = mmc_priv(mmc); | 864 | struct sh_mmcif_host *host = mmc_priv(mmc); |
845 | struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; | 865 | struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; |
866 | unsigned long flags; | ||
867 | |||
868 | spin_lock_irqsave(&host->lock, flags); | ||
869 | if (host->state != STATE_IDLE) { | ||
870 | spin_unlock_irqrestore(&host->lock, flags); | ||
871 | return; | ||
872 | } | ||
873 | |||
874 | host->state = STATE_IOS; | ||
875 | spin_unlock_irqrestore(&host->lock, flags); | ||
846 | 876 | ||
847 | if (ios->power_mode == MMC_POWER_UP) { | 877 | if (ios->power_mode == MMC_POWER_UP) { |
848 | if (p->set_pwr) | 878 | if (p->set_pwr) |
@@ -852,6 +882,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
852 | sh_mmcif_clock_control(host, 0); | 882 | sh_mmcif_clock_control(host, 0); |
853 | if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) | 883 | if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) |
854 | p->down_pwr(host->pd); | 884 | p->down_pwr(host->pd); |
885 | host->state = STATE_IDLE; | ||
855 | return; | 886 | return; |
856 | } | 887 | } |
857 | 888 | ||
@@ -859,6 +890,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
859 | sh_mmcif_clock_control(host, ios->clock); | 890 | sh_mmcif_clock_control(host, ios->clock); |
860 | 891 | ||
861 | host->bus_width = ios->bus_width; | 892 | host->bus_width = ios->bus_width; |
893 | host->state = STATE_IDLE; | ||
862 | } | 894 | } |
863 | 895 | ||
864 | static int sh_mmcif_get_cd(struct mmc_host *mmc) | 896 | static int sh_mmcif_get_cd(struct mmc_host *mmc) |
@@ -996,6 +1028,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) | |||
996 | host->pd = pdev; | 1028 | host->pd = pdev; |
997 | 1029 | ||
998 | init_completion(&host->intr_wait); | 1030 | init_completion(&host->intr_wait); |
1031 | spin_lock_init(&host->lock); | ||
999 | 1032 | ||
1000 | mmc->ops = &sh_mmcif_ops; | 1033 | mmc->ops = &sh_mmcif_ops; |
1001 | mmc->f_max = host->clk; | 1034 | mmc->f_max = host->clk; |
@@ -1025,6 +1058,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) | |||
1025 | 1058 | ||
1026 | mmc_add_host(mmc); | 1059 | mmc_add_host(mmc); |
1027 | 1060 | ||
1061 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | ||
1062 | |||
1028 | ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); | 1063 | ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); |
1029 | if (ret) { | 1064 | if (ret) { |
1030 | dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); | 1065 | dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); |
@@ -1037,7 +1072,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) | |||
1037 | goto clean_up2; | 1072 | goto clean_up2; |
1038 | } | 1073 | } |
1039 | 1074 | ||
1040 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | ||
1041 | sh_mmcif_detect(host->mmc); | 1075 | sh_mmcif_detect(host->mmc); |
1042 | 1076 | ||
1043 | dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); | 1077 | dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); |
@@ -1063,11 +1097,11 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) | |||
1063 | mmc_remove_host(host->mmc); | 1097 | mmc_remove_host(host->mmc); |
1064 | sh_mmcif_release_dma(host); | 1098 | sh_mmcif_release_dma(host); |
1065 | 1099 | ||
1100 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | ||
1101 | |||
1066 | if (host->addr) | 1102 | if (host->addr) |
1067 | iounmap(host->addr); | 1103 | iounmap(host->addr); |
1068 | 1104 | ||
1069 | sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); | ||
1070 | |||
1071 | irq[0] = platform_get_irq(pdev, 0); | 1105 | irq[0] = platform_get_irq(pdev, 0); |
1072 | irq[1] = platform_get_irq(pdev, 1); | 1106 | irq[1] = platform_get_irq(pdev, 1); |
1073 | 1107 | ||