1*e5502d15SYixun Lan // SPDX-License-Identifier: GPL-2.0
2*e5502d15SYixun Lan /*
3*e5502d15SYixun Lan * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
4*e5502d15SYixun Lan * Copyright (c) 2025 Yixun Lan <dlan@gentoo.org>
5*e5502d15SYixun Lan */
6*e5502d15SYixun Lan
7*e5502d15SYixun Lan #include <linux/bitfield.h>
8*e5502d15SYixun Lan #include <linux/clk.h>
9*e5502d15SYixun Lan #include <linux/delay.h>
10*e5502d15SYixun Lan #include <linux/iopoll.h>
11*e5502d15SYixun Lan #include <linux/init.h>
12*e5502d15SYixun Lan #include <linux/mmc/card.h>
13*e5502d15SYixun Lan #include <linux/mmc/host.h>
14*e5502d15SYixun Lan #include <linux/mmc/mmc.h>
15*e5502d15SYixun Lan #include <linux/module.h>
16*e5502d15SYixun Lan #include <linux/of.h>
17*e5502d15SYixun Lan #include <linux/of_device.h>
18*e5502d15SYixun Lan #include <linux/platform_device.h>
19*e5502d15SYixun Lan
20*e5502d15SYixun Lan #include "sdhci.h"
21*e5502d15SYixun Lan #include "sdhci-pltfm.h"
22*e5502d15SYixun Lan
23*e5502d15SYixun Lan #define SDHC_MMC_CTRL_REG 0x114
24*e5502d15SYixun Lan #define MISC_INT_EN BIT(1)
25*e5502d15SYixun Lan #define MISC_INT BIT(2)
26*e5502d15SYixun Lan #define ENHANCE_STROBE_EN BIT(8)
27*e5502d15SYixun Lan #define MMC_HS400 BIT(9)
28*e5502d15SYixun Lan #define MMC_HS200 BIT(10)
29*e5502d15SYixun Lan #define MMC_CARD_MODE BIT(12)
30*e5502d15SYixun Lan
31*e5502d15SYixun Lan #define SDHC_TX_CFG_REG 0x11C
32*e5502d15SYixun Lan #define TX_INT_CLK_SEL BIT(30)
33*e5502d15SYixun Lan #define TX_MUX_SEL BIT(31)
34*e5502d15SYixun Lan
35*e5502d15SYixun Lan #define SDHC_PHY_CTRL_REG 0x160
36*e5502d15SYixun Lan #define PHY_FUNC_EN BIT(0)
37*e5502d15SYixun Lan #define PHY_PLL_LOCK BIT(1)
38*e5502d15SYixun Lan #define HOST_LEGACY_MODE BIT(31)
39*e5502d15SYixun Lan
40*e5502d15SYixun Lan #define SDHC_PHY_FUNC_REG 0x164
41*e5502d15SYixun Lan #define PHY_TEST_EN BIT(7)
42*e5502d15SYixun Lan #define HS200_USE_RFIFO BIT(15)
43*e5502d15SYixun Lan
44*e5502d15SYixun Lan #define SDHC_PHY_DLLCFG 0x168
45*e5502d15SYixun Lan #define DLL_PREDLY_NUM GENMASK(3, 2)
46*e5502d15SYixun Lan #define DLL_FULLDLY_RANGE GENMASK(5, 4)
47*e5502d15SYixun Lan #define DLL_VREG_CTRL GENMASK(7, 6)
48*e5502d15SYixun Lan #define DLL_ENABLE BIT(31)
49*e5502d15SYixun Lan
50*e5502d15SYixun Lan #define SDHC_PHY_DLLCFG1 0x16C
51*e5502d15SYixun Lan #define DLL_REG1_CTRL GENMASK(7, 0)
52*e5502d15SYixun Lan #define DLL_REG2_CTRL GENMASK(15, 8)
53*e5502d15SYixun Lan #define DLL_REG3_CTRL GENMASK(23, 16)
54*e5502d15SYixun Lan #define DLL_REG4_CTRL GENMASK(31, 24)
55*e5502d15SYixun Lan
56*e5502d15SYixun Lan #define SDHC_PHY_DLLSTS 0x170
57*e5502d15SYixun Lan #define DLL_LOCK_STATE BIT(0)
58*e5502d15SYixun Lan
59*e5502d15SYixun Lan #define SDHC_PHY_PADCFG_REG 0x178
60*e5502d15SYixun Lan #define PHY_DRIVE_SEL GENMASK(2, 0)
61*e5502d15SYixun Lan #define RX_BIAS_CTRL BIT(5)
62*e5502d15SYixun Lan
63*e5502d15SYixun Lan struct spacemit_sdhci_host {
64*e5502d15SYixun Lan struct clk *clk_core;
65*e5502d15SYixun Lan struct clk *clk_io;
66*e5502d15SYixun Lan };
67*e5502d15SYixun Lan
68*e5502d15SYixun Lan /* All helper functions will update clr/set while preserve rest bits */
spacemit_sdhci_setbits(struct sdhci_host * host,u32 val,int reg)69*e5502d15SYixun Lan static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg)
70*e5502d15SYixun Lan {
71*e5502d15SYixun Lan sdhci_writel(host, sdhci_readl(host, reg) | val, reg);
72*e5502d15SYixun Lan }
73*e5502d15SYixun Lan
spacemit_sdhci_clrbits(struct sdhci_host * host,u32 val,int reg)74*e5502d15SYixun Lan static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg)
75*e5502d15SYixun Lan {
76*e5502d15SYixun Lan sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg);
77*e5502d15SYixun Lan }
78*e5502d15SYixun Lan
spacemit_sdhci_clrsetbits(struct sdhci_host * host,u32 clr,u32 set,int reg)79*e5502d15SYixun Lan static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg)
80*e5502d15SYixun Lan {
81*e5502d15SYixun Lan u32 val = sdhci_readl(host, reg);
82*e5502d15SYixun Lan
83*e5502d15SYixun Lan val = (val & ~clr) | set;
84*e5502d15SYixun Lan sdhci_writel(host, val, reg);
85*e5502d15SYixun Lan }
86*e5502d15SYixun Lan
spacemit_sdhci_reset(struct sdhci_host * host,u8 mask)87*e5502d15SYixun Lan static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
88*e5502d15SYixun Lan {
89*e5502d15SYixun Lan sdhci_reset(host, mask);
90*e5502d15SYixun Lan
91*e5502d15SYixun Lan if (mask != SDHCI_RESET_ALL)
92*e5502d15SYixun Lan return;
93*e5502d15SYixun Lan
94*e5502d15SYixun Lan spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
95*e5502d15SYixun Lan
96*e5502d15SYixun Lan spacemit_sdhci_clrsetbits(host, PHY_DRIVE_SEL,
97*e5502d15SYixun Lan RX_BIAS_CTRL | FIELD_PREP(PHY_DRIVE_SEL, 4),
98*e5502d15SYixun Lan SDHC_PHY_PADCFG_REG);
99*e5502d15SYixun Lan
100*e5502d15SYixun Lan if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC))
101*e5502d15SYixun Lan spacemit_sdhci_setbits(host, MMC_CARD_MODE, SDHC_MMC_CTRL_REG);
102*e5502d15SYixun Lan }
103*e5502d15SYixun Lan
spacemit_sdhci_set_uhs_signaling(struct sdhci_host * host,unsigned int timing)104*e5502d15SYixun Lan static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
105*e5502d15SYixun Lan {
106*e5502d15SYixun Lan if (timing == MMC_TIMING_MMC_HS200)
107*e5502d15SYixun Lan spacemit_sdhci_setbits(host, MMC_HS200, SDHC_MMC_CTRL_REG);
108*e5502d15SYixun Lan
109*e5502d15SYixun Lan if (timing == MMC_TIMING_MMC_HS400)
110*e5502d15SYixun Lan spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
111*e5502d15SYixun Lan
112*e5502d15SYixun Lan sdhci_set_uhs_signaling(host, timing);
113*e5502d15SYixun Lan
114*e5502d15SYixun Lan if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO))
115*e5502d15SYixun Lan spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2);
116*e5502d15SYixun Lan }
117*e5502d15SYixun Lan
spacemit_sdhci_set_clock(struct sdhci_host * host,unsigned int clock)118*e5502d15SYixun Lan static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
119*e5502d15SYixun Lan {
120*e5502d15SYixun Lan struct mmc_host *mmc = host->mmc;
121*e5502d15SYixun Lan
122*e5502d15SYixun Lan if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50)
123*e5502d15SYixun Lan spacemit_sdhci_setbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
124*e5502d15SYixun Lan else
125*e5502d15SYixun Lan spacemit_sdhci_clrbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
126*e5502d15SYixun Lan
127*e5502d15SYixun Lan sdhci_set_clock(host, clock);
128*e5502d15SYixun Lan };
129*e5502d15SYixun Lan
spacemit_sdhci_phy_dll_init(struct sdhci_host * host)130*e5502d15SYixun Lan static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host)
131*e5502d15SYixun Lan {
132*e5502d15SYixun Lan u32 state;
133*e5502d15SYixun Lan int ret;
134*e5502d15SYixun Lan
135*e5502d15SYixun Lan spacemit_sdhci_clrsetbits(host, DLL_PREDLY_NUM | DLL_FULLDLY_RANGE | DLL_VREG_CTRL,
136*e5502d15SYixun Lan FIELD_PREP(DLL_PREDLY_NUM, 1) |
137*e5502d15SYixun Lan FIELD_PREP(DLL_FULLDLY_RANGE, 1) |
138*e5502d15SYixun Lan FIELD_PREP(DLL_VREG_CTRL, 1),
139*e5502d15SYixun Lan SDHC_PHY_DLLCFG);
140*e5502d15SYixun Lan
141*e5502d15SYixun Lan spacemit_sdhci_clrsetbits(host, DLL_REG1_CTRL,
142*e5502d15SYixun Lan FIELD_PREP(DLL_REG1_CTRL, 0x92),
143*e5502d15SYixun Lan SDHC_PHY_DLLCFG1);
144*e5502d15SYixun Lan
145*e5502d15SYixun Lan spacemit_sdhci_setbits(host, DLL_ENABLE, SDHC_PHY_DLLCFG);
146*e5502d15SYixun Lan
147*e5502d15SYixun Lan ret = readl_poll_timeout(host->ioaddr + SDHC_PHY_DLLSTS, state,
148*e5502d15SYixun Lan state & DLL_LOCK_STATE, 2, 100);
149*e5502d15SYixun Lan if (ret == -ETIMEDOUT)
150*e5502d15SYixun Lan dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n");
151*e5502d15SYixun Lan }
152*e5502d15SYixun Lan
spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host * mmc,struct mmc_ios * ios)153*e5502d15SYixun Lan static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
154*e5502d15SYixun Lan {
155*e5502d15SYixun Lan struct sdhci_host *host = mmc_priv(mmc);
156*e5502d15SYixun Lan
157*e5502d15SYixun Lan if (!ios->enhanced_strobe) {
158*e5502d15SYixun Lan spacemit_sdhci_clrbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
159*e5502d15SYixun Lan return;
160*e5502d15SYixun Lan }
161*e5502d15SYixun Lan
162*e5502d15SYixun Lan spacemit_sdhci_setbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
163*e5502d15SYixun Lan spacemit_sdhci_phy_dll_init(host);
164*e5502d15SYixun Lan }
165*e5502d15SYixun Lan
spacemit_sdhci_clk_get_max_clock(struct sdhci_host * host)166*e5502d15SYixun Lan static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host)
167*e5502d15SYixun Lan {
168*e5502d15SYixun Lan struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
169*e5502d15SYixun Lan
170*e5502d15SYixun Lan return clk_get_rate(pltfm_host->clk);
171*e5502d15SYixun Lan }
172*e5502d15SYixun Lan
spacemit_sdhci_pre_select_hs400(struct mmc_host * mmc)173*e5502d15SYixun Lan static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc)
174*e5502d15SYixun Lan {
175*e5502d15SYixun Lan struct sdhci_host *host = mmc_priv(mmc);
176*e5502d15SYixun Lan
177*e5502d15SYixun Lan spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
178*e5502d15SYixun Lan host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
179*e5502d15SYixun Lan
180*e5502d15SYixun Lan return 0;
181*e5502d15SYixun Lan }
182*e5502d15SYixun Lan
spacemit_sdhci_post_select_hs400(struct mmc_host * mmc)183*e5502d15SYixun Lan static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc)
184*e5502d15SYixun Lan {
185*e5502d15SYixun Lan struct sdhci_host *host = mmc_priv(mmc);
186*e5502d15SYixun Lan
187*e5502d15SYixun Lan spacemit_sdhci_phy_dll_init(host);
188*e5502d15SYixun Lan host->mmc->caps &= ~MMC_CAP_WAIT_WHILE_BUSY;
189*e5502d15SYixun Lan }
190*e5502d15SYixun Lan
spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host * mmc)191*e5502d15SYixun Lan static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc)
192*e5502d15SYixun Lan {
193*e5502d15SYixun Lan struct sdhci_host *host = mmc_priv(mmc);
194*e5502d15SYixun Lan
195*e5502d15SYixun Lan spacemit_sdhci_clrbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
196*e5502d15SYixun Lan spacemit_sdhci_clrbits(host, MMC_HS400 | MMC_HS200 | ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
197*e5502d15SYixun Lan spacemit_sdhci_clrbits(host, HS200_USE_RFIFO, SDHC_PHY_FUNC_REG);
198*e5502d15SYixun Lan
199*e5502d15SYixun Lan udelay(5);
200*e5502d15SYixun Lan
201*e5502d15SYixun Lan spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
202*e5502d15SYixun Lan }
203*e5502d15SYixun Lan
spacemit_sdhci_get_clocks(struct device * dev,struct sdhci_pltfm_host * pltfm_host)204*e5502d15SYixun Lan static inline int spacemit_sdhci_get_clocks(struct device *dev,
205*e5502d15SYixun Lan struct sdhci_pltfm_host *pltfm_host)
206*e5502d15SYixun Lan {
207*e5502d15SYixun Lan struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
208*e5502d15SYixun Lan
209*e5502d15SYixun Lan sdhst->clk_core = devm_clk_get_enabled(dev, "core");
210*e5502d15SYixun Lan if (IS_ERR(sdhst->clk_core))
211*e5502d15SYixun Lan return -EINVAL;
212*e5502d15SYixun Lan
213*e5502d15SYixun Lan sdhst->clk_io = devm_clk_get_enabled(dev, "io");
214*e5502d15SYixun Lan if (IS_ERR(sdhst->clk_io))
215*e5502d15SYixun Lan return -EINVAL;
216*e5502d15SYixun Lan
217*e5502d15SYixun Lan pltfm_host->clk = sdhst->clk_io;
218*e5502d15SYixun Lan
219*e5502d15SYixun Lan return 0;
220*e5502d15SYixun Lan }
221*e5502d15SYixun Lan
222*e5502d15SYixun Lan static const struct sdhci_ops spacemit_sdhci_ops = {
223*e5502d15SYixun Lan .get_max_clock = spacemit_sdhci_clk_get_max_clock,
224*e5502d15SYixun Lan .reset = spacemit_sdhci_reset,
225*e5502d15SYixun Lan .set_bus_width = sdhci_set_bus_width,
226*e5502d15SYixun Lan .set_clock = spacemit_sdhci_set_clock,
227*e5502d15SYixun Lan .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling,
228*e5502d15SYixun Lan };
229*e5502d15SYixun Lan
230*e5502d15SYixun Lan static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
231*e5502d15SYixun Lan .ops = &spacemit_sdhci_ops,
232*e5502d15SYixun Lan .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
233*e5502d15SYixun Lan SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
234*e5502d15SYixun Lan SDHCI_QUIRK_32BIT_ADMA_SIZE |
235*e5502d15SYixun Lan SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
236*e5502d15SYixun Lan SDHCI_QUIRK_BROKEN_CARD_DETECTION |
237*e5502d15SYixun Lan SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
238*e5502d15SYixun Lan .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
239*e5502d15SYixun Lan SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
240*e5502d15SYixun Lan };
241*e5502d15SYixun Lan
242*e5502d15SYixun Lan static const struct of_device_id spacemit_sdhci_of_match[] = {
243*e5502d15SYixun Lan { .compatible = "spacemit,k1-sdhci" },
244*e5502d15SYixun Lan { /* sentinel */ }
245*e5502d15SYixun Lan };
246*e5502d15SYixun Lan MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
247*e5502d15SYixun Lan
spacemit_sdhci_probe(struct platform_device * pdev)248*e5502d15SYixun Lan static int spacemit_sdhci_probe(struct platform_device *pdev)
249*e5502d15SYixun Lan {
250*e5502d15SYixun Lan struct device *dev = &pdev->dev;
251*e5502d15SYixun Lan struct spacemit_sdhci_host *sdhst;
252*e5502d15SYixun Lan struct sdhci_pltfm_host *pltfm_host;
253*e5502d15SYixun Lan struct sdhci_host *host;
254*e5502d15SYixun Lan struct mmc_host_ops *mops;
255*e5502d15SYixun Lan int ret;
256*e5502d15SYixun Lan
257*e5502d15SYixun Lan host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
258*e5502d15SYixun Lan if (IS_ERR(host))
259*e5502d15SYixun Lan return PTR_ERR(host);
260*e5502d15SYixun Lan
261*e5502d15SYixun Lan pltfm_host = sdhci_priv(host);
262*e5502d15SYixun Lan
263*e5502d15SYixun Lan ret = mmc_of_parse(host->mmc);
264*e5502d15SYixun Lan if (ret)
265*e5502d15SYixun Lan goto err_pltfm;
266*e5502d15SYixun Lan
267*e5502d15SYixun Lan sdhci_get_of_property(pdev);
268*e5502d15SYixun Lan
269*e5502d15SYixun Lan if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) {
270*e5502d15SYixun Lan mops = &host->mmc_host_ops;
271*e5502d15SYixun Lan mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400;
272*e5502d15SYixun Lan mops->hs400_complete = spacemit_sdhci_post_select_hs400;
273*e5502d15SYixun Lan mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200;
274*e5502d15SYixun Lan mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe;
275*e5502d15SYixun Lan }
276*e5502d15SYixun Lan
277*e5502d15SYixun Lan host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
278*e5502d15SYixun Lan
279*e5502d15SYixun Lan if (spacemit_sdhci_get_clocks(dev, pltfm_host))
280*e5502d15SYixun Lan goto err_pltfm;
281*e5502d15SYixun Lan
282*e5502d15SYixun Lan ret = sdhci_add_host(host);
283*e5502d15SYixun Lan if (ret)
284*e5502d15SYixun Lan goto err_pltfm;
285*e5502d15SYixun Lan
286*e5502d15SYixun Lan return 0;
287*e5502d15SYixun Lan
288*e5502d15SYixun Lan err_pltfm:
289*e5502d15SYixun Lan sdhci_pltfm_free(pdev);
290*e5502d15SYixun Lan return ret;
291*e5502d15SYixun Lan }
292*e5502d15SYixun Lan
293*e5502d15SYixun Lan static struct platform_driver spacemit_sdhci_driver = {
294*e5502d15SYixun Lan .driver = {
295*e5502d15SYixun Lan .name = "sdhci-spacemit",
296*e5502d15SYixun Lan .of_match_table = spacemit_sdhci_of_match,
297*e5502d15SYixun Lan },
298*e5502d15SYixun Lan .probe = spacemit_sdhci_probe,
299*e5502d15SYixun Lan .remove = sdhci_pltfm_remove,
300*e5502d15SYixun Lan };
301*e5502d15SYixun Lan module_platform_driver(spacemit_sdhci_driver);
302*e5502d15SYixun Lan
303*e5502d15SYixun Lan MODULE_DESCRIPTION("SpacemiT SDHCI platform driver");
304*e5502d15SYixun Lan MODULE_LICENSE("GPL");
305