diff options
author | Ludovic Desroches <ludovic.desroches@atmel.com> | 2012-07-24 09:30:03 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-09-04 13:58:14 -0400 |
commit | e919fd200033e80b26f152d22c00a8fae7f8d548 (patch) | |
tree | 09cf3583765d3acefe8a09b948c4827da928e9c6 | |
parent | 77dcb3f4c344d4c9619803848f1aba4d271dac7b (diff) |
mmc: atmel-mci: add device tree support
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r-- | Documentation/devicetree/bindings/mmc/atmel-hsmci.txt | 68 | ||||
-rw-r--r-- | drivers/mmc/host/atmel-mci.c | 85 |
2 files changed, 151 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/mmc/atmel-hsmci.txt b/Documentation/devicetree/bindings/mmc/atmel-hsmci.txt new file mode 100644 index 000000000000..0a85c70cd30a --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/atmel-hsmci.txt | |||
@@ -0,0 +1,68 @@ | |||
1 | * Atmel High Speed MultiMedia Card Interface | ||
2 | |||
3 | This controller on atmel products provides an interface for MMC, SD and SDIO | ||
4 | types of memory cards. | ||
5 | |||
6 | This file documents differences between the core properties described | ||
7 | by mmc.txt and the properties used by the atmel-mci driver. | ||
8 | |||
9 | 1) MCI node | ||
10 | |||
11 | Required properties: | ||
12 | - compatible: should be "atmel,hsmci" | ||
13 | - #address-cells: should be one. The cell is the slot id. | ||
14 | - #size-cells: should be zero. | ||
15 | - at least one slot node | ||
16 | |||
17 | The node contains child nodes for each slot that the platform uses | ||
18 | |||
19 | Example MCI node: | ||
20 | |||
21 | mmc0: mmc@f0008000 { | ||
22 | compatible = "atmel,hsmci"; | ||
23 | reg = <0xf0008000 0x600>; | ||
24 | interrupts = <12 4>; | ||
25 | #address-cells = <1>; | ||
26 | #size-cells = <0>; | ||
27 | |||
28 | [ child node definitions...] | ||
29 | }; | ||
30 | |||
31 | 2) slot nodes | ||
32 | |||
33 | Required properties: | ||
34 | - reg: should contain the slot id. | ||
35 | - bus-width: number of data lines connected to the controller | ||
36 | |||
37 | Optional properties: | ||
38 | - cd-gpios: specify GPIOs for card detection | ||
39 | - cd-inverted: invert the value of external card detect gpio line | ||
40 | - wp-gpios: specify GPIOs for write protection | ||
41 | |||
42 | Example slot node: | ||
43 | |||
44 | slot@0 { | ||
45 | reg = <0>; | ||
46 | bus-width = <4>; | ||
47 | cd-gpios = <&pioD 15 0> | ||
48 | cd-inverted; | ||
49 | }; | ||
50 | |||
51 | Example full MCI node: | ||
52 | mmc0: mmc@f0008000 { | ||
53 | compatible = "atmel,hsmci"; | ||
54 | reg = <0xf0008000 0x600>; | ||
55 | interrupts = <12 4>; | ||
56 | #address-cells = <1>; | ||
57 | #size-cells = <0>; | ||
58 | slot@0 { | ||
59 | reg = <0>; | ||
60 | bus-width = <4>; | ||
61 | cd-gpios = <&pioD 15 0> | ||
62 | cd-inverted; | ||
63 | }; | ||
64 | slot@1 { | ||
65 | reg = <1>; | ||
66 | bus-width = <4>; | ||
67 | }; | ||
68 | }; | ||
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index a53c7c478e05..8c72828239b2 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c | |||
@@ -19,6 +19,9 @@ | |||
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/ioport.h> | 20 | #include <linux/ioport.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/of.h> | ||
23 | #include <linux/of_device.h> | ||
24 | #include <linux/of_gpio.h> | ||
22 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
23 | #include <linux/scatterlist.h> | 26 | #include <linux/scatterlist.h> |
24 | #include <linux/seq_file.h> | 27 | #include <linux/seq_file.h> |
@@ -500,6 +503,70 @@ err: | |||
500 | dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); | 503 | dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); |
501 | } | 504 | } |
502 | 505 | ||
506 | #if defined(CONFIG_OF) | ||
507 | static const struct of_device_id atmci_dt_ids[] = { | ||
508 | { .compatible = "atmel,hsmci" }, | ||
509 | { /* sentinel */ } | ||
510 | }; | ||
511 | |||
512 | MODULE_DEVICE_TABLE(of, atmci_dt_ids); | ||
513 | |||
514 | static struct mci_platform_data __devinit* | ||
515 | atmci_of_init(struct platform_device *pdev) | ||
516 | { | ||
517 | struct device_node *np = pdev->dev.of_node; | ||
518 | struct device_node *cnp; | ||
519 | struct mci_platform_data *pdata; | ||
520 | u32 slot_id; | ||
521 | |||
522 | if (!np) { | ||
523 | dev_err(&pdev->dev, "device node not found\n"); | ||
524 | return ERR_PTR(-EINVAL); | ||
525 | } | ||
526 | |||
527 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | ||
528 | if (!pdata) { | ||
529 | dev_err(&pdev->dev, "could not allocate memory for pdata\n"); | ||
530 | return ERR_PTR(-ENOMEM); | ||
531 | } | ||
532 | |||
533 | for_each_child_of_node(np, cnp) { | ||
534 | if (of_property_read_u32(cnp, "reg", &slot_id)) { | ||
535 | dev_warn(&pdev->dev, "reg property is missing for %s\n", | ||
536 | cnp->full_name); | ||
537 | continue; | ||
538 | } | ||
539 | |||
540 | if (slot_id >= ATMCI_MAX_NR_SLOTS) { | ||
541 | dev_warn(&pdev->dev, "can't have more than %d slots\n", | ||
542 | ATMCI_MAX_NR_SLOTS); | ||
543 | break; | ||
544 | } | ||
545 | |||
546 | if (of_property_read_u32(cnp, "bus-width", | ||
547 | &pdata->slot[slot_id].bus_width)) | ||
548 | pdata->slot[slot_id].bus_width = 1; | ||
549 | |||
550 | pdata->slot[slot_id].detect_pin = | ||
551 | of_get_named_gpio(cnp, "cd-gpios", 0); | ||
552 | |||
553 | pdata->slot[slot_id].detect_is_active_high = | ||
554 | of_property_read_bool(cnp, "cd-inverted"); | ||
555 | |||
556 | pdata->slot[slot_id].wp_pin = | ||
557 | of_get_named_gpio(cnp, "wp-gpios", 0); | ||
558 | } | ||
559 | |||
560 | return pdata; | ||
561 | } | ||
562 | #else /* CONFIG_OF */ | ||
563 | static inline struct mci_platform_data* | ||
564 | atmci_of_init(struct platform_device *dev) | ||
565 | { | ||
566 | return ERR_PTR(-EINVAL); | ||
567 | } | ||
568 | #endif | ||
569 | |||
503 | static inline unsigned int atmci_get_version(struct atmel_mci *host) | 570 | static inline unsigned int atmci_get_version(struct atmel_mci *host) |
504 | { | 571 | { |
505 | return atmci_readl(host, ATMCI_VERSION) & 0x00000fff; | 572 | return atmci_readl(host, ATMCI_VERSION) & 0x00000fff; |
@@ -2046,6 +2113,13 @@ static int __init atmci_init_slot(struct atmel_mci *host, | |||
2046 | slot->sdc_reg = sdc_reg; | 2113 | slot->sdc_reg = sdc_reg; |
2047 | slot->sdio_irq = sdio_irq; | 2114 | slot->sdio_irq = sdio_irq; |
2048 | 2115 | ||
2116 | dev_dbg(&mmc->class_dev, | ||
2117 | "slot[%u]: bus_width=%u, detect_pin=%d, " | ||
2118 | "detect_is_active_high=%s, wp_pin=%d\n", | ||
2119 | id, slot_data->bus_width, slot_data->detect_pin, | ||
2120 | slot_data->detect_is_active_high ? "true" : "false", | ||
2121 | slot_data->wp_pin); | ||
2122 | |||
2049 | mmc->ops = &atmci_ops; | 2123 | mmc->ops = &atmci_ops; |
2050 | mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512); | 2124 | mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512); |
2051 | mmc->f_max = host->bus_hz / 2; | 2125 | mmc->f_max = host->bus_hz / 2; |
@@ -2268,8 +2342,14 @@ static int __init atmci_probe(struct platform_device *pdev) | |||
2268 | if (!regs) | 2342 | if (!regs) |
2269 | return -ENXIO; | 2343 | return -ENXIO; |
2270 | pdata = pdev->dev.platform_data; | 2344 | pdata = pdev->dev.platform_data; |
2271 | if (!pdata) | 2345 | if (!pdata) { |
2272 | return -ENXIO; | 2346 | pdata = atmci_of_init(pdev); |
2347 | if (IS_ERR(pdata)) { | ||
2348 | dev_err(&pdev->dev, "platform data not available\n"); | ||
2349 | return PTR_ERR(pdata); | ||
2350 | } | ||
2351 | } | ||
2352 | |||
2273 | irq = platform_get_irq(pdev, 0); | 2353 | irq = platform_get_irq(pdev, 0); |
2274 | if (irq < 0) | 2354 | if (irq < 0) |
2275 | return irq; | 2355 | return irq; |
@@ -2487,6 +2567,7 @@ static struct platform_driver atmci_driver = { | |||
2487 | .driver = { | 2567 | .driver = { |
2488 | .name = "atmel_mci", | 2568 | .name = "atmel_mci", |
2489 | .pm = ATMCI_PM_OPS, | 2569 | .pm = ATMCI_PM_OPS, |
2570 | .of_match_table = of_match_ptr(atmci_dt_ids), | ||
2490 | }, | 2571 | }, |
2491 | }; | 2572 | }; |
2492 | 2573 | ||