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 void dw_mci_hi3798cv200_set_ios(struct dw_mci
*host
, struct mmc_ios
*ios
)
28 struct hi3798cv200_priv
*priv
= host
->priv
;
31 val
= mci_readl(host
, UHS_REG
);
32 if (ios
->timing
== MMC_TIMING_MMC_DDR52
||
33 ios
->timing
== MMC_TIMING_UHS_DDR50
)
36 val
&= ~SDMMC_UHS_DDR
;
37 mci_writel(host
, UHS_REG
, val
);
39 val
= mci_readl(host
, ENABLE_SHIFT
);
40 if (ios
->timing
== MMC_TIMING_MMC_DDR52
)
41 val
|= SDMMC_ENABLE_PHASE
;
43 val
&= ~SDMMC_ENABLE_PHASE
;
44 mci_writel(host
, ENABLE_SHIFT
, val
);
46 val
= mci_readl(host
, DDR_REG
);
47 if (ios
->timing
== MMC_TIMING_MMC_HS400
)
48 val
|= SDMMC_DDR_HS400
;
50 val
&= ~SDMMC_DDR_HS400
;
51 mci_writel(host
, DDR_REG
, val
);
53 if (ios
->timing
== MMC_TIMING_MMC_HS
||
54 ios
->timing
== MMC_TIMING_LEGACY
)
55 clk_set_phase(priv
->drive_clk
, 180);
56 else if (ios
->timing
== MMC_TIMING_MMC_HS200
)
57 clk_set_phase(priv
->drive_clk
, 135);
60 static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot
*slot
,
63 int degrees
[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
64 struct dw_mci
*host
= slot
->host
;
65 struct hi3798cv200_priv
*priv
= host
->priv
;
66 int raise_point
= -1, fall_point
= -1;
67 int err
, prev_err
= -1;
71 for (i
= 0; i
< ARRAY_SIZE(degrees
); i
++) {
72 clk_set_phase(priv
->sample_clk
, degrees
[i
]);
73 mci_writel(host
, RINTSTS
, ALL_INT_CLR
);
75 err
= mmc_send_tuning(slot
->mmc
, opcode
, NULL
);
86 if (raise_point
!= -1 && fall_point
!= -1)
95 if (raise_point
== -1)
98 fall_point
= ARRAY_SIZE(degrees
) - 1;
99 if (fall_point
< raise_point
) {
100 if ((raise_point
+ fall_point
) >
101 (ARRAY_SIZE(degrees
) - 1))
104 i
= (raise_point
+ ARRAY_SIZE(degrees
) - 1) / 2;
106 i
= (raise_point
+ fall_point
) / 2;
109 clk_set_phase(priv
->sample_clk
, degrees
[i
]);
110 dev_dbg(host
->dev
, "Tuning clk_sample[%d, %d], set[%d]\n",
111 raise_point
, fall_point
, degrees
[i
]);
113 dev_err(host
->dev
, "No valid clk_sample shift! use default\n");
117 mci_writel(host
, RINTSTS
, ALL_INT_CLR
);
121 static int dw_mci_hi3798cv200_init(struct dw_mci
*host
)
123 struct hi3798cv200_priv
*priv
;
126 priv
= devm_kzalloc(host
->dev
, sizeof(*priv
), GFP_KERNEL
);
130 priv
->sample_clk
= devm_clk_get(host
->dev
, "ciu-sample");
131 if (IS_ERR(priv
->sample_clk
)) {
132 dev_err(host
->dev
, "failed to get ciu-sample clock\n");
133 return PTR_ERR(priv
->sample_clk
);
136 priv
->drive_clk
= devm_clk_get(host
->dev
, "ciu-drive");
137 if (IS_ERR(priv
->drive_clk
)) {
138 dev_err(host
->dev
, "failed to get ciu-drive clock\n");
139 return PTR_ERR(priv
->drive_clk
);
142 ret
= clk_prepare_enable(priv
->sample_clk
);
144 dev_err(host
->dev
, "failed to enable ciu-sample clock\n");
148 ret
= clk_prepare_enable(priv
->drive_clk
);
150 dev_err(host
->dev
, "failed to enable ciu-drive clock\n");
151 goto disable_sample_clk
;
158 clk_disable_unprepare(priv
->sample_clk
);
162 static const struct dw_mci_drv_data hi3798cv200_data
= {
163 .init
= dw_mci_hi3798cv200_init
,
164 .set_ios
= dw_mci_hi3798cv200_set_ios
,
165 .execute_tuning
= dw_mci_hi3798cv200_execute_tuning
,
168 static int dw_mci_hi3798cv200_probe(struct platform_device
*pdev
)
170 return dw_mci_pltfm_register(pdev
, &hi3798cv200_data
);
173 static int dw_mci_hi3798cv200_remove(struct platform_device
*pdev
)
175 struct dw_mci
*host
= platform_get_drvdata(pdev
);
176 struct hi3798cv200_priv
*priv
= host
->priv
;
178 clk_disable_unprepare(priv
->drive_clk
);
179 clk_disable_unprepare(priv
->sample_clk
);
181 return dw_mci_pltfm_remove(pdev
);
184 static const struct of_device_id dw_mci_hi3798cv200_match
[] = {
185 { .compatible
= "hisilicon,hi3798cv200-dw-mshc", },
189 MODULE_DEVICE_TABLE(of
, dw_mci_hi3798cv200_match
);
190 static struct platform_driver dw_mci_hi3798cv200_driver
= {
191 .probe
= dw_mci_hi3798cv200_probe
,
192 .remove
= dw_mci_hi3798cv200_remove
,
194 .name
= "dwmmc_hi3798cv200",
195 .of_match_table
= dw_mci_hi3798cv200_match
,
198 module_platform_driver(dw_mci_hi3798cv200_driver
);
200 MODULE_DESCRIPTION("HiSilicon Hi3798CV200 Specific DW-MSHC Driver Extension");
201 MODULE_LICENSE("GPL v2");
202 MODULE_ALIAS("platform:dwmmc_hi3798cv200");