diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-acpi.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-acpi.c | 174 |
1 files changed, 129 insertions, 45 deletions
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 08ae0ff13513..b988997a1e80 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c | |||
| @@ -73,6 +73,7 @@ struct sdhci_acpi_slot { | |||
| 73 | unsigned int caps2; | 73 | unsigned int caps2; |
| 74 | mmc_pm_flag_t pm_caps; | 74 | mmc_pm_flag_t pm_caps; |
| 75 | unsigned int flags; | 75 | unsigned int flags; |
| 76 | size_t priv_size; | ||
| 76 | int (*probe_slot)(struct platform_device *, const char *, const char *); | 77 | int (*probe_slot)(struct platform_device *, const char *, const char *); |
| 77 | int (*remove_slot)(struct platform_device *); | 78 | int (*remove_slot)(struct platform_device *); |
| 78 | }; | 79 | }; |
| @@ -82,13 +83,118 @@ struct sdhci_acpi_host { | |||
| 82 | const struct sdhci_acpi_slot *slot; | 83 | const struct sdhci_acpi_slot *slot; |
| 83 | struct platform_device *pdev; | 84 | struct platform_device *pdev; |
| 84 | bool use_runtime_pm; | 85 | bool use_runtime_pm; |
| 86 | unsigned long private[0] ____cacheline_aligned; | ||
| 85 | }; | 87 | }; |
| 86 | 88 | ||
| 89 | static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c) | ||
| 90 | { | ||
| 91 | return (void *)c->private; | ||
| 92 | } | ||
| 93 | |||
| 87 | static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) | 94 | static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) |
| 88 | { | 95 | { |
| 89 | return c->slot && (c->slot->flags & flag); | 96 | return c->slot && (c->slot->flags & flag); |
| 90 | } | 97 | } |
| 91 | 98 | ||
| 99 | enum { | ||
| 100 | INTEL_DSM_FNS = 0, | ||
| 101 | INTEL_DSM_V18_SWITCH = 3, | ||
| 102 | INTEL_DSM_V33_SWITCH = 4, | ||
| 103 | }; | ||
| 104 | |||
| 105 | struct intel_host { | ||
| 106 | u32 dsm_fns; | ||
| 107 | }; | ||
| 108 | |||
| 109 | static const guid_t intel_dsm_guid = | ||
| 110 | GUID_INIT(0xF6C13EA5, 0x65CD, 0x461F, | ||
| 111 | 0xAB, 0x7A, 0x29, 0xF7, 0xE8, 0xD5, 0xBD, 0x61); | ||
| 112 | |||
| 113 | static int __intel_dsm(struct intel_host *intel_host, struct device *dev, | ||
| 114 | unsigned int fn, u32 *result) | ||
| 115 | { | ||
| 116 | union acpi_object *obj; | ||
| 117 | int err = 0; | ||
| 118 | |||
| 119 | obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL); | ||
| 120 | if (!obj) | ||
| 121 | return -EOPNOTSUPP; | ||
| 122 | |||
| 123 | if (obj->type == ACPI_TYPE_INTEGER) { | ||
| 124 | *result = obj->integer.value; | ||
| 125 | } else if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length > 0) { | ||
| 126 | size_t len = min_t(size_t, obj->buffer.length, 4); | ||
| 127 | |||
| 128 | *result = 0; | ||
| 129 | memcpy(result, obj->buffer.pointer, len); | ||
| 130 | } else { | ||
| 131 | dev_err(dev, "%s DSM fn %u obj->type %d obj->buffer.length %d\n", | ||
| 132 | __func__, fn, obj->type, obj->buffer.length); | ||
| 133 | err = -EINVAL; | ||
| 134 | } | ||
| 135 | |||
| 136 | ACPI_FREE(obj); | ||
| 137 | |||
| 138 | return err; | ||
| 139 | } | ||
| 140 | |||
| 141 | static int intel_dsm(struct intel_host *intel_host, struct device *dev, | ||
| 142 | unsigned int fn, u32 *result) | ||
| 143 | { | ||
| 144 | if (fn > 31 || !(intel_host->dsm_fns & (1 << fn))) | ||
| 145 | return -EOPNOTSUPP; | ||
| 146 | |||
| 147 | return __intel_dsm(intel_host, dev, fn, result); | ||
| 148 | } | ||
| 149 | |||
| 150 | static void intel_dsm_init(struct intel_host *intel_host, struct device *dev, | ||
| 151 | struct mmc_host *mmc) | ||
| 152 | { | ||
| 153 | int err; | ||
| 154 | |||
| 155 | err = __intel_dsm(intel_host, dev, INTEL_DSM_FNS, &intel_host->dsm_fns); | ||
| 156 | if (err) { | ||
| 157 | pr_debug("%s: DSM not supported, error %d\n", | ||
| 158 | mmc_hostname(mmc), err); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | |||
| 162 | pr_debug("%s: DSM function mask %#x\n", | ||
| 163 | mmc_hostname(mmc), intel_host->dsm_fns); | ||
| 164 | } | ||
| 165 | |||
| 166 | static int intel_start_signal_voltage_switch(struct mmc_host *mmc, | ||
| 167 | struct mmc_ios *ios) | ||
| 168 | { | ||
| 169 | struct device *dev = mmc_dev(mmc); | ||
| 170 | struct sdhci_acpi_host *c = dev_get_drvdata(dev); | ||
| 171 | struct intel_host *intel_host = sdhci_acpi_priv(c); | ||
| 172 | unsigned int fn; | ||
| 173 | u32 result = 0; | ||
| 174 | int err; | ||
| 175 | |||
| 176 | err = sdhci_start_signal_voltage_switch(mmc, ios); | ||
| 177 | if (err) | ||
| 178 | return err; | ||
| 179 | |||
| 180 | switch (ios->signal_voltage) { | ||
| 181 | case MMC_SIGNAL_VOLTAGE_330: | ||
| 182 | fn = INTEL_DSM_V33_SWITCH; | ||
| 183 | break; | ||
| 184 | case MMC_SIGNAL_VOLTAGE_180: | ||
| 185 | fn = INTEL_DSM_V18_SWITCH; | ||
| 186 | break; | ||
| 187 | default: | ||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | err = intel_dsm(intel_host, dev, fn, &result); | ||
| 192 | pr_debug("%s: %s DSM fn %u error %d result %u\n", | ||
| 193 | mmc_hostname(mmc), __func__, fn, err, result); | ||
| 194 | |||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 92 | static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) | 198 | static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) |
| 93 | { | 199 | { |
| 94 | u8 reg; | 200 | u8 reg; |
| @@ -269,56 +375,26 @@ out: | |||
| 269 | return ret; | 375 | return ret; |
| 270 | } | 376 | } |
| 271 | 377 | ||
| 272 | static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, | 378 | static int intel_probe_slot(struct platform_device *pdev, const char *hid, |
| 273 | const char *hid, const char *uid) | 379 | const char *uid) |
| 274 | { | 380 | { |
| 275 | struct sdhci_acpi_host *c = platform_get_drvdata(pdev); | 381 | struct sdhci_acpi_host *c = platform_get_drvdata(pdev); |
| 276 | struct sdhci_host *host; | 382 | struct intel_host *intel_host = sdhci_acpi_priv(c); |
| 277 | 383 | struct sdhci_host *host = c->host; | |
| 278 | if (!c || !c->host) | ||
| 279 | return 0; | ||
| 280 | |||
| 281 | host = c->host; | ||
| 282 | |||
| 283 | /* Platform specific code during emmc probe slot goes here */ | ||
| 284 | 384 | ||
| 285 | if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") && | 385 | if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") && |
| 286 | sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 && | 386 | sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 && |
| 287 | sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807) | 387 | sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807) |
| 288 | host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ | 388 | host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ |
| 289 | 389 | ||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev, | ||
| 294 | const char *hid, const char *uid) | ||
| 295 | { | ||
| 296 | struct sdhci_acpi_host *c = platform_get_drvdata(pdev); | ||
| 297 | |||
| 298 | if (!c || !c->host) | ||
| 299 | return 0; | ||
| 300 | |||
| 301 | /* Platform specific code during sdio probe slot goes here */ | ||
| 302 | |||
| 303 | return 0; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, | ||
| 307 | const char *hid, const char *uid) | ||
| 308 | { | ||
| 309 | struct sdhci_acpi_host *c = platform_get_drvdata(pdev); | ||
| 310 | struct sdhci_host *host; | ||
| 311 | |||
| 312 | if (!c || !c->host || !c->slot) | ||
| 313 | return 0; | ||
| 314 | |||
| 315 | host = c->host; | ||
| 316 | |||
| 317 | /* Platform specific code during sd probe slot goes here */ | ||
| 318 | |||
| 319 | if (hid && !strcmp(hid, "80865ACA")) | 390 | if (hid && !strcmp(hid, "80865ACA")) |
| 320 | host->mmc_host_ops.get_cd = bxt_get_cd; | 391 | host->mmc_host_ops.get_cd = bxt_get_cd; |
| 321 | 392 | ||
| 393 | intel_dsm_init(intel_host, &pdev->dev, host->mmc); | ||
| 394 | |||
| 395 | host->mmc_host_ops.start_signal_voltage_switch = | ||
| 396 | intel_start_signal_voltage_switch; | ||
| 397 | |||
| 322 | return 0; | 398 | return 0; |
| 323 | } | 399 | } |
| 324 | 400 | ||
| @@ -332,7 +408,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { | |||
| 332 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | | 408 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | |
| 333 | SDHCI_QUIRK2_STOP_WITH_TC | | 409 | SDHCI_QUIRK2_STOP_WITH_TC | |
| 334 | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, | 410 | SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400, |
| 335 | .probe_slot = sdhci_acpi_emmc_probe_slot, | 411 | .probe_slot = intel_probe_slot, |
| 412 | .priv_size = sizeof(struct intel_host), | ||
| 336 | }; | 413 | }; |
| 337 | 414 | ||
| 338 | static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { | 415 | static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { |
| @@ -343,7 +420,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { | |||
| 343 | MMC_CAP_WAIT_WHILE_BUSY, | 420 | MMC_CAP_WAIT_WHILE_BUSY, |
| 344 | .flags = SDHCI_ACPI_RUNTIME_PM, | 421 | .flags = SDHCI_ACPI_RUNTIME_PM, |
| 345 | .pm_caps = MMC_PM_KEEP_POWER, | 422 | .pm_caps = MMC_PM_KEEP_POWER, |
| 346 | .probe_slot = sdhci_acpi_sdio_probe_slot, | 423 | .probe_slot = intel_probe_slot, |
| 424 | .priv_size = sizeof(struct intel_host), | ||
| 347 | }; | 425 | }; |
| 348 | 426 | ||
| 349 | static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { | 427 | static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { |
| @@ -353,7 +431,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { | |||
| 353 | .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | | 431 | .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | |
| 354 | SDHCI_QUIRK2_STOP_WITH_TC, | 432 | SDHCI_QUIRK2_STOP_WITH_TC, |
| 355 | .caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM, | 433 | .caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM, |
| 356 | .probe_slot = sdhci_acpi_sd_probe_slot, | 434 | .probe_slot = intel_probe_slot, |
| 435 | .priv_size = sizeof(struct intel_host), | ||
| 357 | }; | 436 | }; |
| 358 | 437 | ||
| 359 | static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = { | 438 | static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = { |
| @@ -429,11 +508,13 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid, | |||
| 429 | static int sdhci_acpi_probe(struct platform_device *pdev) | 508 | static int sdhci_acpi_probe(struct platform_device *pdev) |
| 430 | { | 509 | { |
| 431 | struct device *dev = &pdev->dev; | 510 | struct device *dev = &pdev->dev; |
| 511 | const struct sdhci_acpi_slot *slot; | ||
| 432 | struct acpi_device *device, *child; | 512 | struct acpi_device *device, *child; |
| 433 | struct sdhci_acpi_host *c; | 513 | struct sdhci_acpi_host *c; |
| 434 | struct sdhci_host *host; | 514 | struct sdhci_host *host; |
| 435 | struct resource *iomem; | 515 | struct resource *iomem; |
| 436 | resource_size_t len; | 516 | resource_size_t len; |
| 517 | size_t priv_size; | ||
| 437 | const char *hid; | 518 | const char *hid; |
| 438 | const char *uid; | 519 | const char *uid; |
| 439 | int err; | 520 | int err; |
| @@ -443,7 +524,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev) | |||
| 443 | return -ENODEV; | 524 | return -ENODEV; |
| 444 | 525 | ||
| 445 | hid = acpi_device_hid(device); | 526 | hid = acpi_device_hid(device); |
| 446 | uid = device->pnp.unique_id; | 527 | uid = acpi_device_uid(device); |
| 528 | |||
| 529 | slot = sdhci_acpi_get_slot(hid, uid); | ||
| 447 | 530 | ||
| 448 | /* Power on the SDHCI controller and its children */ | 531 | /* Power on the SDHCI controller and its children */ |
| 449 | acpi_device_fix_up_power(device); | 532 | acpi_device_fix_up_power(device); |
| @@ -467,13 +550,14 @@ static int sdhci_acpi_probe(struct platform_device *pdev) | |||
| 467 | if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) | 550 | if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) |
| 468 | return -ENOMEM; | 551 | return -ENOMEM; |
| 469 | 552 | ||
| 470 | host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); | 553 | priv_size = slot ? slot->priv_size : 0; |
| 554 | host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host) + priv_size); | ||
| 471 | if (IS_ERR(host)) | 555 | if (IS_ERR(host)) |
| 472 | return PTR_ERR(host); | 556 | return PTR_ERR(host); |
| 473 | 557 | ||
| 474 | c = sdhci_priv(host); | 558 | c = sdhci_priv(host); |
| 475 | c->host = host; | 559 | c->host = host; |
| 476 | c->slot = sdhci_acpi_get_slot(hid, uid); | 560 | c->slot = slot; |
| 477 | c->pdev = pdev; | 561 | c->pdev = pdev; |
| 478 | c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); | 562 | c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); |
| 479 | 563 | ||
