diff options
author | Kishon Vijay Abraham I <kishon@ti.com> | 2018-02-05 07:50:19 -0500 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2018-03-05 03:00:58 -0500 |
commit | 8d20b2eae6c47b09552364afa20511aa43f2367c (patch) | |
tree | 7bab335c864c71c4a149e782ccd3d804f53a367a | |
parent | 7d33c3581536b8193b9788a63a68e7bfc22cdc31 (diff) |
mmc: sdhci_omap: Add support to set IODELAY values
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r-- | drivers/mmc/host/sdhci-omap.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index e24ae903f7ba..428849387992 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/pm_runtime.h> | 26 | #include <linux/pm_runtime.h> |
27 | #include <linux/regulator/consumer.h> | 27 | #include <linux/regulator/consumer.h> |
28 | #include <linux/pinctrl/consumer.h> | ||
28 | 29 | ||
29 | #include "sdhci-pltfm.h" | 30 | #include "sdhci-pltfm.h" |
30 | 31 | ||
@@ -90,8 +91,12 @@ | |||
90 | 91 | ||
91 | #define MAX_PHASE_DELAY 0x7C | 92 | #define MAX_PHASE_DELAY 0x7C |
92 | 93 | ||
94 | /* sdhci-omap controller flags */ | ||
95 | #define SDHCI_OMAP_REQUIRE_IODELAY BIT(0) | ||
96 | |||
93 | struct sdhci_omap_data { | 97 | struct sdhci_omap_data { |
94 | u32 offset; | 98 | u32 offset; |
99 | u8 flags; | ||
95 | }; | 100 | }; |
96 | 101 | ||
97 | struct sdhci_omap_host { | 102 | struct sdhci_omap_host { |
@@ -102,8 +107,16 @@ struct sdhci_omap_host { | |||
102 | struct sdhci_host *host; | 107 | struct sdhci_host *host; |
103 | u8 bus_mode; | 108 | u8 bus_mode; |
104 | u8 power_mode; | 109 | u8 power_mode; |
110 | u8 timing; | ||
111 | u8 flags; | ||
112 | |||
113 | struct pinctrl *pinctrl; | ||
114 | struct pinctrl_state **pinctrl_state; | ||
105 | }; | 115 | }; |
106 | 116 | ||
117 | static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); | ||
118 | static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host); | ||
119 | |||
107 | static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host, | 120 | static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host, |
108 | unsigned int offset) | 121 | unsigned int offset) |
109 | { | 122 | { |
@@ -436,6 +449,31 @@ static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc, | |||
436 | return 0; | 449 | return 0; |
437 | } | 450 | } |
438 | 451 | ||
452 | static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing) | ||
453 | { | ||
454 | int ret; | ||
455 | struct pinctrl_state *pinctrl_state; | ||
456 | struct device *dev = omap_host->dev; | ||
457 | |||
458 | if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY)) | ||
459 | return; | ||
460 | |||
461 | if (omap_host->timing == timing) | ||
462 | return; | ||
463 | |||
464 | sdhci_omap_stop_clock(omap_host); | ||
465 | |||
466 | pinctrl_state = omap_host->pinctrl_state[timing]; | ||
467 | ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state); | ||
468 | if (ret) { | ||
469 | dev_err(dev, "failed to select pinctrl state\n"); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | sdhci_omap_start_clock(omap_host); | ||
474 | omap_host->timing = timing; | ||
475 | } | ||
476 | |||
439 | static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host, | 477 | static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host, |
440 | u8 power_mode) | 478 | u8 power_mode) |
441 | { | 479 | { |
@@ -472,6 +510,7 @@ static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
472 | omap_host = sdhci_pltfm_priv(pltfm_host); | 510 | omap_host = sdhci_pltfm_priv(pltfm_host); |
473 | 511 | ||
474 | sdhci_omap_set_bus_mode(omap_host, ios->bus_mode); | 512 | sdhci_omap_set_bus_mode(omap_host, ios->bus_mode); |
513 | sdhci_omap_set_timing(omap_host, ios->timing); | ||
475 | sdhci_set_ios(mmc, ios); | 514 | sdhci_set_ios(mmc, ios); |
476 | sdhci_omap_set_power_mode(omap_host, ios->power_mode); | 515 | sdhci_omap_set_power_mode(omap_host, ios->power_mode); |
477 | } | 516 | } |
@@ -680,6 +719,7 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = { | |||
680 | 719 | ||
681 | static const struct sdhci_omap_data dra7_data = { | 720 | static const struct sdhci_omap_data dra7_data = { |
682 | .offset = 0x200, | 721 | .offset = 0x200, |
722 | .flags = SDHCI_OMAP_REQUIRE_IODELAY, | ||
683 | }; | 723 | }; |
684 | 724 | ||
685 | static const struct of_device_id omap_sdhci_match[] = { | 725 | static const struct of_device_id omap_sdhci_match[] = { |
@@ -688,6 +728,108 @@ static const struct of_device_id omap_sdhci_match[] = { | |||
688 | }; | 728 | }; |
689 | MODULE_DEVICE_TABLE(of, omap_sdhci_match); | 729 | MODULE_DEVICE_TABLE(of, omap_sdhci_match); |
690 | 730 | ||
731 | static struct pinctrl_state | ||
732 | *sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode, | ||
733 | u32 *caps, u32 capmask) | ||
734 | { | ||
735 | struct device *dev = omap_host->dev; | ||
736 | struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV); | ||
737 | |||
738 | if (!(*caps & capmask)) | ||
739 | goto ret; | ||
740 | |||
741 | pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode); | ||
742 | if (IS_ERR(pinctrl_state)) { | ||
743 | dev_err(dev, "no pinctrl state for %s mode", mode); | ||
744 | *caps &= ~capmask; | ||
745 | } | ||
746 | |||
747 | ret: | ||
748 | return pinctrl_state; | ||
749 | } | ||
750 | |||
751 | static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host | ||
752 | *omap_host) | ||
753 | { | ||
754 | struct device *dev = omap_host->dev; | ||
755 | struct sdhci_host *host = omap_host->host; | ||
756 | struct mmc_host *mmc = host->mmc; | ||
757 | u32 *caps = &mmc->caps; | ||
758 | u32 *caps2 = &mmc->caps2; | ||
759 | struct pinctrl_state *state; | ||
760 | struct pinctrl_state **pinctrl_state; | ||
761 | |||
762 | if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY)) | ||
763 | return 0; | ||
764 | |||
765 | pinctrl_state = devm_kzalloc(dev, sizeof(*pinctrl_state) * | ||
766 | (MMC_TIMING_MMC_HS200 + 1), GFP_KERNEL); | ||
767 | if (!pinctrl_state) | ||
768 | return -ENOMEM; | ||
769 | |||
770 | omap_host->pinctrl = devm_pinctrl_get(omap_host->dev); | ||
771 | if (IS_ERR(omap_host->pinctrl)) { | ||
772 | dev_err(dev, "Cannot get pinctrl\n"); | ||
773 | return PTR_ERR(omap_host->pinctrl); | ||
774 | } | ||
775 | |||
776 | state = pinctrl_lookup_state(omap_host->pinctrl, "default"); | ||
777 | if (IS_ERR(state)) { | ||
778 | dev_err(dev, "no pinctrl state for default mode\n"); | ||
779 | return PTR_ERR(state); | ||
780 | } | ||
781 | pinctrl_state[MMC_TIMING_LEGACY] = state; | ||
782 | |||
783 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps, | ||
784 | MMC_CAP_UHS_SDR104); | ||
785 | if (!IS_ERR(state)) | ||
786 | pinctrl_state[MMC_TIMING_UHS_SDR104] = state; | ||
787 | |||
788 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps, | ||
789 | MMC_CAP_UHS_DDR50); | ||
790 | if (!IS_ERR(state)) | ||
791 | pinctrl_state[MMC_TIMING_UHS_DDR50] = state; | ||
792 | |||
793 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps, | ||
794 | MMC_CAP_UHS_SDR50); | ||
795 | if (!IS_ERR(state)) | ||
796 | pinctrl_state[MMC_TIMING_UHS_SDR50] = state; | ||
797 | |||
798 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps, | ||
799 | MMC_CAP_UHS_SDR25); | ||
800 | if (!IS_ERR(state)) | ||
801 | pinctrl_state[MMC_TIMING_UHS_SDR25] = state; | ||
802 | |||
803 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps, | ||
804 | MMC_CAP_UHS_SDR12); | ||
805 | if (!IS_ERR(state)) | ||
806 | pinctrl_state[MMC_TIMING_UHS_SDR12] = state; | ||
807 | |||
808 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps, | ||
809 | MMC_CAP_1_8V_DDR); | ||
810 | if (!IS_ERR(state)) | ||
811 | pinctrl_state[MMC_TIMING_MMC_DDR52] = state; | ||
812 | |||
813 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, | ||
814 | MMC_CAP_SD_HIGHSPEED); | ||
815 | if (!IS_ERR(state)) | ||
816 | pinctrl_state[MMC_TIMING_SD_HS] = state; | ||
817 | |||
818 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, | ||
819 | MMC_CAP_MMC_HIGHSPEED); | ||
820 | if (!IS_ERR(state)) | ||
821 | pinctrl_state[MMC_TIMING_MMC_HS] = state; | ||
822 | |||
823 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2, | ||
824 | MMC_CAP2_HS200_1_8V_SDR); | ||
825 | if (!IS_ERR(state)) | ||
826 | pinctrl_state[MMC_TIMING_MMC_HS200] = state; | ||
827 | |||
828 | omap_host->pinctrl_state = pinctrl_state; | ||
829 | |||
830 | return 0; | ||
831 | } | ||
832 | |||
691 | static int sdhci_omap_probe(struct platform_device *pdev) | 833 | static int sdhci_omap_probe(struct platform_device *pdev) |
692 | { | 834 | { |
693 | int ret; | 835 | int ret; |
@@ -724,6 +866,8 @@ static int sdhci_omap_probe(struct platform_device *pdev) | |||
724 | omap_host->base = host->ioaddr; | 866 | omap_host->base = host->ioaddr; |
725 | omap_host->dev = dev; | 867 | omap_host->dev = dev; |
726 | omap_host->power_mode = MMC_POWER_UNDEFINED; | 868 | omap_host->power_mode = MMC_POWER_UNDEFINED; |
869 | omap_host->timing = MMC_TIMING_LEGACY; | ||
870 | omap_host->flags = data->flags; | ||
727 | host->ioaddr += offset; | 871 | host->ioaddr += offset; |
728 | 872 | ||
729 | mmc = host->mmc; | 873 | mmc = host->mmc; |
@@ -772,6 +916,10 @@ static int sdhci_omap_probe(struct platform_device *pdev) | |||
772 | goto err_put_sync; | 916 | goto err_put_sync; |
773 | } | 917 | } |
774 | 918 | ||
919 | ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host); | ||
920 | if (ret) | ||
921 | goto err_put_sync; | ||
922 | |||
775 | host->mmc_host_ops.get_ro = mmc_gpio_get_ro; | 923 | host->mmc_host_ops.get_ro = mmc_gpio_get_ro; |
776 | host->mmc_host_ops.start_signal_voltage_switch = | 924 | host->mmc_host_ops.start_signal_voltage_switch = |
777 | sdhci_omap_start_signal_voltage_switch; | 925 | sdhci_omap_start_signal_voltage_switch; |