Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: sdhci-s3c: Choose sdhci_ops based on variant

The difference between old S3C64xx and newer Exynos4 SDHCI controller
variants is in clock handling (the "no_divider" field in drvdata).
Choose the proper sdhci_ops based on the variant instead of patching
ops in probe, if Exynos4 is used.

This allows making struct sdhci_ops const for code safety and probably
opens further options in the future, as the dynamic pointer ops table is
not anymore that dynamic.

Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/20240414-mmc-const-sdhci-ops-v2-5-262f81faadac@kernel.org
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Krzysztof Kozlowski and committed by
Ulf Hansson
e4c02779 24922c1a

+17 -14
+17 -14
drivers/mmc/host/sdhci-s3c.c
··· 130 130 * struct sdhci_s3c_drv_data - S3C SDHCI platform specific driver data 131 131 * @sdhci_quirks: sdhci host specific quirks. 132 132 * @no_divider: no or non-standard internal clock divider. 133 + * @ops: sdhci_ops to use for this variant 133 134 * 134 135 * Specifies platform specific configuration of sdhci controller. 135 136 * Note: A structure for driver specific platform data is used for future 136 137 * expansion of its usage. 137 138 */ 138 139 struct sdhci_s3c_drv_data { 139 - unsigned int sdhci_quirks; 140 - bool no_divider; 140 + unsigned int sdhci_quirks; 141 + bool no_divider; 142 + const struct sdhci_ops *ops; 141 143 }; 142 144 143 145 static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) ··· 414 412 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 415 413 } 416 414 417 - static struct sdhci_ops sdhci_s3c_ops = { 415 + static const struct sdhci_ops sdhci_s3c_ops_s3c6410 = { 418 416 .get_max_clock = sdhci_s3c_get_max_clk, 419 417 .set_clock = sdhci_s3c_set_clock, 420 418 .get_min_clock = sdhci_s3c_get_min_clock, 419 + .set_bus_width = sdhci_set_bus_width, 420 + .reset = sdhci_reset, 421 + .set_uhs_signaling = sdhci_set_uhs_signaling, 422 + }; 423 + 424 + static const struct sdhci_ops sdhci_s3c_ops_exynos4 __maybe_unused = { 425 + .get_max_clock = sdhci_cmu_get_max_clock, 426 + .set_clock = sdhci_cmu_set_clock, 427 + .get_min_clock = sdhci_cmu_get_min_clock, 421 428 .set_bus_width = sdhci_set_bus_width, 422 429 .reset = sdhci_reset, 423 430 .set_uhs_signaling = sdhci_set_uhs_signaling, ··· 571 560 pdata->cfg_gpio(pdev, pdata->max_width); 572 561 573 562 host->hw_name = "samsung-hsmmc"; 574 - host->ops = &sdhci_s3c_ops; 563 + host->ops = &sdhci_s3c_ops_s3c6410; 575 564 host->quirks = 0; 576 565 host->quirks2 = 0; 577 566 host->irq = irq; ··· 581 570 host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; 582 571 if (drv_data) { 583 572 host->quirks |= drv_data->sdhci_quirks; 573 + host->ops = drv_data->ops; 584 574 sc->no_divider = drv_data->no_divider; 585 575 } 586 576 ··· 628 616 629 617 /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ 630 618 host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; 631 - 632 - /* 633 - * If controller does not have internal clock divider, 634 - * we can use overriding functions instead of default. 635 - */ 636 - if (sc->no_divider) { 637 - sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; 638 - sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; 639 - sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; 640 - } 641 619 642 620 /* It supports additional host capabilities if needed */ 643 621 if (pdata->host_caps) ··· 760 758 #ifdef CONFIG_OF 761 759 static const struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { 762 760 .no_divider = true, 761 + .ops = &sdhci_s3c_ops_exynos4, 763 762 }; 764 763 765 764 static const struct of_device_id sdhci_s3c_dt_match[] = {