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 | ||
