diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 352 |
1 files changed, 339 insertions, 13 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4b2322518909..2c5e15d9f864 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include <linux/mmc/core.h> | 30 | #include <linux/mmc/core.h> |
31 | #include <linux/io.h> | 31 | #include <linux/io.h> |
32 | #include <linux/semaphore.h> | 32 | #include <linux/semaphore.h> |
33 | #include <linux/gpio.h> | ||
34 | #include <linux/regulator/consumer.h> | ||
33 | #include <plat/dma.h> | 35 | #include <plat/dma.h> |
34 | #include <mach/hardware.h> | 36 | #include <mach/hardware.h> |
35 | #include <plat/board.h> | 37 | #include <plat/board.h> |
@@ -146,6 +148,15 @@ struct omap_hsmmc_host { | |||
146 | struct clk *fclk; | 148 | struct clk *fclk; |
147 | struct clk *iclk; | 149 | struct clk *iclk; |
148 | struct clk *dbclk; | 150 | struct clk *dbclk; |
151 | /* | ||
152 | * vcc == configured supply | ||
153 | * vcc_aux == optional | ||
154 | * - MMC1, supply for DAT4..DAT7 | ||
155 | * - MMC2/MMC2, external level shifter voltage supply, for | ||
156 | * chip (SDIO, eMMC, etc) or transceiver (MMC2 only) | ||
157 | */ | ||
158 | struct regulator *vcc; | ||
159 | struct regulator *vcc_aux; | ||
149 | struct semaphore sem; | 160 | struct semaphore sem; |
150 | struct work_struct mmc_carddetect_work; | 161 | struct work_struct mmc_carddetect_work; |
151 | void __iomem *base; | 162 | void __iomem *base; |
@@ -171,10 +182,308 @@ struct omap_hsmmc_host { | |||
171 | int vdd; | 182 | int vdd; |
172 | int protect_card; | 183 | int protect_card; |
173 | int reqs_blocked; | 184 | int reqs_blocked; |
185 | int use_reg; | ||
174 | 186 | ||
175 | struct omap_mmc_platform_data *pdata; | 187 | struct omap_mmc_platform_data *pdata; |
176 | }; | 188 | }; |
177 | 189 | ||
190 | static int omap_hsmmc_card_detect(struct device *dev, int slot) | ||
191 | { | ||
192 | struct omap_mmc_platform_data *mmc = dev->platform_data; | ||
193 | |||
194 | /* NOTE: assumes card detect signal is active-low */ | ||
195 | return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); | ||
196 | } | ||
197 | |||
198 | static int omap_hsmmc_get_wp(struct device *dev, int slot) | ||
199 | { | ||
200 | struct omap_mmc_platform_data *mmc = dev->platform_data; | ||
201 | |||
202 | /* NOTE: assumes write protect signal is active-high */ | ||
203 | return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); | ||
204 | } | ||
205 | |||
206 | static int omap_hsmmc_get_cover_state(struct device *dev, int slot) | ||
207 | { | ||
208 | struct omap_mmc_platform_data *mmc = dev->platform_data; | ||
209 | |||
210 | /* NOTE: assumes card detect signal is active-low */ | ||
211 | return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); | ||
212 | } | ||
213 | |||
214 | #ifdef CONFIG_PM | ||
215 | |||
216 | static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot) | ||
217 | { | ||
218 | struct omap_mmc_platform_data *mmc = dev->platform_data; | ||
219 | |||
220 | disable_irq(mmc->slots[0].card_detect_irq); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int omap_hsmmc_resume_cdirq(struct device *dev, int slot) | ||
225 | { | ||
226 | struct omap_mmc_platform_data *mmc = dev->platform_data; | ||
227 | |||
228 | enable_irq(mmc->slots[0].card_detect_irq); | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | #else | ||
233 | |||
234 | #define omap_hsmmc_suspend_cdirq NULL | ||
235 | #define omap_hsmmc_resume_cdirq NULL | ||
236 | |||
237 | #endif | ||
238 | |||
239 | static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on, | ||
240 | int vdd) | ||
241 | { | ||
242 | struct omap_hsmmc_host *host = | ||
243 | platform_get_drvdata(to_platform_device(dev)); | ||
244 | int ret; | ||
245 | |||
246 | if (mmc_slot(host).before_set_reg) | ||
247 | mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); | ||
248 | |||
249 | if (power_on) | ||
250 | ret = mmc_regulator_set_ocr(host->vcc, vdd); | ||
251 | else | ||
252 | ret = mmc_regulator_set_ocr(host->vcc, 0); | ||
253 | |||
254 | if (mmc_slot(host).after_set_reg) | ||
255 | mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); | ||
256 | |||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on, | ||
261 | int vdd) | ||
262 | { | ||
263 | struct omap_hsmmc_host *host = | ||
264 | platform_get_drvdata(to_platform_device(dev)); | ||
265 | int ret = 0; | ||
266 | |||
267 | /* | ||
268 | * If we don't see a Vcc regulator, assume it's a fixed | ||
269 | * voltage always-on regulator. | ||
270 | */ | ||
271 | if (!host->vcc) | ||
272 | return 0; | ||
273 | |||
274 | if (mmc_slot(host).before_set_reg) | ||
275 | mmc_slot(host).before_set_reg(dev, slot, power_on, vdd); | ||
276 | |||
277 | /* | ||
278 | * Assume Vcc regulator is used only to power the card ... OMAP | ||
279 | * VDDS is used to power the pins, optionally with a transceiver to | ||
280 | * support cards using voltages other than VDDS (1.8V nominal). When a | ||
281 | * transceiver is used, DAT3..7 are muxed as transceiver control pins. | ||
282 | * | ||
283 | * In some cases this regulator won't support enable/disable; | ||
284 | * e.g. it's a fixed rail for a WLAN chip. | ||
285 | * | ||
286 | * In other cases vcc_aux switches interface power. Example, for | ||
287 | * eMMC cards it represents VccQ. Sometimes transceivers or SDIO | ||
288 | * chips/cards need an interface voltage rail too. | ||
289 | */ | ||
290 | if (power_on) { | ||
291 | ret = mmc_regulator_set_ocr(host->vcc, vdd); | ||
292 | /* Enable interface voltage rail, if needed */ | ||
293 | if (ret == 0 && host->vcc_aux) { | ||
294 | ret = regulator_enable(host->vcc_aux); | ||
295 | if (ret < 0) | ||
296 | ret = mmc_regulator_set_ocr(host->vcc, 0); | ||
297 | } | ||
298 | } else { | ||
299 | if (host->vcc_aux) { | ||
300 | ret = regulator_is_enabled(host->vcc_aux); | ||
301 | if (ret > 0) | ||
302 | ret = regulator_disable(host->vcc_aux); | ||
303 | } | ||
304 | if (ret == 0) | ||
305 | ret = mmc_regulator_set_ocr(host->vcc, 0); | ||
306 | } | ||
307 | |||
308 | if (mmc_slot(host).after_set_reg) | ||
309 | mmc_slot(host).after_set_reg(dev, slot, power_on, vdd); | ||
310 | |||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | static int omap_hsmmc_1_set_sleep(struct device *dev, int slot, int sleep, | ||
315 | int vdd, int cardsleep) | ||
316 | { | ||
317 | struct omap_hsmmc_host *host = | ||
318 | platform_get_drvdata(to_platform_device(dev)); | ||
319 | int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; | ||
320 | |||
321 | return regulator_set_mode(host->vcc, mode); | ||
322 | } | ||
323 | |||
324 | static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep, | ||
325 | int vdd, int cardsleep) | ||
326 | { | ||
327 | struct omap_hsmmc_host *host = | ||
328 | platform_get_drvdata(to_platform_device(dev)); | ||
329 | int err, mode; | ||
330 | |||
331 | /* | ||
332 | * If we don't see a Vcc regulator, assume it's a fixed | ||
333 | * voltage always-on regulator. | ||
334 | */ | ||
335 | if (!host->vcc) | ||
336 | return 0; | ||
337 | |||
338 | mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL; | ||
339 | |||
340 | if (!host->vcc_aux) | ||
341 | return regulator_set_mode(host->vcc, mode); | ||
342 | |||
343 | if (cardsleep) { | ||
344 | /* VCC can be turned off if card is asleep */ | ||
345 | if (sleep) | ||
346 | err = mmc_regulator_set_ocr(host->vcc, 0); | ||
347 | else | ||
348 | err = mmc_regulator_set_ocr(host->vcc, vdd); | ||
349 | } else | ||
350 | err = regulator_set_mode(host->vcc, mode); | ||
351 | if (err) | ||
352 | return err; | ||
353 | return regulator_set_mode(host->vcc_aux, mode); | ||
354 | } | ||
355 | |||
356 | static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata) | ||
357 | { | ||
358 | int ret; | ||
359 | |||
360 | if (gpio_is_valid(pdata->slots[0].switch_pin)) { | ||
361 | pdata->suspend = omap_hsmmc_suspend_cdirq; | ||
362 | pdata->resume = omap_hsmmc_resume_cdirq; | ||
363 | if (pdata->slots[0].cover) | ||
364 | pdata->slots[0].get_cover_state = | ||
365 | omap_hsmmc_get_cover_state; | ||
366 | else | ||
367 | pdata->slots[0].card_detect = omap_hsmmc_card_detect; | ||
368 | pdata->slots[0].card_detect_irq = | ||
369 | gpio_to_irq(pdata->slots[0].switch_pin); | ||
370 | ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd"); | ||
371 | if (ret) | ||
372 | return ret; | ||
373 | ret = gpio_direction_input(pdata->slots[0].switch_pin); | ||
374 | if (ret) | ||
375 | goto err_free_sp; | ||
376 | } else | ||
377 | pdata->slots[0].switch_pin = -EINVAL; | ||
378 | |||
379 | if (gpio_is_valid(pdata->slots[0].gpio_wp)) { | ||
380 | pdata->slots[0].get_ro = omap_hsmmc_get_wp; | ||
381 | ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp"); | ||
382 | if (ret) | ||
383 | goto err_free_cd; | ||
384 | ret = gpio_direction_input(pdata->slots[0].gpio_wp); | ||
385 | if (ret) | ||
386 | goto err_free_wp; | ||
387 | } else | ||
388 | pdata->slots[0].gpio_wp = -EINVAL; | ||
389 | |||
390 | return 0; | ||
391 | |||
392 | err_free_wp: | ||
393 | gpio_free(pdata->slots[0].gpio_wp); | ||
394 | err_free_cd: | ||
395 | if (gpio_is_valid(pdata->slots[0].switch_pin)) | ||
396 | err_free_sp: | ||
397 | gpio_free(pdata->slots[0].switch_pin); | ||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata) | ||
402 | { | ||
403 | if (gpio_is_valid(pdata->slots[0].gpio_wp)) | ||
404 | gpio_free(pdata->slots[0].gpio_wp); | ||
405 | if (gpio_is_valid(pdata->slots[0].switch_pin)) | ||
406 | gpio_free(pdata->slots[0].switch_pin); | ||
407 | } | ||
408 | |||
409 | static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) | ||
410 | { | ||
411 | struct regulator *reg; | ||
412 | int ret = 0; | ||
413 | |||
414 | switch (host->id) { | ||
415 | case OMAP_MMC1_DEVID: | ||
416 | /* On-chip level shifting via PBIAS0/PBIAS1 */ | ||
417 | mmc_slot(host).set_power = omap_hsmmc_1_set_power; | ||
418 | mmc_slot(host).set_sleep = omap_hsmmc_1_set_sleep; | ||
419 | break; | ||
420 | case OMAP_MMC2_DEVID: | ||
421 | case OMAP_MMC3_DEVID: | ||
422 | /* Off-chip level shifting, or none */ | ||
423 | mmc_slot(host).set_power = omap_hsmmc_23_set_power; | ||
424 | mmc_slot(host).set_sleep = omap_hsmmc_23_set_sleep; | ||
425 | break; | ||
426 | default: | ||
427 | pr_err("MMC%d configuration not supported!\n", host->id); | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | reg = regulator_get(host->dev, "vmmc"); | ||
432 | if (IS_ERR(reg)) { | ||
433 | dev_dbg(host->dev, "vmmc regulator missing\n"); | ||
434 | /* | ||
435 | * HACK: until fixed.c regulator is usable, | ||
436 | * we don't require a main regulator | ||
437 | * for MMC2 or MMC3 | ||
438 | */ | ||
439 | if (host->id == OMAP_MMC1_DEVID) { | ||
440 | ret = PTR_ERR(reg); | ||
441 | goto err; | ||
442 | } | ||
443 | } else { | ||
444 | host->vcc = reg; | ||
445 | mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg); | ||
446 | |||
447 | /* Allow an aux regulator */ | ||
448 | reg = regulator_get(host->dev, "vmmc_aux"); | ||
449 | host->vcc_aux = IS_ERR(reg) ? NULL : reg; | ||
450 | |||
451 | /* | ||
452 | * UGLY HACK: workaround regulator framework bugs. | ||
453 | * When the bootloader leaves a supply active, it's | ||
454 | * initialized with zero usecount ... and we can't | ||
455 | * disable it without first enabling it. Until the | ||
456 | * framework is fixed, we need a workaround like this | ||
457 | * (which is safe for MMC, but not in general). | ||
458 | */ | ||
459 | if (regulator_is_enabled(host->vcc) > 0) { | ||
460 | regulator_enable(host->vcc); | ||
461 | regulator_disable(host->vcc); | ||
462 | } | ||
463 | if (host->vcc_aux) { | ||
464 | if (regulator_is_enabled(reg) > 0) { | ||
465 | regulator_enable(reg); | ||
466 | regulator_disable(reg); | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | |||
471 | return 0; | ||
472 | |||
473 | err: | ||
474 | mmc_slot(host).set_power = NULL; | ||
475 | mmc_slot(host).set_sleep = NULL; | ||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) | ||
480 | { | ||
481 | regulator_put(host->vcc); | ||
482 | regulator_put(host->vcc_aux); | ||
483 | mmc_slot(host).set_power = NULL; | ||
484 | mmc_slot(host).set_sleep = NULL; | ||
485 | } | ||
486 | |||
178 | /* | 487 | /* |
179 | * Stop clock to the card | 488 | * Stop clock to the card |
180 | */ | 489 | */ |
@@ -835,7 +1144,7 @@ static void omap_hsmmc_detect(struct work_struct *work) | |||
835 | sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); | 1144 | sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); |
836 | 1145 | ||
837 | if (slot->card_detect) | 1146 | if (slot->card_detect) |
838 | carddetect = slot->card_detect(slot->card_detect_irq); | 1147 | carddetect = slot->card_detect(host->dev, host->slot_id); |
839 | else { | 1148 | else { |
840 | omap_hsmmc_protect_card(host); | 1149 | omap_hsmmc_protect_card(host); |
841 | carddetect = -ENOSYS; | 1150 | carddetect = -ENOSYS; |
@@ -1242,7 +1551,7 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc) | |||
1242 | 1551 | ||
1243 | if (!mmc_slot(host).card_detect) | 1552 | if (!mmc_slot(host).card_detect) |
1244 | return -ENOSYS; | 1553 | return -ENOSYS; |
1245 | return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq); | 1554 | return mmc_slot(host).card_detect(host->dev, host->slot_id); |
1246 | } | 1555 | } |
1247 | 1556 | ||
1248 | static int omap_hsmmc_get_ro(struct mmc_host *mmc) | 1557 | static int omap_hsmmc_get_ro(struct mmc_host *mmc) |
@@ -1616,7 +1925,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1616 | struct mmc_host *mmc; | 1925 | struct mmc_host *mmc; |
1617 | struct omap_hsmmc_host *host = NULL; | 1926 | struct omap_hsmmc_host *host = NULL; |
1618 | struct resource *res; | 1927 | struct resource *res; |
1619 | int ret = 0, irq; | 1928 | int ret, irq; |
1620 | 1929 | ||
1621 | if (pdata == NULL) { | 1930 | if (pdata == NULL) { |
1622 | dev_err(&pdev->dev, "Platform Data is missing\n"); | 1931 | dev_err(&pdev->dev, "Platform Data is missing\n"); |
@@ -1638,10 +1947,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1638 | if (res == NULL) | 1947 | if (res == NULL) |
1639 | return -EBUSY; | 1948 | return -EBUSY; |
1640 | 1949 | ||
1950 | ret = omap_hsmmc_gpio_init(pdata); | ||
1951 | if (ret) | ||
1952 | goto err; | ||
1953 | |||
1641 | mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); | 1954 | mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); |
1642 | if (!mmc) { | 1955 | if (!mmc) { |
1643 | ret = -ENOMEM; | 1956 | ret = -ENOMEM; |
1644 | goto err; | 1957 | goto err_alloc; |
1645 | } | 1958 | } |
1646 | 1959 | ||
1647 | host = mmc_priv(mmc); | 1960 | host = mmc_priv(mmc); |
@@ -1781,7 +2094,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1781 | goto err_irq; | 2094 | goto err_irq; |
1782 | } | 2095 | } |
1783 | 2096 | ||
1784 | /* initialize power supplies, gpios, etc */ | ||
1785 | if (pdata->init != NULL) { | 2097 | if (pdata->init != NULL) { |
1786 | if (pdata->init(&pdev->dev) != 0) { | 2098 | if (pdata->init(&pdev->dev) != 0) { |
1787 | dev_dbg(mmc_dev(host->mmc), | 2099 | dev_dbg(mmc_dev(host->mmc), |
@@ -1789,6 +2101,14 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1789 | goto err_irq_cd_init; | 2101 | goto err_irq_cd_init; |
1790 | } | 2102 | } |
1791 | } | 2103 | } |
2104 | |||
2105 | if (!mmc_slot(host).set_power) { | ||
2106 | ret = omap_hsmmc_reg_get(host); | ||
2107 | if (ret) | ||
2108 | goto err_reg; | ||
2109 | host->use_reg = 1; | ||
2110 | } | ||
2111 | |||
1792 | mmc->ocr_avail = mmc_slot(host).ocr_mask; | 2112 | mmc->ocr_avail = mmc_slot(host).ocr_mask; |
1793 | 2113 | ||
1794 | /* Request IRQ for card detect */ | 2114 | /* Request IRQ for card detect */ |
@@ -1823,19 +2143,22 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) | |||
1823 | ret = device_create_file(&mmc->class_dev, | 2143 | ret = device_create_file(&mmc->class_dev, |
1824 | &dev_attr_cover_switch); | 2144 | &dev_attr_cover_switch); |
1825 | if (ret < 0) | 2145 | if (ret < 0) |
1826 | goto err_cover_switch; | 2146 | goto err_slot_name; |
1827 | } | 2147 | } |
1828 | 2148 | ||
1829 | omap_hsmmc_debugfs(mmc); | 2149 | omap_hsmmc_debugfs(mmc); |
1830 | 2150 | ||
1831 | return 0; | 2151 | return 0; |
1832 | 2152 | ||
1833 | err_cover_switch: | ||
1834 | device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); | ||
1835 | err_slot_name: | 2153 | err_slot_name: |
1836 | mmc_remove_host(mmc); | 2154 | mmc_remove_host(mmc); |
1837 | err_irq_cd: | ||
1838 | free_irq(mmc_slot(host).card_detect_irq, host); | 2155 | free_irq(mmc_slot(host).card_detect_irq, host); |
2156 | err_irq_cd: | ||
2157 | if (host->use_reg) | ||
2158 | omap_hsmmc_reg_put(host); | ||
2159 | err_reg: | ||
2160 | if (host->pdata->cleanup) | ||
2161 | host->pdata->cleanup(&pdev->dev); | ||
1839 | err_irq_cd_init: | 2162 | err_irq_cd_init: |
1840 | free_irq(host->irq, host); | 2163 | free_irq(host->irq, host); |
1841 | err_irq: | 2164 | err_irq: |
@@ -1847,14 +2170,14 @@ err_irq: | |||
1847 | clk_disable(host->dbclk); | 2170 | clk_disable(host->dbclk); |
1848 | clk_put(host->dbclk); | 2171 | clk_put(host->dbclk); |
1849 | } | 2172 | } |
1850 | |||
1851 | err1: | 2173 | err1: |
1852 | iounmap(host->base); | 2174 | iounmap(host->base); |
2175 | platform_set_drvdata(pdev, NULL); | ||
2176 | mmc_free_host(mmc); | ||
2177 | err_alloc: | ||
2178 | omap_hsmmc_gpio_free(pdata); | ||
1853 | err: | 2179 | err: |
1854 | dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); | ||
1855 | release_mem_region(res->start, res->end - res->start + 1); | 2180 | release_mem_region(res->start, res->end - res->start + 1); |
1856 | if (host) | ||
1857 | mmc_free_host(mmc); | ||
1858 | return ret; | 2181 | return ret; |
1859 | } | 2182 | } |
1860 | 2183 | ||
@@ -1866,6 +2189,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev) | |||
1866 | if (host) { | 2189 | if (host) { |
1867 | mmc_host_enable(host->mmc); | 2190 | mmc_host_enable(host->mmc); |
1868 | mmc_remove_host(host->mmc); | 2191 | mmc_remove_host(host->mmc); |
2192 | if (host->use_reg) | ||
2193 | omap_hsmmc_reg_put(host); | ||
1869 | if (host->pdata->cleanup) | 2194 | if (host->pdata->cleanup) |
1870 | host->pdata->cleanup(&pdev->dev); | 2195 | host->pdata->cleanup(&pdev->dev); |
1871 | free_irq(host->irq, host); | 2196 | free_irq(host->irq, host); |
@@ -1884,6 +2209,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) | |||
1884 | 2209 | ||
1885 | mmc_free_host(host->mmc); | 2210 | mmc_free_host(host->mmc); |
1886 | iounmap(host->base); | 2211 | iounmap(host->base); |
2212 | omap_hsmmc_gpio_free(pdev->dev.platform_data); | ||
1887 | } | 2213 | } |
1888 | 2214 | ||
1889 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 2215 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |