Skip to content

Commit 25d0438

Browse files
185264646storulf
authored andcommitted
mmc: dw_mmc: add support for hi3798mv200
Add support for Hi3798MV200 specific extension. Signed-off-by: Yang Xiwen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ulf Hansson <[email protected]>
1 parent cddacdc commit 25d0438

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

drivers/mmc/host/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,15 @@ config MMC_DW_HI3798CV200
798798
Synopsys DesignWare Memory Card Interface driver. Select this option
799799
for platforms based on HiSilicon Hi3798CV200 SoC.
800800

801+
config MMC_DW_HI3798MV200
802+
tristate "Hi3798MV200 specific extensions for Synopsys DW Memory Card Interface"
803+
depends on MMC_DW
804+
select MMC_DW_PLTFM
805+
help
806+
This selects support for HiSilicon Hi3798MV200 SoC specific extensions to the
807+
Synopsys DesignWare Memory Card Interface driver. Select this option
808+
for platforms based on HiSilicon Hi3798MV200 SoC.
809+
801810
config MMC_DW_K3
802811
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
803812
depends on MMC_DW

drivers/mmc/host/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
5151
obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o
5252
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
5353
obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
54+
obj-$(CONFIG_MMC_DW_HI3798MV200) += dw_mmc-hi3798mv200.o
5455
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
5556
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
5657
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o

drivers/mmc/host/dw_mmc-hi3798mv200.c

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Modified from dw_mmc-hi3798cv200.c
4+
*
5+
* Copyright (c) 2024 Yang Xiwen <[email protected]>
6+
* Copyright (c) 2018 HiSilicon Technologies Co., Ltd.
7+
*/
8+
9+
#include <linux/clk.h>
10+
#include <linux/mfd/syscon.h>
11+
#include <linux/mmc/host.h>
12+
#include <linux/module.h>
13+
#include <linux/of_address.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/regmap.h>
16+
17+
#include "dw_mmc.h"
18+
#include "dw_mmc-pltfm.h"
19+
20+
#define SDMMC_TUNING_CTRL 0x118
21+
#define SDMMC_TUNING_FIND_EDGE BIT(5)
22+
23+
#define ALL_INT_CLR 0x1ffff
24+
25+
/* DLL ctrl reg */
26+
#define SAP_DLL_CTRL_DLLMODE BIT(16)
27+
28+
struct dw_mci_hi3798mv200_priv {
29+
struct clk *sample_clk;
30+
struct clk *drive_clk;
31+
struct regmap *crg_reg;
32+
u32 sap_dll_offset;
33+
struct mmc_clk_phase_map phase_map;
34+
};
35+
36+
static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios)
37+
{
38+
struct dw_mci_hi3798mv200_priv *priv = host->priv;
39+
struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing];
40+
u32 val;
41+
42+
val = mci_readl(host, ENABLE_SHIFT);
43+
if (ios->timing == MMC_TIMING_MMC_DDR52
44+
|| ios->timing == MMC_TIMING_UHS_DDR50)
45+
val |= SDMMC_ENABLE_PHASE;
46+
else
47+
val &= ~SDMMC_ENABLE_PHASE;
48+
mci_writel(host, ENABLE_SHIFT, val);
49+
50+
val = mci_readl(host, DDR_REG);
51+
if (ios->timing == MMC_TIMING_MMC_HS400)
52+
val |= SDMMC_DDR_HS400;
53+
else
54+
val &= ~SDMMC_DDR_HS400;
55+
mci_writel(host, DDR_REG, val);
56+
57+
if (clk_set_rate(host->ciu_clk, ios->clock))
58+
dev_warn(host->dev, "Failed to set rate to %u\n", ios->clock);
59+
else
60+
/*
61+
* CLK_MUX_ROUND_NEAREST is enabled for this clock
62+
* The actual clock rate is not what we set, but a rounded value
63+
* so we should get the rate once again
64+
*/
65+
host->bus_hz = clk_get_rate(host->ciu_clk);
66+
67+
if (phase.valid) {
68+
clk_set_phase(priv->drive_clk, phase.out_deg);
69+
clk_set_phase(priv->sample_clk, phase.in_deg);
70+
} else {
71+
dev_warn(host->dev,
72+
"The phase entry for timing mode %d is missing in device tree.\n",
73+
ios->timing);
74+
}
75+
}
76+
77+
static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot)
78+
{
79+
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
80+
81+
return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
82+
}
83+
84+
static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot)
85+
{
86+
struct dw_mci_hi3798mv200_priv *priv = slot->host->priv;
87+
88+
return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE);
89+
}
90+
91+
static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot,
92+
u32 opcode)
93+
{
94+
static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 };
95+
struct dw_mci *host = slot->host;
96+
struct dw_mci_hi3798mv200_priv *priv = host->priv;
97+
int raise_point = -1, fall_point = -1, mid;
98+
int err, prev_err = -1;
99+
int found = 0;
100+
int regval;
101+
int i;
102+
int ret;
103+
104+
ret = dw_mci_hi3798mv200_enable_tuning(slot);
105+
if (ret < 0)
106+
return ret;
107+
108+
for (i = 0; i < ARRAY_SIZE(degrees); i++) {
109+
clk_set_phase(priv->sample_clk, degrees[i]);
110+
mci_writel(host, RINTSTS, ALL_INT_CLR);
111+
112+
/*
113+
* HiSilicon implemented a tuning mechanism.
114+
* It needs special interaction with the DLL.
115+
*
116+
* Treat edge(flip) found as an error too.
117+
*/
118+
err = mmc_send_tuning(slot->mmc, opcode, NULL);
119+
regval = mci_readl(host, TUNING_CTRL);
120+
if (err || (regval & SDMMC_TUNING_FIND_EDGE))
121+
err = 1;
122+
else
123+
found = 1;
124+
125+
if (i > 0) {
126+
if (err && !prev_err)
127+
fall_point = i - 1;
128+
if (!err && prev_err)
129+
raise_point = i;
130+
}
131+
132+
if (raise_point != -1 && fall_point != -1)
133+
goto tuning_out;
134+
135+
prev_err = err;
136+
err = 0;
137+
}
138+
139+
tuning_out:
140+
ret = dw_mci_hi3798mv200_disable_tuning(slot);
141+
if (ret < 0)
142+
return ret;
143+
144+
if (found) {
145+
if (raise_point == -1)
146+
raise_point = 0;
147+
if (fall_point == -1)
148+
fall_point = ARRAY_SIZE(degrees) - 1;
149+
if (fall_point < raise_point) {
150+
if ((raise_point + fall_point) >
151+
(ARRAY_SIZE(degrees) - 1))
152+
mid = fall_point / 2;
153+
else
154+
mid = (raise_point + ARRAY_SIZE(degrees) - 1) / 2;
155+
} else {
156+
mid = (raise_point + fall_point) / 2;
157+
}
158+
159+
/*
160+
* We don't care what timing we are tuning for,
161+
* simply use the same phase for all timing needs tuning.
162+
*/
163+
priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid];
164+
priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid];
165+
priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid];
166+
167+
clk_set_phase(priv->sample_clk, degrees[mid]);
168+
dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n",
169+
raise_point, fall_point, degrees[mid]);
170+
ret = 0;
171+
} else {
172+
dev_err(host->dev, "No valid clk_sample shift!\n");
173+
ret = -EINVAL;
174+
}
175+
176+
mci_writel(host, RINTSTS, ALL_INT_CLR);
177+
178+
return ret;
179+
}
180+
181+
static int dw_mci_hi3798mv200_init(struct dw_mci *host)
182+
{
183+
struct dw_mci_hi3798mv200_priv *priv;
184+
struct device_node *np = host->dev->of_node;
185+
int ret;
186+
187+
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
188+
if (!priv)
189+
return -ENOMEM;
190+
191+
mmc_of_parse_clk_phase(host->dev, &priv->phase_map);
192+
193+
priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample");
194+
if (IS_ERR(priv->sample_clk))
195+
return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk),
196+
"failed to get enabled ciu-sample clock\n");
197+
198+
priv->drive_clk = devm_clk_get_enabled(host->dev, "ciu-drive");
199+
if (IS_ERR(priv->drive_clk))
200+
return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk),
201+
"failed to get enabled ciu-drive clock\n");
202+
203+
priv->crg_reg = syscon_regmap_lookup_by_phandle(np, "hisilicon,sap-dll-reg");
204+
if (IS_ERR(priv->crg_reg))
205+
return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg),
206+
"failed to get CRG reg\n");
207+
208+
ret = of_property_read_u32_index(np, "hisilicon,sap-dll-reg", 1, &priv->sap_dll_offset);
209+
if (ret)
210+
return dev_err_probe(host->dev, ret, "failed to get sample DLL register offset\n");
211+
212+
host->priv = priv;
213+
return 0;
214+
}
215+
216+
static const struct dw_mci_drv_data hi3798mv200_data = {
217+
.common_caps = MMC_CAP_CMD23,
218+
.init = dw_mci_hi3798mv200_init,
219+
.set_ios = dw_mci_hi3798mv200_set_ios,
220+
.execute_tuning = dw_mci_hi3798mv200_execute_tuning_mix_mode,
221+
};
222+
223+
static const struct of_device_id dw_mci_hi3798mv200_match[] = {
224+
{ .compatible = "hisilicon,hi3798mv200-dw-mshc" },
225+
{},
226+
};
227+
228+
static int dw_mci_hi3798mv200_probe(struct platform_device *pdev)
229+
{
230+
return dw_mci_pltfm_register(pdev, &hi3798mv200_data);
231+
}
232+
233+
static void dw_mci_hi3798mv200_remove(struct platform_device *pdev)
234+
{
235+
dw_mci_pltfm_remove(pdev);
236+
}
237+
238+
MODULE_DEVICE_TABLE(of, dw_mci_hi3798mv200_match);
239+
static struct platform_driver dw_mci_hi3798mv200_driver = {
240+
.probe = dw_mci_hi3798mv200_probe,
241+
.remove_new = dw_mci_hi3798mv200_remove,
242+
.driver = {
243+
.name = "dwmmc_hi3798mv200",
244+
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
245+
.of_match_table = dw_mci_hi3798mv200_match,
246+
},
247+
};
248+
module_platform_driver(dw_mci_hi3798mv200_driver);
249+
250+
MODULE_DESCRIPTION("HiSilicon Hi3798MV200 Specific DW-MSHC Driver Extension");
251+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)