aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mmc/samsung-sdhci.txt53
-rw-r--r--drivers/mmc/host/sdhci-s3c.c158
2 files changed, 205 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt
new file mode 100644
index 000000000000..630a7d7f4718
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt
@@ -0,0 +1,53 @@
1* Samsung's SDHCI Controller device tree bindings
2
3Samsung's SDHCI controller is used as a connectivity interface with external
4MMC, SD and eMMC storage mediums. This file documents differences between the
5core mmc properties described by mmc.txt and the properties used by the
6Samsung implmentation of the SDHCI controller.
7
8Note: The mmc core bindings documentation states that if none of the core
9card-detect bindings are used, then the standard sdhci card detect mechanism
10is used. The Samsung's SDHCI controller bindings extends this as listed below.
11
12[A] The property "samsung,cd-pinmux-gpio" can be used as stated in the
13 "Optional Board Specific Properties" section below.
14
15[B] If core card-detect bindings and "samsung,cd-pinmux-gpio" property
16 is not specified, it is assumed that there is no card detection
17 mechanism used.
18
19Required SoC Specific Properties:
20- compatible: should be one of the following
21 - "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
22 controller.
23 - "samsung,exynos4210-sdhci": For controllers compatible with Exynos4 sdhci
24 controller.
25
26Required Board Specific Properties:
27- gpios: Should specify the gpios used for clock, command and data lines. The
28 gpio specifier format depends on the gpio controller.
29
30Optional Board Specific Properties:
31- samsung,cd-pinmux-gpio: Specifies the card detect line that is routed
32 through a pinmux to the card-detect pin of the card slot. This property
33 should be used only if none of the mmc core card-detect properties are
34 used.
35
36Example:
37 sdhci@12530000 {
38 compatible = "samsung,exynos4210-sdhci";
39 reg = <0x12530000 0x100>;
40 interrupts = <0 75 0>;
41 bus-width = <4>;
42 cd-gpios = <&gpk2 2 2 3 3>;
43 gpios = <&gpk2 0 2 0 3>, /* clock line */
44 <&gpk2 1 2 0 3>, /* command line */
45 <&gpk2 3 2 3 3>, /* data line 0 */
46 <&gpk2 4 2 3 3>, /* data line 1 */
47 <&gpk2 5 2 3 3>, /* data line 2 */
48 <&gpk2 6 2 3 3>; /* data line 3 */
49 };
50
51 Note: This example shows both SoC specific and board specific properties
52 in a single device node. The properties can be actually be seperated
53 into SoC specific node and board specific node.
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 00969ba03277..0c77b10d2619 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -34,6 +34,9 @@
34 34
35#define MAX_BUS_CLK (4) 35#define MAX_BUS_CLK (4)
36 36
37/* Number of gpio's used is max data bus width + command and clock lines */
38#define NUM_GPIOS(x) (x + 2)
39
37/** 40/**
38 * struct sdhci_s3c - S3C SDHCI instance 41 * struct sdhci_s3c - S3C SDHCI instance
39 * @host: The SDHCI host created 42 * @host: The SDHCI host created
@@ -41,6 +44,7 @@
41 * @ioarea: The resource created when we claimed the IO area. 44 * @ioarea: The resource created when we claimed the IO area.
42 * @pdata: The platform data for this controller. 45 * @pdata: The platform data for this controller.
43 * @cur_clk: The index of the current bus clock. 46 * @cur_clk: The index of the current bus clock.
47 * @gpios: List of gpio numbers parsed from device tree.
44 * @clk_io: The clock for the internal bus interface. 48 * @clk_io: The clock for the internal bus interface.
45 * @clk_bus: The clocks that are available for the SD/MMC bus clock. 49 * @clk_bus: The clocks that are available for the SD/MMC bus clock.
46 */ 50 */
@@ -52,6 +56,7 @@ struct sdhci_s3c {
52 unsigned int cur_clk; 56 unsigned int cur_clk;
53 int ext_cd_irq; 57 int ext_cd_irq;
54 int ext_cd_gpio; 58 int ext_cd_gpio;
59 int *gpios;
55 60
56 struct clk *clk_io; 61 struct clk *clk_io;
57 struct clk *clk_bus[MAX_BUS_CLK]; 62 struct clk *clk_bus[MAX_BUS_CLK];
@@ -422,9 +427,121 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
422 } 427 }
423} 428}
424 429
430#ifdef CONFIG_OF
431static int __devinit sdhci_s3c_parse_dt(struct device *dev,
432 struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
433{
434 struct device_node *node = dev->of_node;
435 struct sdhci_s3c *ourhost = to_s3c(host);
436 u32 max_width;
437 int gpio, cnt, ret;
438
439 /* if the bus-width property is not specified, assume width as 1 */
440 if (of_property_read_u32(node, "bus-width", &max_width))
441 max_width = 1;
442 pdata->max_width = max_width;
443
444 ourhost->gpios = devm_kzalloc(dev, NUM_GPIOS(pdata->max_width) *
445 sizeof(int), GFP_KERNEL);
446 if (!ourhost->gpios)
447 return -ENOMEM;
448
449 /* get the card detection method */
450 if (of_get_property(node, "broken-cd", 0)) {
451 pdata->cd_type = S3C_SDHCI_CD_NONE;
452 goto setup_bus;
453 }
454
455 if (of_get_property(node, "non-removable", 0)) {
456 pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
457 goto setup_bus;
458 }
459
460 gpio = of_get_named_gpio(node, "cd-gpios", 0);
461 if (gpio_is_valid(gpio)) {
462 pdata->cd_type = S3C_SDHCI_CD_GPIO;
463 goto found_cd;
464 } else if (gpio != -ENOENT) {
465 dev_err(dev, "invalid card detect gpio specified\n");
466 return -EINVAL;
467 }
468
469 gpio = of_get_named_gpio(node, "samsung,cd-pinmux-gpio", 0);
470 if (gpio_is_valid(gpio)) {
471 pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
472 goto found_cd;
473 } else if (gpio != -ENOENT) {
474 dev_err(dev, "invalid card detect gpio specified\n");
475 return -EINVAL;
476 }
477
478 dev_info(dev, "assuming no card detect line available\n");
479 pdata->cd_type = S3C_SDHCI_CD_NONE;
480
481 found_cd:
482 if (pdata->cd_type == S3C_SDHCI_CD_GPIO) {
483 pdata->ext_cd_gpio = gpio;
484 ourhost->ext_cd_gpio = -1;
485 if (of_get_property(node, "cd-inverted", NULL))
486 pdata->ext_cd_gpio_invert = 1;
487 } else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
488 ret = gpio_request(gpio, "sdhci-cd");
489 if (ret) {
490 dev_err(dev, "card detect gpio request failed\n");
491 return -EINVAL;
492 }
493 ourhost->ext_cd_gpio = gpio;
494 }
495
496 setup_bus:
497 /* get the gpios for command, clock and data lines */
498 for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
499 gpio = of_get_gpio(node, cnt);
500 if (!gpio_is_valid(gpio)) {
501 dev_err(dev, "invalid gpio[%d]\n", cnt);
502 goto err_free_dt_cd_gpio;
503 }
504 ourhost->gpios[cnt] = gpio;
505 }
506
507 for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) {
508 ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio");
509 if (ret) {
510 dev_err(dev, "gpio[%d] request failed\n", cnt);
511 goto err_free_dt_gpios;
512 }
513 }
514
515 return 0;
516
517 err_free_dt_gpios:
518 while (--cnt >= 0)
519 gpio_free(ourhost->gpios[cnt]);
520 err_free_dt_cd_gpio:
521 if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
522 gpio_free(ourhost->ext_cd_gpio);
523 return -EINVAL;
524}
525#else
526static int __devinit sdhci_s3c_parse_dt(struct device *dev,
527 struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
528{
529 return -EINVAL;
530}
531#endif
532
533static const struct of_device_id sdhci_s3c_dt_match[];
534
425static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data( 535static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
426 struct platform_device *pdev) 536 struct platform_device *pdev)
427{ 537{
538#ifdef CONFIG_OF
539 if (pdev->dev.of_node) {
540 const struct of_device_id *match;
541 match = of_match_node(sdhci_s3c_dt_match, pdev->dev.of_node);
542 return (struct sdhci_s3c_drv_data *)match->data;
543 }
544#endif
428 return (struct sdhci_s3c_drv_data *) 545 return (struct sdhci_s3c_drv_data *)
429 platform_get_device_id(pdev)->driver_data; 546 platform_get_device_id(pdev)->driver_data;
430} 547}
@@ -439,7 +556,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
439 struct resource *res; 556 struct resource *res;
440 int ret, irq, ptr, clks; 557 int ret, irq, ptr, clks;
441 558
442 if (!pdev->dev.platform_data) { 559 if (!pdev->dev.platform_data && !pdev->dev.of_node) {
443 dev_err(dev, "no device data specified\n"); 560 dev_err(dev, "no device data specified\n");
444 return -ENOENT; 561 return -ENOENT;
445 } 562 }
@@ -455,21 +572,28 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
455 dev_err(dev, "sdhci_alloc_host() failed\n"); 572 dev_err(dev, "sdhci_alloc_host() failed\n");
456 return PTR_ERR(host); 573 return PTR_ERR(host);
457 } 574 }
575 sc = sdhci_priv(host);
458 576
459 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 577 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
460 if (!pdata) { 578 if (!pdata) {
461 ret = -ENOMEM; 579 ret = -ENOMEM;
462 goto err_io_clk; 580 goto err_pdata;
581 }
582
583 if (pdev->dev.of_node) {
584 ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
585 if (ret)
586 goto err_pdata;
587 } else {
588 memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
589 sc->ext_cd_gpio = -1; /* invalid gpio number */
463 } 590 }
464 memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
465 591
466 drv_data = sdhci_s3c_get_driver_data(pdev); 592 drv_data = sdhci_s3c_get_driver_data(pdev);
467 sc = sdhci_priv(host);
468 593
469 sc->host = host; 594 sc->host = host;
470 sc->pdev = pdev; 595 sc->pdev = pdev;
471 sc->pdata = pdata; 596 sc->pdata = pdata;
472 sc->ext_cd_gpio = -1; /* invalid gpio number */
473 597
474 platform_set_drvdata(pdev, host); 598 platform_set_drvdata(pdev, host);
475 599
@@ -633,6 +757,12 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
633 clk_put(sc->clk_io); 757 clk_put(sc->clk_io);
634 758
635 err_io_clk: 759 err_io_clk:
760 for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
761 gpio_free(sc->gpios[ptr]);
762 if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL)
763 gpio_free(sc->ext_cd_gpio);
764
765 err_pdata:
636 sdhci_free_host(host); 766 sdhci_free_host(host);
637 767
638 return ret; 768 return ret;
@@ -640,9 +770,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
640 770
641static int __devexit sdhci_s3c_remove(struct platform_device *pdev) 771static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
642{ 772{
643 struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
644 struct sdhci_host *host = platform_get_drvdata(pdev); 773 struct sdhci_host *host = platform_get_drvdata(pdev);
645 struct sdhci_s3c *sc = sdhci_priv(host); 774 struct sdhci_s3c *sc = sdhci_priv(host);
775 struct s3c_sdhci_platdata *pdata = sc->pdata;
646 int ptr; 776 int ptr;
647 777
648 if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) 778 if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
@@ -667,6 +797,11 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
667 clk_disable(sc->clk_io); 797 clk_disable(sc->clk_io);
668 clk_put(sc->clk_io); 798 clk_put(sc->clk_io);
669 799
800 if (pdev->dev.of_node) {
801 for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++)
802 gpio_free(sc->gpios[ptr]);
803 }
804
670 sdhci_free_host(host); 805 sdhci_free_host(host);
671 platform_set_drvdata(pdev, NULL); 806 platform_set_drvdata(pdev, NULL);
672 807
@@ -739,6 +874,16 @@ static struct platform_device_id sdhci_s3c_driver_ids[] = {
739}; 874};
740MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids); 875MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
741 876
877#ifdef CONFIG_OF
878static const struct of_device_id sdhci_s3c_dt_match[] = {
879 { .compatible = "samsung,s3c6410-sdhci", },
880 { .compatible = "samsung,exynos4210-sdhci",
881 .data = (void *)EXYNOS4_SDHCI_DRV_DATA },
882 {},
883};
884MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);
885#endif
886
742static struct platform_driver sdhci_s3c_driver = { 887static struct platform_driver sdhci_s3c_driver = {
743 .probe = sdhci_s3c_probe, 888 .probe = sdhci_s3c_probe,
744 .remove = __devexit_p(sdhci_s3c_remove), 889 .remove = __devexit_p(sdhci_s3c_remove),
@@ -746,6 +891,7 @@ static struct platform_driver sdhci_s3c_driver = {
746 .driver = { 891 .driver = {
747 .owner = THIS_MODULE, 892 .owner = THIS_MODULE,
748 .name = "s3c-sdhci", 893 .name = "s3c-sdhci",
894 .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
749 .pm = SDHCI_S3C_PMOPS, 895 .pm = SDHCI_S3C_PMOPS,
750 }, 896 },
751}; 897};