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