.. | .. |
---|
15 | 15 | * iProc SDHCI platform driver |
---|
16 | 16 | */ |
---|
17 | 17 | |
---|
| 18 | +#include <linux/acpi.h> |
---|
18 | 19 | #include <linux/delay.h> |
---|
19 | 20 | #include <linux/module.h> |
---|
20 | 21 | #include <linux/mmc/host.h> |
---|
.. | .. |
---|
162 | 163 | sdhci_iproc_writel(host, newval, reg & ~3); |
---|
163 | 164 | } |
---|
164 | 165 | |
---|
| 166 | +static unsigned int sdhci_iproc_get_max_clock(struct sdhci_host *host) |
---|
| 167 | +{ |
---|
| 168 | + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
---|
| 169 | + |
---|
| 170 | + if (pltfm_host->clk) |
---|
| 171 | + return sdhci_pltfm_clk_get_max_clock(host); |
---|
| 172 | + else |
---|
| 173 | + return pltfm_host->clock; |
---|
| 174 | +} |
---|
| 175 | + |
---|
| 176 | +/* |
---|
| 177 | + * There is a known bug on BCM2711's SDHCI core integration where the |
---|
| 178 | + * controller will hang when the difference between the core clock and the bus |
---|
| 179 | + * clock is too great. Specifically this can be reproduced under the following |
---|
| 180 | + * conditions: |
---|
| 181 | + * |
---|
| 182 | + * - No SD card plugged in, polling thread is running, probing cards at |
---|
| 183 | + * 100 kHz. |
---|
| 184 | + * - BCM2711's core clock configured at 500MHz or more |
---|
| 185 | + * |
---|
| 186 | + * So we set 200kHz as the minimum clock frequency available for that SoC. |
---|
| 187 | + */ |
---|
| 188 | +static unsigned int sdhci_iproc_bcm2711_get_min_clock(struct sdhci_host *host) |
---|
| 189 | +{ |
---|
| 190 | + return 200000; |
---|
| 191 | +} |
---|
| 192 | + |
---|
165 | 193 | static const struct sdhci_ops sdhci_iproc_ops = { |
---|
166 | 194 | .set_clock = sdhci_set_clock, |
---|
167 | | - .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
---|
| 195 | + .get_max_clock = sdhci_iproc_get_max_clock, |
---|
168 | 196 | .set_bus_width = sdhci_set_bus_width, |
---|
169 | 197 | .reset = sdhci_reset, |
---|
170 | 198 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
---|
.. | .. |
---|
178 | 206 | .write_w = sdhci_iproc_writew, |
---|
179 | 207 | .write_b = sdhci_iproc_writeb, |
---|
180 | 208 | .set_clock = sdhci_set_clock, |
---|
181 | | - .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
---|
| 209 | + .get_max_clock = sdhci_iproc_get_max_clock, |
---|
182 | 210 | .set_bus_width = sdhci_set_bus_width, |
---|
183 | 211 | .reset = sdhci_reset, |
---|
184 | 212 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
---|
.. | .. |
---|
250 | 278 | .mmc_caps = 0x00000000, |
---|
251 | 279 | }; |
---|
252 | 280 | |
---|
| 281 | +static const struct sdhci_ops sdhci_iproc_bcm2711_ops = { |
---|
| 282 | + .read_l = sdhci_iproc_readl, |
---|
| 283 | + .read_w = sdhci_iproc_readw, |
---|
| 284 | + .read_b = sdhci_iproc_readb, |
---|
| 285 | + .write_l = sdhci_iproc_writel, |
---|
| 286 | + .write_w = sdhci_iproc_writew, |
---|
| 287 | + .write_b = sdhci_iproc_writeb, |
---|
| 288 | + .set_clock = sdhci_set_clock, |
---|
| 289 | + .set_power = sdhci_set_power_and_bus_voltage, |
---|
| 290 | + .get_max_clock = sdhci_iproc_get_max_clock, |
---|
| 291 | + .get_min_clock = sdhci_iproc_bcm2711_get_min_clock, |
---|
| 292 | + .set_bus_width = sdhci_set_bus_width, |
---|
| 293 | + .reset = sdhci_reset, |
---|
| 294 | + .set_uhs_signaling = sdhci_set_uhs_signaling, |
---|
| 295 | +}; |
---|
| 296 | + |
---|
| 297 | +static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = { |
---|
| 298 | + .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, |
---|
| 299 | + .ops = &sdhci_iproc_bcm2711_ops, |
---|
| 300 | +}; |
---|
| 301 | + |
---|
| 302 | +static const struct sdhci_iproc_data bcm2711_data = { |
---|
| 303 | + .pdata = &sdhci_bcm2711_pltfm_data, |
---|
| 304 | + .mmc_caps = MMC_CAP_3_3V_DDR, |
---|
| 305 | +}; |
---|
| 306 | + |
---|
253 | 307 | static const struct of_device_id sdhci_iproc_of_match[] = { |
---|
254 | 308 | { .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data }, |
---|
| 309 | + { .compatible = "brcm,bcm2711-emmc2", .data = &bcm2711_data }, |
---|
255 | 310 | { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_cygnus_data}, |
---|
256 | 311 | { .compatible = "brcm,sdhci-iproc", .data = &iproc_data }, |
---|
257 | 312 | { } |
---|
258 | 313 | }; |
---|
259 | 314 | MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); |
---|
260 | 315 | |
---|
| 316 | +#ifdef CONFIG_ACPI |
---|
| 317 | +/* |
---|
| 318 | + * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks |
---|
| 319 | + * which are provided by the ACPI table. |
---|
| 320 | + */ |
---|
| 321 | +static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = { |
---|
| 322 | + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | |
---|
| 323 | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | |
---|
| 324 | + SDHCI_QUIRK_NO_HISPD_BIT, |
---|
| 325 | + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
---|
| 326 | + .ops = &sdhci_iproc_32only_ops, |
---|
| 327 | +}; |
---|
| 328 | + |
---|
| 329 | +static const struct sdhci_iproc_data bcm_arasan_data = { |
---|
| 330 | + .pdata = &sdhci_bcm_arasan_data, |
---|
| 331 | +}; |
---|
| 332 | + |
---|
| 333 | +static const struct acpi_device_id sdhci_iproc_acpi_ids[] = { |
---|
| 334 | + { .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data }, |
---|
| 335 | + { .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data }, |
---|
| 336 | + { .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data }, |
---|
| 337 | + { .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data }, |
---|
| 338 | + { /* sentinel */ } |
---|
| 339 | +}; |
---|
| 340 | +MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids); |
---|
| 341 | +#endif |
---|
| 342 | + |
---|
261 | 343 | static int sdhci_iproc_probe(struct platform_device *pdev) |
---|
262 | 344 | { |
---|
263 | | - const struct of_device_id *match; |
---|
264 | | - const struct sdhci_iproc_data *iproc_data; |
---|
| 345 | + struct device *dev = &pdev->dev; |
---|
| 346 | + const struct sdhci_iproc_data *iproc_data = NULL; |
---|
265 | 347 | struct sdhci_host *host; |
---|
266 | 348 | struct sdhci_iproc_host *iproc_host; |
---|
267 | 349 | struct sdhci_pltfm_host *pltfm_host; |
---|
268 | 350 | int ret; |
---|
269 | 351 | |
---|
270 | | - match = of_match_device(sdhci_iproc_of_match, &pdev->dev); |
---|
271 | | - if (!match) |
---|
272 | | - return -EINVAL; |
---|
273 | | - iproc_data = match->data; |
---|
| 352 | + iproc_data = device_get_match_data(dev); |
---|
| 353 | + if (!iproc_data) |
---|
| 354 | + return -ENODEV; |
---|
274 | 355 | |
---|
275 | 356 | host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host)); |
---|
276 | 357 | if (IS_ERR(host)) |
---|
.. | .. |
---|
285 | 366 | if (ret) |
---|
286 | 367 | goto err; |
---|
287 | 368 | |
---|
288 | | - sdhci_get_of_property(pdev); |
---|
| 369 | + sdhci_get_property(pdev); |
---|
289 | 370 | |
---|
290 | 371 | host->mmc->caps |= iproc_host->data->mmc_caps; |
---|
291 | 372 | |
---|
292 | | - pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); |
---|
293 | | - if (IS_ERR(pltfm_host->clk)) { |
---|
294 | | - ret = PTR_ERR(pltfm_host->clk); |
---|
295 | | - goto err; |
---|
296 | | - } |
---|
297 | | - ret = clk_prepare_enable(pltfm_host->clk); |
---|
298 | | - if (ret) { |
---|
299 | | - dev_err(&pdev->dev, "failed to enable host clk\n"); |
---|
300 | | - goto err; |
---|
| 373 | + if (dev->of_node) { |
---|
| 374 | + pltfm_host->clk = devm_clk_get(dev, NULL); |
---|
| 375 | + if (IS_ERR(pltfm_host->clk)) { |
---|
| 376 | + ret = PTR_ERR(pltfm_host->clk); |
---|
| 377 | + goto err; |
---|
| 378 | + } |
---|
| 379 | + ret = clk_prepare_enable(pltfm_host->clk); |
---|
| 380 | + if (ret) { |
---|
| 381 | + dev_err(dev, "failed to enable host clk\n"); |
---|
| 382 | + goto err; |
---|
| 383 | + } |
---|
301 | 384 | } |
---|
302 | 385 | |
---|
303 | 386 | if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { |
---|
.. | .. |
---|
312 | 395 | return 0; |
---|
313 | 396 | |
---|
314 | 397 | err_clk: |
---|
315 | | - clk_disable_unprepare(pltfm_host->clk); |
---|
| 398 | + if (dev->of_node) |
---|
| 399 | + clk_disable_unprepare(pltfm_host->clk); |
---|
316 | 400 | err: |
---|
317 | 401 | sdhci_pltfm_free(pdev); |
---|
318 | 402 | return ret; |
---|
.. | .. |
---|
321 | 405 | static struct platform_driver sdhci_iproc_driver = { |
---|
322 | 406 | .driver = { |
---|
323 | 407 | .name = "sdhci-iproc", |
---|
| 408 | + .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
---|
324 | 409 | .of_match_table = sdhci_iproc_of_match, |
---|
| 410 | + .acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids), |
---|
325 | 411 | .pm = &sdhci_pltfm_pmops, |
---|
326 | 412 | }, |
---|
327 | 413 | .probe = sdhci_iproc_probe, |
---|