diff options
author | Thomas Abraham <thomas.abraham@linaro.org> | 2012-09-17 14:16:40 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-10-03 10:05:17 -0400 |
commit | c91eab4b2564f2424268113ab348eacf9381c2d9 (patch) | |
tree | 598b2e39dd78ccb33f730340aa0163dc76979d0a | |
parent | b4967aa58e2bbafbb280dd4f0c5a777181500e41 (diff) |
mmc: dw_mmc: add device tree support
Add device tree based discovery support.
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Will Newton <will.newton@imgtec.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r-- | Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt | 79 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc-pltfm.c | 9 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 130 |
3 files changed, 211 insertions, 7 deletions
diff --git a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt new file mode 100644 index 000000000000..06cd32d08052 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt | |||
@@ -0,0 +1,79 @@ | |||
1 | * Synopsis Designware Mobile Storage Host Controller | ||
2 | |||
3 | The Synopsis designware mobile storage host controller is used to interface | ||
4 | a SoC with storage medium such as eMMC or SD/MMC cards. This file documents | ||
5 | differences between the core mmc properties described by mmc.txt and the | ||
6 | properties used by the Synopsis Designware Mobile Storage Host Controller. | ||
7 | |||
8 | Required Properties: | ||
9 | |||
10 | * compatible: should be | ||
11 | - snps,dw-mshc: for controllers compliant with synopsis dw-mshc. | ||
12 | * #address-cells: should be 1. | ||
13 | * #size-cells: should be 0. | ||
14 | |||
15 | # Slots: The slot specific information are contained within child-nodes with | ||
16 | each child-node representing a supported slot. There should be atleast one | ||
17 | child node representing a card slot. The name of the child node representing | ||
18 | the slot is recommended to be slot@n where n is the unique number of the slot | ||
19 | connnected to the controller. The following are optional properties which | ||
20 | can be included in the slot child node. | ||
21 | |||
22 | * reg: specifies the physical slot number. The valid values of this | ||
23 | property is 0 to (num-slots -1), where num-slots is the value | ||
24 | specified by the num-slots property. | ||
25 | |||
26 | * bus-width: as documented in mmc core bindings. | ||
27 | |||
28 | * wp-gpios: specifies the write protect gpio line. The format of the | ||
29 | gpio specifier depends on the gpio controller. If the write-protect | ||
30 | line is not available, this property is optional. | ||
31 | |||
32 | Optional properties: | ||
33 | |||
34 | * num-slots: specifies the number of slots supported by the controller. | ||
35 | The number of physical slots actually used could be equal or less than the | ||
36 | value specified by num-slots. If this property is not specified, the value | ||
37 | of num-slot property is assumed to be 1. | ||
38 | |||
39 | * fifo-depth: The maximum size of the tx/rx fifo's. If this property is not | ||
40 | specified, the default value of the fifo size is determined from the | ||
41 | controller registers. | ||
42 | |||
43 | * card-detect-delay: Delay in milli-seconds before detecting card after card | ||
44 | insert event. The default value is 0. | ||
45 | |||
46 | * supports-highspeed: Enables support for high speed cards (upto 50MHz) | ||
47 | |||
48 | * broken-cd: as documented in mmc core bindings. | ||
49 | |||
50 | Aliases: | ||
51 | |||
52 | - All the MSHC controller nodes should be represented in the aliases node using | ||
53 | the following format 'mshc{n}' where n is a unique number for the alias. | ||
54 | |||
55 | Example: | ||
56 | |||
57 | The MSHC controller node can be split into two portions, SoC specific and | ||
58 | board specific portions as listed below. | ||
59 | |||
60 | dwmmc0@12200000 { | ||
61 | compatible = "snps,dw-mshc"; | ||
62 | reg = <0x12200000 0x1000>; | ||
63 | interrupts = <0 75 0>; | ||
64 | #address-cells = <1>; | ||
65 | #size-cells = <0>; | ||
66 | }; | ||
67 | |||
68 | dwmmc0@12200000 { | ||
69 | num-slots = <1>; | ||
70 | supports-highspeed; | ||
71 | broken-cd; | ||
72 | fifo-depth = <0x80>; | ||
73 | card-detect-delay = <200>; | ||
74 | |||
75 | slot@0 { | ||
76 | reg = <0>; | ||
77 | bus-width = <8>; | ||
78 | }; | ||
79 | }; | ||
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index ce7be1ac6621..72059050540f 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <linux/mmc/host.h> | 19 | #include <linux/mmc/host.h> |
20 | #include <linux/mmc/mmc.h> | 20 | #include <linux/mmc/mmc.h> |
21 | #include <linux/mmc/dw_mmc.h> | 21 | #include <linux/mmc/dw_mmc.h> |
22 | #include <linux/of.h> | ||
23 | |||
22 | #include "dw_mmc.h" | 24 | #include "dw_mmc.h" |
23 | 25 | ||
24 | static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev) | 26 | static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev) |
@@ -94,10 +96,17 @@ static int dw_mci_pltfm_resume(struct device *dev) | |||
94 | 96 | ||
95 | static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); | 97 | static SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); |
96 | 98 | ||
99 | static const struct of_device_id dw_mci_pltfm_match[] = { | ||
100 | { .compatible = "snps,dw-mshc", }, | ||
101 | {}, | ||
102 | }; | ||
103 | MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); | ||
104 | |||
97 | static struct platform_driver dw_mci_pltfm_driver = { | 105 | static struct platform_driver dw_mci_pltfm_driver = { |
98 | .remove = __exit_p(dw_mci_pltfm_remove), | 106 | .remove = __exit_p(dw_mci_pltfm_remove), |
99 | .driver = { | 107 | .driver = { |
100 | .name = "dw_mmc", | 108 | .name = "dw_mmc", |
109 | .of_match_table = of_match_ptr(dw_mci_pltfm_match), | ||
101 | .pm = &dw_mci_pltfm_pmops, | 110 | .pm = &dw_mci_pltfm_pmops, |
102 | }, | 111 | }, |
103 | }; | 112 | }; |
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 455a155406bc..c792466fb8ac 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/bitops.h> | 33 | #include <linux/bitops.h> |
34 | #include <linux/regulator/consumer.h> | 34 | #include <linux/regulator/consumer.h> |
35 | #include <linux/workqueue.h> | 35 | #include <linux/workqueue.h> |
36 | #include <linux/of.h> | ||
36 | 37 | ||
37 | #include "dw_mmc.h" | 38 | #include "dw_mmc.h" |
38 | 39 | ||
@@ -1769,10 +1770,57 @@ static void dw_mci_work_routine_card(struct work_struct *work) | |||
1769 | } | 1770 | } |
1770 | } | 1771 | } |
1771 | 1772 | ||
1773 | #ifdef CONFIG_OF | ||
1774 | /* given a slot id, find out the device node representing that slot */ | ||
1775 | static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) | ||
1776 | { | ||
1777 | struct device_node *np; | ||
1778 | const __be32 *addr; | ||
1779 | int len; | ||
1780 | |||
1781 | if (!dev || !dev->of_node) | ||
1782 | return NULL; | ||
1783 | |||
1784 | for_each_child_of_node(dev->of_node, np) { | ||
1785 | addr = of_get_property(np, "reg", &len); | ||
1786 | if (!addr || (len < sizeof(int))) | ||
1787 | continue; | ||
1788 | if (be32_to_cpup(addr) == slot) | ||
1789 | return np; | ||
1790 | } | ||
1791 | return NULL; | ||
1792 | } | ||
1793 | |||
1794 | /* find out bus-width for a given slot */ | ||
1795 | static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) | ||
1796 | { | ||
1797 | struct device_node *np = dw_mci_of_find_slot_node(dev, slot); | ||
1798 | u32 bus_wd = 1; | ||
1799 | |||
1800 | if (!np) | ||
1801 | return 1; | ||
1802 | |||
1803 | if (of_property_read_u32(np, "bus-width", &bus_wd)) | ||
1804 | dev_err(dev, "bus-width property not found, assuming width" | ||
1805 | " as 1\n"); | ||
1806 | return bus_wd; | ||
1807 | } | ||
1808 | #else /* CONFIG_OF */ | ||
1809 | static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) | ||
1810 | { | ||
1811 | return 1; | ||
1812 | } | ||
1813 | static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) | ||
1814 | { | ||
1815 | return NULL; | ||
1816 | } | ||
1817 | #endif /* CONFIG_OF */ | ||
1818 | |||
1772 | static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) | 1819 | static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) |
1773 | { | 1820 | { |
1774 | struct mmc_host *mmc; | 1821 | struct mmc_host *mmc; |
1775 | struct dw_mci_slot *slot; | 1822 | struct dw_mci_slot *slot; |
1823 | u8 bus_width; | ||
1776 | 1824 | ||
1777 | mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); | 1825 | mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); |
1778 | if (!mmc) | 1826 | if (!mmc) |
@@ -1782,6 +1830,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) | |||
1782 | slot->id = id; | 1830 | slot->id = id; |
1783 | slot->mmc = mmc; | 1831 | slot->mmc = mmc; |
1784 | slot->host = host; | 1832 | slot->host = host; |
1833 | host->slot[id] = slot; | ||
1785 | 1834 | ||
1786 | mmc->ops = &dw_mci_ops; | 1835 | mmc->ops = &dw_mci_ops; |
1787 | mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); | 1836 | mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); |
@@ -1806,8 +1855,18 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) | |||
1806 | mmc->caps2 = host->pdata->caps2; | 1855 | mmc->caps2 = host->pdata->caps2; |
1807 | 1856 | ||
1808 | if (host->pdata->get_bus_wd) | 1857 | if (host->pdata->get_bus_wd) |
1809 | if (host->pdata->get_bus_wd(slot->id) >= 4) | 1858 | bus_width = host->pdata->get_bus_wd(slot->id); |
1810 | mmc->caps |= MMC_CAP_4_BIT_DATA; | 1859 | else if (host->dev->of_node) |
1860 | bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id); | ||
1861 | else | ||
1862 | bus_width = 1; | ||
1863 | |||
1864 | switch (bus_width) { | ||
1865 | case 8: | ||
1866 | mmc->caps |= MMC_CAP_8_BIT_DATA; | ||
1867 | case 4: | ||
1868 | mmc->caps |= MMC_CAP_4_BIT_DATA; | ||
1869 | } | ||
1811 | 1870 | ||
1812 | if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) | 1871 | if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) |
1813 | mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; | 1872 | mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; |
@@ -1852,7 +1911,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) | |||
1852 | else | 1911 | else |
1853 | clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); | 1912 | clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); |
1854 | 1913 | ||
1855 | host->slot[id] = slot; | ||
1856 | mmc_add_host(mmc); | 1914 | mmc_add_host(mmc); |
1857 | 1915 | ||
1858 | #if defined(CONFIG_DEBUG_FS) | 1916 | #if defined(CONFIG_DEBUG_FS) |
@@ -1944,16 +2002,74 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host) | |||
1944 | return false; | 2002 | return false; |
1945 | } | 2003 | } |
1946 | 2004 | ||
2005 | #ifdef CONFIG_OF | ||
2006 | static struct dw_mci_of_quirks { | ||
2007 | char *quirk; | ||
2008 | int id; | ||
2009 | } of_quirks[] = { | ||
2010 | { | ||
2011 | .quirk = "supports-highspeed", | ||
2012 | .id = DW_MCI_QUIRK_HIGHSPEED, | ||
2013 | }, { | ||
2014 | .quirk = "broken-cd", | ||
2015 | .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, | ||
2016 | }, | ||
2017 | }; | ||
2018 | |||
2019 | static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) | ||
2020 | { | ||
2021 | struct dw_mci_board *pdata; | ||
2022 | struct device *dev = host->dev; | ||
2023 | struct device_node *np = dev->of_node; | ||
2024 | int idx; | ||
2025 | |||
2026 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
2027 | if (!pdata) { | ||
2028 | dev_err(dev, "could not allocate memory for pdata\n"); | ||
2029 | return ERR_PTR(-ENOMEM); | ||
2030 | } | ||
2031 | |||
2032 | /* find out number of slots supported */ | ||
2033 | if (of_property_read_u32(dev->of_node, "num-slots", | ||
2034 | &pdata->num_slots)) { | ||
2035 | dev_info(dev, "num-slots property not found, " | ||
2036 | "assuming 1 slot is available\n"); | ||
2037 | pdata->num_slots = 1; | ||
2038 | } | ||
2039 | |||
2040 | /* get quirks */ | ||
2041 | for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++) | ||
2042 | if (of_get_property(np, of_quirks[idx].quirk, NULL)) | ||
2043 | pdata->quirks |= of_quirks[idx].id; | ||
2044 | |||
2045 | if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth)) | ||
2046 | dev_info(dev, "fifo-depth property not found, using " | ||
2047 | "value of FIFOTH register as default\n"); | ||
2048 | |||
2049 | of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); | ||
2050 | |||
2051 | return pdata; | ||
2052 | } | ||
2053 | |||
2054 | #else /* CONFIG_OF */ | ||
2055 | static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) | ||
2056 | { | ||
2057 | return ERR_PTR(-EINVAL); | ||
2058 | } | ||
2059 | #endif /* CONFIG_OF */ | ||
2060 | |||
1947 | int dw_mci_probe(struct dw_mci *host) | 2061 | int dw_mci_probe(struct dw_mci *host) |
1948 | { | 2062 | { |
1949 | int width, i, ret = 0; | 2063 | int width, i, ret = 0; |
1950 | u32 fifo_size; | 2064 | u32 fifo_size; |
1951 | int init_slots = 0; | 2065 | int init_slots = 0; |
1952 | 2066 | ||
1953 | if (!host->pdata || !host->pdata->init) { | 2067 | if (!host->pdata) { |
1954 | dev_err(host->dev, | 2068 | host->pdata = dw_mci_parse_dt(host); |
1955 | "Platform data must supply init function\n"); | 2069 | if (IS_ERR(host->pdata)) { |
1956 | return -ENODEV; | 2070 | dev_err(host->dev, "platform data not available\n"); |
2071 | return -EINVAL; | ||
2072 | } | ||
1957 | } | 2073 | } |
1958 | 2074 | ||
1959 | if (!host->pdata->select_slot && host->pdata->num_slots > 1) { | 2075 | if (!host->pdata->select_slot && host->pdata->num_slots > 1) { |