]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9d11d12a MK |
2 | /* |
3 | * Qualcomm SDHCI driver - SD/eMMC controller | |
4 | * | |
5 | * (C) Copyright 2015 Mateusz Kulikowski <[email protected]> | |
6 | * | |
7 | * Based on Linux driver | |
9d11d12a MK |
8 | */ |
9 | ||
10 | #include <common.h> | |
11 | #include <clk.h> | |
12 | #include <dm.h> | |
13 | #include <sdhci.h> | |
14 | #include <wait_bit.h> | |
15 | #include <asm/io.h> | |
16 | #include <linux/bitops.h> | |
17 | ||
18 | /* Non-standard registers needed for SDHCI startup */ | |
19 | #define SDCC_MCI_POWER 0x0 | |
20 | #define SDCC_MCI_POWER_SW_RST BIT(7) | |
21 | ||
22 | /* This is undocumented register */ | |
23 | #define SDCC_MCI_VERSION 0x50 | |
24 | #define SDCC_MCI_VERSION_MAJOR_SHIFT 28 | |
25 | #define SDCC_MCI_VERSION_MAJOR_MASK (0xf << SDCC_MCI_VERSION_MAJOR_SHIFT) | |
26 | #define SDCC_MCI_VERSION_MINOR_MASK 0xff | |
27 | ||
28 | #define SDCC_MCI_STATUS2 0x6C | |
29 | #define SDCC_MCI_STATUS2_MCI_ACT 0x1 | |
30 | #define SDCC_MCI_HC_MODE 0x78 | |
31 | ||
32 | /* Offset to SDHCI registers */ | |
33 | #define SDCC_SDHCI_OFFSET 0x900 | |
34 | ||
35 | /* Non standard (?) SDHCI register */ | |
36 | #define SDHCI_VENDOR_SPEC_CAPABILITIES0 0x11c | |
37 | ||
12293f6d SG |
38 | struct msm_sdhc_plat { |
39 | struct mmc_config cfg; | |
40 | struct mmc mmc; | |
41 | }; | |
42 | ||
9d11d12a MK |
43 | struct msm_sdhc { |
44 | struct sdhci_host host; | |
45 | void *base; | |
46 | }; | |
47 | ||
48 | DECLARE_GLOBAL_DATA_PTR; | |
49 | ||
50 | static int msm_sdc_clk_init(struct udevice *dev) | |
51 | { | |
e160f7d4 SG |
52 | int node = dev_of_offset(dev); |
53 | uint clk_rate = fdtdec_get_uint(gd->fdt_blob, node, "clock-frequency", | |
54 | 400000); | |
9d11d12a MK |
55 | uint clkd[2]; /* clk_id and clk_no */ |
56 | int clk_offset; | |
135aa950 SW |
57 | struct udevice *clk_dev; |
58 | struct clk clk; | |
9d11d12a MK |
59 | int ret; |
60 | ||
e160f7d4 | 61 | ret = fdtdec_get_int_array(gd->fdt_blob, node, "clock", clkd, 2); |
9d11d12a MK |
62 | if (ret) |
63 | return ret; | |
64 | ||
65 | clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]); | |
66 | if (clk_offset < 0) | |
67 | return clk_offset; | |
68 | ||
135aa950 | 69 | ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev); |
9d11d12a MK |
70 | if (ret) |
71 | return ret; | |
72 | ||
135aa950 SW |
73 | clk.id = clkd[1]; |
74 | ret = clk_request(clk_dev, &clk); | |
75 | if (ret < 0) | |
76 | return ret; | |
77 | ||
78 | ret = clk_set_rate(&clk, clk_rate); | |
79 | clk_free(&clk); | |
9d11d12a MK |
80 | if (ret < 0) |
81 | return ret; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int msm_sdc_probe(struct udevice *dev) | |
87 | { | |
12293f6d | 88 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
12293f6d | 89 | struct msm_sdhc_plat *plat = dev_get_platdata(dev); |
9d11d12a MK |
90 | struct msm_sdhc *prv = dev_get_priv(dev); |
91 | struct sdhci_host *host = &prv->host; | |
92 | u32 core_version, core_minor, core_major; | |
12293f6d | 93 | u32 caps; |
9d11d12a MK |
94 | int ret; |
95 | ||
96 | host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B; | |
97 | ||
6d0e34bf SH |
98 | host->max_clk = 0; |
99 | ||
9d11d12a MK |
100 | /* Init clocks */ |
101 | ret = msm_sdc_clk_init(dev); | |
102 | if (ret) | |
103 | return ret; | |
104 | ||
105 | /* Reset the core and Enable SDHC mode */ | |
106 | writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST, | |
107 | prv->base + SDCC_MCI_POWER); | |
108 | ||
109 | ||
110 | /* Wait for reset to be written to register */ | |
48263504 ÁFR |
111 | if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, |
112 | SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { | |
9d11d12a MK |
113 | printf("msm_sdhci: reset request failed\n"); |
114 | return -EIO; | |
115 | } | |
116 | ||
117 | /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ | |
48263504 ÁFR |
118 | if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, |
119 | SDCC_MCI_POWER_SW_RST, false, 2, false)) { | |
9d11d12a MK |
120 | printf("msm_sdhci: stuck in reset\n"); |
121 | return -ETIMEDOUT; | |
122 | } | |
123 | ||
124 | /* Enable host-controller mode */ | |
125 | writel(1, prv->base + SDCC_MCI_HC_MODE); | |
126 | ||
127 | core_version = readl(prv->base + SDCC_MCI_VERSION); | |
128 | ||
129 | core_major = (core_version & SDCC_MCI_VERSION_MAJOR_MASK); | |
130 | core_major >>= SDCC_MCI_VERSION_MAJOR_SHIFT; | |
131 | ||
132 | core_minor = core_version & SDCC_MCI_VERSION_MINOR_MASK; | |
133 | ||
134 | /* | |
135 | * Support for some capabilities is not advertised by newer | |
136 | * controller versions and must be explicitly enabled. | |
137 | */ | |
138 | if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { | |
12293f6d | 139 | caps = readl(host->ioaddr + SDHCI_CAPABILITIES); |
9d11d12a MK |
140 | caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; |
141 | writel(caps, host->ioaddr + SDHCI_VENDOR_SPEC_CAPABILITIES0); | |
142 | } | |
143 | ||
14bed52d | 144 | ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); |
12293f6d | 145 | host->mmc = &plat->mmc; |
eb9d3ca3 MK |
146 | if (ret) |
147 | return ret; | |
12293f6d | 148 | host->mmc->priv = &prv->host; |
eb9d3ca3 | 149 | host->mmc->dev = dev; |
12293f6d | 150 | upriv->mmc = host->mmc; |
eb9d3ca3 | 151 | |
12293f6d | 152 | return sdhci_probe(dev); |
9d11d12a MK |
153 | } |
154 | ||
155 | static int msm_sdc_remove(struct udevice *dev) | |
156 | { | |
157 | struct msm_sdhc *priv = dev_get_priv(dev); | |
158 | ||
159 | /* Disable host-controller mode */ | |
160 | writel(0, priv->base + SDCC_MCI_HC_MODE); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int msm_ofdata_to_platdata(struct udevice *dev) | |
166 | { | |
167 | struct udevice *parent = dev->parent; | |
168 | struct msm_sdhc *priv = dev_get_priv(dev); | |
169 | struct sdhci_host *host = &priv->host; | |
e160f7d4 | 170 | int node = dev_of_offset(dev); |
9d11d12a MK |
171 | |
172 | host->name = strdup(dev->name); | |
a821c4af | 173 | host->ioaddr = (void *)devfdt_get_addr(dev); |
e160f7d4 SG |
174 | host->bus_width = fdtdec_get_int(gd->fdt_blob, node, "bus-width", 4); |
175 | host->index = fdtdec_get_uint(gd->fdt_blob, node, "index", 0); | |
9d11d12a | 176 | priv->base = (void *)fdtdec_get_addr_size_auto_parent(gd->fdt_blob, |
e160f7d4 | 177 | dev_of_offset(parent), node, "reg", 1, NULL, false); |
9d11d12a MK |
178 | if (priv->base == (void *)FDT_ADDR_T_NONE || |
179 | host->ioaddr == (void *)FDT_ADDR_T_NONE) | |
180 | return -EINVAL; | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
12293f6d SG |
185 | static int msm_sdc_bind(struct udevice *dev) |
186 | { | |
12293f6d | 187 | struct msm_sdhc_plat *plat = dev_get_platdata(dev); |
12293f6d | 188 | |
24f5aec3 | 189 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
12293f6d SG |
190 | } |
191 | ||
9d11d12a MK |
192 | static const struct udevice_id msm_mmc_ids[] = { |
193 | { .compatible = "qcom,sdhci-msm-v4" }, | |
194 | { } | |
195 | }; | |
196 | ||
197 | U_BOOT_DRIVER(msm_sdc_drv) = { | |
198 | .name = "msm_sdc", | |
199 | .id = UCLASS_MMC, | |
200 | .of_match = msm_mmc_ids, | |
201 | .ofdata_to_platdata = msm_ofdata_to_platdata, | |
12293f6d | 202 | .ops = &sdhci_ops, |
12293f6d | 203 | .bind = msm_sdc_bind, |
9d11d12a MK |
204 | .probe = msm_sdc_probe, |
205 | .remove = msm_sdc_remove, | |
206 | .priv_auto_alloc_size = sizeof(struct msm_sdhc), | |
12293f6d | 207 | .platdata_auto_alloc_size = sizeof(struct msm_sdhc_plat), |
9d11d12a | 208 | }; |