1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
7 #include <linux/mfd/syscon.h>
8 #include <linux/mmc/host.h>
9 #include <linux/module.h>
10 #include <linux/of_address.h>
11 #include <linux/platform_device.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/regmap.h>
14 #include <linux/regulator/consumer.h>
17 #include "dw_mmc-pltfm.h"
19 #define ALL_INT_CLR 0x1ffff
21 struct hi3798cv200_priv
{
22 struct clk
*sample_clk
;
23 struct clk
*drive_clk
;
26 static unsigned long dw_mci_hi3798cv200_caps
[] = {
32 static void dw_mci_hi3798cv200_set_ios(struct dw_mci
*host
, struct mmc_ios
*ios
)
34 struct hi3798cv200_priv
*priv
= host
->priv
;
37 val
= mci_readl(host
, UHS_REG
);
38 if (ios
->timing
== MMC_TIMING_MMC_DDR52
||
39 ios
->timing
== MMC_TIMING_UHS_DDR50
)
42 val
&= ~SDMMC_UHS_DDR
;
43 mci_writel(host
, UHS_REG
, val
);
45 val
= mci_readl(host
, ENABLE_SHIFT
);
46 if (ios
->timing
== MMC_TIMING_MMC_DDR52
)
47 val
|= SDMMC_ENABLE_PHASE
;
49 val
&= ~SDMMC_ENABLE_PHASE
;
50 mci_writel(host
, ENABLE_SHIFT
, val
);
52 val
= mci_readl(host
, DDR_REG
);
53 if (ios
->timing
== MMC_TIMING_MMC_HS400
)
54 val
|= SDMMC_DDR_HS400
;
56 val
&= ~SDMMC_DDR_HS400
;
57 mci_writel(host
, DDR_REG
, val
);
59 if (ios
->timing
== MMC_TIMING_MMC_HS
||
60 ios
->timing
== MMC_TIMING_LEGACY
)
61 clk_set_phase(priv
->drive_clk
, 180);
62 else if (ios
->timing
== MMC_TIMING_MMC_HS200
)
63 clk_set_phase(priv
->drive_clk
, 135);
66 static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot
*slot
,
69 int degrees
[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
70 struct dw_mci
*host
= slot
->host
;
71 struct hi3798cv200_priv
*priv
= host
->priv
;
72 int raise_point
= -1, fall_point
= -1;
73 int err
, prev_err
= -1;
77 for (i
= 0; i
< ARRAY_SIZE(degrees
); i
++) {
78 clk_set_phase(priv
->sample_clk
, degrees
[i
]);
79 mci_writel(host
, RINTSTS
, ALL_INT_CLR
);
81 err
= mmc_send_tuning(slot
->mmc
, opcode
, NULL
);
92 if (raise_point
!= -1 && fall_point
!= -1)
101 if (raise_point
== -1)
103 if (fall_point
== -1)
104 fall_point
= ARRAY_SIZE(degrees
) - 1;
105 if (fall_point
< raise_point
) {
106 if ((raise_point
+ fall_point
) >
107 (ARRAY_SIZE(degrees
) - 1))
110 i
= (raise_point
+ ARRAY_SIZE(degrees
) - 1) / 2;
112 i
= (raise_point
+ fall_point
) / 2;
115 clk_set_phase(priv
->sample_clk
, degrees
[i
]);
116 dev_dbg(host
->dev
, "Tuning clk_sample[%d, %d], set[%d]\n",
117 raise_point
, fall_point
, degrees
[i
]);
119 dev_err(host
->dev
, "No valid clk_sample shift! use default\n");
123 mci_writel(host
, RINTSTS
, ALL_INT_CLR
);
127 static int dw_mci_hi3798cv200_init(struct dw_mci
*host
)
129 struct hi3798cv200_priv
*priv
;
132 priv
= devm_kzalloc(host
->dev
, sizeof(*priv
), GFP_KERNEL
);
136 priv
->sample_clk
= devm_clk_get(host
->dev
, "ciu-sample");
137 if (IS_ERR(priv
->sample_clk
)) {
138 dev_err(host
->dev
, "failed to get ciu-sample clock\n");
139 return PTR_ERR(priv
->sample_clk
);
142 priv
->drive_clk
= devm_clk_get(host
->dev
, "ciu-drive");
143 if (IS_ERR(priv
->drive_clk
)) {
144 dev_err(host
->dev
, "failed to get ciu-drive clock\n");
145 return PTR_ERR(priv
->drive_clk
);
148 ret
= clk_prepare_enable(priv
->sample_clk
);
150 dev_err(host
->dev
, "failed to enable ciu-sample clock\n");
154 ret
= clk_prepare_enable(priv
->drive_clk
);
156 dev_err(host
->dev
, "failed to enable ciu-drive clock\n");
157 goto disable_sample_clk
;
164 clk_disable_unprepare(priv
->sample_clk
);
168 static const struct dw_mci_drv_data hi3798cv200_data
= {
169 .caps
= dw_mci_hi3798cv200_caps
,
170 .num_caps
= ARRAY_SIZE(dw_mci_hi3798cv200_caps
),
171 .init
= dw_mci_hi3798cv200_init
,
172 .set_ios
= dw_mci_hi3798cv200_set_ios
,
173 .execute_tuning
= dw_mci_hi3798cv200_execute_tuning
,
176 static int dw_mci_hi3798cv200_probe(struct platform_device
*pdev
)
178 return dw_mci_pltfm_register(pdev
, &hi3798cv200_data
);
181 static int dw_mci_hi3798cv200_remove(struct platform_device
*pdev
)
183 struct dw_mci
*host
= platform_get_drvdata(pdev
);
184 struct hi3798cv200_priv
*priv
= host
->priv
;
186 clk_disable_unprepare(priv
->drive_clk
);
187 clk_disable_unprepare(priv
->sample_clk
);
189 return dw_mci_pltfm_remove(pdev
);
192 static const struct of_device_id dw_mci_hi3798cv200_match
[] = {
193 { .compatible
= "hisilicon,hi3798cv200-dw-mshc", },
197 MODULE_DEVICE_TABLE(of
, dw_mci_hi3798cv200_match
);
198 static struct platform_driver dw_mci_hi3798cv200_driver
= {
199 .probe
= dw_mci_hi3798cv200_probe
,
200 .remove
= dw_mci_hi3798cv200_remove
,
202 .name
= "dwmmc_hi3798cv200",
203 .of_match_table
= dw_mci_hi3798cv200_match
,
206 module_platform_driver(dw_mci_hi3798cv200_driver
);
208 MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
209 MODULE_LICENSE("GPL v2");
210 MODULE_ALIAS("platform:dwmmc_hi3798cv200");