diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index f0b819d98a87..0a7f2614c6f0 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/clk.h> | 19 | #include <linux/clk.h> |
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <linux/gpio.h> | ||
21 | 22 | ||
22 | #include <linux/mmc/host.h> | 23 | #include <linux/mmc/host.h> |
23 | 24 | ||
@@ -44,6 +45,8 @@ struct sdhci_s3c { | |||
44 | struct resource *ioarea; | 45 | struct resource *ioarea; |
45 | struct s3c_sdhci_platdata *pdata; | 46 | struct s3c_sdhci_platdata *pdata; |
46 | unsigned int cur_clk; | 47 | unsigned int cur_clk; |
48 | int ext_cd_irq; | ||
49 | int ext_cd_gpio; | ||
47 | 50 | ||
48 | struct clk *clk_io; | 51 | struct clk *clk_io; |
49 | struct clk *clk_bus[MAX_BUS_CLK]; | 52 | struct clk *clk_bus[MAX_BUS_CLK]; |
@@ -235,6 +238,61 @@ static struct sdhci_ops sdhci_s3c_ops = { | |||
235 | .get_min_clock = sdhci_s3c_get_min_clock, | 238 | .get_min_clock = sdhci_s3c_get_min_clock, |
236 | }; | 239 | }; |
237 | 240 | ||
241 | static void sdhci_s3c_notify_change(struct platform_device *dev, int state) | ||
242 | { | ||
243 | struct sdhci_host *host = platform_get_drvdata(dev); | ||
244 | if (host) { | ||
245 | mutex_lock(&host->lock); | ||
246 | if (state) { | ||
247 | dev_dbg(&dev->dev, "card inserted.\n"); | ||
248 | host->flags &= ~SDHCI_DEVICE_DEAD; | ||
249 | host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; | ||
250 | } else { | ||
251 | dev_dbg(&dev->dev, "card removed.\n"); | ||
252 | host->flags |= SDHCI_DEVICE_DEAD; | ||
253 | host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; | ||
254 | } | ||
255 | sdhci_card_detect(host); | ||
256 | mutex_unlock(&host->lock); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) | ||
261 | { | ||
262 | struct sdhci_s3c *sc = dev_id; | ||
263 | int status = gpio_get_value(sc->ext_cd_gpio); | ||
264 | if (sc->pdata->ext_cd_gpio_invert) | ||
265 | status = !status; | ||
266 | sdhci_s3c_notify_change(sc->pdev, status); | ||
267 | return IRQ_HANDLED; | ||
268 | } | ||
269 | |||
270 | static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) | ||
271 | { | ||
272 | struct s3c_sdhci_platdata *pdata = sc->pdata; | ||
273 | struct device *dev = &sc->pdev->dev; | ||
274 | |||
275 | if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { | ||
276 | sc->ext_cd_gpio = pdata->ext_cd_gpio; | ||
277 | sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); | ||
278 | if (sc->ext_cd_irq && | ||
279 | request_threaded_irq(sc->ext_cd_irq, NULL, | ||
280 | sdhci_s3c_gpio_card_detect_thread, | ||
281 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
282 | dev_name(dev), sc) == 0) { | ||
283 | int status = gpio_get_value(sc->ext_cd_gpio); | ||
284 | if (pdata->ext_cd_gpio_invert) | ||
285 | status = !status; | ||
286 | sdhci_s3c_notify_change(sc->pdev, status); | ||
287 | } else { | ||
288 | dev_warn(dev, "cannot request irq for card detect\n"); | ||
289 | sc->ext_cd_irq = 0; | ||
290 | } | ||
291 | } else { | ||
292 | dev_err(dev, "cannot request gpio for card detect\n"); | ||
293 | } | ||
294 | } | ||
295 | |||
238 | static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | 296 | static int __devinit sdhci_s3c_probe(struct platform_device *pdev) |
239 | { | 297 | { |
240 | struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; | 298 | struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; |
@@ -272,6 +330,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | |||
272 | sc->host = host; | 330 | sc->host = host; |
273 | sc->pdev = pdev; | 331 | sc->pdev = pdev; |
274 | sc->pdata = pdata; | 332 | sc->pdata = pdata; |
333 | sc->ext_cd_gpio = -1; /* invalid gpio number */ | ||
275 | 334 | ||
276 | platform_set_drvdata(pdev, host); | 335 | platform_set_drvdata(pdev, host); |
277 | 336 | ||
@@ -353,6 +412,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | |||
353 | * SDHCI block, or a missing configuration that needs to be set. */ | 412 | * SDHCI block, or a missing configuration that needs to be set. */ |
354 | host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; | 413 | host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ; |
355 | 414 | ||
415 | if (pdata->cd_type == S3C_SDHCI_CD_NONE || | ||
416 | pdata->cd_type == S3C_SDHCI_CD_PERMANENT) | ||
417 | host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; | ||
418 | |||
419 | if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT) | ||
420 | host->mmc->caps = MMC_CAP_NONREMOVABLE; | ||
421 | |||
356 | host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | | 422 | host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | |
357 | SDHCI_QUIRK_32BIT_DMA_SIZE); | 423 | SDHCI_QUIRK_32BIT_DMA_SIZE); |
358 | 424 | ||
@@ -365,6 +431,15 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | |||
365 | goto err_add_host; | 431 | goto err_add_host; |
366 | } | 432 | } |
367 | 433 | ||
434 | /* The following two methods of card detection might call | ||
435 | sdhci_s3c_notify_change() immediately, so they can be called | ||
436 | only after sdhci_add_host(). Setup errors are ignored. */ | ||
437 | if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) | ||
438 | pdata->ext_cd_init(&sdhci_s3c_notify_change); | ||
439 | if (pdata->cd_type == S3C_SDHCI_CD_GPIO && | ||
440 | gpio_is_valid(pdata->ext_cd_gpio)) | ||
441 | sdhci_s3c_setup_card_detect_gpio(sc); | ||
442 | |||
368 | return 0; | 443 | return 0; |
369 | 444 | ||
370 | err_add_host: | 445 | err_add_host: |
@@ -389,10 +464,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | |||
389 | 464 | ||
390 | static int __devexit sdhci_s3c_remove(struct platform_device *pdev) | 465 | static int __devexit sdhci_s3c_remove(struct platform_device *pdev) |
391 | { | 466 | { |
467 | struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; | ||
392 | struct sdhci_host *host = platform_get_drvdata(pdev); | 468 | struct sdhci_host *host = platform_get_drvdata(pdev); |
393 | struct sdhci_s3c *sc = sdhci_priv(host); | 469 | struct sdhci_s3c *sc = sdhci_priv(host); |
394 | int ptr; | 470 | int ptr; |
395 | 471 | ||
472 | if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) | ||
473 | pdata->ext_cd_cleanup(&sdhci_s3c_notify_change); | ||
474 | |||
475 | if (sc->ext_cd_irq) | ||
476 | free_irq(sc->ext_cd_irq, sc); | ||
477 | |||
478 | if (gpio_is_valid(sc->ext_cd_gpio)) | ||
479 | gpio_free(sc->ext_cd_gpio); | ||
480 | |||
396 | sdhci_remove_host(host, 1); | 481 | sdhci_remove_host(host, 1); |
397 | 482 | ||
398 | for (ptr = 0; ptr < 3; ptr++) { | 483 | for (ptr = 0; ptr < 3; ptr++) { |