Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
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");