diff options
author | Juha Yrjola <juha.yrjola@solidboot.com> | 2008-03-26 16:08:57 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-04-18 14:05:28 -0400 |
commit | abfbe5f7854a083ca324282bf7e39f10bc438313 (patch) | |
tree | c2034a5437daa6fdc973fd2837fa892b4f55e80f /drivers/mmc/host/omap.c | |
parent | 4bc9e35556bf4444014ba65b80abb2fb9f70899a (diff) |
MMC: OMAP: Introduce new multislot structure and change driver to use it
Introduce new MMC multislot structure and change driver to use it.
Note that MMC clocking is now enabled in mmc_omap_select_slot()
and disabled in mmc_omap_release_slot().
Signed-off-by: Juha Yrjola <juha.yrjola@solidboot.com>
Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Carlos Eduardo Aguiar <carlos.aguiar@indt.org.br>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/omap.c')
-rw-r--r-- | drivers/mmc/host/omap.c | 402 |
1 files changed, 292 insertions, 110 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 7d17c899c39..59eac721184 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <asm/mach-types.h> | 32 | #include <asm/mach-types.h> |
33 | 33 | ||
34 | #include <asm/arch/board.h> | 34 | #include <asm/arch/board.h> |
35 | #include <asm/arch/mmc.h> | ||
35 | #include <asm/arch/gpio.h> | 36 | #include <asm/arch/gpio.h> |
36 | #include <asm/arch/dma.h> | 37 | #include <asm/arch/dma.h> |
37 | #include <asm/arch/mux.h> | 38 | #include <asm/arch/mux.h> |
@@ -95,6 +96,22 @@ | |||
95 | * when the cover switch is open */ | 96 | * when the cover switch is open */ |
96 | #define OMAP_MMC_SWITCH_POLL_DELAY 500 | 97 | #define OMAP_MMC_SWITCH_POLL_DELAY 500 |
97 | 98 | ||
99 | struct mmc_omap_host; | ||
100 | |||
101 | struct mmc_omap_slot { | ||
102 | int id; | ||
103 | unsigned int vdd; | ||
104 | u16 saved_con; | ||
105 | u16 bus_mode; | ||
106 | unsigned int fclk_freq; | ||
107 | unsigned powered:1; | ||
108 | |||
109 | struct mmc_request *mrq; | ||
110 | struct mmc_omap_host *host; | ||
111 | struct mmc_host *mmc; | ||
112 | struct omap_mmc_slot_data *pdata; | ||
113 | }; | ||
114 | |||
98 | struct mmc_omap_host { | 115 | struct mmc_omap_host { |
99 | int initialized; | 116 | int initialized; |
100 | int suspended; | 117 | int suspended; |
@@ -129,13 +146,98 @@ struct mmc_omap_host { | |||
129 | unsigned dma_len; | 146 | unsigned dma_len; |
130 | 147 | ||
131 | short power_pin; | 148 | short power_pin; |
132 | short wp_pin; | ||
133 | 149 | ||
134 | struct work_struct switch_work; | 150 | struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS]; |
135 | struct timer_list switch_timer; | 151 | struct mmc_omap_slot *current_slot; |
136 | int switch_last_state; | 152 | spinlock_t slot_lock; |
153 | wait_queue_head_t slot_wq; | ||
154 | int nr_slots; | ||
155 | |||
156 | struct omap_mmc_platform_data *pdata; | ||
137 | }; | 157 | }; |
138 | 158 | ||
159 | static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) | ||
160 | { | ||
161 | struct mmc_omap_host *host = slot->host; | ||
162 | unsigned long flags; | ||
163 | |||
164 | if (claimed) | ||
165 | goto no_claim; | ||
166 | spin_lock_irqsave(&host->slot_lock, flags); | ||
167 | while (host->mmc != NULL) { | ||
168 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
169 | wait_event(host->slot_wq, host->mmc == NULL); | ||
170 | spin_lock_irqsave(&host->slot_lock, flags); | ||
171 | } | ||
172 | host->mmc = slot->mmc; | ||
173 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
174 | no_claim: | ||
175 | clk_enable(host->fclk); | ||
176 | if (host->current_slot != slot) { | ||
177 | if (host->pdata->switch_slot != NULL) | ||
178 | host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); | ||
179 | host->current_slot = slot; | ||
180 | } | ||
181 | |||
182 | /* Doing the dummy read here seems to work around some bug | ||
183 | * at least in OMAP24xx silicon where the command would not | ||
184 | * start after writing the CMD register. Sigh. */ | ||
185 | OMAP_MMC_READ(host, CON); | ||
186 | |||
187 | OMAP_MMC_WRITE(host, CON, slot->saved_con); | ||
188 | } | ||
189 | |||
190 | static void mmc_omap_start_request(struct mmc_omap_host *host, | ||
191 | struct mmc_request *req); | ||
192 | |||
193 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot) | ||
194 | { | ||
195 | struct mmc_omap_host *host = slot->host; | ||
196 | unsigned long flags; | ||
197 | int i; | ||
198 | |||
199 | BUG_ON(slot == NULL || host->mmc == NULL); | ||
200 | clk_disable(host->fclk); | ||
201 | |||
202 | spin_lock_irqsave(&host->slot_lock, flags); | ||
203 | /* Check for any pending requests */ | ||
204 | for (i = 0; i < host->nr_slots; i++) { | ||
205 | struct mmc_omap_slot *new_slot; | ||
206 | struct mmc_request *rq; | ||
207 | |||
208 | if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) | ||
209 | continue; | ||
210 | |||
211 | new_slot = host->slots[i]; | ||
212 | /* The current slot should not have a request in queue */ | ||
213 | BUG_ON(new_slot == host->current_slot); | ||
214 | |||
215 | host->mmc = new_slot->mmc; | ||
216 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
217 | mmc_omap_select_slot(new_slot, 1); | ||
218 | rq = new_slot->mrq; | ||
219 | new_slot->mrq = NULL; | ||
220 | mmc_omap_start_request(host, rq); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | host->mmc = NULL; | ||
225 | wake_up(&host->slot_wq); | ||
226 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
227 | } | ||
228 | |||
229 | static ssize_t | ||
230 | mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, | ||
231 | char *buf) | ||
232 | { | ||
233 | struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); | ||
234 | struct mmc_omap_slot *slot = mmc_priv(mmc); | ||
235 | |||
236 | return sprintf(buf, "%s\n", slot->pdata->name); | ||
237 | } | ||
238 | |||
239 | static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); | ||
240 | |||
139 | static void | 241 | static void |
140 | mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | 242 | mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) |
141 | { | 243 | { |
@@ -180,7 +282,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
180 | 282 | ||
181 | cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); | 283 | cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); |
182 | 284 | ||
183 | if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) | 285 | if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN) |
184 | cmdreg |= 1 << 6; | 286 | cmdreg |= 1 << 6; |
185 | 287 | ||
186 | if (cmd->flags & MMC_RSP_BUSY) | 288 | if (cmd->flags & MMC_RSP_BUSY) |
@@ -189,8 +291,6 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
189 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) | 291 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) |
190 | cmdreg |= 1 << 15; | 292 | cmdreg |= 1 << 15; |
191 | 293 | ||
192 | clk_enable(host->fclk); | ||
193 | |||
194 | OMAP_MMC_WRITE(host, CTO, 200); | 294 | OMAP_MMC_WRITE(host, CTO, 200); |
195 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); | 295 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); |
196 | OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); | 296 | OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); |
@@ -442,6 +542,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
442 | if (status & OMAP_MMC_STAT_CMD_TOUT) { | 542 | if (status & OMAP_MMC_STAT_CMD_TOUT) { |
443 | /* Timeouts are routine with some commands */ | 543 | /* Timeouts are routine with some commands */ |
444 | if (host->cmd) { | 544 | if (host->cmd) { |
545 | struct mmc_omap_slot *slot = | ||
546 | host->current_slot; | ||
445 | dev_err(mmc_dev(host->mmc), | 547 | dev_err(mmc_dev(host->mmc), |
446 | "command timeout, CMD %d\n", | 548 | "command timeout, CMD %d\n", |
447 | host->cmd->opcode); | 549 | host->cmd->opcode); |
@@ -747,11 +849,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) | |||
747 | } | 849 | } |
748 | } | 850 | } |
749 | 851 | ||
750 | static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) | 852 | static void mmc_omap_start_request(struct mmc_omap_host *host, |
853 | struct mmc_request *req) | ||
751 | { | 854 | { |
752 | struct mmc_omap_host *host = mmc_priv(mmc); | 855 | BUG_ON(host->mrq != NULL); |
753 | |||
754 | WARN_ON(host->mrq != NULL); | ||
755 | 856 | ||
756 | host->mrq = req; | 857 | host->mrq = req; |
757 | 858 | ||
@@ -760,6 +861,26 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) | |||
760 | mmc_omap_start_command(host, req->cmd); | 861 | mmc_omap_start_command(host, req->cmd); |
761 | if (host->dma_in_use) | 862 | if (host->dma_in_use) |
762 | omap_start_dma(host->dma_ch); | 863 | omap_start_dma(host->dma_ch); |
864 | BUG_ON(irqs_disabled()); | ||
865 | } | ||
866 | |||
867 | static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) | ||
868 | { | ||
869 | struct mmc_omap_slot *slot = mmc_priv(mmc); | ||
870 | struct mmc_omap_host *host = slot->host; | ||
871 | unsigned long flags; | ||
872 | |||
873 | spin_lock_irqsave(&host->slot_lock, flags); | ||
874 | if (host->mmc != NULL) { | ||
875 | BUG_ON(slot->mrq != NULL); | ||
876 | slot->mrq = req; | ||
877 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
878 | return; | ||
879 | } else | ||
880 | host->mmc = mmc; | ||
881 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
882 | mmc_omap_select_slot(slot, 1); | ||
883 | mmc_omap_start_request(host, req); | ||
763 | } | 884 | } |
764 | 885 | ||
765 | static void innovator_fpga_socket_power(int on) | 886 | static void innovator_fpga_socket_power(int on) |
@@ -813,7 +934,8 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) | |||
813 | 934 | ||
814 | static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | 935 | static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) |
815 | { | 936 | { |
816 | struct mmc_omap_host *host = mmc_priv(mmc); | 937 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
938 | struct mmc_omap_host *host = slot->host; | ||
817 | int func_clk_rate = clk_get_rate(host->fclk); | 939 | int func_clk_rate = clk_get_rate(host->fclk); |
818 | int dsor; | 940 | int dsor; |
819 | 941 | ||
@@ -830,6 +952,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | |||
830 | if (dsor > 250) | 952 | if (dsor > 250) |
831 | dsor = 250; | 953 | dsor = 250; |
832 | 954 | ||
955 | slot->fclk_freq = func_clk_rate / dsor; | ||
956 | |||
833 | if (ios->bus_width == MMC_BUS_WIDTH_4) | 957 | if (ios->bus_width == MMC_BUS_WIDTH_4) |
834 | dsor |= 1 << 15; | 958 | dsor |= 1 << 15; |
835 | 959 | ||
@@ -838,9 +962,9 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) | |||
838 | 962 | ||
839 | static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | 963 | static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
840 | { | 964 | { |
841 | struct mmc_omap_host *host = mmc_priv(mmc); | 965 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
842 | int dsor; | 966 | struct mmc_omap_host *host = slot->host; |
843 | int i; | 967 | int i, dsor; |
844 | 968 | ||
845 | dsor = mmc_omap_calc_divisor(mmc, ios); | 969 | dsor = mmc_omap_calc_divisor(mmc, ios); |
846 | host->bus_mode = ios->bus_mode; | 970 | host->bus_mode = ios->bus_mode; |
@@ -878,32 +1002,101 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
878 | clk_disable(host->fclk); | 1002 | clk_disable(host->fclk); |
879 | } | 1003 | } |
880 | 1004 | ||
881 | static int mmc_omap_get_ro(struct mmc_host *mmc) | ||
882 | { | ||
883 | struct mmc_omap_host *host = mmc_priv(mmc); | ||
884 | |||
885 | return host->wp_pin && omap_get_gpio_datain(host->wp_pin); | ||
886 | } | ||
887 | |||
888 | static const struct mmc_host_ops mmc_omap_ops = { | 1005 | static const struct mmc_host_ops mmc_omap_ops = { |
889 | .request = mmc_omap_request, | 1006 | .request = mmc_omap_request, |
890 | .set_ios = mmc_omap_set_ios, | 1007 | .set_ios = mmc_omap_set_ios, |
891 | .get_ro = mmc_omap_get_ro, | ||
892 | }; | 1008 | }; |
893 | 1009 | ||
894 | static int __init mmc_omap_probe(struct platform_device *pdev) | 1010 | static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) |
895 | { | 1011 | { |
896 | struct omap_mmc_conf *minfo = pdev->dev.platform_data; | 1012 | struct mmc_omap_slot *slot = NULL; |
897 | struct mmc_host *mmc; | 1013 | struct mmc_host *mmc; |
1014 | int r; | ||
1015 | |||
1016 | mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev); | ||
1017 | if (mmc == NULL) | ||
1018 | return -ENOMEM; | ||
1019 | |||
1020 | slot = mmc_priv(mmc); | ||
1021 | slot->host = host; | ||
1022 | slot->mmc = mmc; | ||
1023 | slot->id = id; | ||
1024 | slot->pdata = &host->pdata->slots[id]; | ||
1025 | |||
1026 | host->slots[id] = slot; | ||
1027 | |||
1028 | mmc->caps = MMC_CAP_MULTIWRITE; | ||
1029 | if (host->pdata->conf.wire4) | ||
1030 | mmc->caps |= MMC_CAP_4_BIT_DATA; | ||
1031 | |||
1032 | mmc->ops = &mmc_omap_ops; | ||
1033 | mmc->f_min = 400000; | ||
1034 | |||
1035 | if (cpu_class_is_omap2()) | ||
1036 | mmc->f_max = 48000000; | ||
1037 | else | ||
1038 | mmc->f_max = 24000000; | ||
1039 | if (host->pdata->max_freq) | ||
1040 | mmc->f_max = min(host->pdata->max_freq, mmc->f_max); | ||
1041 | mmc->ocr_avail = slot->pdata->ocr_mask; | ||
1042 | |||
1043 | /* Use scatterlist DMA to reduce per-transfer costs. | ||
1044 | * NOTE max_seg_size assumption that small blocks aren't | ||
1045 | * normally used (except e.g. for reading SD registers). | ||
1046 | */ | ||
1047 | mmc->max_phys_segs = 32; | ||
1048 | mmc->max_hw_segs = 32; | ||
1049 | mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ | ||
1050 | mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ | ||
1051 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
1052 | mmc->max_seg_size = mmc->max_req_size; | ||
1053 | |||
1054 | r = mmc_add_host(mmc); | ||
1055 | if (r < 0) | ||
1056 | goto err_remove_host; | ||
1057 | |||
1058 | if (slot->pdata->name != NULL) { | ||
1059 | r = device_create_file(&mmc->class_dev, | ||
1060 | &dev_attr_slot_name); | ||
1061 | if (r < 0) | ||
1062 | goto err_remove_host; | ||
1063 | } | ||
1064 | |||
1065 | return 0; | ||
1066 | |||
1067 | err_remove_host: | ||
1068 | mmc_remove_host(mmc); | ||
1069 | mmc_free_host(mmc); | ||
1070 | return r; | ||
1071 | } | ||
1072 | |||
1073 | static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) | ||
1074 | { | ||
1075 | struct mmc_host *mmc = slot->mmc; | ||
1076 | |||
1077 | if (slot->pdata->name != NULL) | ||
1078 | device_remove_file(&mmc->class_dev, &dev_attr_slot_name); | ||
1079 | |||
1080 | mmc_remove_host(mmc); | ||
1081 | mmc_free_host(mmc); | ||
1082 | } | ||
1083 | |||
1084 | static int __init mmc_omap_probe(struct platform_device *pdev) | ||
1085 | { | ||
1086 | struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; | ||
898 | struct mmc_omap_host *host = NULL; | 1087 | struct mmc_omap_host *host = NULL; |
899 | struct resource *res; | 1088 | struct resource *res; |
900 | int ret = 0; | 1089 | int i, ret = 0; |
901 | int irq; | 1090 | int irq; |
902 | 1091 | ||
903 | if (minfo == NULL) { | 1092 | if (pdata == NULL) { |
904 | dev_err(&pdev->dev, "platform data missing\n"); | 1093 | dev_err(&pdev->dev, "platform data missing\n"); |
905 | return -ENXIO; | 1094 | return -ENXIO; |
906 | } | 1095 | } |
1096 | if (pdata->nr_slots == 0) { | ||
1097 | dev_err(&pdev->dev, "no slots\n"); | ||
1098 | return -ENXIO; | ||
1099 | } | ||
907 | 1100 | ||
908 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1101 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
909 | irq = platform_get_irq(pdev, 0); | 1102 | irq = platform_get_irq(pdev, 0); |
@@ -911,28 +1104,39 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
911 | return -ENXIO; | 1104 | return -ENXIO; |
912 | 1105 | ||
913 | res = request_mem_region(res->start, res->end - res->start + 1, | 1106 | res = request_mem_region(res->start, res->end - res->start + 1, |
914 | pdev->name); | 1107 | pdev->name); |
915 | if (res == NULL) | 1108 | if (res == NULL) |
916 | return -EBUSY; | 1109 | return -EBUSY; |
917 | 1110 | ||
918 | mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); | 1111 | host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL); |
919 | if (mmc == NULL) { | 1112 | if (host == NULL) { |
920 | ret = -ENOMEM; | 1113 | ret = -ENOMEM; |
921 | goto err_free_mem_region; | 1114 | goto err_free_mem_region; |
922 | } | 1115 | } |
923 | 1116 | ||
924 | host = mmc_priv(mmc); | ||
925 | host->mmc = mmc; | ||
926 | |||
927 | spin_lock_init(&host->dma_lock); | 1117 | spin_lock_init(&host->dma_lock); |
928 | init_timer(&host->dma_timer); | 1118 | init_timer(&host->dma_timer); |
1119 | spin_lock_init(&host->slot_lock); | ||
1120 | init_waitqueue_head(&host->slot_wq); | ||
1121 | |||
929 | host->dma_timer.function = mmc_omap_dma_timer; | 1122 | host->dma_timer.function = mmc_omap_dma_timer; |
930 | host->dma_timer.data = (unsigned long) host; | 1123 | host->dma_timer.data = (unsigned long) host; |
931 | 1124 | ||
1125 | host->pdata = pdata; | ||
1126 | host->dev = &pdev->dev; | ||
1127 | platform_set_drvdata(pdev, host); | ||
1128 | |||
932 | host->id = pdev->id; | 1129 | host->id = pdev->id; |
933 | host->mem_res = res; | 1130 | host->mem_res = res; |
934 | host->irq = irq; | 1131 | host->irq = irq; |
935 | 1132 | ||
1133 | host->use_dma = 1; | ||
1134 | host->dma_ch = -1; | ||
1135 | |||
1136 | host->irq = irq; | ||
1137 | host->phys_base = host->mem_res->start; | ||
1138 | host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); | ||
1139 | |||
936 | if (cpu_is_omap24xx()) { | 1140 | if (cpu_is_omap24xx()) { |
937 | host->iclk = clk_get(&pdev->dev, "mmc_ick"); | 1141 | host->iclk = clk_get(&pdev->dev, "mmc_ick"); |
938 | if (IS_ERR(host->iclk)) | 1142 | if (IS_ERR(host->iclk)) |
@@ -950,70 +1154,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
950 | goto err_free_iclk; | 1154 | goto err_free_iclk; |
951 | } | 1155 | } |
952 | 1156 | ||
953 | /* REVISIT: | 1157 | ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); |
954 | * Also, use minfo->cover to decide how to manage | 1158 | if (ret) |
955 | * the card detect sensing. | 1159 | goto err_free_fclk; |
956 | */ | ||
957 | host->power_pin = minfo->power_pin; | ||
958 | host->wp_pin = minfo->wp_pin; | ||
959 | host->use_dma = 1; | ||
960 | host->dma_ch = -1; | ||
961 | |||
962 | host->irq = irq; | ||
963 | host->phys_base = host->mem_res->start; | ||
964 | host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); | ||
965 | |||
966 | mmc->ops = &mmc_omap_ops; | ||
967 | mmc->f_min = 400000; | ||
968 | mmc->f_max = 24000000; | ||
969 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | ||
970 | mmc->caps = MMC_CAP_MULTIWRITE; | ||
971 | 1160 | ||
972 | if (minfo->wire4) | 1161 | if (pdata->init != NULL) { |
973 | mmc->caps |= MMC_CAP_4_BIT_DATA; | 1162 | ret = pdata->init(&pdev->dev); |
1163 | if (ret < 0) | ||
1164 | goto err_free_irq; | ||
1165 | } | ||
974 | 1166 | ||
975 | /* Use scatterlist DMA to reduce per-transfer costs. | 1167 | host->nr_slots = pdata->nr_slots; |
976 | * NOTE max_seg_size assumption that small blocks aren't | 1168 | for (i = 0; i < pdata->nr_slots; i++) { |
977 | * normally used (except e.g. for reading SD registers). | 1169 | ret = mmc_omap_new_slot(host, i); |
978 | */ | 1170 | if (ret < 0) { |
979 | mmc->max_phys_segs = 32; | 1171 | while (--i >= 0) |
980 | mmc->max_hw_segs = 32; | 1172 | mmc_omap_remove_slot(host->slots[i]); |
981 | mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ | ||
982 | mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ | ||
983 | mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; | ||
984 | mmc->max_seg_size = mmc->max_req_size; | ||
985 | 1173 | ||
986 | if (host->power_pin >= 0) { | 1174 | goto err_plat_cleanup; |
987 | if ((ret = omap_request_gpio(host->power_pin)) != 0) { | ||
988 | dev_err(mmc_dev(host->mmc), | ||
989 | "Unable to get GPIO pin for MMC power\n"); | ||
990 | goto err_free_fclk; | ||
991 | } | 1175 | } |
992 | omap_set_gpio_direction(host->power_pin, 0); | ||
993 | } | 1176 | } |
994 | 1177 | ||
995 | ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); | ||
996 | if (ret) | ||
997 | goto err_free_power_gpio; | ||
998 | |||
999 | host->dev = &pdev->dev; | ||
1000 | platform_set_drvdata(pdev, host); | ||
1001 | |||
1002 | mmc_add_host(mmc); | ||
1003 | |||
1004 | return 0; | 1178 | return 0; |
1005 | 1179 | ||
1006 | /* FIXME: Free other resources too. */ | 1180 | err_plat_cleanup: |
1007 | if (host) { | 1181 | if (pdata->cleanup) |
1008 | if (host->iclk && !IS_ERR(host->iclk)) | 1182 | pdata->cleanup(&pdev->dev); |
1009 | clk_put(host->iclk); | 1183 | err_free_irq: |
1010 | if (host->fclk && !IS_ERR(host->fclk)) | 1184 | free_irq(host->irq, host); |
1011 | clk_put(host->fclk); | ||
1012 | mmc_free_host(host->mmc); | ||
1013 | } | ||
1014 | err_free_power_gpio: | ||
1015 | if (host->power_pin >= 0) | ||
1016 | omap_free_gpio(host->power_pin); | ||
1017 | err_free_fclk: | 1185 | err_free_fclk: |
1018 | clk_put(host->fclk); | 1186 | clk_put(host->fclk); |
1019 | err_free_iclk: | 1187 | err_free_iclk: |
@@ -1022,7 +1190,7 @@ err_free_iclk: | |||
1022 | clk_put(host->iclk); | 1190 | clk_put(host->iclk); |
1023 | } | 1191 | } |
1024 | err_free_mmc_host: | 1192 | err_free_mmc_host: |
1025 | mmc_free_host(host->mmc); | 1193 | kfree(host); |
1026 | err_free_mem_region: | 1194 | err_free_mem_region: |
1027 | release_mem_region(res->start, res->end - res->start + 1); | 1195 | release_mem_region(res->start, res->end - res->start + 1); |
1028 | return ret; | 1196 | return ret; |
@@ -1031,16 +1199,18 @@ err_free_mem_region: | |||
1031 | static int mmc_omap_remove(struct platform_device *pdev) | 1199 | static int mmc_omap_remove(struct platform_device *pdev) |
1032 | { | 1200 | { |
1033 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1201 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
1202 | int i; | ||
1034 | 1203 | ||
1035 | platform_set_drvdata(pdev, NULL); | 1204 | platform_set_drvdata(pdev, NULL); |
1036 | 1205 | ||
1037 | BUG_ON(host == NULL); | 1206 | BUG_ON(host == NULL); |
1038 | 1207 | ||
1039 | mmc_remove_host(host->mmc); | 1208 | for (i = 0; i < host->nr_slots; i++) |
1040 | free_irq(host->irq, host); | 1209 | mmc_omap_remove_slot(host->slots[i]); |
1210 | |||
1211 | if (host->pdata->cleanup) | ||
1212 | host->pdata->cleanup(&pdev->dev); | ||
1041 | 1213 | ||
1042 | if (host->power_pin >= 0) | ||
1043 | omap_free_gpio(host->power_pin); | ||
1044 | if (host->iclk && !IS_ERR(host->iclk)) | 1214 | if (host->iclk && !IS_ERR(host->iclk)) |
1045 | clk_put(host->iclk); | 1215 | clk_put(host->iclk); |
1046 | if (host->fclk && !IS_ERR(host->fclk)) | 1216 | if (host->fclk && !IS_ERR(host->fclk)) |
@@ -1049,7 +1219,7 @@ static int mmc_omap_remove(struct platform_device *pdev) | |||
1049 | release_mem_region(pdev->resource[0].start, | 1219 | release_mem_region(pdev->resource[0].start, |
1050 | pdev->resource[0].end - pdev->resource[0].start + 1); | 1220 | pdev->resource[0].end - pdev->resource[0].start + 1); |
1051 | 1221 | ||
1052 | mmc_free_host(host->mmc); | 1222 | kfree(host); |
1053 | 1223 | ||
1054 | return 0; | 1224 | return 0; |
1055 | } | 1225 | } |
@@ -1057,35 +1227,47 @@ static int mmc_omap_remove(struct platform_device *pdev) | |||
1057 | #ifdef CONFIG_PM | 1227 | #ifdef CONFIG_PM |
1058 | static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) | 1228 | static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) |
1059 | { | 1229 | { |
1060 | int ret = 0; | 1230 | int i, ret = 0; |
1061 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1231 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
1062 | 1232 | ||
1063 | if (host && host->suspended) | 1233 | if (host == NULL || host->suspended) |
1064 | return 0; | 1234 | return 0; |
1065 | 1235 | ||
1066 | if (host) { | 1236 | for (i = 0; i < host->nr_slots; i++) { |
1067 | ret = mmc_suspend_host(host->mmc, mesg); | 1237 | struct mmc_omap_slot *slot; |
1068 | if (ret == 0) | 1238 | |
1069 | host->suspended = 1; | 1239 | slot = host->slots[i]; |
1240 | ret = mmc_suspend_host(slot->mmc, mesg); | ||
1241 | if (ret < 0) { | ||
1242 | while (--i >= 0) { | ||
1243 | slot = host->slots[i]; | ||
1244 | mmc_resume_host(slot->mmc); | ||
1245 | } | ||
1246 | return ret; | ||
1247 | } | ||
1070 | } | 1248 | } |
1071 | return ret; | 1249 | host->suspended = 1; |
1250 | return 0; | ||
1072 | } | 1251 | } |
1073 | 1252 | ||
1074 | static int mmc_omap_resume(struct platform_device *pdev) | 1253 | static int mmc_omap_resume(struct platform_device *pdev) |
1075 | { | 1254 | { |
1076 | int ret = 0; | 1255 | int i, ret = 0; |
1077 | struct mmc_omap_host *host = platform_get_drvdata(pdev); | 1256 | struct mmc_omap_host *host = platform_get_drvdata(pdev); |
1078 | 1257 | ||
1079 | if (host && !host->suspended) | 1258 | if (host == NULL || !host->suspended) |
1080 | return 0; | 1259 | return 0; |
1081 | 1260 | ||
1082 | if (host) { | 1261 | for (i = 0; i < host->nr_slots; i++) { |
1083 | ret = mmc_resume_host(host->mmc); | 1262 | struct mmc_omap_slot *slot; |
1084 | if (ret == 0) | 1263 | slot = host->slots[i]; |
1085 | host->suspended = 0; | 1264 | ret = mmc_resume_host(slot->mmc); |
1086 | } | 1265 | if (ret < 0) |
1266 | return ret; | ||
1087 | 1267 | ||
1088 | return ret; | 1268 | host->suspended = 0; |
1269 | } | ||
1270 | return 0; | ||
1089 | } | 1271 | } |
1090 | #else | 1272 | #else |
1091 | #define mmc_omap_suspend NULL | 1273 | #define mmc_omap_suspend NULL |