0001
0002
0003
0004
0005
0006 #include <linux/clk.h>
0007 #include <linux/mfd/syscon.h>
0008 #include <linux/mmc/host.h>
0009 #include <linux/module.h>
0010 #include <linux/of_address.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/pm_runtime.h>
0013 #include <linux/regmap.h>
0014 #include <linux/regulator/consumer.h>
0015
0016 #include "dw_mmc.h"
0017 #include "dw_mmc-pltfm.h"
0018
0019 #define ALL_INT_CLR 0x1ffff
0020
0021 struct hi3798cv200_priv {
0022 struct clk *sample_clk;
0023 struct clk *drive_clk;
0024 };
0025
0026 static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
0027 {
0028 struct hi3798cv200_priv *priv = host->priv;
0029 u32 val;
0030
0031 val = mci_readl(host, UHS_REG);
0032 if (ios->timing == MMC_TIMING_MMC_DDR52 ||
0033 ios->timing == MMC_TIMING_UHS_DDR50)
0034 val |= SDMMC_UHS_DDR;
0035 else
0036 val &= ~SDMMC_UHS_DDR;
0037 mci_writel(host, UHS_REG, val);
0038
0039 val = mci_readl(host, ENABLE_SHIFT);
0040 if (ios->timing == MMC_TIMING_MMC_DDR52)
0041 val |= SDMMC_ENABLE_PHASE;
0042 else
0043 val &= ~SDMMC_ENABLE_PHASE;
0044 mci_writel(host, ENABLE_SHIFT, val);
0045
0046 val = mci_readl(host, DDR_REG);
0047 if (ios->timing == MMC_TIMING_MMC_HS400)
0048 val |= SDMMC_DDR_HS400;
0049 else
0050 val &= ~SDMMC_DDR_HS400;
0051 mci_writel(host, DDR_REG, val);
0052
0053 if (ios->timing == MMC_TIMING_MMC_HS ||
0054 ios->timing == MMC_TIMING_LEGACY)
0055 clk_set_phase(priv->drive_clk, 180);
0056 else if (ios->timing == MMC_TIMING_MMC_HS200)
0057 clk_set_phase(priv->drive_clk, 135);
0058 }
0059
0060 static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot,
0061 u32 opcode)
0062 {
0063 static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
0064 struct dw_mci *host = slot->host;
0065 struct hi3798cv200_priv *priv = host->priv;
0066 int raise_point = -1, fall_point = -1;
0067 int err, prev_err = -1;
0068 int found = 0;
0069 int i;
0070
0071 for (i = 0; i < ARRAY_SIZE(degrees); i++) {
0072 clk_set_phase(priv->sample_clk, degrees[i]);
0073 mci_writel(host, RINTSTS, ALL_INT_CLR);
0074
0075 err = mmc_send_tuning(slot->mmc, opcode, NULL);
0076 if (!err)
0077 found = 1;
0078
0079 if (i > 0) {
0080 if (err && !prev_err)
0081 fall_point = i - 1;
0082 if (!err && prev_err)
0083 raise_point = i;
0084 }
0085
0086 if (raise_point != -1 && fall_point != -1)
0087 goto tuning_out;
0088
0089 prev_err = err;
0090 err = 0;
0091 }
0092
0093 tuning_out:
0094 if (found) {
0095 if (raise_point == -1)
0096 raise_point = 0;
0097 if (fall_point == -1)
0098 fall_point = ARRAY_SIZE(degrees) - 1;
0099 if (fall_point < raise_point) {
0100 if ((raise_point + fall_point) >
0101 (ARRAY_SIZE(degrees) - 1))
0102 i = fall_point / 2;
0103 else
0104 i = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
0105 } else {
0106 i = (raise_point + fall_point) / 2;
0107 }
0108
0109 clk_set_phase(priv->sample_clk, degrees[i]);
0110 dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
0111 raise_point, fall_point, degrees[i]);
0112 } else {
0113 dev_err(host->dev, "No valid clk_sample shift! use default\n");
0114 err = -EINVAL;
0115 }
0116
0117 mci_writel(host, RINTSTS, ALL_INT_CLR);
0118 return err;
0119 }
0120
0121 static int dw_mci_hi3798cv200_init(struct dw_mci *host)
0122 {
0123 struct hi3798cv200_priv *priv;
0124 int ret;
0125
0126 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
0127 if (!priv)
0128 return -ENOMEM;
0129
0130 priv->sample_clk = devm_clk_get(host->dev, "ciu-sample");
0131 if (IS_ERR(priv->sample_clk)) {
0132 dev_err(host->dev, "failed to get ciu-sample clock\n");
0133 return PTR_ERR(priv->sample_clk);
0134 }
0135
0136 priv->drive_clk = devm_clk_get(host->dev, "ciu-drive");
0137 if (IS_ERR(priv->drive_clk)) {
0138 dev_err(host->dev, "failed to get ciu-drive clock\n");
0139 return PTR_ERR(priv->drive_clk);
0140 }
0141
0142 ret = clk_prepare_enable(priv->sample_clk);
0143 if (ret) {
0144 dev_err(host->dev, "failed to enable ciu-sample clock\n");
0145 return ret;
0146 }
0147
0148 ret = clk_prepare_enable(priv->drive_clk);
0149 if (ret) {
0150 dev_err(host->dev, "failed to enable ciu-drive clock\n");
0151 goto disable_sample_clk;
0152 }
0153
0154 host->priv = priv;
0155 return 0;
0156
0157 disable_sample_clk:
0158 clk_disable_unprepare(priv->sample_clk);
0159 return ret;
0160 }
0161
0162 static const struct dw_mci_drv_data hi3798cv200_data = {
0163 .common_caps = MMC_CAP_CMD23,
0164 .init = dw_mci_hi3798cv200_init,
0165 .set_ios = dw_mci_hi3798cv200_set_ios,
0166 .execute_tuning = dw_mci_hi3798cv200_execute_tuning,
0167 };
0168
0169 static int dw_mci_hi3798cv200_probe(struct platform_device *pdev)
0170 {
0171 return dw_mci_pltfm_register(pdev, &hi3798cv200_data);
0172 }
0173
0174 static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
0175 {
0176 struct dw_mci *host = platform_get_drvdata(pdev);
0177 struct hi3798cv200_priv *priv = host->priv;
0178
0179 clk_disable_unprepare(priv->drive_clk);
0180 clk_disable_unprepare(priv->sample_clk);
0181
0182 dw_mci_pltfm_remove(pdev);
0183
0184 return 0;
0185 }
0186
0187 static const struct of_device_id dw_mci_hi3798cv200_match[] = {
0188 { .compatible = "hisilicon,hi3798cv200-dw-mshc", },
0189 {},
0190 };
0191
0192 MODULE_DEVICE_TABLE(of, dw_mci_hi3798cv200_match);
0193 static struct platform_driver dw_mci_hi3798cv200_driver = {
0194 .probe = dw_mci_hi3798cv200_probe,
0195 .remove = dw_mci_hi3798cv200_remove,
0196 .driver = {
0197 .name = "dwmmc_hi3798cv200",
0198 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0199 .of_match_table = dw_mci_hi3798cv200_match,
0200 },
0201 };
0202 module_platform_driver(dw_mci_hi3798cv200_driver);
0203
0204 MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
0205 MODULE_LICENSE("GPL v2");
0206 MODULE_ALIAS("platform:dwmmc_hi3798cv200");