aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-s3c.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
-rw-r--r--drivers/mmc/host/sdhci-s3c.c85
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
241static 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
260static 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
270static 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
238static int __devinit sdhci_s3c_probe(struct platform_device *pdev) 296static 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
390static int __devexit sdhci_s3c_remove(struct platform_device *pdev) 465static 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++) {